Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 254ee07942 | |||
| 0bf8bd2230 | |||
| b5225f07cb | |||
| abed7690d0 | |||
| f96174ddbe | |||
| e5b713841a | |||
| 3b1b12069f | |||
| 1635f7351d | |||
| ce5ae411c1 | |||
| d1bc32fb31 | |||
| f9f87f25d4 | |||
| 89aa6b7881 | |||
| edf9e0c1ed | |||
| c2e520ad9d | |||
| 7649187095 | |||
| fe39d0fd10 | |||
| 98b83b7208 | |||
| 57321979ae | |||
| e3ce0c5887 | |||
| 95af72c70c | |||
| 3b37769624 | |||
| d150df1c5c | |||
| a322e27e05 | |||
| 98fe0badbe | |||
| 44b841a25d | |||
| 9c8bf820de | |||
| 4d9aa65e78 | |||
| 0f3942558f | |||
| ba074b0116 | |||
| 6aa97048df | |||
| d9a5c79ea6 | |||
| 9d7714c714 | |||
| 8291be375a | |||
| 0a73737f69 | |||
| 2fd1b6c913 | |||
| ea38a1d880 | |||
| f576e13c51 | |||
| ee7bb8731a | |||
| da0c3ff394 | |||
| 6ae559b42b | |||
| 95ca6ebdaa | |||
| 0d6964d9c8 | |||
| ba386d4b2c | |||
| b5ffb51e18 | |||
| a9f674497a | |||
| 1e8a92ccb4 | |||
| b9178bb8c7 | |||
| 520b29d0d5 | |||
| fe397e6953 | |||
| dcb741d827 | |||
| 508c205d35 | |||
| d5294bb5d0 | |||
| 6ed9be31ca | |||
| 3bbd3a8a65 | |||
| 73a608895a | |||
| 96c73baeb3 | |||
| 73113f5eb8 | |||
| 7b74b86891 | |||
| 0903b8227f | |||
| fa525ad610 | |||
| 0f2bb5dde5 | |||
| 84dc1b6845 | |||
| 4ce1a37772 | |||
| 8dd4cc5c40 | |||
| 6aed145dae | |||
| d15cc77b0b | |||
| 24a75d37fb | |||
| 7e80dae59b |
+3
-2
@@ -37,7 +37,7 @@ matrix:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache']
|
||||
env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug'
|
||||
env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug' COVERAGE=ON
|
||||
|
||||
- os: linux
|
||||
compiler: "gcc-4.8-debug"
|
||||
@@ -136,6 +136,7 @@ install:
|
||||
- export CC=${CCOMPILER} CXX=${CXXCOMPILER}
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DCOVERAGE=${COVERAGE:-OFF} -DBUILD_TOOLS=1 -DENABLE_CCACHE=ON
|
||||
- echo "travis_fold:start:MAKE"
|
||||
- make osrm-extract --jobs=3
|
||||
- make --jobs=${JOBS}
|
||||
- make tests --jobs=${JOBS}
|
||||
- make benchmarks --jobs=${JOBS}
|
||||
@@ -174,5 +175,5 @@ after_success:
|
||||
fi
|
||||
- |
|
||||
if [ -n "${COVERAGE}" ]; then
|
||||
coveralls --build-root build --exclude unit_tests --exclude third_party --exclude node_modules --gcov-options '\-lp'
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
fi
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
# 5.2.0 RC1
|
||||
Changes from 5.1.0
|
||||
|
||||
- API:
|
||||
- new parameter `annotate` for `route` and `match` requests. Returns additional data about each
|
||||
coordinate along the selected/matched route line.
|
||||
- Introducing Intersections for Route Steps. This changes the API format in multiple ways.
|
||||
- `bearing_before`/`bearing_after` of `StepManeuver` are now deprecated and will be removed in the next major release
|
||||
- `location` of `StepManeuvers` is now deprecated and will be removed in the next major release
|
||||
- every `RouteStep` now has property `intersections` containing a list of `Intersection` objects.
|
||||
|
||||
- Profile changes:
|
||||
- duration parser now accepts P[n]DT[n]H[n]M[n]S, P[n]W, PTHHMMSS and PTHH:MM:SS ISO8601 formats.
|
||||
|
||||
- Infrastructure:
|
||||
- Better support for osrm-routed binary upgrade on the fly [UNIX specific]:
|
||||
- Open sockets with SO_REUSEPORT to allow multiple osrm-routed processes serving requests from the same port.
|
||||
- Add SIGNAL_PARENT_WHEN_READY environment variable to enable osrm-routed signal its parent with USR1 when it's running and waiting for requests.
|
||||
- BREAKING: Intersection Classification adds a new file to the mix (osrm.icd). This breaks the fileformat for older versions.
|
||||
- Disable http access logging via DISABLE_ACCESS_LOGGING environment
|
||||
variable.
|
||||
|
||||
- Guidance:
|
||||
- improved detection of turning streets, not reporting new-name in wrong situations
|
||||
|
||||
# 5.1.0
|
||||
Changes with regard to 5.0.0
|
||||
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@ endif()
|
||||
|
||||
project(OSRM C CXX)
|
||||
set(OSRM_VERSION_MAJOR 5)
|
||||
set(OSRM_VERSION_MINOR 1)
|
||||
set(OSRM_VERSION_MINOR 2)
|
||||
set(OSRM_VERSION_PATCH 0)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
@@ -32,7 +32,7 @@ endif()
|
||||
option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON)
|
||||
option(ENABLE_JSON_LOGGING "Adds additional JSON debug logging to the response" OFF)
|
||||
option(BUILD_TOOLS "Build OSRM tools" OFF)
|
||||
option(BUILD_COMPONENTS "Build osrm-components" ON)
|
||||
option(BUILD_COMPONENTS "Build osrm-components" OFF)
|
||||
option(ENABLE_ASSERTIONS OFF)
|
||||
option(COVERAGE OFF)
|
||||
option(SANITIZER OFF)
|
||||
@@ -54,7 +54,7 @@ configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/util/version.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp
|
||||
)
|
||||
file(GLOB UtilGlob src/util/*.cpp)
|
||||
file(GLOB UtilGlob src/util/*.cpp src/util/*/*.cpp)
|
||||
file(GLOB ExtractorGlob src/extractor/*.cpp src/extractor/*/*.cpp)
|
||||
file(GLOB ContractorGlob src/contractor/*.cpp)
|
||||
file(GLOB StorageGlob src/storage/*.cpp)
|
||||
@@ -233,7 +233,7 @@ include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
|
||||
|
||||
|
||||
find_package(Boost 1.49.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32 AND NOT Boost_USE_STATIC_LIBS)
|
||||
add_definitions(-DBOOST_TEST_DYN_LINK)
|
||||
endif()
|
||||
add_definitions(-DBOOST_SPIRIT_USE_PHOENIX_V3)
|
||||
|
||||
@@ -8,7 +8,7 @@ The Open Source Routing Machine is a high performance routing engine written in
|
||||
|:-------------|:-------|
|
||||
| Linux | [](https://travis-ci.org/Project-OSRM/osrm-backend) |
|
||||
| Windows | [](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) |
|
||||
| Coverage | [](https://coveralls.io/github/Project-OSRM/osrm-backend?branch=master) |
|
||||
| Coverage | [](https://codecov.io/gh/Project-OSRM/osrm-backend) |
|
||||
|
||||
## Building
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
coverage:
|
||||
|
||||
ignore:
|
||||
- unit_tests/.*
|
||||
- third_party/.*
|
||||
|
||||
comment: off
|
||||
+9
-1
@@ -5,15 +5,23 @@ RUN apt-get install -y build-essential git-core python-pip python-software-prope
|
||||
|
||||
RUN apt-get -y install gcc-4.8 g++-4.8 libboost1.55-all-dev llvm-3.4
|
||||
RUN apt-get -y install libbz2-dev libstxxl-dev libstxxl1 libxml2-dev
|
||||
RUN apt-get -y install libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev ruby1.9
|
||||
RUN apt-get -y install libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev
|
||||
RUN apt-get -y install curl cmake cmake-curses-gui
|
||||
|
||||
RUN pip install awscli
|
||||
|
||||
|
||||
# luabind
|
||||
RUN curl https://gist.githubusercontent.com/DennisOSRM/f2eb7b948e6fe1ae319e/raw/install-luabind.sh | sudo bash
|
||||
|
||||
WORKDIR /opt
|
||||
RUN git clone --depth 1 --branch v0.31.0 https://github.com/creationix/nvm.git
|
||||
RUN /bin/bash -c "source /opt/nvm/nvm.sh && nvm install v4"
|
||||
|
||||
RUN useradd -ms /bin/bash mapbox
|
||||
USER mapbox
|
||||
ENV HOME /home/mapbox
|
||||
WORKDIR /home/mapbox
|
||||
|
||||
RUN echo "source /opt/nvm/nvm.sh" > .bashrc
|
||||
RUN echo "source /home/mapbox/.bashrc" > .profile
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ docker run \
|
||||
-e "CXX=clang++" \
|
||||
-v `pwd`:/home/mapbox/osrm-backend \
|
||||
-t mapbox/osrm:linux \
|
||||
osrm-backend/docker/test.sh
|
||||
/bin/bash -lc "osrm-backend/docker/test.sh"
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ docker run \
|
||||
-e "CXX=g++" \
|
||||
-v `pwd`:/home/mapbox/osrm-backend \
|
||||
-t mapbox/osrm:linux \
|
||||
osrm-backend/docker/test.sh
|
||||
/bin/bash -lc "osrm-backend/docker/test.sh"
|
||||
|
||||
+5
-6
@@ -4,11 +4,8 @@ set -e
|
||||
set -o pipefail
|
||||
|
||||
export CMAKEOPTIONS="-DCMAKE_BUILD_TYPE=Release"
|
||||
export PATH=$PATH:/home/mapbox/.gem/ruby/1.9.1/bin:/home/mapbox/osrm-backend/vendor/bundle/ruby/1.9.1/bin
|
||||
|
||||
cd /home/mapbox/osrm-backend
|
||||
gem install --user-install bundler
|
||||
bundle install --path vendor/bundle
|
||||
[ -d build ] && rm -rf build
|
||||
mkdir -p build
|
||||
cd build
|
||||
@@ -16,7 +13,9 @@ cmake .. $CMAKEOPTIONS -DBUILD_TOOLS=1
|
||||
|
||||
make -j`nproc`
|
||||
make tests -j`nproc`
|
||||
./datastructure-tests
|
||||
./algorithm-tests
|
||||
#./unit_tests/server-tests
|
||||
#./unit_tests/library-tests
|
||||
#./unit_tests/extractor-tests
|
||||
#./unit_tests/util-tests
|
||||
cd ..
|
||||
bundle exec cucumber -p verify
|
||||
npm test
|
||||
|
||||
+88
-4
@@ -1,3 +1,18 @@
|
||||
## Environent Variables
|
||||
|
||||
### SIGNAL_PARENT_WHEN_READY
|
||||
|
||||
If the SIGNAL_PARENT_WHEN_READY environment variable is set osrm-routed will
|
||||
send the USR1 signal to its parent when it will be running and waiting for
|
||||
requests. This could be used to upgrade osrm-routed to a new binary on the fly
|
||||
without any service downtime - no incoming requests will be lost.
|
||||
|
||||
### DISABLE_ACCESS_LOGGING
|
||||
|
||||
If the DISABLE_ACCESS_LOGGING environment variable is set osrm-routed will
|
||||
**not** log any http requests to standard output. This can be useful in high
|
||||
traffic setup.
|
||||
|
||||
## HTTP API
|
||||
|
||||
`osrm-routed` supports only `GET` requests of the form. If you your response size
|
||||
@@ -145,6 +160,7 @@ In addition to the [general options](#general-options) the following options are
|
||||
|------------|------------------------------------------|-------------------------------------------------------------------------------|
|
||||
|alternatives|`true`, `false` (default) |Search for alternative routes and return as well.\* |
|
||||
|steps |`true`, `false` (default) |Return route steps for each route leg |
|
||||
|annotate |`true`, `false` (default) |Returns additional metadata for each coordinate along the route geometry. |
|
||||
|geometries |`polyline` (default), `geojson` |Returned route geometry format (influences overview and per step) |
|
||||
|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
|
||||
|continue_straight |`default` (default), `true`, `false`|Forces the route to keep going straight at waypoints and don't do a uturn even if it would be faster. Default value depends on the profile. |
|
||||
@@ -247,6 +263,7 @@ In addition to the [general options](#general-options) the following options are
|
||||
|------------|------------------------------------------------|------------------------------------------------------------------------------------------|
|
||||
|steps |`true`, `false` (default) |Return route steps for each route |
|
||||
|geometries |`polyline` (default), `geojson` |Returned route geometry format (influences overview and per step) |
|
||||
|annotate |`true`, `false` (default) |Returns additional metadata for each coordinate along the route geometry. |
|
||||
|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
|
||||
|timestamps |`{timestamp};{timestamp}[;{timestamp} ...]` |Timestamp of the input location. |
|
||||
|radiuses |`{radius};{radius}[;{radius} ...]` |Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy.|
|
||||
@@ -292,6 +309,7 @@ In addition to the [general options](#general-options) the following options are
|
||||
|Option |Values |Description |
|
||||
|------------|------------------------------------------------|---------------------------------------------------------------------------|
|
||||
|steps |`true`, `false` (default) |Return route instructions for each trip |
|
||||
|annotate |`true`, `false` (default) |Returns additional metadata for each coordinate along the route geometry. |
|
||||
|geometries |`polyline` (default), `geojson` |Returned route geometry format (influences overview and per step) |
|
||||
|overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.|
|
||||
|
||||
@@ -377,15 +395,26 @@ Represents a route between two waypoints.
|
||||
| true | array of `RouteStep` objects describing the turn-by-turn instructions |
|
||||
| false | empty array |
|
||||
|
||||
- `annotation`: Additional details about each coordinate along the route geometry:
|
||||
|
||||
| annotate | |
|
||||
|--------------|-----------------------------------------------------------------------|
|
||||
| true | returns distance and durations of each coordinate along the route |
|
||||
| false | will not exist |
|
||||
|
||||
#### Example
|
||||
|
||||
With `steps=false`:
|
||||
With `steps=false` and `annotate=true`:
|
||||
|
||||
```json
|
||||
{
|
||||
"distance": 30.0,
|
||||
"duration": 100.0,
|
||||
"steps": []
|
||||
"annotation": {
|
||||
"distance": [5,5,10,5,5],
|
||||
"duration": [15,15,40,15,15]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -409,9 +438,35 @@ step.
|
||||
- `name`: The name of the way along which travel proceeds.
|
||||
- `mode`: A string signifying the mode of transportation.
|
||||
- `maneuver`: A `StepManeuver` object representing the maneuver.
|
||||
- `intersections`: A list of `Intersections` that are passed along the segment, the very first belonging to the StepManeuver
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
{
|
||||
"distance":152.3,
|
||||
"duration":15.6,
|
||||
"name":"Lortzingstraße",
|
||||
"maneuver":{
|
||||
"type":"depart",
|
||||
"modifier":"left"
|
||||
},
|
||||
"geometry":"{lu_IypwpAVrAvAdI",
|
||||
"mode":"driving",
|
||||
"intersections":[
|
||||
{"location":[13.39677,52.54366],
|
||||
"out":1,
|
||||
"bearings":[66,246],
|
||||
"entry":["true","true"]},
|
||||
{"location":[13.394718,52.543096],
|
||||
"in":0,
|
||||
"out":2,
|
||||
"bearings":[60,150,240,330],
|
||||
"entry":["false","true","true","true"]
|
||||
}
|
||||
]}
|
||||
```
|
||||
|
||||
### StepManeuver
|
||||
|
||||
#### Properties
|
||||
@@ -421,7 +476,8 @@ step.
|
||||
direction of travel immediately before the maneuver.
|
||||
- `bearing_after`: The clockwise angle from true north to the
|
||||
direction of travel immediately after the maneuver.
|
||||
- `type` A string indicating the type of maneuver
|
||||
- `type` A string indicating the type of maneuver. **new identifiers might be introduced without API change**
|
||||
Types unknown to the client should be handled like the `turn` type, the existance of correct `modifier` values is guranteed.
|
||||
|
||||
| `type` | Description |
|
||||
|-------------------|--------------------------------------------------------------|
|
||||
@@ -473,10 +529,38 @@ step.
|
||||
| `type` | Description |
|
||||
|------------------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
| `roundabout` | Number of the roundabout exit to take. If exit is `undefined` the destination is on the roundabout. |
|
||||
| `turn` or `end of road`| Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` |
|
||||
| else | Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` |
|
||||
|
||||
|
||||
New maneuver `type` and `modifier` and new properties (potentially depending on `type`) may be introduced in the future without an API version change.
|
||||
New properties (potentially depending on `type`) may be introduced in the future without an API version change.
|
||||
|
||||
### Intersections
|
||||
|
||||
An intersection gives a full representation of any cross-way the path passes bay. For every step, the very first intersection (`intersections[0]`) corresponds to the
|
||||
location of the StepManeuver. Further intersections are listed for every cross-way until the next turn instruction.
|
||||
|
||||
#### Properties
|
||||
|
||||
- `location`: A `[longitude, latitude]` pair describing the location of the turn.
|
||||
- `bearings`: A list of bearing values (e.g. [0,90,180,270]) that are available at the intersection. The bearings describe all available roads at the intersection.
|
||||
- `entry`: A list of entry flags, corresponding in a 1:1 relationship to the bearings. A value of `true` indicates that the respective road could be entered on a valid route.
|
||||
`false` indicates that the turn onto the respective road would violate a restriction.
|
||||
- `in`: index into bearings/entry array. Used to calculate the bearing just before the turn. Namely, the clockwise angle from true north to the
|
||||
direction of travel immediately before the maneuver/passing the intersection. Bearings are given relative to the intersection. To get the bearing
|
||||
in the direction of driving, the bearing has to be rotated by a value of 180. The value is not supplied for `depart` maneuvers.
|
||||
- `out`: index into the bearings/entry array. Used to extract the bearing just after the turn. Namely, The clockwise angle from true north to the
|
||||
direction of travel immediately after the maneuver/passing the intersection. The value is not supplied for `arrive` maneuvers.
|
||||
|
||||
#### Example
|
||||
```
|
||||
{
|
||||
"location":[13.394718,52.543096],
|
||||
"in":0,
|
||||
"out":2,
|
||||
"bearings":[60,150,240,330],
|
||||
"entry":["false","true","true","true"]
|
||||
}
|
||||
```
|
||||
|
||||
### Waypoint
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ endif()
|
||||
add_executable(osrm-example example.cpp)
|
||||
|
||||
find_package(LibOSRM REQUIRED)
|
||||
find_package(Boost 1.49.0 COMPONENTS filesystem system thread REQUIRED)
|
||||
find_package(Boost 1.49.0 COMPONENTS filesystem system thread iostreams REQUIRED)
|
||||
|
||||
target_link_libraries(osrm-example ${LibOSRM_LIBRARIES} ${Boost_LIBRARIES})
|
||||
include_directories(SYSTEM ${LibOSRM_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
||||
|
||||
@@ -344,4 +344,4 @@ Feature: Collapse
|
||||
| waypoints | route | turns |
|
||||
| a,d | first,first,first,first | depart,continue left,continue right,arrive |
|
||||
| a,e | first,second,second | depart,turn left,arrive |
|
||||
| a,f | first,third,third | depart,new name straight,arrive |
|
||||
| a,f | first,third,third | depart,turn straight,arrive |
|
||||
|
||||
@@ -16,9 +16,42 @@ Feature: Continue Instructions
|
||||
| bd | primary |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,continue left,arrive |
|
||||
| a,d | abc,bd,bd | depart,new name straight,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,continue left,arrive |
|
||||
| a,d | abc,bd,bd | depart,turn straight,arrive |
|
||||
|
||||
Scenario: Road turning left and straight
|
||||
Given the node map
|
||||
| | | c | |
|
||||
| a | | b | d |
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name |
|
||||
| abc | primary | road |
|
||||
| bd | primary | road |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | road,road,road | depart,continue left,arrive |
|
||||
| a,d | road,road | depart,arrive |
|
||||
|
||||
Scenario: Road turning left and straight
|
||||
Given the node map
|
||||
| | | c | |
|
||||
| a | | b | d |
|
||||
| | | e | |
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name |
|
||||
| abc | primary | road |
|
||||
| bd | primary | road |
|
||||
| be | primary | road |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | road,road,road | depart,continue left,arrive |
|
||||
| a,d | road,road | depart,arrive |
|
||||
| a,e | road,road,road | depart,continue right,arrive |
|
||||
|
||||
Scenario: Road turning right
|
||||
Given the node map
|
||||
@@ -31,9 +64,9 @@ Feature: Continue Instructions
|
||||
| bd | primary |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,continue right,arrive |
|
||||
| a,d | abc,bd,bd | depart,new name straight,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,continue right,arrive |
|
||||
| a,d | abc,bd,bd | depart,turn straight,arrive |
|
||||
|
||||
Scenario: Road turning slight left
|
||||
Given the node map
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
@routing @guidance @intersections
|
||||
Feature: Intersections Data
|
||||
|
||||
Background:
|
||||
Given the profile "car"
|
||||
Given a grid size of 10 meters
|
||||
|
||||
Scenario: Passing Three Way South
|
||||
Given the node map
|
||||
| a | | b | | c |
|
||||
| | | d | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name |
|
||||
| ab | through |
|
||||
| bc | through |
|
||||
| bd | corner |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,c | through,through | depart,arrive | true:90,true:90 true:180 false:270;true:270 |
|
||||
|
||||
Scenario: Passing Three Way North
|
||||
Given the node map
|
||||
| | | d | | |
|
||||
| a | | b | | c |
|
||||
|
||||
And the ways
|
||||
| nodes | name |
|
||||
| ab | through |
|
||||
| bc | through |
|
||||
| bd | corner |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,c | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
|
||||
|
||||
Scenario: Passing Oneway Street In
|
||||
Given the node map
|
||||
| | | d | | |
|
||||
| a | | b | | c |
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway |
|
||||
| ab | through | no |
|
||||
| bc | through | no |
|
||||
| db | corner | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,c | through,through | depart,arrive | true:90,false:0 true:90 false:270;true:270 |
|
||||
|
||||
Scenario: Passing Oneway Street Out
|
||||
Given the node map
|
||||
| | | d | | |
|
||||
| a | | b | | c |
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway |
|
||||
| ab | through | no |
|
||||
| bc | through | no |
|
||||
| bd | corner | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,c | through,through | depart,arrive | true:90,true:0 true:90 false:270;true:270 |
|
||||
|
||||
Scenario: Passing Two Intersections
|
||||
Given the node map
|
||||
| | | e | | | | |
|
||||
| a | | b | | c | | d |
|
||||
| | | | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name |
|
||||
| ab | through |
|
||||
| bc | through |
|
||||
| cd | through |
|
||||
| be | corner |
|
||||
| cf | corner |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,d | through,through | depart,arrive | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
|
||||
|
||||
Scenario: Regression test #2424
|
||||
Given the node map
|
||||
| | | e | | | | | | i | | | | |
|
||||
| a | | b | | c | | d | | h | | k | | m |
|
||||
| | | | | f | | | | | | l | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name |
|
||||
| abcd | Fritz-Elsas-Straße |
|
||||
| hkm | Fritz-Elsas-Straße |
|
||||
| dhi | Martin-Luther-Straße |
|
||||
| be | corner |
|
||||
| kl | corner |
|
||||
| cf | corner |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,m | Fritz-Elsas-Straße,Fritz-Elsas-Straße| depart,arrive |
|
||||
|
||||
Scenario: Passing Two Intersections, Collapsing
|
||||
Given the node map
|
||||
| | | e | | | | |
|
||||
| a | | b | | c | | d |
|
||||
| | | | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | name |
|
||||
| ab | through |
|
||||
| bc | throughbridge |
|
||||
| cd | through |
|
||||
| be | corner |
|
||||
| cf | corner |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| a,d | through,through | depart,arrive | true:90,true:0 true:90 false:270,true:90 true:180 false:270;true:270 |
|
||||
| f,a | corner,throughbridge,through | depart,end of road left,arrive | true:0;true:90 false:180 true:270,true:0 false:90 true:270;true:90 |
|
||||
|
||||
Scenario: Roundabouts
|
||||
Given the node map
|
||||
| | | | | e | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | a | | | | |
|
||||
| | | | 1 | | 4 | | | |
|
||||
| | | | | | | | | |
|
||||
| f | | b | | | | d | | h |
|
||||
| | | | | | | | | |
|
||||
| | | | 2 | | 3 | | | |
|
||||
| | | | | c | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | g | | | | |
|
||||
|
||||
And the ways
|
||||
| nodes | junction |
|
||||
| abcda | roundabout |
|
||||
| ea | |
|
||||
| fb | |
|
||||
| gc | |
|
||||
| hd | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | intersections |
|
||||
| e,f | ea,fb,fb | depart,abcda-exit-1,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90 |
|
||||
| e,g | ea,gc,gc | depart,abcda-exit-2,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0|
|
||||
| e,h | ea,hd,hd | depart,abcda-exit-3,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:270 |
|
||||
| e,2 | ea,abcda,abcda | depart,abcda-exit-undefined,arrive | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:327 +-1|
|
||||
| 1,g | abcda,gc,gc | depart,abcda-exit-2,arrive | true:214;true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:0|
|
||||
| 1,3 | abcda,abcda | depart,arrive | true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:214|
|
||||
@@ -33,7 +33,9 @@ module.exports = function () {
|
||||
var subMatchings = [],
|
||||
turns = '',
|
||||
route = '',
|
||||
duration = '';
|
||||
duration = '',
|
||||
annotation = '';
|
||||
|
||||
|
||||
if (res.statusCode === 200) {
|
||||
if (headers.has('matchings')) {
|
||||
@@ -54,6 +56,11 @@ module.exports = function () {
|
||||
if (json.matchings.length != 1) throw new Error('*** Checking duration only supported for matchings with one subtrace');
|
||||
duration = json.matchings[0].duration;
|
||||
}
|
||||
|
||||
if (headers.has('annotation')) {
|
||||
if (json.matchings.length != 1) throw new Error('*** Checking annotation only supported for matchings with one subtrace');
|
||||
annotation = this.annotationList(json.matchings[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (headers.has('turns')) {
|
||||
@@ -68,6 +75,10 @@ module.exports = function () {
|
||||
got.duration = duration.toString();
|
||||
}
|
||||
|
||||
if (headers.has('annotation')) {
|
||||
got.annotation = annotation.toString();
|
||||
}
|
||||
|
||||
var ok = true;
|
||||
var encodedResult = '',
|
||||
extendedTarget = '';
|
||||
@@ -134,6 +145,7 @@ module.exports = function () {
|
||||
this.requestUrl(row.request, afterRequest);
|
||||
} else {
|
||||
var params = this.queryParams;
|
||||
params['annotate'] = 'true';
|
||||
got = {};
|
||||
for (var k in row) {
|
||||
var match = k.match(/param:(.*)/);
|
||||
|
||||
@@ -46,8 +46,8 @@ module.exports = function () {
|
||||
if (headers.has('trips')) {
|
||||
subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => {
|
||||
var toAdd = [];
|
||||
if (i === 0) toAdd.push(sl.steps[0].maneuver.location);
|
||||
toAdd.push(sl.steps[sl.steps.length-1].maneuver.location);
|
||||
if (i === 0) toAdd.push(sl.steps[0].intersections[0].location);
|
||||
toAdd.push(sl.steps[sl.steps.length-1].intersections[0].location);
|
||||
return toAdd;
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ module.exports = function () {
|
||||
});
|
||||
};
|
||||
|
||||
['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties'].forEach(file => {
|
||||
['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.properties','osrm.icd'].forEach(file => {
|
||||
q.defer(rename, file);
|
||||
});
|
||||
|
||||
@@ -273,7 +273,7 @@ module.exports = function () {
|
||||
|
||||
var q = d3.queue();
|
||||
|
||||
['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level'].forEach((file) => {
|
||||
['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges','osrm.datasource_indexes','osrm.datasource_names','osrm.level','osrm.icd'].forEach((file) => {
|
||||
q.defer(rename, file);
|
||||
});
|
||||
|
||||
|
||||
@@ -52,24 +52,91 @@ module.exports = {
|
||||
match (got, want) {
|
||||
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
||||
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
|
||||
matchRe = want.match(/^\/(.*)\/$/);
|
||||
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+\+\-(.+)$/);
|
||||
|
||||
function inRange(margin, got, want) {
|
||||
var fromR = parseFloat(want) - margin,
|
||||
toR = parseFloat(want) + margin;
|
||||
return parseFloat(got) >= fromR && parseFloat(got) <= toR;
|
||||
}
|
||||
function parseIntersectionString(str) {
|
||||
return str.split(';')
|
||||
.map((turn_intersections) => turn_intersections
|
||||
.split(',')
|
||||
.map((intersection) => intersection
|
||||
.split(' ')
|
||||
.map((entry_bearing_pair) => entry_bearing_pair
|
||||
.split(':'))));
|
||||
}
|
||||
|
||||
if (got === want) {
|
||||
return true;
|
||||
} else if (matchBearingListAbs) {
|
||||
let want_and_margin = want.split('+-'),
|
||||
margin = parseFloat(want_and_margin[1].trim()),
|
||||
want_pairs = want_and_margin[0].trim().split(',').map((pair) => pair.split('->')),
|
||||
got_pairs = got.split(',').map((pair) => pair.split('->'));
|
||||
if (want_pairs.length != got_pairs.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < want_pairs.length; ++i)
|
||||
{
|
||||
if (!inRange(margin, got_pairs[i][0], want_pairs[i][0]) ||
|
||||
!inRange(margin, got_pairs[i][1], want_pairs[i][1]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (matchIntersectionListAbs) {
|
||||
let margin = parseFloat(want.split('+-')[1]),
|
||||
want_intersections = parseIntersectionString(want.split('+-')[0].trim()),
|
||||
got_intersections = parseIntersectionString(got);
|
||||
if (want_intersections.length != got_intersections.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (let step_idx = 0; step_idx < want_intersections.length; ++step_idx)
|
||||
{
|
||||
if (want_intersections[step_idx].length != got_intersections[step_idx].length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (let intersection_idx = 0; intersection_idx < want_intersections[step_idx].length; ++intersection_idx)
|
||||
{
|
||||
if (want_intersections[step_idx][intersection_idx].length != got_intersections[step_idx][intersection_idx].length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (let pair_idx = 0; pair_idx < want_intersections[step_idx][intersection_idx].length; ++pair_idx)
|
||||
{
|
||||
let want_pair = want_intersections[step_idx][intersection_idx][pair_idx],
|
||||
got_pair = got_intersections[step_idx][intersection_idx][pair_idx];
|
||||
if (got_pair[0] != want_pair[0] ||
|
||||
!inRange(margin, got_pair[1], want_pair[1]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (matchPercent) { // percentage range: 100 ~ 5%
|
||||
var target = parseFloat(matchPercent[1]),
|
||||
percentage = parseFloat(matchPercent[2]);
|
||||
if (target === 0) {
|
||||
return true;
|
||||
} else {
|
||||
var ratio = Math.abs(1 - parseFloat(got) / target);
|
||||
let ratio = Math.abs(1 - parseFloat(got) / target);
|
||||
return 100 * ratio < percentage;
|
||||
}
|
||||
} else if (matchAbs) { // absolute range: 100 +-5
|
||||
var margin = parseFloat(matchAbs[2]),
|
||||
fromR = parseFloat(matchAbs[1]) - margin,
|
||||
toR = parseFloat(matchAbs[1]) + margin;
|
||||
return parseFloat(got) >= fromR && parseFloat(got) <= toR;
|
||||
let margin = parseFloat(matchAbs[2]);
|
||||
return inRange(margin, got, matchAbs[1]);
|
||||
} else if (matchRe) { // regex: /a,b,.*/
|
||||
return got.match(matchRe[1]);
|
||||
} else {
|
||||
|
||||
@@ -138,7 +138,17 @@ module.exports = function () {
|
||||
};
|
||||
|
||||
this.bearingList = (instructions) => {
|
||||
return this.extractInstructionList(instructions, s => s.maneuver.bearing_after);
|
||||
return this.extractInstructionList(instructions, s => s.maneuver.bearing_before + '->' + s.maneuver.bearing_after);
|
||||
};
|
||||
|
||||
this.annotationList = (instructions) => {
|
||||
// Pull out all the distinct segment distances, skipping the arrive
|
||||
// instructions, and the leading 0 on all timestamps arrays.
|
||||
var pairs = [];
|
||||
for (var i in instructions.annotation.duration) {
|
||||
pairs.push(instructions.annotation.duration[i]+':'+instructions.annotation.distance[i]);
|
||||
}
|
||||
return pairs.join(',');
|
||||
};
|
||||
|
||||
this.turnList = (instructions) => {
|
||||
@@ -168,6 +178,19 @@ module.exports = function () {
|
||||
.join(',');
|
||||
};
|
||||
|
||||
this.intersectionList = (instructions) => {
|
||||
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
|
||||
.map( v => {
|
||||
return v.intersections
|
||||
.map( intersection => {
|
||||
var string = intersection.entry[0]+':'+intersection.bearings[0], i;
|
||||
for( i = 1; i < intersection.bearings.length; ++i )
|
||||
string = string + ' ' + intersection.entry[i]+':'+intersection.bearings[i];
|
||||
return string;
|
||||
}).join(',');
|
||||
}).join(';');
|
||||
};
|
||||
|
||||
this.modeList = (instructions) => {
|
||||
return this.extractInstructionList(instructions, s => s.mode);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ module.exports = function () {
|
||||
var afterRequest = (err, res, body) => {
|
||||
if (err) return cb(err);
|
||||
if (body && body.length) {
|
||||
var instructions, bearings, turns, modes, times, distances, summary;
|
||||
var instructions, bearings, turns, modes, times, distances, summary, intersections;
|
||||
|
||||
var json = JSON.parse(body);
|
||||
|
||||
@@ -41,6 +41,7 @@ module.exports = function () {
|
||||
instructions = this.wayList(json.routes[0]);
|
||||
bearings = this.bearingList(json.routes[0]);
|
||||
turns = this.turnList(json.routes[0]);
|
||||
intersections = this.intersectionList(json.routes[0]);
|
||||
modes = this.modeList(json.routes[0]);
|
||||
times = this.timeList(json.routes[0]);
|
||||
distances = this.distanceList(json.routes[0]);
|
||||
@@ -108,6 +109,10 @@ module.exports = function () {
|
||||
}
|
||||
}
|
||||
|
||||
if (headers.has('intersections')) {
|
||||
got.intersections = (intersections || '').trim();
|
||||
}
|
||||
|
||||
var putValue = (key, value) => {
|
||||
if (headers.has(key)) got[key] = instructions ? value : '';
|
||||
};
|
||||
|
||||
@@ -14,8 +14,8 @@ Feature: Compass bearing
|
||||
| ab |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 315,0 |
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 0->315,315->0|
|
||||
|
||||
Scenario: Bearing when going west
|
||||
Given the node map
|
||||
@@ -26,8 +26,8 @@ Feature: Compass bearing
|
||||
| ab |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 270,0 |
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 0->270,270->0|
|
||||
|
||||
Scenario: Bearing af 45 degree intervals
|
||||
Given the node map
|
||||
@@ -48,14 +48,14 @@ Feature: Compass bearing
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| x | a | xa,xa | 0,0 |
|
||||
| x | b | xb,xb | 315,0 |
|
||||
| x | c | xc,xc | 270,0 |
|
||||
| x | d | xd,xd | 225,0 |
|
||||
| x | e | xe,xe | 180,0 |
|
||||
| x | f | xf,xf | 135,0 |
|
||||
| x | g | xg,xg | 90,0 |
|
||||
| x | h | xh,xh | 45,0 |
|
||||
| x | a | xa,xa | 0->0,0->0|
|
||||
| x | b | xb,xb | 0->315,315->0|
|
||||
| x | c | xc,xc | 0->270,270->0|
|
||||
| x | d | xd,xd | 0->225,225->0|
|
||||
| x | e | xe,xe | 0->180,180->0|
|
||||
| x | f | xf,xf | 0->135,135->0|
|
||||
| x | g | xg,xg | 0->90,90->0|
|
||||
| x | h | xh,xh | 0->45,45->0|
|
||||
|
||||
Scenario: Bearing in a roundabout
|
||||
Given the node map
|
||||
@@ -76,9 +76,9 @@ Feature: Compass bearing
|
||||
| ha | yes |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| c | b | cd,de,ef,fg,gh,ha,ab,ab | 270,225,180,135,90,45,0,0 |
|
||||
| g | f | gh,ha,ab,bc,cd,de,ef,ef | 90,45,0,315,270,225,180,0 |
|
||||
| from | to | route | bearing |
|
||||
| c | b | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 |
|
||||
| g | f | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 |
|
||||
|
||||
Scenario: Bearing should stay constant when zig-zagging
|
||||
Given the node map
|
||||
@@ -97,7 +97,7 @@ Feature: Compass bearing
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | h | ab,bc,cd,de,ef,fg,gh,gh | 0,135,0,135,0,135,0,0 |
|
||||
| a | h | ab,bc,cd,de,ef,fg,gh,gh | 0->0,0->135,135->0,0->135,135->0,0->135,135->0,0->0 |
|
||||
|
||||
Scenario: Bearings on an east-west way.
|
||||
Given the node map
|
||||
@@ -108,37 +108,37 @@ Feature: Compass bearing
|
||||
| abcdef |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | b | abcdef,abcdef | 90,0 |
|
||||
| a | c | abcdef,abcdef | 90,0 |
|
||||
| a | d | abcdef,abcdef | 90,0 |
|
||||
| a | e | abcdef,abcdef | 90,0 |
|
||||
| a | f | abcdef,abcdef | 90,0 |
|
||||
| b | a | abcdef,abcdef | 270,0 |
|
||||
| b | c | abcdef,abcdef | 90,0 |
|
||||
| b | d | abcdef,abcdef | 90,0 |
|
||||
| b | e | abcdef,abcdef | 90,0 |
|
||||
| b | f | abcdef,abcdef | 90,0 |
|
||||
| c | a | abcdef,abcdef | 270,0 |
|
||||
| c | b | abcdef,abcdef | 270,0 |
|
||||
| c | d | abcdef,abcdef | 90,0 |
|
||||
| c | e | abcdef,abcdef | 90,0 |
|
||||
| c | f | abcdef,abcdef | 90,0 |
|
||||
| d | a | abcdef,abcdef | 270,0 |
|
||||
| d | b | abcdef,abcdef | 270,0 |
|
||||
| d | c | abcdef,abcdef | 270,0 |
|
||||
| d | e | abcdef,abcdef | 90,0 |
|
||||
| d | f | abcdef,abcdef | 90,0 |
|
||||
| e | a | abcdef,abcdef | 270,0 |
|
||||
| e | b | abcdef,abcdef | 270,0 |
|
||||
| e | c | abcdef,abcdef | 270,0 |
|
||||
| e | d | abcdef,abcdef | 270,0 |
|
||||
| e | f | abcdef,abcdef | 90,0 |
|
||||
| f | a | abcdef,abcdef | 270,0 |
|
||||
| f | b | abcdef,abcdef | 270,0 |
|
||||
| f | c | abcdef,abcdef | 270,0 |
|
||||
| f | d | abcdef,abcdef | 270,0 |
|
||||
| f | e | abcdef,abcdef | 270,0 |
|
||||
| from | to | route | bearing |
|
||||
| a | b | abcdef,abcdef | 0->90,90->0 |
|
||||
| a | c | abcdef,abcdef | 0->90,90->0 |
|
||||
| a | d | abcdef,abcdef | 0->90,90->0 |
|
||||
| a | e | abcdef,abcdef | 0->90,90->0 |
|
||||
| a | f | abcdef,abcdef | 0->90,90->0 |
|
||||
| b | a | abcdef,abcdef | 0->270,270->0 |
|
||||
| b | c | abcdef,abcdef | 0->90,90->0 |
|
||||
| b | d | abcdef,abcdef | 0->90,90->0 |
|
||||
| b | e | abcdef,abcdef | 0->90,90->0 |
|
||||
| b | f | abcdef,abcdef | 0->90,90->0 |
|
||||
| c | a | abcdef,abcdef | 0->270,270->0 |
|
||||
| c | b | abcdef,abcdef | 0->270,270->0 |
|
||||
| c | d | abcdef,abcdef | 0->90,90->0 |
|
||||
| c | e | abcdef,abcdef | 0->90,90->0 |
|
||||
| c | f | abcdef,abcdef | 0->90,90->0 |
|
||||
| d | a | abcdef,abcdef | 0->270,270->0 |
|
||||
| d | b | abcdef,abcdef | 0->270,270->0 |
|
||||
| d | c | abcdef,abcdef | 0->270,270->0 |
|
||||
| d | e | abcdef,abcdef | 0->90,90->0 |
|
||||
| d | f | abcdef,abcdef | 0->90,90->0 |
|
||||
| e | a | abcdef,abcdef | 0->270,270->0 |
|
||||
| e | b | abcdef,abcdef | 0->270,270->0 |
|
||||
| e | c | abcdef,abcdef | 0->270,270->0 |
|
||||
| e | d | abcdef,abcdef | 0->270,270->0 |
|
||||
| e | f | abcdef,abcdef | 0->90,90->0 |
|
||||
| f | a | abcdef,abcdef | 0->270,270->0 |
|
||||
| f | b | abcdef,abcdef | 0->270,270->0 |
|
||||
| f | c | abcdef,abcdef | 0->270,270->0 |
|
||||
| f | d | abcdef,abcdef | 0->270,270->0 |
|
||||
| f | e | abcdef,abcdef | 0->270,270->0 |
|
||||
|
||||
Scenario: Bearings at high latitudes
|
||||
# The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html,
|
||||
@@ -161,19 +161,19 @@ Feature: Compass bearing
|
||||
| bd |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 0,0 |
|
||||
| b | c | bc,bc | 90,0 |
|
||||
| c | d | cd,cd | 180,0 |
|
||||
| d | a | da,da | 270,0 |
|
||||
| b | a | ab,ab | 180,0 |
|
||||
| c | b | bc,bc | 270,0 |
|
||||
| d | c | cd,cd | 0,0 |
|
||||
| a | d | da,da | 90,0 |
|
||||
| a | c | ac,ac | 45,0 |
|
||||
| c | a | ac,ac | 225,0 |
|
||||
| b | d | bd,bd | 135,0 |
|
||||
| d | b | bd,bd | 315,0 |
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 0->0,0->0 |
|
||||
| b | c | bc,bc | 0->90,90->0 |
|
||||
| c | d | cd,cd | 0->180,180->0 |
|
||||
| d | a | da,da | 0->270,270->0 |
|
||||
| b | a | ab,ab | 0->180,180->0 |
|
||||
| c | b | bc,bc | 0->270,270->0 |
|
||||
| d | c | cd,cd | 0->0,0->0 |
|
||||
| a | d | da,da | 0->90,90->0 |
|
||||
| a | c | ac,ac | 0->45,45->0 |
|
||||
| c | a | ac,ac | 0->225,225->0 |
|
||||
| b | d | bd,bd | 0->135,135->0 |
|
||||
| d | b | bd,bd | 0->315,315->0 |
|
||||
|
||||
Scenario: Bearings at high negative latitudes
|
||||
# The coordinas below was calculated using http://www.movable-type.co.uk/scripts/latlong.html,
|
||||
@@ -196,16 +196,16 @@ Feature: Compass bearing
|
||||
| bd |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 180,0 |
|
||||
| b | c | bc,bc | 90,0 |
|
||||
| c | d | cd,cd | 0,0 |
|
||||
| d | a | da,da | 270,0 |
|
||||
| b | a | ab,ab | 0,0 |
|
||||
| c | b | bc,bc | 270,0 |
|
||||
| d | c | cd,cd | 180,0 |
|
||||
| a | d | da,da | 90,0 |
|
||||
| a | c | ac,ac | 135,0 |
|
||||
| c | a | ac,ac | 315,0 |
|
||||
| b | d | bd,bd | 45,0 |
|
||||
| d | b | bd,bd | 225,0 |
|
||||
| from | to | route | bearing |
|
||||
| a | b | ab,ab | 0->180,180->0 |
|
||||
| b | c | bc,bc | 0->90,90->0 |
|
||||
| c | d | cd,cd | 0->0,0->0 |
|
||||
| d | a | da,da | 0->270,270->0 |
|
||||
| b | a | ab,ab | 0->0,0->0 |
|
||||
| c | b | bc,bc | 0->270,270->0 |
|
||||
| d | c | cd,cd | 0->180,180->0 |
|
||||
| a | d | da,da | 0->90,90->0 |
|
||||
| a | c | ac,ac | 0->135,135->0 |
|
||||
| c | a | ac,ac | 0->315,315->0 |
|
||||
| b | d | bd,bd | 0->45,45->0 |
|
||||
| d | b | bd,bd | 0->225,225->0 |
|
||||
|
||||
@@ -15,11 +15,11 @@ Feature: Bearing parameter
|
||||
|
||||
When I route I should get
|
||||
| from | to | bearings | route | bearing |
|
||||
| b | c | 90 90 | ad,ad | 90,0 |
|
||||
| b | c | 90 90 | ad,ad | 0->90,90->0|
|
||||
| b | c | 180 90 | | |
|
||||
| b | c | 80 100 | ad,ad | 90,0 |
|
||||
| b | c | 80 100 | ad,ad | 0->90,90->0|
|
||||
| b | c | 79 100 | | |
|
||||
| b | c | 79,11 100 | ad,ad | 90,0 |
|
||||
| b | c | 79,11 100 | ad,ad | 0->90,90->0|
|
||||
|
||||
Scenario: Testbot - Intial bearing in simple case
|
||||
Given the node map
|
||||
@@ -35,10 +35,10 @@ Feature: Bearing parameter
|
||||
When I route I should get
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | c | 0 0 | | |
|
||||
| 0 | c | 45 45 | bc,bc | 45 ~3% |
|
||||
| 0 | c | 45 45 | bc,bc | 0->44,44->0 +- 1|
|
||||
| 0 | c | 85 85 | | |
|
||||
| 0 | c | 95 95 | | |
|
||||
| 0 | c | 135 135 | ac,ac | 135 ~1% |
|
||||
| 0 | c | 135 135 | ac,ac | 0->135,135->0 +- 1|
|
||||
| 0 | c | 180 180 | | |
|
||||
|
||||
Scenario: Testbot - Initial bearing on split way
|
||||
@@ -55,19 +55,19 @@ Feature: Bearing parameter
|
||||
|
||||
When I route I should get
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | b | 10 10 | bc,bc | 0,0 |
|
||||
| 0 | b | 90 90 | ab,ab | 90,0 |
|
||||
| 0 | b | 10 10 | bc,bc | 0->0,0->0 |
|
||||
| 0 | b | 90 90 | ab,ab | 0->90,90->0 |
|
||||
# The returned bearing is wrong here, it's based on the snapped
|
||||
# coordinates, not the acutal edge bearing. This should be
|
||||
# fixed one day, but it's only a problem when we snap two vias
|
||||
# to the same point - DP
|
||||
#| 0 | b | 170 170 | da | 180 |
|
||||
#| 0 | b | 189 189 | da | 180 |
|
||||
| 0 | 1 | 90 270 | ab,bc,cd,cd | 90,0,270,0 |
|
||||
| 1 | d | 10 10 | bc,bc | 0,0 |
|
||||
| 1 | d | 90 90 | ab,bc,cd,da,da | 90,0,270,180,0 |
|
||||
| 1 | 0 | 189 189 | da,da | 180,0 |
|
||||
| 1 | d | 270 270 | cd,cd | 270,0 |
|
||||
| 0 | 1 | 90 270 | ab,bc,cd,cd | 0->90,90->0,0->270,270->0 |
|
||||
| 1 | d | 10 10 | bc,bc | 0->0,0->0 |
|
||||
| 1 | d | 90 90 | ab,bc,cd,da,da | 0->90,90->0,0->270,270->180,180->0 |
|
||||
| 1 | 0 | 189 189 | da,da | 0->180,180->0 |
|
||||
| 1 | d | 270 270 | cd,cd | 0->270,270->0 |
|
||||
| 1 | d | 349 349 | | |
|
||||
|
||||
Scenario: Testbot - Initial bearing in all direction
|
||||
@@ -101,11 +101,11 @@ Feature: Bearing parameter
|
||||
|
||||
When I route I should get
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | q | 0 90 | ia,ab,bc,cd,de,ef,fg,gh,ha,ha | 0,90,180,180,270,270,0,0,90,0 |
|
||||
| 0 | a | 45 90 | jb,bc,cd,de,ef,fg,gh,ha,ha | 45,180,180,270,270,0,0,90,0 |
|
||||
| 0 | q | 90 90 | kc,cd,de,ef,fg,gh,ha,ha | 90,180,270,270,0,0,90,0 |
|
||||
| 0 | a | 135 90 | ld,de,ef,fg,gh,ha,ha | 135,270,270,0,0,90,0 |
|
||||
| 0 | a | 180 90 | me,ef,fg,gh,ha,ha | 180,270,0,0,90,0 |
|
||||
| 0 | a | 225 90 | nf,fg,gh,ha,ha | 225,0,0,90,0 |
|
||||
| 0 | a | 270 90 | og,gh,ha,ha | 270,0,90,0 |
|
||||
| 0 | a | 315 90 | ph,ha,ha | 315,90,0 |
|
||||
| 0 | q | 0 90 | ia,ab,bc,cd,de,ef,fg,gh,ha,ha | 0->0,0->90,90->180,180->180,180->270,270->270,270->0,0->0,0->90,90->0 |
|
||||
| 0 | a | 45 90 | jb,bc,cd,de,ef,fg,gh,ha,ha | 0->45,45->180,180->180,180->270,270->270,270->0,0->0,0->90,90->0 |
|
||||
| 0 | q | 90 90 | kc,cd,de,ef,fg,gh,ha,ha | 0->90,90->180,180->270,270->270,270->0,0->0,0->90,90->0 |
|
||||
| 0 | a | 135 90 | ld,de,ef,fg,gh,ha,ha | 0->135,135->270,270->270,270->0,0->0,0->90,90->0 |
|
||||
| 0 | a | 180 90 | me,ef,fg,gh,ha,ha | 0->180,180->270,270->0,0->0,0->90,90->0 |
|
||||
| 0 | a | 225 90 | nf,fg,gh,ha,ha | 0->225,225->0,0->0,0->90,90->0 |
|
||||
| 0 | a | 270 90 | og,gh,ha,ha | 0->270,270->0,0->90,90->0 |
|
||||
| 0 | a | 315 90 | ph,ha,ha | 0->315,315->90,90->0 |
|
||||
|
||||
@@ -18,5 +18,5 @@ Feature: Geometry Compression
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | distance | speed |
|
||||
| b | e | abcdef,abcdef | 589m | 36 km/h |
|
||||
| e | b | abcdef,abcdef | 589m | 36 km/h |
|
||||
| b | e | abcdef,abcdef | 588.8m | 36 km/h |
|
||||
| e | b | abcdef,abcdef | 588.8m | 36 km/h |
|
||||
|
||||
@@ -104,3 +104,18 @@ Feature: Basic Map Matching
|
||||
| trace | matchings |
|
||||
| dcba | hg,gf,fe |
|
||||
| efgh | ab,bc,cd |
|
||||
|
||||
Scenario: Testbot - Duration details
|
||||
Given the node map
|
||||
| a | b | c | d | e | | g | h |
|
||||
| | | i | | | | | |
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| abcdegh | no |
|
||||
| ci | no |
|
||||
|
||||
When I match I should get
|
||||
| trace | matchings | annotation |
|
||||
| abeh | abcedgh | 1:9.897633,0:0,1:10.008842,1:10.008842,1:10.008842,0:0,2:20.017685,1:10.008842 |
|
||||
| abci | abc,ci | 1:9.897633,0:0,1:10.008842,0:0.111209,1:10.010367 |
|
||||
|
||||
@@ -24,12 +24,12 @@ Feature: Projection to nearest point on road
|
||||
Scenario: Projection onto way at high latitudes, 1km distance
|
||||
When I route I should get
|
||||
| from | to | route | bearing | distance |
|
||||
| b | a | abc,abc | 225,0 +-1 | 1000m +- 7 |
|
||||
| b | c | abc,abc | 45,0 +-1 | 1000m +- 7 |
|
||||
| a | d | abc,abc | 45,0 +-1 | 1000m +- 7 |
|
||||
| d | a | abc,abc | 225,0 +-1 | 1000m +- 7 |
|
||||
| c | d | abc,abc | 225,0 +-1 | 1000m +- 8 |
|
||||
| d | c | abc,abc | 45 +-1 | 1000m +- 8 |
|
||||
| b | a | abc,abc | 0->225,225->0 | 1000m +- 7 |
|
||||
| b | c | abc,abc | 0->45,45->0 | 1000m +- 7 |
|
||||
| a | d | abc,abc | 0->45,45->0 | 1000m +- 7 |
|
||||
| d | a | abc,abc | 0->225,225->0 | 1000m +- 7 |
|
||||
| c | d | abc,abc | 0->225,224->0 | 1000m +- 8 |
|
||||
| d | c | abc,abc | 0->44,45->0 | 1000m +- 8 |
|
||||
|
||||
Scenario: Projection onto way at high latitudes, no distance
|
||||
When I route I should get
|
||||
|
||||
@@ -33,8 +33,8 @@ namespace json
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::string instructionTypeToString(extractor::guidance::TurnType type);
|
||||
std::string instructionModifierToString(extractor::guidance::DirectionModifier modifier);
|
||||
std::string instructionTypeToString(extractor::guidance::TurnType::Enum type);
|
||||
std::string instructionModifierToString(extractor::guidance::DirectionModifier::Enum modifier);
|
||||
|
||||
util::json::Array coordinateToLonLat(const util::Coordinate coordinate);
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ struct MatchParameters : public RouteParameters
|
||||
{
|
||||
MatchParameters()
|
||||
: RouteParameters(false,
|
||||
false,
|
||||
false,
|
||||
RouteParameters::GeometriesType::Polyline,
|
||||
RouteParameters::OverviewType::Simplified,
|
||||
|
||||
@@ -176,9 +176,33 @@ class RouteAPI : public BaseAPI
|
||||
});
|
||||
}
|
||||
|
||||
return json::makeRoute(route,
|
||||
auto result = json::makeRoute(route,
|
||||
json::makeRouteLegs(std::move(legs), std::move(step_geometries)),
|
||||
std::move(json_overview));
|
||||
|
||||
if (parameters.annotation)
|
||||
{
|
||||
util::json::Array durations;
|
||||
util::json::Array distances;
|
||||
for (const auto idx : util::irange<std::size_t>(0UL, leg_geometries.size()))
|
||||
{
|
||||
auto &leg_geometry = leg_geometries[idx];
|
||||
std::for_each(leg_geometry.annotations.begin(),
|
||||
leg_geometry.annotations.end(),
|
||||
[this, &durations, &distances](const guidance::LegGeometry::Annotation &step) {
|
||||
durations.values.push_back(step.duration);
|
||||
distances.values.push_back(step.distance);
|
||||
});
|
||||
}
|
||||
|
||||
util::json::Object details;
|
||||
details.values["distance"] = std::move(distances);
|
||||
details.values["duration"] = std::move(durations);
|
||||
|
||||
result.values["annotation"] = std::move(details);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const RouteParameters ¶meters;
|
||||
|
||||
@@ -72,17 +72,20 @@ struct RouteParameters : public BaseParameters
|
||||
template <typename... Args>
|
||||
RouteParameters(const bool steps_,
|
||||
const bool alternatives_,
|
||||
const bool annotation_,
|
||||
const GeometriesType geometries_,
|
||||
const OverviewType overview_,
|
||||
const boost::optional<bool> continue_straight_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
|
||||
geometries{geometries_}, overview{overview_}, continue_straight{continue_straight_}
|
||||
annotation{annotation_}, geometries{geometries_}, overview{overview_},
|
||||
continue_straight{continue_straight_}
|
||||
{
|
||||
}
|
||||
|
||||
bool steps = false;
|
||||
bool alternatives = false;
|
||||
bool annotation = false;
|
||||
GeometriesType geometries = GeometriesType::Polyline;
|
||||
OverviewType overview = OverviewType::Simplified;
|
||||
boost::optional<bool> continue_straight;
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
// Exposes all data access interfaces to the algorithms via base class ptr
|
||||
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "extractor/external_memory_node.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "extractor/external_memory_node.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
@@ -17,9 +19,9 @@
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -145,6 +147,15 @@ class BaseDataFacade
|
||||
virtual std::string GetTimestamp() const = 0;
|
||||
|
||||
virtual bool GetContinueStraightDefault() const = 0;
|
||||
|
||||
virtual BearingClassID GetBearingClassID(const NodeID id) const = 0;
|
||||
|
||||
virtual util::guidance::BearingClass
|
||||
GetBearingClass(const BearingClassID bearing_class_id) const = 0;
|
||||
|
||||
virtual EntryClassID GetEntryClassID(const EdgeID eid) const = 0;
|
||||
|
||||
virtual util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,24 @@
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
|
||||
#include "storage/storage_config.hpp"
|
||||
#include "engine/geospatial_query.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "storage/storage_config.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
#include "util/shared_memory_vector_wrapper.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
@@ -89,6 +92,18 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
boost::filesystem::path file_index_path;
|
||||
util::RangeTable<16, false> m_name_table;
|
||||
|
||||
// bearing classes by node based node
|
||||
util::ShM<BearingClassID, false>::vector m_bearing_class_id_table;
|
||||
// entry class IDs by edge based egde
|
||||
util::ShM<EntryClassID, false>::vector m_entry_class_id_list;
|
||||
// the look-up table for entry classes. An entry class lists the possibility of entry for all
|
||||
// available turns. For every turn, there is an associated entry class.
|
||||
util::ShM<util::guidance::EntryClass, false>::vector m_entry_class_table;
|
||||
// the look-up table for distinct bearing classes. A bearing class lists the available bearings
|
||||
// at an intersection
|
||||
util::RangeTable<16, false> m_bearing_ranges_table;
|
||||
util::ShM<DiscreteBearing, false>::vector m_bearing_values_table;
|
||||
|
||||
void LoadProfileProperties(const boost::filesystem::path &properties_path)
|
||||
{
|
||||
boost::filesystem::ifstream in_stream(properties_path);
|
||||
@@ -97,7 +112,8 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
throw util::exception("Could not open " + properties_path.string() + " for reading.");
|
||||
}
|
||||
|
||||
in_stream.read(reinterpret_cast<char*>(&m_profile_properties), sizeof(m_profile_properties));
|
||||
in_stream.read(reinterpret_cast<char *>(&m_profile_properties),
|
||||
sizeof(m_profile_properties));
|
||||
}
|
||||
|
||||
void LoadTimestamp(const boost::filesystem::path ×tamp_path)
|
||||
@@ -154,6 +170,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
m_name_ID_list.resize(number_of_edges);
|
||||
m_turn_instruction_list.resize(number_of_edges);
|
||||
m_travel_mode_list.resize(number_of_edges);
|
||||
m_entry_class_id_list.resize(number_of_edges);
|
||||
|
||||
extractor::OriginalEdgeData current_edge_data;
|
||||
for (unsigned i = 0; i < number_of_edges; ++i)
|
||||
@@ -164,6 +181,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
m_name_ID_list[i] = current_edge_data.name_id;
|
||||
m_turn_instruction_list[i] = current_edge_data.turn_instruction;
|
||||
m_travel_mode_list[i] = current_edge_data.travel_mode;
|
||||
m_entry_class_id_list[i] = current_edge_data.entry_classid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +242,8 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
boost::filesystem::ifstream datasources_stream(datasource_indexes_file, std::ios::binary);
|
||||
if (!datasources_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + datasource_indexes_file.string() + " for reading!");
|
||||
throw util::exception("Could not open " + datasource_indexes_file.string() +
|
||||
" for reading!");
|
||||
}
|
||||
BOOST_ASSERT(datasources_stream);
|
||||
|
||||
@@ -241,7 +260,8 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
boost::filesystem::ifstream datasourcenames_stream(datasource_names_file, std::ios::binary);
|
||||
if (!datasourcenames_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + datasource_names_file.string() + " for reading!");
|
||||
throw util::exception("Could not open " + datasource_names_file.string() +
|
||||
" for reading!");
|
||||
}
|
||||
BOOST_ASSERT(datasourcenames_stream);
|
||||
std::string name;
|
||||
@@ -277,6 +297,52 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
}
|
||||
}
|
||||
|
||||
void LoadIntersectionClasses(const boost::filesystem::path &intersection_class_file)
|
||||
{
|
||||
std::ifstream intersection_stream(intersection_class_file.string(), std::ios::binary);
|
||||
if (!intersection_stream)
|
||||
throw util::exception("Could not open " + intersection_class_file.string() +
|
||||
" for reading.");
|
||||
|
||||
if (!util::readAndCheckFingerprint(intersection_stream))
|
||||
throw util::exception("Fingeprint does not match in " +
|
||||
intersection_class_file.string());
|
||||
|
||||
{
|
||||
util::SimpleLogger().Write(logINFO) << "Loading Bearing Class IDs";
|
||||
std::vector<BearingClassID> bearing_class_id;
|
||||
if (!util::deserializeVector(intersection_stream, bearing_class_id))
|
||||
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
|
||||
|
||||
m_bearing_class_id_table.resize(bearing_class_id.size());
|
||||
std::copy(bearing_class_id.begin(), bearing_class_id.end(),
|
||||
&m_bearing_class_id_table[0]);
|
||||
}
|
||||
{
|
||||
util::SimpleLogger().Write(logINFO) << "Loading Bearing Classes";
|
||||
// read the range table
|
||||
intersection_stream >> m_bearing_ranges_table;
|
||||
std::vector<util::guidance::BearingClass> bearing_classes;
|
||||
// and the actual bearing values
|
||||
std::uint64_t num_bearings;
|
||||
intersection_stream >> num_bearings;
|
||||
m_bearing_values_table.resize(num_bearings);
|
||||
intersection_stream.read(reinterpret_cast<char *>(&m_bearing_values_table[0]),
|
||||
sizeof(m_bearing_values_table[0]) * num_bearings);
|
||||
if (!static_cast<bool>(intersection_stream))
|
||||
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
|
||||
}
|
||||
{
|
||||
util::SimpleLogger().Write(logINFO) << "Loading Entry Classes";
|
||||
std::vector<util::guidance::EntryClass> entry_classes;
|
||||
if (!util::deserializeVector(intersection_stream, entry_classes))
|
||||
throw util::exception("Reading from " + intersection_class_file.string() + " failed.");
|
||||
|
||||
m_entry_class_table.resize(entry_classes.size());
|
||||
std::copy(entry_classes.begin(), entry_classes.end(), &m_entry_class_table[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~InternalDataFacade()
|
||||
{
|
||||
@@ -284,7 +350,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
m_geospatial_query.reset();
|
||||
}
|
||||
|
||||
explicit InternalDataFacade(const storage::StorageConfig& config)
|
||||
explicit InternalDataFacade(const storage::StorageConfig &config)
|
||||
{
|
||||
ram_index_path = config.ram_index_path;
|
||||
file_index_path = config.file_index_path;
|
||||
@@ -302,8 +368,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
LoadGeometries(config.geometries_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading datasource info";
|
||||
LoadDatasourceInfo(config.datasource_names_path,
|
||||
config.datasource_indexes_path);
|
||||
LoadDatasourceInfo(config.datasource_names_path, config.datasource_indexes_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading timestamp";
|
||||
LoadTimestamp(config.timestamp_path);
|
||||
@@ -316,6 +381,9 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
|
||||
util::SimpleLogger().Write() << "loading rtree";
|
||||
LoadRTree();
|
||||
|
||||
util::SimpleLogger().Write() << "loading intersection class data";
|
||||
LoadIntersectionClasses(config.intersection_class_path);
|
||||
}
|
||||
|
||||
// search graph access
|
||||
@@ -452,9 +520,8 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
bearing, bearing_range);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance) const override final
|
||||
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
const util::Coordinate input_coordinate, const double max_distance) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
@@ -548,8 +615,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
result_nodes.clear();
|
||||
result_nodes.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
|
||||
result_nodes.emplace_back(edge.node_id);
|
||||
});
|
||||
}
|
||||
@@ -564,8 +630,7 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
result_weights.clear();
|
||||
result_weights.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
|
||||
result_weights.emplace_back(edge.weight);
|
||||
});
|
||||
}
|
||||
@@ -592,11 +657,9 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
}
|
||||
else
|
||||
{
|
||||
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id)
|
||||
{
|
||||
result_datasources.push_back(datasource_id);
|
||||
});
|
||||
std::for_each(
|
||||
m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +672,40 @@ class InternalDataFacade final : public BaseDataFacade
|
||||
|
||||
std::string GetTimestamp() const override final { return m_timestamp; }
|
||||
|
||||
bool GetContinueStraightDefault() const override final { return m_profile_properties.continue_straight_at_waypoint; }
|
||||
bool GetContinueStraightDefault() const override final
|
||||
{
|
||||
return m_profile_properties.continue_straight_at_waypoint;
|
||||
}
|
||||
|
||||
BearingClassID GetBearingClassID(const NodeID nid) const override final
|
||||
{
|
||||
return m_bearing_class_id_table.at(nid);
|
||||
}
|
||||
|
||||
util::guidance::BearingClass
|
||||
GetBearingClass(const BearingClassID bearing_class_id) const override final
|
||||
{
|
||||
BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
|
||||
auto range = m_bearing_ranges_table.GetRange(bearing_class_id);
|
||||
|
||||
util::guidance::BearingClass result;
|
||||
|
||||
for (auto itr = m_bearing_values_table.begin() + range.front();
|
||||
itr != m_bearing_values_table.begin() + range.back() + 1; ++itr)
|
||||
result.add(*itr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EntryClassID GetEntryClassID(const EdgeID eid) const override final
|
||||
{
|
||||
return m_entry_class_id_list.at(eid);
|
||||
}
|
||||
|
||||
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
|
||||
{
|
||||
return m_entry_class_table.at(entry_class_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,20 @@
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
|
||||
#include "engine/geospatial_query.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@@ -30,9 +33,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -49,7 +52,7 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
using QueryGraph = util::StaticGraph<EdgeData, true>;
|
||||
using GraphNode = QueryGraph::NodeArrayEntry;
|
||||
using GraphEdge = QueryGraph::EdgeArrayEntry;
|
||||
using NameIndexBlock = util::RangeTable<16, true>::BlockT;
|
||||
using IndexBlock = util::RangeTable<16, true>::BlockT;
|
||||
using InputEdge = QueryGraph::InputEdge;
|
||||
using RTreeLeaf = super::RTreeLeaf;
|
||||
using SharedRTree =
|
||||
@@ -94,6 +97,18 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
|
||||
std::shared_ptr<util::RangeTable<16, true>> m_name_table;
|
||||
|
||||
// bearing classes by node based node
|
||||
util::ShM<BearingClassID, true>::vector m_bearing_class_id_table;
|
||||
// entry class IDs
|
||||
util::ShM<EntryClassID, true>::vector m_entry_class_id_list;
|
||||
// the look-up table for entry classes. An entry class lists the possibility of entry for all
|
||||
// available turns. Such a class id is stored with every edge.
|
||||
util::ShM<util::guidance::EntryClass, true>::vector m_entry_class_table;
|
||||
// the look-up table for distinct bearing classes. A bearing class lists the available bearings
|
||||
// at an intersection
|
||||
std::shared_ptr<util::RangeTable<16, true>> m_bearing_ranges_table;
|
||||
util::ShM<DiscreteBearing, true>::vector m_bearing_values_table;
|
||||
|
||||
void LoadChecksum()
|
||||
{
|
||||
m_check_sum = *data_layout->GetBlockPtr<unsigned>(shared_memory,
|
||||
@@ -171,6 +186,13 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
util::ShM<unsigned, true>::vector name_id_list(
|
||||
name_id_list_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_ID_LIST]);
|
||||
m_name_ID_list = std::move(name_id_list);
|
||||
|
||||
auto entry_class_id_list_ptr = data_layout->GetBlockPtr<EntryClassID>(
|
||||
shared_memory, storage::SharedDataLayout::ENTRY_CLASSID);
|
||||
typename util::ShM<EntryClassID, true>::vector entry_class_id_list(
|
||||
entry_class_id_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::ENTRY_CLASSID]);
|
||||
m_entry_class_id_list = std::move(entry_class_id_list);
|
||||
}
|
||||
|
||||
void LoadViaNodeList()
|
||||
@@ -186,11 +208,11 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
{
|
||||
auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
|
||||
shared_memory, storage::SharedDataLayout::NAME_OFFSETS);
|
||||
auto blocks_ptr = data_layout->GetBlockPtr<NameIndexBlock>(
|
||||
auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
|
||||
shared_memory, storage::SharedDataLayout::NAME_BLOCKS);
|
||||
util::ShM<unsigned, true>::vector name_offsets(
|
||||
offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_OFFSETS]);
|
||||
util::ShM<NameIndexBlock, true>::vector name_blocks(
|
||||
util::ShM<IndexBlock, true>::vector name_blocks(
|
||||
blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::NAME_BLOCKS]);
|
||||
|
||||
auto names_list_ptr = data_layout->GetBlockPtr<char>(
|
||||
@@ -263,6 +285,40 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
m_datasource_name_lengths = std::move(datasource_name_lengths);
|
||||
}
|
||||
|
||||
void LoadIntersectionClasses()
|
||||
{
|
||||
auto bearing_class_id_ptr = data_layout->GetBlockPtr<BearingClassID>(
|
||||
shared_memory, storage::SharedDataLayout::BEARING_CLASSID);
|
||||
typename util::ShM<BearingClassID, true>::vector bearing_class_id_table(
|
||||
bearing_class_id_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::BEARING_CLASSID]);
|
||||
m_bearing_class_id_table = std::move(bearing_class_id_table);
|
||||
|
||||
auto bearing_class_ptr = data_layout->GetBlockPtr<DiscreteBearing>(
|
||||
shared_memory, storage::SharedDataLayout::BEARING_VALUES);
|
||||
typename util::ShM<DiscreteBearing, true>::vector bearing_class_table(
|
||||
bearing_class_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_VALUES]);
|
||||
m_bearing_values_table = std::move(bearing_class_table);
|
||||
|
||||
auto offsets_ptr = data_layout->GetBlockPtr<unsigned>(
|
||||
shared_memory, storage::SharedDataLayout::BEARING_OFFSETS);
|
||||
auto blocks_ptr = data_layout->GetBlockPtr<IndexBlock>(
|
||||
shared_memory, storage::SharedDataLayout::BEARING_BLOCKS);
|
||||
util::ShM<unsigned, true>::vector bearing_offsets(
|
||||
offsets_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_OFFSETS]);
|
||||
util::ShM<IndexBlock, true>::vector bearing_blocks(
|
||||
blocks_ptr, data_layout->num_entries[storage::SharedDataLayout::BEARING_BLOCKS]);
|
||||
|
||||
m_bearing_ranges_table = util::make_unique<util::RangeTable<16, true>>(
|
||||
bearing_offsets, bearing_blocks, static_cast<unsigned>(m_bearing_values_table.size()));
|
||||
|
||||
auto entry_class_ptr = data_layout->GetBlockPtr<util::guidance::EntryClass>(
|
||||
shared_memory, storage::SharedDataLayout::ENTRY_CLASS);
|
||||
typename util::ShM<util::guidance::EntryClass, true>::vector entry_class_table(
|
||||
entry_class_ptr, data_layout->num_entries[storage::SharedDataLayout::ENTRY_CLASS]);
|
||||
m_entry_class_table = std::move(entry_class_table);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~SharedDataFacade() {}
|
||||
|
||||
@@ -350,6 +406,7 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
LoadCoreInformation();
|
||||
LoadProfileProperties();
|
||||
LoadRTree();
|
||||
LoadIntersectionClasses();
|
||||
|
||||
util::SimpleLogger().Write() << "number of geometries: "
|
||||
<< m_coordinate_list.size();
|
||||
@@ -420,8 +477,7 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
result_nodes.clear();
|
||||
result_nodes.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
|
||||
result_nodes.emplace_back(edge.node_id);
|
||||
});
|
||||
}
|
||||
@@ -436,8 +492,7 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
result_weights.clear();
|
||||
result_weights.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) {
|
||||
result_weights.emplace_back(edge.weight);
|
||||
});
|
||||
}
|
||||
@@ -634,11 +689,9 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
}
|
||||
else
|
||||
{
|
||||
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id)
|
||||
{
|
||||
result_datasources.push_back(datasource_id);
|
||||
});
|
||||
std::for_each(
|
||||
m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,6 +716,33 @@ class SharedDataFacade final : public BaseDataFacade
|
||||
{
|
||||
return m_profile_properties->continue_straight_at_waypoint;
|
||||
}
|
||||
|
||||
BearingClassID GetBearingClassID(const NodeID id) const override final
|
||||
{
|
||||
return m_bearing_class_id_table.at(id);
|
||||
}
|
||||
|
||||
util::guidance::BearingClass
|
||||
GetBearingClass(const BearingClassID bearing_class_id) const override final
|
||||
{
|
||||
BOOST_ASSERT(bearing_class_id != INVALID_BEARING_CLASSID);
|
||||
auto range = m_bearing_ranges_table->GetRange(bearing_class_id);
|
||||
util::guidance::BearingClass result;
|
||||
for (auto itr = m_bearing_values_table.begin() + range.front();
|
||||
itr != m_bearing_values_table.begin() + range.back() + 1; ++itr)
|
||||
result.add(*itr);
|
||||
return result;
|
||||
}
|
||||
|
||||
EntryClassID GetEntryClassID(const EdgeID eid) const override final
|
||||
{
|
||||
return m_entry_class_id_list.at(eid);
|
||||
}
|
||||
|
||||
util::guidance::EntryClass GetEntryClass(const EntryClassID entry_class_id) const override final
|
||||
{
|
||||
return m_entry_class_table.at(entry_class_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,34 +43,40 @@ LegGeometry assembleGeometry(const DataFacadeT &facade,
|
||||
geometry.segment_offsets.push_back(0);
|
||||
geometry.locations.push_back(source_node.location);
|
||||
|
||||
auto cumulative_distance = 0.;
|
||||
auto current_distance = 0.;
|
||||
auto prev_coordinate = geometry.locations.front();
|
||||
for (const auto &path_point : leg_data)
|
||||
{
|
||||
auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node);
|
||||
current_distance +=
|
||||
current_distance =
|
||||
util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate);
|
||||
cumulative_distance += current_distance;
|
||||
|
||||
// all changes to this check have to be matched with assemble_steps
|
||||
if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
|
||||
{
|
||||
geometry.segment_distances.push_back(current_distance);
|
||||
geometry.segment_distances.push_back(cumulative_distance);
|
||||
geometry.segment_offsets.push_back(geometry.locations.size());
|
||||
current_distance = 0.;
|
||||
cumulative_distance = 0.;
|
||||
}
|
||||
|
||||
prev_coordinate = coordinate;
|
||||
geometry.annotations.emplace_back(LegGeometry::Annotation{current_distance, path_point.duration_until_turn / 10.});
|
||||
geometry.locations.push_back(std::move(coordinate));
|
||||
}
|
||||
current_distance +=
|
||||
current_distance =
|
||||
util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location);
|
||||
cumulative_distance += current_distance;
|
||||
// segment leading to the target node
|
||||
geometry.segment_distances.push_back(current_distance);
|
||||
geometry.segment_distances.push_back(cumulative_distance);
|
||||
geometry.annotations.emplace_back(LegGeometry::Annotation{current_distance, target_node.forward_weight / 10.});
|
||||
geometry.segment_offsets.push_back(geometry.locations.size());
|
||||
geometry.locations.push_back(target_node.location);
|
||||
|
||||
BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1);
|
||||
BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size());
|
||||
BOOST_ASSERT(geometry.annotations.size() == geometry.locations.size() - 1);
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstddef>
|
||||
@@ -25,14 +28,10 @@ namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index);
|
||||
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const WaypointType waypoint_type,
|
||||
const LegGeometry &leg_geometry);
|
||||
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry);
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry);
|
||||
std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index);
|
||||
} // ns detail
|
||||
|
||||
template <typename DataFacadeT>
|
||||
@@ -64,13 +63,18 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
std::size_t segment_index = 0;
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
|
||||
auto bearings = detail::getDepartBearings(leg_geometry);
|
||||
|
||||
StepManeuver maneuver{source_node.location, bearings.first,
|
||||
bearings.second, extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
WaypointType::Depart, 0};
|
||||
Intersection intersection{
|
||||
source_node.location,
|
||||
std::vector<short>({bearings.second}),
|
||||
std::vector<bool>({true}), Intersection::NO_INDEX, 0};
|
||||
|
||||
if (leg_data.size() > 0)
|
||||
{
|
||||
|
||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
|
||||
maneuver.location = source_node.location;
|
||||
|
||||
// PathData saves the information we need of the segment _before_ the turn,
|
||||
// but a RouteStep is with regard to the segment after the turn.
|
||||
// We need to skip the first segment because it is already covered by the
|
||||
@@ -91,17 +95,35 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
BOOST_ASSERT(segment_duration >= 0);
|
||||
const auto name = facade.GetNameForID(step_name_id);
|
||||
const auto distance = leg_geometry.segment_distances[segment_index];
|
||||
steps.push_back(RouteStep{step_name_id, name, NO_ROTARY_NAME,
|
||||
segment_duration / 10.0, distance, path_point.travel_mode,
|
||||
maneuver, leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
if (leg_data_index + 1 < leg_data.size()){
|
||||
|
||||
if (leg_data_index + 1 < leg_data.size())
|
||||
{
|
||||
step_name_id = leg_data[leg_data_index + 1].name_id;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
step_name_id = target_node.name_id;
|
||||
}
|
||||
maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
|
||||
leg_geometry, segment_index);
|
||||
steps.push_back(RouteStep{
|
||||
step_name_id, name, NO_ROTARY_NAME, segment_duration / 10.0, distance,
|
||||
path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1, {intersection}});
|
||||
|
||||
bearings = detail::getIntermediateBearings(leg_geometry, segment_index);
|
||||
const auto entry_class = facade.GetEntryClass(path_point.entry_classid);
|
||||
const auto bearing_class = facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node));
|
||||
intersection.in = bearing_class.findMatchingBearing(util::bearing::reverseBearing(bearings.first));
|
||||
intersection.out = bearing_class.findMatchingBearing(bearings.second);
|
||||
intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
|
||||
intersection.bearings.clear();
|
||||
std::copy(bearing_class.getAvailableBearings().begin(), bearing_class.getAvailableBearings().end(),
|
||||
std::back_inserter(intersection.bearings));
|
||||
intersection.entry.clear();
|
||||
for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size()))
|
||||
{
|
||||
intersection.entry.push_back(entry_class.allowsEntry(idx));
|
||||
}
|
||||
maneuver = {intersection.location, bearings.first, bearings.second, path_point.turn_instruction, WaypointType::None, 0};
|
||||
segment_index++;
|
||||
segment_duration = 0;
|
||||
}
|
||||
@@ -109,10 +131,10 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
const auto distance = leg_geometry.segment_distances[segment_index];
|
||||
const int duration = segment_duration + target_duration;
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id),
|
||||
NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver,
|
||||
leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
steps.push_back(RouteStep{
|
||||
step_name_id, facade.GetNameForID(step_name_id), NO_ROTARY_NAME, duration / 10.,
|
||||
distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1, {intersection}});
|
||||
}
|
||||
// In this case the source + target are on the same edge segment
|
||||
else
|
||||
@@ -123,29 +145,42 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
// |---| source_duration
|
||||
// |---------| target_duration
|
||||
|
||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
|
||||
int duration = target_duration - source_duration;
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
|
||||
steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id),
|
||||
NO_ROTARY_NAME, duration / 10.,
|
||||
leg_geometry.segment_distances[segment_index], source_mode,
|
||||
std::move(maneuver), leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
steps.push_back(RouteStep{
|
||||
source_node.name_id, facade.GetNameForID(source_node.name_id), NO_ROTARY_NAME,
|
||||
duration / 10., leg_geometry.segment_distances[segment_index], source_mode,
|
||||
std::move(maneuver), leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1, {intersection}});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
||||
bearings = detail::getArriveBearings(leg_geometry);
|
||||
// This step has length zero, the only reason we need it is the target location
|
||||
auto final_maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
|
||||
maneuver = {intersection.location, bearings.first, bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, 0};
|
||||
intersection = {
|
||||
target_node.location,
|
||||
std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}),
|
||||
std::vector<bool>({true}), 0, Intersection::NO_INDEX};
|
||||
|
||||
BOOST_ASSERT(!leg_geometry.locations.empty());
|
||||
steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id),
|
||||
NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode,
|
||||
final_maneuver, leg_geometry.locations.size() - 1,
|
||||
leg_geometry.locations.size()});
|
||||
std::move(maneuver), leg_geometry.locations.size() - 1,
|
||||
leg_geometry.locations.size(),
|
||||
{intersection}});
|
||||
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,13 @@ struct LegGeometry
|
||||
// length of the segment in meters
|
||||
std::vector<double> segment_distances;
|
||||
|
||||
// Per-coordinate metadata
|
||||
struct Annotation {
|
||||
double distance;
|
||||
double duration;
|
||||
};
|
||||
std::vector<Annotation> annotations;
|
||||
|
||||
std::size_t FrontIndex(std::size_t segment_index) const
|
||||
{
|
||||
return segment_offsets[segment_index];
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "engine/guidance/step_maneuver.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@@ -21,6 +24,27 @@ namespace guidance
|
||||
// Notable exceptions are Departure and Arrival steps.
|
||||
// Departue: s --> a --> b. Represents the segment s,a with location being s.
|
||||
// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment.
|
||||
|
||||
// A represenetation of intermediate intersections
|
||||
struct Intersection
|
||||
{
|
||||
static const constexpr std::size_t NO_INDEX = std::numeric_limits<std::size_t>::max();
|
||||
util::Coordinate location;
|
||||
std::vector<short> bearings;
|
||||
std::vector<bool> entry;
|
||||
std::size_t in;
|
||||
std::size_t out;
|
||||
};
|
||||
|
||||
inline Intersection getInvalidIntersection()
|
||||
{
|
||||
return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}},
|
||||
{},
|
||||
{},
|
||||
Intersection::NO_INDEX,
|
||||
Intersection::NO_INDEX};
|
||||
}
|
||||
|
||||
struct RouteStep
|
||||
{
|
||||
unsigned name_id;
|
||||
@@ -33,11 +57,21 @@ struct RouteStep
|
||||
// indices into the locations array stored the LegGeometry
|
||||
std::size_t geometry_begin;
|
||||
std::size_t geometry_end;
|
||||
std::vector<Intersection> intersections;
|
||||
};
|
||||
|
||||
inline RouteStep getInvalidRouteStep()
|
||||
{
|
||||
return {0, "", "", 0, 0, TRAVEL_MODE_INACCESSIBLE, getInvalidStepManeuver(), 0, 0};
|
||||
return {0,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
TRAVEL_MODE_INACCESSIBLE,
|
||||
getInvalidStepManeuver(),
|
||||
0,
|
||||
0,
|
||||
{getInvalidIntersection()}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,14 @@ enum class WaypointType : std::uint8_t
|
||||
Depart,
|
||||
};
|
||||
|
||||
// A represenetation of intermediate intersections
|
||||
struct IntermediateIntersection
|
||||
{
|
||||
double duration;
|
||||
double distance;
|
||||
util::Coordinate location;
|
||||
};
|
||||
|
||||
struct StepManeuver
|
||||
{
|
||||
util::Coordinate location;
|
||||
double bearing_before;
|
||||
double bearing_after;
|
||||
short bearing_before;
|
||||
short bearing_after;
|
||||
extractor::guidance::TurnInstruction instruction;
|
||||
WaypointType waypoint_type;
|
||||
unsigned exit;
|
||||
std::vector<IntermediateIntersection> intersections;
|
||||
};
|
||||
|
||||
inline StepManeuver getInvalidStepManeuver()
|
||||
@@ -47,8 +38,7 @@ inline StepManeuver getInvalidStepManeuver()
|
||||
0,
|
||||
extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
WaypointType::None,
|
||||
0,
|
||||
{}};
|
||||
0};
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
|
||||
@@ -49,7 +49,7 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc
|
||||
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
|
||||
}
|
||||
|
||||
inline extractor::guidance::DirectionModifier angleToDirectionModifier(const double bearing)
|
||||
inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(const double bearing)
|
||||
{
|
||||
if (bearing < 135)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define RAW_ROUTE_DATA_H
|
||||
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
@@ -29,6 +29,8 @@ struct PathData
|
||||
extractor::guidance::TurnInstruction turn_instruction;
|
||||
// travel mode of the street that leads to the turn
|
||||
extractor::TravelMode travel_mode : 4;
|
||||
// entry class of the turn, indicating possibility of turns
|
||||
EntryClassID entry_classid;
|
||||
};
|
||||
|
||||
struct InternalRouteResult
|
||||
|
||||
@@ -162,8 +162,6 @@ class AlternativeRouting final
|
||||
if (path_is_a_loop)
|
||||
{
|
||||
// Self Loop
|
||||
BOOST_ASSERT(forward_heap1.GetData(middle_node).parent == middle_node &&
|
||||
reverse_heap1.GetData(middle_node).parent == middle_node);
|
||||
packed_forward_path.push_back(middle_node);
|
||||
packed_forward_path.push_back(middle_node);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef ROUTING_BASE_HPP
|
||||
#define ROUTING_BASE_HPP
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <numeric>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -226,6 +226,11 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
recursion_stack.emplace(*std::prev(current), *current);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(*packed_path_begin == phantom_node_pair.source_phantom.forward_segment_id.id ||
|
||||
*packed_path_begin == phantom_node_pair.source_phantom.reverse_segment_id.id);
|
||||
BOOST_ASSERT(*std::prev(packed_path_end) == phantom_node_pair.target_phantom.forward_segment_id.id ||
|
||||
*std::prev(packed_path_end) == phantom_node_pair.target_phantom.reverse_segment_id.id);
|
||||
|
||||
std::pair<NodeID, NodeID> edge;
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
@@ -317,13 +322,12 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
for (std::size_t i = start_index; i < end_index; ++i)
|
||||
{
|
||||
unpacked_path.push_back(
|
||||
PathData{id_vector[i],
|
||||
name_index,
|
||||
weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
travel_mode});
|
||||
PathData{id_vector[i], name_index, weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), travel_mode,
|
||||
INVALID_ENTRY_CLASSID});
|
||||
}
|
||||
BOOST_ASSERT(unpacked_path.size() > 0);
|
||||
unpacked_path.back().entry_classid = facade->GetEntryClassID(ed.id);
|
||||
unpacked_path.back().turn_instruction = turn_instruction;
|
||||
unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
|
||||
}
|
||||
@@ -376,14 +380,12 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
{
|
||||
BOOST_ASSERT(i < id_vector.size());
|
||||
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
|
||||
unpacked_path.push_back(
|
||||
PathData{id_vector[i],
|
||||
phantom_node_pair.target_phantom.name_id,
|
||||
weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
target_traversed_in_reverse
|
||||
? phantom_node_pair.target_phantom.backward_travel_mode
|
||||
: phantom_node_pair.target_phantom.forward_travel_mode});
|
||||
unpacked_path.push_back(PathData{
|
||||
id_vector[i], phantom_node_pair.target_phantom.name_id, weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
|
||||
: phantom_node_pair.target_phantom.forward_travel_mode,
|
||||
INVALID_ENTRY_CLASSID});
|
||||
}
|
||||
|
||||
if (unpacked_path.size() > 0)
|
||||
@@ -641,9 +643,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
}
|
||||
// TODO check if unordered_set might be faster
|
||||
// sort by id and increasing by distance
|
||||
auto entry_point_comparator =
|
||||
[](const std::pair<NodeID, EdgeWeight> &lhs, const std::pair<NodeID, EdgeWeight> &rhs)
|
||||
{
|
||||
auto entry_point_comparator = [](const std::pair<NodeID, EdgeWeight> &lhs,
|
||||
const std::pair<NodeID, EdgeWeight> &rhs) {
|
||||
return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second < rhs.second);
|
||||
};
|
||||
std::sort(forward_entry_points.begin(), forward_entry_points.end(), entry_point_comparator);
|
||||
|
||||
@@ -3,33 +3,35 @@
|
||||
#ifndef EDGE_BASED_GRAPH_FACTORY_HPP_
|
||||
#define EDGE_BASED_GRAPH_FACTORY_HPP_
|
||||
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/guidance/turn_analysis.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "extractor/guidance/turn_analysis.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
@@ -67,6 +69,12 @@ class EdgeBasedGraphFactory
|
||||
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
|
||||
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
|
||||
|
||||
// These access functions don't destroy the content
|
||||
const std::vector<BearingClassID> &GetBearingClassIds() const;
|
||||
std::vector<BearingClassID> &GetBearingClassIds();
|
||||
std::vector<util::guidance::BearingClass> GetBearingClasses() const;
|
||||
std::vector<util::guidance::EntryClass> GetEntryClasses() const;
|
||||
|
||||
unsigned GetHighestEdgeID();
|
||||
|
||||
// Basic analysis of a turn (u --(e1)-- v --(e2)-- w)
|
||||
@@ -127,6 +135,10 @@ class EdgeBasedGraphFactory
|
||||
std::size_t restricted_turns_counter;
|
||||
std::size_t skipped_uturns_counter;
|
||||
std::size_t skipped_barrier_turns_counter;
|
||||
|
||||
std::unordered_map<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
|
||||
std::vector<BearingClassID> bearing_class_by_node_based_node;
|
||||
std::unordered_map<util::guidance::EntryClass, EntryClassID> entry_class_hash;
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
#ifndef EXTRACTION_HELPER_FUNCTIONS_HPP
|
||||
#define EXTRACTION_HELPER_FUNCTIONS_HPP
|
||||
|
||||
#include "util/cast.hpp"
|
||||
#include "util/iso_8601_duration_parser.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string_regex.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/spirit/include/phoenix.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
@@ -17,83 +12,106 @@ namespace osrm
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
inline bool simple_duration_is_valid(const std::string &s)
|
||||
namespace detail
|
||||
{
|
||||
boost::regex simple_format(
|
||||
"((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)",
|
||||
boost::regex_constants::icase | boost::regex_constants::perl);
|
||||
|
||||
const bool simple_matched = regex_match(s, simple_format);
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
if (simple_matched)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool iso_8601_duration_is_valid(const std::string &s)
|
||||
template <typename Iterator> struct iso_8601_grammar : qi::grammar<Iterator, unsigned()>
|
||||
{
|
||||
util::iso_8601_grammar<std::string::const_iterator> iso_parser;
|
||||
const bool result = boost::spirit::qi::parse(s.begin(), s.end(), iso_parser);
|
||||
iso_8601_grammar()
|
||||
: iso_8601_grammar::base_type(root)
|
||||
|
||||
// check if the was an error with the request
|
||||
if (result && (0 != iso_parser.get_duration()))
|
||||
{
|
||||
return true;
|
||||
using qi::_1;
|
||||
using qi::_a;
|
||||
using qi::_b;
|
||||
using qi::_c;
|
||||
using qi::_pass;
|
||||
using qi::_val;
|
||||
using qi::eoi;
|
||||
using qi::eps;
|
||||
using qi::uint_;
|
||||
using qi::char_;
|
||||
|
||||
hh = uint2_p[_pass = bind([](unsigned x) { return x < 24; }, _1), _val = _1];
|
||||
mm = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
|
||||
ss = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
|
||||
|
||||
osm_time
|
||||
= (uint_p[_a = _1] >> eoi) [_val = _a * 60]
|
||||
| (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> eoi) [_val = _a * 3600 + _b * 60]
|
||||
| (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> ':' >> uint_p[_c = _1] >> eoi) [_val = _a * 3600 + _b * 60 + _c]
|
||||
;
|
||||
|
||||
alternative_time
|
||||
= ('T' >> hh[_a = _1] >> mm[_b = _1] >> ss[_c = _1]) [_val = _a * 3600 + _b * 60 + _c]
|
||||
;
|
||||
|
||||
extended_time
|
||||
= ('T' >> hh[_a = _1] >> ':' >> mm[_b = _1] >> ':' >> ss[_c = _1]) [_val = _a * 3600 + _b * 60 + _c]
|
||||
;
|
||||
|
||||
standard_time
|
||||
= ('T'
|
||||
>> -(uint_ >> char_("Hh"))[_a = _1]
|
||||
>> -(uint_ >> char_("Mm"))[_b = _1]
|
||||
>> -(uint_ >> char_("Ss"))[_c = _1]) [_val = _a * 3600 + _b * 60 + _c]
|
||||
;
|
||||
|
||||
standard_date
|
||||
= (uint_ >> char_("Dd")) [_val = _1 * 86400]
|
||||
;
|
||||
|
||||
standard_week
|
||||
= (uint_ >> char_("Ww")) [_val = _1 * 604800]
|
||||
;
|
||||
|
||||
iso_period
|
||||
= osm_time [_val = _1]
|
||||
| ('P' >> standard_week >> eoi) [_val = _1]
|
||||
| ('P' >> ( alternative_time[_a = 0, _b = _1]
|
||||
| extended_time[_a = 0, _b = _1]
|
||||
| (eps[_a = 0, _b = 0] >> -standard_date[_a = _1] >> -standard_time[_b = _1] ) )
|
||||
>> eoi) [_val = _a + _b]
|
||||
;
|
||||
|
||||
root = iso_period;
|
||||
}
|
||||
return false;
|
||||
|
||||
qi::rule<Iterator, unsigned()> root;
|
||||
qi::rule<Iterator, unsigned(), qi::locals<unsigned, unsigned>> iso_period;
|
||||
qi::rule<Iterator, unsigned(), qi::locals<unsigned, unsigned, unsigned>> osm_time, standard_time, alternative_time, extended_time;
|
||||
qi::rule<Iterator, unsigned()> standard_date, standard_week;
|
||||
qi::rule<Iterator, unsigned()> hh, mm, ss;
|
||||
|
||||
qi::uint_parser<unsigned, 10, 1, 2> uint_p;
|
||||
qi::uint_parser<unsigned, 10, 2, 2> uint2_p;
|
||||
};
|
||||
}
|
||||
|
||||
inline bool durationIsValid(const std::string &s)
|
||||
{
|
||||
return simple_duration_is_valid(s) || iso_8601_duration_is_valid(s);
|
||||
static detail::iso_8601_grammar<std::string::const_iterator> const iso_8601_grammar;
|
||||
|
||||
std::string::const_iterator iter = s.begin();
|
||||
unsigned duration = 0;
|
||||
boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration);
|
||||
|
||||
return !s.empty() && iter == s.end();
|
||||
}
|
||||
|
||||
inline unsigned parseDuration(const std::string &s)
|
||||
{
|
||||
if (simple_duration_is_valid(s))
|
||||
{
|
||||
unsigned hours = 0;
|
||||
unsigned minutes = 0;
|
||||
unsigned seconds = 0;
|
||||
boost::regex e(
|
||||
"((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)",
|
||||
boost::regex_constants::icase | boost::regex_constants::perl);
|
||||
static detail::iso_8601_grammar<std::string::const_iterator> const iso_8601_grammar;
|
||||
|
||||
std::vector<std::string> result;
|
||||
boost::algorithm::split_regex(result, s, boost::regex(":"));
|
||||
const bool matched = regex_match(s, e);
|
||||
if (matched)
|
||||
{
|
||||
if (1 == result.size())
|
||||
{
|
||||
minutes = std::stoul(result[0]);
|
||||
}
|
||||
if (2 == result.size())
|
||||
{
|
||||
minutes = std::stoul(result[1]);
|
||||
hours = std::stoul(result[0]);
|
||||
}
|
||||
if (3 == result.size())
|
||||
{
|
||||
seconds = std::stoul(result[2]);
|
||||
minutes = std::stoul(result[1]);
|
||||
hours = std::stoul(result[0]);
|
||||
}
|
||||
return (3600 * hours + 60 * minutes + seconds);
|
||||
}
|
||||
}
|
||||
else if (iso_8601_duration_is_valid(s))
|
||||
{
|
||||
util::iso_8601_grammar<std::string::const_iterator> iso_parser;
|
||||
boost::spirit::qi::parse(s.begin(), s.end(), iso_parser);
|
||||
std::string::const_iterator iter = s.begin();
|
||||
unsigned duration = 0;
|
||||
boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration);
|
||||
|
||||
return iso_parser.get_duration();
|
||||
}
|
||||
|
||||
return std::numeric_limits<unsigned>::max();
|
||||
return !s.empty() && iter == s.end() ? duration : std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define EXTRACTOR_HPP
|
||||
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/extractor_config.hpp"
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "extractor/extractor_config.hpp"
|
||||
#include "extractor/graph_compressor.hpp"
|
||||
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
@@ -52,14 +55,16 @@ class Extractor
|
||||
ExtractorConfig config;
|
||||
|
||||
std::pair<std::size_t, std::size_t>
|
||||
BuildEdgeExpandedGraph(lua_State* lua_state,
|
||||
const ProfileProperties& profile_properties,
|
||||
BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
const ProfileProperties &profile_properties,
|
||||
std::vector<QueryNode> &internal_to_external_node_map,
|
||||
std::vector<EdgeBasedNode> &node_based_edge_list,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list);
|
||||
void WriteProfileProperties(const std::string& output_path, const ProfileProperties& properties) const;
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
const std::string &intersection_class_output_file);
|
||||
void WriteProfileProperties(const std::string &output_path,
|
||||
const ProfileProperties &properties) const;
|
||||
void WriteNodeMapping(const std::vector<QueryNode> &internal_to_external_node_map);
|
||||
void FindComponents(unsigned max_edge_id,
|
||||
const util::DeallocatingVector<EdgeBasedEdge> &edges,
|
||||
@@ -76,6 +81,12 @@ class Extractor
|
||||
void WriteEdgeBasedGraph(const std::string &output_file_filename,
|
||||
const size_t max_edge_id,
|
||||
util::DeallocatingVector<EdgeBasedEdge> const &edge_based_edge_list);
|
||||
|
||||
void WriteIntersectionClassificationData(
|
||||
const std::string &output_file_name,
|
||||
const std::vector<std::uint32_t> &node_based_intersection_classes,
|
||||
const std::vector<util::guidance::BearingClass> &bearing_classes,
|
||||
const std::vector<util::guidance::EntryClass> &entry_classes) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ struct ExtractorConfig
|
||||
edge_penalty_path = basepath + ".osrm.edge_penalties";
|
||||
edge_based_node_weights_output_path = basepath + ".osrm.enw";
|
||||
profile_properties_output_path = basepath + ".osrm.properties";
|
||||
intersection_class_data_output_path = basepath + ".osrm.icd";
|
||||
}
|
||||
|
||||
boost::filesystem::path config_file_path;
|
||||
@@ -90,6 +91,7 @@ struct ExtractorConfig
|
||||
std::string rtree_nodes_output_path;
|
||||
std::string rtree_leafs_output_path;
|
||||
std::string profile_properties_output_path;
|
||||
std::string intersection_class_data_output_path;
|
||||
|
||||
unsigned requested_num_threads;
|
||||
unsigned small_component_size;
|
||||
|
||||
@@ -49,7 +49,7 @@ class IntersectionHandler
|
||||
std::size_t countValid(const Intersection &intersection) const;
|
||||
|
||||
// Decide on a basic turn types
|
||||
TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Get the Instruction for an obvious turn
|
||||
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
|
||||
|
||||
@@ -41,7 +41,8 @@ const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, t
|
||||
const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true};
|
||||
const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = {
|
||||
0, 36, 93, 121, 136, 163, 220, 255};
|
||||
const constexpr double discrete_angle_step_size = 360. / 256.;
|
||||
|
||||
const constexpr double discrete_angle_step_size = 360. / 24;
|
||||
|
||||
template <typename IteratorType>
|
||||
util::Coordinate
|
||||
@@ -144,13 +145,13 @@ getRepresentativeCoordinate(const NodeID from_node,
|
||||
}
|
||||
|
||||
// shift an instruction around the degree circle in CCW order
|
||||
inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier)
|
||||
inline DirectionModifier::Enum forcedShiftCCW(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
return static_cast<DirectionModifier>((static_cast<std::uint32_t>(modifier) + 1) %
|
||||
detail::num_direction_modifiers);
|
||||
return static_cast<DirectionModifier::Enum>((static_cast<std::uint32_t>(modifier) + 1) %
|
||||
detail::num_direction_modifiers);
|
||||
}
|
||||
|
||||
inline DirectionModifier shiftCCW(const DirectionModifier modifier)
|
||||
inline DirectionModifier::Enum shiftCCW(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
if (detail::shiftable_ccw[static_cast<int>(modifier)])
|
||||
return forcedShiftCCW(modifier);
|
||||
@@ -159,14 +160,14 @@ inline DirectionModifier shiftCCW(const DirectionModifier modifier)
|
||||
}
|
||||
|
||||
// shift an instruction around the degree circle in CW order
|
||||
inline DirectionModifier forcedShiftCW(const DirectionModifier modifier)
|
||||
inline DirectionModifier::Enum forcedShiftCW(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
return static_cast<DirectionModifier>(
|
||||
return static_cast<DirectionModifier::Enum>(
|
||||
(static_cast<std::uint32_t>(modifier) + detail::num_direction_modifiers - 1) %
|
||||
detail::num_direction_modifiers);
|
||||
}
|
||||
|
||||
inline DirectionModifier shiftCW(const DirectionModifier modifier)
|
||||
inline DirectionModifier::Enum shiftCW(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
if (detail::shiftable_cw[static_cast<int>(modifier)])
|
||||
return forcedShiftCW(modifier);
|
||||
@@ -174,7 +175,7 @@ inline DirectionModifier shiftCW(const DirectionModifier modifier)
|
||||
return modifier;
|
||||
}
|
||||
|
||||
inline bool isBasic(const TurnType type)
|
||||
inline bool isBasic(const TurnType::Enum type)
|
||||
{
|
||||
return type == TurnType::Turn || type == TurnType::EndOfRoad;
|
||||
}
|
||||
@@ -218,7 +219,7 @@ inline bool isSlightTurn(const TurnInstruction turn)
|
||||
turn.direction_modifier == DirectionModifier::SlightLeft);
|
||||
}
|
||||
|
||||
inline bool isSlightModifier(const DirectionModifier direction_modifier)
|
||||
inline bool isSlightModifier(const DirectionModifier::Enum direction_modifier)
|
||||
{
|
||||
return (direction_modifier == DirectionModifier::Straight ||
|
||||
direction_modifier == DirectionModifier::SlightRight ||
|
||||
@@ -246,15 +247,17 @@ inline bool isConflict(const TurnInstruction first, const TurnInstruction second
|
||||
inline DiscreteAngle discretizeAngle(const double angle)
|
||||
{
|
||||
BOOST_ASSERT(angle >= 0. && angle <= 360.);
|
||||
return DiscreteAngle(static_cast<std::uint8_t>(angle / detail::discrete_angle_step_size));
|
||||
return DiscreteAngle(static_cast<std::uint8_t>(
|
||||
(angle + 0.5 * detail::discrete_angle_step_size) / detail::discrete_angle_step_size));
|
||||
}
|
||||
|
||||
inline double angleFromDiscreteAngle(const DiscreteAngle angle)
|
||||
{
|
||||
return static_cast<double>(angle) * detail::discrete_angle_step_size;
|
||||
return static_cast<double>(angle) * detail::discrete_angle_step_size +
|
||||
0.5 * detail::discrete_angle_step_size;
|
||||
}
|
||||
|
||||
inline double getAngularPenalty(const double angle, DirectionModifier modifier)
|
||||
inline double getAngularPenalty(const double angle, DirectionModifier::Enum modifier)
|
||||
{
|
||||
// these are not aligned with getTurnDirection but represent an ideal center
|
||||
const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
|
||||
@@ -275,16 +278,16 @@ inline double getTurnConfidence(const double angle, TurnInstruction instruction)
|
||||
}
|
||||
|
||||
// swaps left <-> right modifier types
|
||||
inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
|
||||
inline DirectionModifier::Enum mirrorDirectionModifier(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
const constexpr DirectionModifier results[] = {
|
||||
const constexpr DirectionModifier::Enum results[] = {
|
||||
DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left,
|
||||
DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight,
|
||||
DirectionModifier::Right, DirectionModifier::SharpRight};
|
||||
return results[modifier];
|
||||
}
|
||||
|
||||
inline bool canBeSuppressed(const TurnType type)
|
||||
inline bool canBeSuppressed(const TurnType::Enum type)
|
||||
{
|
||||
if (type == TurnType::Turn)
|
||||
return true;
|
||||
@@ -297,7 +300,7 @@ inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class)
|
||||
road_class == FunctionalRoadClass::SERVICE;
|
||||
}
|
||||
|
||||
inline bool isDistinct(const DirectionModifier first, const DirectionModifier second)
|
||||
inline bool isDistinct(const DirectionModifier::Enum first, const DirectionModifier::Enum second)
|
||||
{
|
||||
if ((first + 1) % detail::num_direction_modifiers == second)
|
||||
return false;
|
||||
@@ -325,8 +328,8 @@ inline bool requiresNameAnnounced(const std::string &from,
|
||||
const std::string &to,
|
||||
const SuffixTable &suffix_table)
|
||||
{
|
||||
//first is empty and the second is not
|
||||
if(from.empty() && !to.empty())
|
||||
// first is empty and the second is not
|
||||
if (from.empty() && !to.empty())
|
||||
return true;
|
||||
|
||||
// FIXME, handle in profile to begin with?
|
||||
@@ -378,8 +381,8 @@ inline bool requiresNameAnnounced(const std::string &from,
|
||||
if (!checkTable(first_prefix_and_suffixes.first))
|
||||
return false;
|
||||
return !first.compare(first_prefix_and_suffixes.first.length(), std::string::npos,
|
||||
second, second_prefix_and_suffixes.first.length(),
|
||||
std::string::npos);
|
||||
second, second_prefix_and_suffixes.first.length(),
|
||||
std::string::npos);
|
||||
}();
|
||||
|
||||
const bool is_suffix_change = [&]() -> bool {
|
||||
@@ -388,7 +391,8 @@ inline bool requiresNameAnnounced(const std::string &from,
|
||||
if (!checkTable(first_prefix_and_suffixes.second))
|
||||
return false;
|
||||
return !first.compare(0, first.length() - first_prefix_and_suffixes.second.length(),
|
||||
second, 0, second.length() - second_prefix_and_suffixes.second.length());
|
||||
second, 0,
|
||||
second.length() - second_prefix_and_suffixes.second.length());
|
||||
}();
|
||||
|
||||
return is_prefix_change || is_suffix_change;
|
||||
@@ -437,7 +441,7 @@ inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoa
|
||||
// turn and vice versa.
|
||||
inline ConnectedRoad mirror(ConnectedRoad road)
|
||||
{
|
||||
const constexpr DirectionModifier mirrored_modifiers[] = {
|
||||
const constexpr DirectionModifier::Enum mirrored_modifiers[] = {
|
||||
DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left,
|
||||
DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight,
|
||||
DirectionModifier::Right, DirectionModifier::SharpRight};
|
||||
|
||||
@@ -46,6 +46,9 @@ class TurnAnalysis
|
||||
// the entry into the turn analysis
|
||||
std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
|
||||
|
||||
// access to the intersection representation for classification purposes
|
||||
Intersection getIntersection(const NodeID from_node, const EdgeID via_eid) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const IntersectionGenerator intersection_generator;
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
#ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
|
||||
#define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
|
||||
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -21,99 +26,12 @@ namespace extractor
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct TurnPossibility
|
||||
{
|
||||
TurnPossibility(DiscreteAngle angle, EdgeID edge_id)
|
||||
: angle(std::move(angle)), edge_id(std::move(edge_id))
|
||||
{
|
||||
}
|
||||
|
||||
TurnPossibility() : angle(0), edge_id(SPECIAL_EDGEID) {}
|
||||
|
||||
DiscreteAngle angle;
|
||||
EdgeID edge_id;
|
||||
};
|
||||
|
||||
struct CompareTurnPossibilities
|
||||
{
|
||||
bool operator()(const std::vector<TurnPossibility> &left,
|
||||
const std::vector<TurnPossibility> &right) const
|
||||
{
|
||||
if (left.size() < right.size())
|
||||
return true;
|
||||
if (left.size() > right.size())
|
||||
return false;
|
||||
for (std::size_t i = 0; i < left.size(); ++i)
|
||||
{
|
||||
if ((((int)left[i].angle + 16) % 256) / 32 < (((int)right[i].angle + 16) % 256) / 32)
|
||||
return true;
|
||||
if ((((int)left[i].angle + 16) % 256) / 32 > (((int)right[i].angle + 16) % 256) / 32)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::vector<TurnPossibility>
|
||||
std::pair<util::guidance::EntryClass,util::guidance::BearingClass>
|
||||
classifyIntersection(NodeID nid,
|
||||
const util::NodeBasedDynamicGraph &graph,
|
||||
const Intersection &intersection,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
{
|
||||
|
||||
std::vector<TurnPossibility> turns;
|
||||
|
||||
if (graph.BeginEdges(nid) == graph.EndEdges(nid))
|
||||
return std::vector<TurnPossibility>();
|
||||
|
||||
const EdgeID base_id = graph.BeginEdges(nid);
|
||||
const auto base_coordinate = getRepresentativeCoordinate(nid, graph.GetTarget(base_id), base_id,
|
||||
graph.GetEdgeData(base_id).reversed,
|
||||
compressed_geometries, query_nodes);
|
||||
const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
|
||||
|
||||
// generate a list of all turn angles between a base edge, the node and a current edge
|
||||
for (const EdgeID eid : graph.GetAdjacentEdgeRange(nid))
|
||||
{
|
||||
const auto edge_coordinate = getRepresentativeCoordinate(
|
||||
nid, graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
|
||||
|
||||
double angle = util::coordinate_calculation::computeAngle(base_coordinate, node_coordinate,
|
||||
edge_coordinate);
|
||||
turns.emplace_back(discretizeAngle(angle), eid);
|
||||
}
|
||||
|
||||
std::sort(turns.begin(), turns.end(),
|
||||
[](const TurnPossibility left, const TurnPossibility right) {
|
||||
return left.angle < right.angle;
|
||||
});
|
||||
|
||||
turns.push_back(turns.front()); // sentinel
|
||||
for (std::size_t turn_nr = 0; turn_nr + 1 < turns.size(); ++turn_nr)
|
||||
{
|
||||
turns[turn_nr].angle = (256 + static_cast<uint32_t>(turns[turn_nr + 1].angle) -
|
||||
static_cast<uint32_t>(turns[turn_nr].angle)) %
|
||||
256; // calculate the difference to the right
|
||||
}
|
||||
turns.pop_back(); // remove sentinel again
|
||||
|
||||
// find largest:
|
||||
std::size_t best_id = 0;
|
||||
DiscreteAngle largest_turn_angle = turns.front().angle;
|
||||
for (std::size_t current_turn_id = 1; current_turn_id < turns.size(); ++current_turn_id)
|
||||
{
|
||||
if (turns[current_turn_id].angle > largest_turn_angle)
|
||||
{
|
||||
largest_turn_angle = turns[current_turn_id].angle;
|
||||
best_id = current_turn_id;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate all angles so the largest angle comes first
|
||||
std::rotate(turns.begin(), turns.begin() + best_id, turns.end());
|
||||
|
||||
return turns;
|
||||
}
|
||||
const std::vector<extractor::QueryNode> &query_nodes);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
|
||||
@@ -20,62 +20,62 @@ namespace detail
|
||||
const constexpr uint8_t num_direction_modifiers = 8;
|
||||
} // detail
|
||||
|
||||
// direction modifiers based on angle
|
||||
// Would be nice to have
|
||||
// enum class DirectionModifier : unsigned char
|
||||
enum DirectionModifier
|
||||
{
|
||||
UTurn,
|
||||
SharpRight,
|
||||
Right,
|
||||
SlightRight,
|
||||
Straight,
|
||||
SlightLeft,
|
||||
Left,
|
||||
SharpLeft
|
||||
};
|
||||
|
||||
// enum class TurnType : unsigned char
|
||||
enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
|
||||
// direction modifiers based on angle
|
||||
namespace DirectionModifier
|
||||
{
|
||||
Invalid, // no valid turn instruction
|
||||
NewName, // no turn, but name changes
|
||||
Continue, // remain on a street
|
||||
Turn, // basic turn
|
||||
Merge, // merge onto a street
|
||||
OnRamp, // special turn (highway ramp on-ramps)
|
||||
OffRamp, // special turn, highway exit
|
||||
Fork, // fork road splitting up
|
||||
EndOfRoad, // T intersection
|
||||
Notification, // Travel Mode Changes, Restrictions apply...
|
||||
EnterRoundabout, // Entering a small Roundabout
|
||||
EnterAndExitRoundabout, // Touching a roundabout
|
||||
EnterRotary, // Enter a rotary
|
||||
EnterAndExitRotary, // Touching a rotary
|
||||
EnterRoundaboutIntersection, // Entering a small Roundabout
|
||||
EnterAndExitRoundaboutIntersection, // Touching a roundabout
|
||||
NoTurn, // end of segment without turn/middle of a segment
|
||||
Suppressed, // location that suppresses a turn
|
||||
EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit
|
||||
ExitRoundabout, // Exiting a small Roundabout
|
||||
EnterRotaryAtExit, // Enter A Rotary at a countable exit
|
||||
ExitRotary, // Exit a rotary
|
||||
EnterRoundaboutIntersectionAtExit, // Entering a small Roundabout at a countable exit
|
||||
ExitRoundaboutIntersection, // Exiting a small Roundabout
|
||||
StayOnRoundabout // Continue on Either a small or a large Roundabout
|
||||
};
|
||||
typedef std::uint8_t Enum;
|
||||
const constexpr Enum UTurn = 0;
|
||||
const constexpr Enum SharpRight = 1;
|
||||
const constexpr Enum Right = 2;
|
||||
const constexpr Enum SlightRight = 3;
|
||||
const constexpr Enum Straight = 4;
|
||||
const constexpr Enum SlightLeft = 5;
|
||||
const constexpr Enum Left = 6;
|
||||
const constexpr Enum SharpLeft = 7;
|
||||
}
|
||||
|
||||
namespace TurnType
|
||||
{
|
||||
typedef std::uint8_t Enum;
|
||||
const constexpr Enum Invalid = 0; // no valid turn instruction
|
||||
const constexpr Enum NewName = 1; // no turn, but name changes
|
||||
const constexpr Enum Continue = 2; // remain on a street
|
||||
const constexpr Enum Turn = 3; // basic turn
|
||||
const constexpr Enum Merge = 4; // merge onto a street
|
||||
const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps)
|
||||
const constexpr Enum OffRamp = 6; // special turn, highway exit
|
||||
const constexpr Enum Fork = 7; // fork road splitting up
|
||||
const constexpr Enum EndOfRoad = 8; // T intersection
|
||||
const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply...
|
||||
const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout
|
||||
const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout
|
||||
const constexpr Enum EnterRotary = 12; // Enter a rotary
|
||||
const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
|
||||
const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
|
||||
const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
|
||||
const constexpr Enum NoTurn = 16; // end of segment without turn/middle of a segment
|
||||
const constexpr Enum Suppressed = 17; // location that suppresses a turn
|
||||
const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit
|
||||
const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout
|
||||
const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit
|
||||
const constexpr Enum ExitRotary = 21; // Exit a rotary
|
||||
const constexpr Enum EnterRoundaboutIntersectionAtExit = 22; // Entering a small Roundabout at a countable exit
|
||||
const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout
|
||||
const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout
|
||||
}
|
||||
|
||||
// turn angle in 1.40625 degree -> 128 == 180 degree
|
||||
struct TurnInstruction
|
||||
{
|
||||
TurnInstruction(const TurnType type = TurnType::Invalid,
|
||||
const DirectionModifier direction_modifier = DirectionModifier::Straight)
|
||||
TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
|
||||
const DirectionModifier::Enum direction_modifier = DirectionModifier::Straight)
|
||||
: type(type), direction_modifier(direction_modifier)
|
||||
{
|
||||
}
|
||||
|
||||
TurnType type : 5;
|
||||
DirectionModifier direction_modifier : 3;
|
||||
TurnType::Enum type : 5;
|
||||
DirectionModifier::Enum direction_modifier : 3;
|
||||
|
||||
static TurnInstruction INVALID()
|
||||
{
|
||||
@@ -87,53 +87,55 @@ struct TurnInstruction
|
||||
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
|
||||
}
|
||||
|
||||
static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier modifier)
|
||||
static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier::Enum modifier)
|
||||
{
|
||||
return TurnInstruction(TurnType::StayOnRoundabout, modifier);
|
||||
}
|
||||
|
||||
static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
|
||||
const DirectionModifier modifier)
|
||||
const DirectionModifier::Enum modifier)
|
||||
{
|
||||
const constexpr TurnType enter_instruction[] = {
|
||||
const constexpr TurnType::Enum enter_instruction[] = {
|
||||
TurnType::Invalid, TurnType::EnterRoundabout, TurnType::EnterRotary,
|
||||
TurnType::EnterRoundaboutIntersection};
|
||||
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
|
||||
}
|
||||
|
||||
static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
|
||||
const DirectionModifier modifier)
|
||||
const DirectionModifier::Enum modifier)
|
||||
{
|
||||
const constexpr TurnType exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout,
|
||||
const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout,
|
||||
TurnType::ExitRotary,
|
||||
TurnType::ExitRoundaboutIntersection};
|
||||
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
|
||||
}
|
||||
|
||||
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
|
||||
const DirectionModifier modifier)
|
||||
const DirectionModifier::Enum modifier)
|
||||
{
|
||||
const constexpr TurnType exit_instruction[] = {
|
||||
const constexpr TurnType::Enum exit_instruction[] = {
|
||||
TurnType::Invalid, TurnType::EnterAndExitRoundabout, TurnType::EnterAndExitRotary,
|
||||
TurnType::EnterAndExitRoundaboutIntersection};
|
||||
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
|
||||
}
|
||||
|
||||
static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
|
||||
const DirectionModifier modifier)
|
||||
const DirectionModifier::Enum modifier)
|
||||
{
|
||||
const constexpr TurnType enter_instruction[] = {
|
||||
const constexpr TurnType::Enum enter_instruction[] = {
|
||||
TurnType::Invalid, TurnType::EnterRoundaboutAtExit, TurnType::EnterRotaryAtExit,
|
||||
TurnType::EnterRoundaboutIntersectionAtExit};
|
||||
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
|
||||
}
|
||||
|
||||
static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
|
||||
static TurnInstruction SUPPRESSED(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
return {TurnType::Suppressed, modifier};
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit one byte");
|
||||
|
||||
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
|
||||
{
|
||||
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#ifndef ORIGINAL_EDGE_DATA_HPP
|
||||
#define ORIGINAL_EDGE_DATA_HPP
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
namespace osrm
|
||||
@@ -17,15 +18,17 @@ struct OriginalEdgeData
|
||||
explicit OriginalEdgeData(NodeID via_node,
|
||||
unsigned name_id,
|
||||
guidance::TurnInstruction turn_instruction,
|
||||
EntryClassID entry_classid,
|
||||
TravelMode travel_mode)
|
||||
: via_node(via_node), name_id(name_id), turn_instruction(turn_instruction),
|
||||
travel_mode(travel_mode)
|
||||
: via_node(via_node), name_id(name_id), entry_classid(entry_classid),
|
||||
turn_instruction(turn_instruction), travel_mode(travel_mode)
|
||||
{
|
||||
}
|
||||
|
||||
OriginalEdgeData()
|
||||
: via_node(std::numeric_limits<unsigned>::max()),
|
||||
name_id(std::numeric_limits<unsigned>::max()),
|
||||
entry_classid(INVALID_ENTRY_CLASSID),
|
||||
turn_instruction(guidance::TurnInstruction::INVALID()),
|
||||
travel_mode(TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
@@ -33,6 +36,7 @@ struct OriginalEdgeData
|
||||
|
||||
NodeID via_node;
|
||||
unsigned name_id;
|
||||
EntryClassID entry_classid;
|
||||
guidance::TurnInstruction turn_instruction;
|
||||
TravelMode travel_mode;
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ struct RouteParametersGrammar : public BaseParametersGrammar<Iterator, Signature
|
||||
base_rule =
|
||||
BaseGrammar::base_rule(qi::_r1)
|
||||
| (qi::lit("steps=") > qi::bool_[ph::bind(&engine::api::RouteParameters::steps, qi::_r1) = qi::_1])
|
||||
| (qi::lit("annotate=") > qi::bool_[ph::bind(&engine::api::RouteParameters::annotation, qi::_r1) = qi::_1])
|
||||
| (qi::lit("geometries=") > geometries_type[ph::bind(&engine::api::RouteParameters::geometries, qi::_r1) = qi::_1])
|
||||
| (qi::lit("overview=") > overview_type[ph::bind(&engine::api::RouteParameters::overview, qi::_r1) = qi::_1])
|
||||
;
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -49,6 +54,10 @@ class Server
|
||||
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
|
||||
|
||||
acceptor.open(endpoint.protocol());
|
||||
#ifdef SO_REUSEPORT
|
||||
const int option = 1;
|
||||
setsockopt(acceptor.native_handle(), SOL_SOCKET, SO_REUSEPORT, &option, sizeof(option));
|
||||
#endif
|
||||
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor.bind(endpoint);
|
||||
acceptor.listen();
|
||||
|
||||
@@ -29,6 +29,7 @@ struct SharedDataLayout
|
||||
GRAPH_EDGE_LIST,
|
||||
COORDINATE_LIST,
|
||||
TURN_INSTRUCTION,
|
||||
ENTRY_CLASSID,
|
||||
TRAVEL_MODE,
|
||||
R_SEARCH_TREE,
|
||||
GEOMETRIES_INDEX,
|
||||
@@ -42,6 +43,11 @@ struct SharedDataLayout
|
||||
DATASOURCE_NAME_OFFSETS,
|
||||
DATASOURCE_NAME_LENGTHS,
|
||||
PROPERTIES,
|
||||
BEARING_CLASSID,
|
||||
BEARING_OFFSETS,
|
||||
BEARING_BLOCKS,
|
||||
BEARING_VALUES,
|
||||
ENTRY_CLASS,
|
||||
NUM_BLOCKS
|
||||
};
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ struct StorageConfig final
|
||||
boost::filesystem::path datasource_indexes_path;
|
||||
boost::filesystem::path names_data_path;
|
||||
boost::filesystem::path properties_path;
|
||||
boost::filesystem::path intersection_class_path;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,14 @@ inline bool CheckInBounds(const int A, const int B, const int range)
|
||||
return normalized_B - range <= normalized_A && normalized_A <= normalized_B + range;
|
||||
}
|
||||
}
|
||||
|
||||
inline double reverseBearing(const double bearing)
|
||||
{
|
||||
if (bearing >= 180)
|
||||
return bearing - 180.;
|
||||
return bearing + 180;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_
|
||||
#define OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
class BearingClass;
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <> struct hash<::osrm::util::guidance::BearingClass>
|
||||
{
|
||||
inline std::size_t operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
class BearingClass
|
||||
{
|
||||
public:
|
||||
// Add a bearing to the set
|
||||
void add(const DiscreteBearing bearing);
|
||||
|
||||
// hashing
|
||||
bool operator==(const BearingClass &other) const;
|
||||
|
||||
// sorting
|
||||
bool operator<(const BearingClass &other) const;
|
||||
|
||||
const std::vector<DiscreteBearing> &getAvailableBearings() const;
|
||||
|
||||
std::size_t findMatchingBearing(const double bearing) const;
|
||||
|
||||
const constexpr static double discrete_step_size = 360. / 24.;
|
||||
static DiscreteBearing getDiscreteBearing(const double bearing);
|
||||
|
||||
private:
|
||||
std::vector<DiscreteBearing> available_bearings;
|
||||
|
||||
// allow hash access to internal representation
|
||||
friend std::size_t std::hash<BearingClass>::operator()(const BearingClass &) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
// make Bearing Class hasbable
|
||||
namespace std
|
||||
{
|
||||
inline size_t hash<::osrm::util::guidance::BearingClass>::
|
||||
operator()(const ::osrm::util::guidance::BearingClass &bearing_class) const
|
||||
{
|
||||
return boost::hash_value(bearing_class.available_bearings);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
#endif /* OSRM_UTIL_GUIDANCE_BEARING_CLASS_HPP_ */
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_
|
||||
#define OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
class EntryClass;
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <> struct hash<::osrm::util::guidance::EntryClass>
|
||||
{
|
||||
inline std::size_t operator()(const ::osrm::util::guidance::EntryClass &entry_class) const;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
class EntryClass
|
||||
{
|
||||
using FlagBaseType = std::uint32_t;
|
||||
|
||||
public:
|
||||
EntryClass();
|
||||
|
||||
// we are hiding the access to the flags behind a protection wall, to make sure the bit logic
|
||||
// isn't tempered with. zero based indexing
|
||||
void activate(std::uint32_t index);
|
||||
|
||||
// check whether a certain turn allows entry
|
||||
bool allowsEntry(std::uint32_t index) const;
|
||||
|
||||
// required for hashing
|
||||
bool operator==(const EntryClass &) const;
|
||||
|
||||
// sorting
|
||||
bool operator<(const EntryClass &) const;
|
||||
|
||||
private:
|
||||
// given a list of possible discrete angles, the available angles flag indicates the presence of
|
||||
// a given turn at the intersection
|
||||
FlagBaseType enabled_entries_flags;
|
||||
|
||||
// allow hash access to internal representation
|
||||
friend std::size_t std::hash<EntryClass>::operator()(const EntryClass &) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace utilr
|
||||
} // namespace osrm
|
||||
|
||||
// make Entry Class hasbable
|
||||
namespace std
|
||||
{
|
||||
inline size_t hash<::osrm::util::guidance::EntryClass>::
|
||||
operator()(const ::osrm::util::guidance::EntryClass &entry_class) const
|
||||
{
|
||||
return hash<::osrm::util::guidance::EntryClass::FlagBaseType>()(
|
||||
entry_class.enabled_entries_flags);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
#endif /* OSRM_UTIL_GUIDANCE_ENTRY_CLASS_HPP_ */
|
||||
@@ -3,7 +3,15 @@
|
||||
|
||||
/* A set of tools required for guidance in both pre and post-processing */
|
||||
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -18,7 +26,7 @@ inline double angularDeviation(const double angle, const double from)
|
||||
return std::min(360 - deviation, deviation);
|
||||
}
|
||||
|
||||
inline extractor::guidance::DirectionModifier getTurnDirection(const double angle)
|
||||
inline extractor::guidance::DirectionModifier::Enum getTurnDirection(const double angle)
|
||||
{
|
||||
// An angle of zero is a u-turn
|
||||
// 180 goes perfectly straight
|
||||
@@ -41,8 +49,8 @@ inline extractor::guidance::DirectionModifier getTurnDirection(const double angl
|
||||
return extractor::guidance::DirectionModifier::UTurn;
|
||||
}
|
||||
|
||||
} /* namespace guidance */
|
||||
} /* namespace util */
|
||||
} /* namespace osrm */
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */
|
||||
|
||||
+22
-1
@@ -8,8 +8,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <fstream>
|
||||
#include <bitset>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "util/fingerprint.hpp"
|
||||
@@ -51,6 +51,16 @@ bool serializeVector(const std::string &filename, const std::vector<simple_type>
|
||||
return static_cast<bool>(stream);
|
||||
}
|
||||
|
||||
template <typename simple_type>
|
||||
bool serializeVector(std::ostream &stream, const std::vector<simple_type> &data)
|
||||
{
|
||||
std::uint64_t count = data.size();
|
||||
stream.write(reinterpret_cast<const char *>(&count), sizeof(count));
|
||||
if (!data.empty())
|
||||
stream.write(reinterpret_cast<const char *>(&data[0]), sizeof(simple_type) * count);
|
||||
return static_cast<bool>(stream);
|
||||
}
|
||||
|
||||
template <typename simple_type>
|
||||
bool deserializeVector(const std::string &filename, std::vector<simple_type> &data)
|
||||
{
|
||||
@@ -67,6 +77,17 @@ bool deserializeVector(const std::string &filename, std::vector<simple_type> &da
|
||||
return static_cast<bool>(stream);
|
||||
}
|
||||
|
||||
template <typename simple_type>
|
||||
bool deserializeVector(std::istream &stream, std::vector<simple_type> &data)
|
||||
{
|
||||
std::uint64_t count = 0;
|
||||
stream.read(reinterpret_cast<char *>(&count), sizeof(count));
|
||||
data.resize(count);
|
||||
if (count)
|
||||
stream.read(reinterpret_cast<char *>(&data[0]), sizeof(simple_type) * count);
|
||||
return static_cast<bool>(stream);
|
||||
}
|
||||
|
||||
inline bool serializeFlags(const boost::filesystem::path &path, const std::vector<bool> &flags)
|
||||
{
|
||||
// TODO this should be replaced with a FILE-based write using error checking
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef ISO_8601_DURATION_PARSER_HPP
|
||||
#define ISO_8601_DURATION_PARSER_HPP
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/spirit/include/qi_action.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
template <typename Iterator> struct iso_8601_grammar : qi::grammar<Iterator>
|
||||
{
|
||||
iso_8601_grammar()
|
||||
: iso_8601_grammar::base_type(iso_period), temp(0), hours(0), minutes(0), seconds(0)
|
||||
{
|
||||
iso_period = qi::lit('P') >> qi::lit('T') >>
|
||||
((value >> hour >> value >> minute >> value >> second) |
|
||||
(value >> hour >> value >> minute) | (value >> hour >> value >> second) |
|
||||
(value >> hour) | (value >> minute >> value >> second) | (value >> minute) |
|
||||
(value >> second));
|
||||
|
||||
value = qi::uint_[boost::bind(&iso_8601_grammar<Iterator>::set_temp, this, ::_1)];
|
||||
second = (qi::lit('s') |
|
||||
qi::lit('S'))[boost::bind(&iso_8601_grammar<Iterator>::set_seconds, this)];
|
||||
minute = (qi::lit('m') |
|
||||
qi::lit('M'))[boost::bind(&iso_8601_grammar<Iterator>::set_minutes, this)];
|
||||
hour = (qi::lit('h') |
|
||||
qi::lit('H'))[boost::bind(&iso_8601_grammar<Iterator>::set_hours, this)];
|
||||
}
|
||||
|
||||
qi::rule<Iterator> iso_period;
|
||||
qi::rule<Iterator, std::string()> value, hour, minute, second;
|
||||
|
||||
unsigned temp;
|
||||
unsigned hours;
|
||||
unsigned minutes;
|
||||
unsigned seconds;
|
||||
|
||||
void set_temp(unsigned number) { temp = number; }
|
||||
|
||||
void set_hours()
|
||||
{
|
||||
if (temp < 24)
|
||||
{
|
||||
hours = temp;
|
||||
}
|
||||
}
|
||||
|
||||
void set_minutes()
|
||||
{
|
||||
if (temp < 60)
|
||||
{
|
||||
minutes = temp;
|
||||
}
|
||||
}
|
||||
|
||||
void set_seconds()
|
||||
{
|
||||
if (temp < 60)
|
||||
{
|
||||
seconds = temp;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get_duration() const
|
||||
{
|
||||
unsigned temp = (3600 * hours + 60 * minutes + seconds);
|
||||
if (temp == 0)
|
||||
{
|
||||
temp = std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ISO_8601_DURATION_PARSER_HPP
|
||||
@@ -74,9 +74,9 @@ class StaticRTree
|
||||
struct LeafNode
|
||||
{
|
||||
LeafNode() : object_count(0), objects() {}
|
||||
uint32_t object_count;
|
||||
std::uint32_t object_count;
|
||||
std::array<EdgeDataT, LEAF_NODE_SIZE> objects;
|
||||
unsigned char leaf_page_padding[LEAF_PAGE_SIZE - sizeof(object_count) - sizeof(objects)];
|
||||
unsigned char leaf_page_padding[LEAF_PAGE_SIZE - sizeof(std::uint32_t) - sizeof(std::array<EdgeDataT, LEAF_NODE_SIZE>)];
|
||||
};
|
||||
static_assert(sizeof(LeafNode) == LEAF_PAGE_SIZE, "LeafNode size does not fit the page size");
|
||||
|
||||
|
||||
@@ -34,13 +34,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// OpenStreetMap node ids are higher than 2^32
|
||||
OSRM_STRONG_TYPEDEF(uint64_t, OSMNodeID)
|
||||
OSRM_STRONG_TYPEDEF_HASHABLE(uint64_t, OSMNodeID)
|
||||
OSRM_STRONG_TYPEDEF(std::uint64_t, OSMNodeID)
|
||||
OSRM_STRONG_TYPEDEF_HASHABLE(std::uint64_t, OSMNodeID)
|
||||
|
||||
OSRM_STRONG_TYPEDEF(uint32_t, OSMWayID)
|
||||
OSRM_STRONG_TYPEDEF_HASHABLE(uint32_t, OSMWayID)
|
||||
OSRM_STRONG_TYPEDEF(std::uint32_t, OSMWayID)
|
||||
OSRM_STRONG_TYPEDEF_HASHABLE(std::uint32_t, OSMWayID)
|
||||
|
||||
static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID(std::numeric_limits<std::uint64_t>::max());
|
||||
static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID(std::numeric_limits<std::uint32_t>::max());
|
||||
@@ -57,6 +58,14 @@ using NodeID = unsigned int;
|
||||
using EdgeID = unsigned int;
|
||||
using EdgeWeight = int;
|
||||
|
||||
using BearingClassID = std::uint32_t;
|
||||
static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
using DiscreteBearing = std::uint16_t;
|
||||
|
||||
using EntryClassID = std::uint16_t;
|
||||
static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max();
|
||||
|
||||
static const NodeID SPECIAL_NODEID = std::numeric_limits<unsigned>::max();
|
||||
static const NodeID SPECIAL_SEGMENTID = std::numeric_limits<int>::max();
|
||||
static const EdgeID SPECIAL_EDGEID = std::numeric_limits<unsigned>::max();
|
||||
|
||||
@@ -47,6 +47,31 @@ inline double latToY(const FloatLatitude latitude)
|
||||
return clamped_y;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr double horner(double, T an) { return an; }
|
||||
|
||||
template<typename T, typename... U>
|
||||
constexpr double horner(double x, T an, U ...a) { return horner(x, a...) * x + an; }
|
||||
|
||||
inline double latToYapprox(const FloatLatitude latitude)
|
||||
{
|
||||
if (latitude < FloatLatitude(-70.) || latitude > FloatLatitude(70.))
|
||||
return latToY(latitude);
|
||||
|
||||
// Approximate the inverse Gudermannian function with the Padé approximant [11/11]: deg → deg
|
||||
// Coefficients are computed for the argument range [-70°,70°] by Remez algorithm |err|_∞=3.387e-12
|
||||
const auto x = static_cast<double>(latitude);
|
||||
return
|
||||
horner(x, 0.00000000000000000000000000e+00, 1.00000000000089108431373566e+00, 2.34439410386997223035693483e-06,
|
||||
-3.21291701673364717170998957e-04, -6.62778508496089940141103135e-10, 3.68188055470304769936079078e-08,
|
||||
6.31192702320492485752941578e-14, -1.77274453235716299127325443e-12, -2.24563810831776747318521450e-18,
|
||||
3.13524754818073129982475171e-17, 2.09014225025314211415458228e-23, -9.82938075991732185095509716e-23) /
|
||||
horner(x, 1.00000000000000000000000000e+00, 2.34439410398970701719081061e-06, -3.72061271627251952928813333e-04,
|
||||
-7.81802389685429267252612620e-10, 5.18418724186576447072888605e-08, 9.37468561198098681003717477e-14,
|
||||
-3.30833288607921773936702558e-12, -4.78446279888774903983338274e-18, 9.32999229169156878168234191e-17,
|
||||
9.17695141954265959600965170e-23, -8.72130728982012387640166055e-22, -3.23083224835967391884404730e-28);
|
||||
}
|
||||
|
||||
inline FloatLatitude clamp(const FloatLatitude lat)
|
||||
{
|
||||
return std::max(std::min(lat, FloatLatitude(detail::MAX_LATITUDE)),
|
||||
@@ -87,7 +112,7 @@ inline double degreeToPixel(FloatLatitude lat, unsigned zoom)
|
||||
|
||||
inline FloatCoordinate fromWGS84(const FloatCoordinate &wgs84_coordinate)
|
||||
{
|
||||
return {wgs84_coordinate.lon, FloatLatitude{latToY(wgs84_coordinate.lat)}};
|
||||
return {wgs84_coordinate.lon, FloatLatitude{latToYapprox(wgs84_coordinate.lat)}};
|
||||
}
|
||||
|
||||
inline FloatCoordinate toWGS84(const FloatCoordinate &mercator_coordinate)
|
||||
|
||||
+405
-263
@@ -2,42 +2,51 @@
|
||||
#include "contractor/crc32_processor.hpp"
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
|
||||
#include "extractor/node_based_edge.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/node_based_edge.hpp"
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <fast-cpp-csv-parser/csv.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/interprocess/file_mapping.hpp>
|
||||
#include <boost/interprocess/mapped_region.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_invoke.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
|
||||
{
|
||||
std::size_t operator()(const std::pair<OSMNodeID, OSMNodeID> &k) const
|
||||
std::size_t operator()(const std::pair<OSMNodeID, OSMNodeID> &k) const noexcept
|
||||
{
|
||||
return static_cast<uint64_t>(k.first) ^ (static_cast<uint64_t>(k.second) << 12);
|
||||
}
|
||||
@@ -45,7 +54,7 @@ template <> struct hash<std::pair<OSMNodeID, OSMNodeID>>
|
||||
|
||||
template <> struct hash<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>>
|
||||
{
|
||||
std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const
|
||||
std::size_t operator()(const std::tuple<OSMNodeID, OSMNodeID, OSMNodeID> &k) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, static_cast<uint64_t>(std::get<0>(k)));
|
||||
@@ -137,6 +146,182 @@ int Contractor::Run()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Utilities for LoadEdgeExpandedGraph to restore my sanity
|
||||
namespace
|
||||
{
|
||||
|
||||
struct Segment final
|
||||
{
|
||||
OSMNodeID from, to;
|
||||
};
|
||||
|
||||
struct SpeedSource final
|
||||
{
|
||||
unsigned speed;
|
||||
std::uint8_t source;
|
||||
};
|
||||
|
||||
struct SegmentSpeedSource final
|
||||
{
|
||||
Segment segment;
|
||||
SpeedSource speed_source;
|
||||
};
|
||||
|
||||
using SegmentSpeedSourceFlatMap = std::vector<SegmentSpeedSource>;
|
||||
|
||||
// Binary Search over a flattened key,val Segment storage
|
||||
SegmentSpeedSourceFlatMap::iterator find(SegmentSpeedSourceFlatMap &map, const Segment &key)
|
||||
{
|
||||
const auto last = end(map);
|
||||
|
||||
const auto by_segment = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
|
||||
return std::tie(lhs.segment.from, lhs.segment.to) >
|
||||
std::tie(rhs.segment.from, rhs.segment.to);
|
||||
};
|
||||
|
||||
auto it = std::lower_bound(begin(map), last, SegmentSpeedSource{key, {0, 0}}, by_segment);
|
||||
|
||||
if (it != last && (std::tie(it->segment.from, it->segment.to) == std::tie(key.from, key.to)))
|
||||
return it;
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
// Convenience aliases. TODO: make actual types at some point in time.
|
||||
// TODO: turn penalties need flat map + binary search optimization, take a look at segment speeds
|
||||
|
||||
using Turn = std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>;
|
||||
using TurnHasher = std::hash<Turn>;
|
||||
using PenaltySource = std::pair<double, std::uint8_t>;
|
||||
using TurnPenaltySourceMap = tbb::concurrent_unordered_map<Turn, PenaltySource, TurnHasher>;
|
||||
|
||||
// Functions for parsing files and creating lookup tables
|
||||
|
||||
SegmentSpeedSourceFlatMap
|
||||
parse_segment_lookup_from_csv_files(const std::vector<std::string> &segment_speed_filenames)
|
||||
{
|
||||
// TODO: shares code with turn penalty lookup parse function
|
||||
|
||||
using Mutex = tbb::spin_mutex;
|
||||
|
||||
// Loaded and parsed in parallel, at the end we combine results in a flattened map-ish view
|
||||
SegmentSpeedSourceFlatMap flatten;
|
||||
Mutex flatten_mutex;
|
||||
|
||||
const auto parse_segment_speed_file = [&](const std::size_t idx) {
|
||||
const auto file_id = idx + 1; // starts at one, zero means we assigned the weight
|
||||
const auto filename = segment_speed_filenames[idx];
|
||||
|
||||
std::ifstream segment_speed_file{filename, std::ios::binary};
|
||||
if (!segment_speed_file)
|
||||
throw util::exception{"Unable to open segment speed file " + filename};
|
||||
|
||||
SegmentSpeedSourceFlatMap local;
|
||||
|
||||
std::uint64_t from_node_id{};
|
||||
std::uint64_t to_node_id{};
|
||||
unsigned speed{};
|
||||
|
||||
for (std::string line; std::getline(segment_speed_file, line);)
|
||||
{
|
||||
using namespace boost::spirit::qi;
|
||||
|
||||
auto it = begin(line);
|
||||
const auto last = end(line);
|
||||
|
||||
// The ulong_long -> uint64_t will likely break on 32bit platforms
|
||||
const auto ok = parse(it, last, //
|
||||
(ulong_long >> ',' >> ulong_long >> ',' >> uint_), //
|
||||
from_node_id, to_node_id, speed); //
|
||||
|
||||
if (!ok || it != last)
|
||||
throw util::exception{"Segment speed file " + filename + " malformed"};
|
||||
|
||||
SegmentSpeedSource val{
|
||||
{static_cast<OSMNodeID>(from_node_id), static_cast<OSMNodeID>(to_node_id)},
|
||||
{speed, static_cast<std::uint8_t>(file_id)}};
|
||||
|
||||
local.push_back(std::move(val));
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::scoped_lock _{flatten_mutex};
|
||||
|
||||
flatten.insert(end(flatten), std::make_move_iterator(begin(local)),
|
||||
std::make_move_iterator(end(local)));
|
||||
}
|
||||
};
|
||||
|
||||
tbb::parallel_for(std::size_t{0}, segment_speed_filenames.size(), parse_segment_speed_file);
|
||||
|
||||
// With flattened map-ish view of all the files, sort and unique them on from,to,source
|
||||
// The greater '>' is used here since we want to give files later on higher precedence
|
||||
const auto sort_by = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
|
||||
return std::tie(lhs.segment.from, lhs.segment.to, lhs.speed_source.source) >
|
||||
std::tie(rhs.segment.from, rhs.segment.to, rhs.speed_source.source);
|
||||
};
|
||||
|
||||
std::stable_sort(begin(flatten), end(flatten), sort_by);
|
||||
|
||||
// Unique only on from,to to take the source precedence into account and remove duplicates
|
||||
const auto unique_by = [](const SegmentSpeedSource &lhs, const SegmentSpeedSource &rhs) {
|
||||
return std::tie(lhs.segment.from, lhs.segment.to) ==
|
||||
std::tie(rhs.segment.from, rhs.segment.to);
|
||||
};
|
||||
|
||||
const auto it = std::unique(begin(flatten), end(flatten), unique_by);
|
||||
|
||||
flatten.erase(it, end(flatten));
|
||||
|
||||
return flatten;
|
||||
}
|
||||
|
||||
TurnPenaltySourceMap
|
||||
parse_turn_penalty_lookup_from_csv_files(const std::vector<std::string> &turn_penalty_filenames)
|
||||
{
|
||||
// TODO: shares code with turn penalty lookup parse function
|
||||
TurnPenaltySourceMap map;
|
||||
|
||||
const auto parse_turn_penalty_file = [&](const std::size_t idx) {
|
||||
const auto file_id = idx + 1; // starts at one, zero means we assigned the weight
|
||||
const auto filename = turn_penalty_filenames[idx];
|
||||
|
||||
std::ifstream turn_penalty_file{filename, std::ios::binary};
|
||||
if (!turn_penalty_file)
|
||||
throw util::exception{"Unable to open turn penalty file " + filename};
|
||||
|
||||
std::uint64_t from_node_id{};
|
||||
std::uint64_t via_node_id{};
|
||||
std::uint64_t to_node_id{};
|
||||
double penalty{};
|
||||
|
||||
for (std::string line; std::getline(turn_penalty_file, line);)
|
||||
{
|
||||
using namespace boost::spirit::qi;
|
||||
|
||||
auto it = begin(line);
|
||||
const auto last = end(line);
|
||||
|
||||
// The ulong_long -> uint64_t will likely break on 32bit platforms
|
||||
const auto ok =
|
||||
parse(it, last, //
|
||||
(ulong_long >> ',' >> ulong_long >> ',' >> ulong_long >> ',' >> double_), //
|
||||
from_node_id, via_node_id, to_node_id, penalty); //
|
||||
|
||||
if (!ok || it != last)
|
||||
throw util::exception{"Turn penalty file " + filename + " malformed"};
|
||||
|
||||
map[std::make_tuple(OSMNodeID(from_node_id), OSMNodeID(via_node_id),
|
||||
OSMNodeID(to_node_id))] = std::make_pair(penalty, file_id);
|
||||
}
|
||||
};
|
||||
|
||||
tbb::parallel_for(std::size_t{0}, turn_penalty_filenames.size(), parse_turn_penalty_file);
|
||||
|
||||
return map;
|
||||
}
|
||||
} // anon ns
|
||||
|
||||
std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
std::string const &edge_based_graph_filename,
|
||||
util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
@@ -150,8 +335,13 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
const std::string &datasource_indexes_filename,
|
||||
const std::string &rtree_leaf_filename)
|
||||
{
|
||||
if (segment_speed_filenames.size() > 255 || turn_penalty_filenames.size() > 255)
|
||||
throw util::exception("Limit of 255 segment speed and turn penalty files each reached");
|
||||
|
||||
util::SimpleLogger().Write() << "Opening " << edge_based_graph_filename;
|
||||
boost::filesystem::ifstream input_stream(edge_based_graph_filename, std::ios::binary);
|
||||
if (!input_stream)
|
||||
throw util::exception("Could not load edge based graph file");
|
||||
|
||||
const bool update_edge_weights = !segment_speed_filenames.empty();
|
||||
const bool update_turn_penalties = !turn_penalty_filenames.empty();
|
||||
@@ -186,134 +376,89 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
util::SimpleLogger().Write() << "Reading " << number_of_edges
|
||||
<< " edges from the edge based graph";
|
||||
|
||||
std::unordered_map<std::pair<OSMNodeID, OSMNodeID>, std::pair<unsigned, uint8_t>>
|
||||
segment_speed_lookup;
|
||||
std::unordered_map<std::tuple<OSMNodeID, OSMNodeID, OSMNodeID>, std::pair<double, uint8_t>>
|
||||
turn_penalty_lookup;
|
||||
SegmentSpeedSourceFlatMap segment_speed_lookup;
|
||||
TurnPenaltySourceMap turn_penalty_lookup;
|
||||
|
||||
// If we update the edge weights, this file will hold the datasource information
|
||||
// for each segment
|
||||
const auto parse_segment_speeds = [&] {
|
||||
if (update_edge_weights)
|
||||
segment_speed_lookup = parse_segment_lookup_from_csv_files(segment_speed_filenames);
|
||||
};
|
||||
|
||||
const auto parse_turn_penalties = [&] {
|
||||
if (update_turn_penalties)
|
||||
turn_penalty_lookup = parse_turn_penalty_lookup_from_csv_files(turn_penalty_filenames);
|
||||
};
|
||||
|
||||
// If we update the edge weights, this file will hold the datasource information for each
|
||||
// segment; the other files will also be conditionally filled concurrently if we make an update
|
||||
std::vector<uint8_t> m_geometry_datasource;
|
||||
|
||||
std::vector<extractor::QueryNode> internal_to_external_node_map;
|
||||
std::vector<unsigned> m_geometry_indices;
|
||||
std::vector<extractor::CompressedEdgeContainer::CompressedEdge> m_geometry_list;
|
||||
|
||||
const auto maybe_load_internal_to_external_node_map = [&] {
|
||||
if (!(update_edge_weights || update_turn_penalties))
|
||||
return;
|
||||
|
||||
boost::filesystem::ifstream nodes_input_stream(nodes_filename, std::ios::binary);
|
||||
|
||||
if (!nodes_input_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + nodes_filename);
|
||||
}
|
||||
|
||||
unsigned number_of_nodes = 0;
|
||||
nodes_input_stream.read((char *)&number_of_nodes, sizeof(unsigned));
|
||||
internal_to_external_node_map.resize(number_of_nodes);
|
||||
|
||||
// Load all the query nodes into a vector
|
||||
nodes_input_stream.read(reinterpret_cast<char *>(&(internal_to_external_node_map[0])),
|
||||
number_of_nodes * sizeof(extractor::QueryNode));
|
||||
};
|
||||
|
||||
const auto maybe_load_geometries = [&] {
|
||||
if (!(update_edge_weights || update_turn_penalties))
|
||||
return;
|
||||
|
||||
std::ifstream geometry_stream(geometry_filename, std::ios::binary);
|
||||
if (!geometry_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + geometry_filename);
|
||||
}
|
||||
unsigned number_of_indices = 0;
|
||||
unsigned number_of_compressed_geometries = 0;
|
||||
|
||||
geometry_stream.read((char *)&number_of_indices, sizeof(unsigned));
|
||||
|
||||
m_geometry_indices.resize(number_of_indices);
|
||||
if (number_of_indices > 0)
|
||||
{
|
||||
geometry_stream.read((char *)&(m_geometry_indices[0]),
|
||||
number_of_indices * sizeof(unsigned));
|
||||
}
|
||||
|
||||
geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned));
|
||||
|
||||
BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries);
|
||||
m_geometry_list.resize(number_of_compressed_geometries);
|
||||
|
||||
if (number_of_compressed_geometries > 0)
|
||||
{
|
||||
geometry_stream.read((char *)&(m_geometry_list[0]),
|
||||
number_of_compressed_geometries *
|
||||
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
}
|
||||
};
|
||||
|
||||
// Folds all our actions into independently concurrently executing lambdas
|
||||
tbb::parallel_invoke(parse_segment_speeds, parse_turn_penalties, //
|
||||
maybe_load_internal_to_external_node_map, maybe_load_geometries);
|
||||
|
||||
if (update_edge_weights || update_turn_penalties)
|
||||
{
|
||||
std::uint8_t segment_file_id = 1;
|
||||
std::uint8_t turn_file_id = 1;
|
||||
|
||||
if (update_edge_weights)
|
||||
{
|
||||
for (auto segment_speed_filename : segment_speed_filenames)
|
||||
{
|
||||
util::SimpleLogger().Write()
|
||||
<< "Segment speed data supplied, will update edge weights from "
|
||||
<< segment_speed_filename;
|
||||
io::CSVReader<3> csv_in(segment_speed_filename);
|
||||
csv_in.set_header("from_node", "to_node", "speed");
|
||||
std::uint64_t from_node_id{};
|
||||
std::uint64_t to_node_id{};
|
||||
unsigned speed{};
|
||||
while (csv_in.read_row(from_node_id, to_node_id, speed))
|
||||
{
|
||||
segment_speed_lookup[std::make_pair(OSMNodeID(from_node_id),
|
||||
OSMNodeID(to_node_id))] =
|
||||
std::make_pair(speed, segment_file_id);
|
||||
}
|
||||
++segment_file_id;
|
||||
|
||||
// Check for overflow
|
||||
if (segment_file_id == 0)
|
||||
{
|
||||
throw util::exception(
|
||||
"Sorry, there's a limit of 255 segment speed files; you supplied too many");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_turn_penalties)
|
||||
{
|
||||
for (auto turn_penalty_filename : turn_penalty_filenames)
|
||||
{
|
||||
util::SimpleLogger().Write()
|
||||
<< "Turn penalty data supplied, will update turn penalties from "
|
||||
<< turn_penalty_filename;
|
||||
io::CSVReader<4> csv_in(turn_penalty_filename);
|
||||
csv_in.set_header("from_node", "via_node", "to_node", "penalty");
|
||||
uint64_t from_node_id{};
|
||||
uint64_t via_node_id{};
|
||||
uint64_t to_node_id{};
|
||||
double penalty{};
|
||||
while (csv_in.read_row(from_node_id, via_node_id, to_node_id, penalty))
|
||||
{
|
||||
turn_penalty_lookup[std::make_tuple(
|
||||
OSMNodeID(from_node_id), OSMNodeID(via_node_id), OSMNodeID(to_node_id))] =
|
||||
std::make_pair(penalty, turn_file_id);
|
||||
}
|
||||
++turn_file_id;
|
||||
|
||||
// Check for overflow
|
||||
if (turn_file_id == 0)
|
||||
{
|
||||
throw util::exception(
|
||||
"Sorry, there's a limit of 255 turn penalty files; you supplied too many");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<extractor::QueryNode> internal_to_external_node_map;
|
||||
|
||||
// Here, we have to update the compressed geometry weights
|
||||
// First, we need the external-to-internal node lookup table
|
||||
{
|
||||
boost::filesystem::ifstream nodes_input_stream(nodes_filename, std::ios::binary);
|
||||
|
||||
if (!nodes_input_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + nodes_filename);
|
||||
}
|
||||
|
||||
unsigned number_of_nodes = 0;
|
||||
nodes_input_stream.read((char *)&number_of_nodes, sizeof(unsigned));
|
||||
internal_to_external_node_map.resize(number_of_nodes);
|
||||
|
||||
// Load all the query nodes into a vector
|
||||
nodes_input_stream.read(reinterpret_cast<char *>(&(internal_to_external_node_map[0])),
|
||||
number_of_nodes * sizeof(extractor::QueryNode));
|
||||
}
|
||||
|
||||
std::vector<unsigned> m_geometry_indices;
|
||||
std::vector<extractor::CompressedEdgeContainer::CompressedEdge> m_geometry_list;
|
||||
|
||||
{
|
||||
std::ifstream geometry_stream(geometry_filename, std::ios::binary);
|
||||
if (!geometry_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + geometry_filename);
|
||||
}
|
||||
unsigned number_of_indices = 0;
|
||||
unsigned number_of_compressed_geometries = 0;
|
||||
|
||||
geometry_stream.read((char *)&number_of_indices, sizeof(unsigned));
|
||||
|
||||
m_geometry_indices.resize(number_of_indices);
|
||||
if (number_of_indices > 0)
|
||||
{
|
||||
geometry_stream.read((char *)&(m_geometry_indices[0]),
|
||||
number_of_indices * sizeof(unsigned));
|
||||
}
|
||||
|
||||
geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned));
|
||||
|
||||
BOOST_ASSERT(m_geometry_indices.back() == number_of_compressed_geometries);
|
||||
m_geometry_list.resize(number_of_compressed_geometries);
|
||||
|
||||
if (number_of_compressed_geometries > 0)
|
||||
{
|
||||
geometry_stream.read(
|
||||
(char *)&(m_geometry_list[0]),
|
||||
number_of_compressed_geometries *
|
||||
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
}
|
||||
}
|
||||
|
||||
// This is a list of the "data source id" for every segment in the compressed
|
||||
// geometry container. We assume that everything so far has come from the
|
||||
@@ -326,141 +471,135 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
// Now, we iterate over all the segments stored in the StaticRTree, updating
|
||||
// the packed geometry weights in the `.geometries` file (note: we do not
|
||||
// update the RTree itself, we just use the leaf nodes to iterate over all segments)
|
||||
{
|
||||
using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
|
||||
|
||||
using LeafNode = util::StaticRTree<extractor::EdgeBasedNode>::LeafNode;
|
||||
using boost::interprocess::file_mapping;
|
||||
using boost::interprocess::mapped_region;
|
||||
using boost::interprocess::read_only;
|
||||
|
||||
std::ifstream leaf_node_file(rtree_leaf_filename, std::ios::binary | std::ios::in | std::ios::ate);
|
||||
if (!leaf_node_file)
|
||||
const file_mapping mapping{rtree_leaf_filename.c_str(), read_only};
|
||||
mapped_region region{mapping, read_only};
|
||||
region.advise(mapped_region::advice_willneed);
|
||||
|
||||
const auto bytes = region.get_size();
|
||||
const auto first = static_cast<const LeafNode *>(region.get_address());
|
||||
const auto last = first + (bytes / sizeof(LeafNode));
|
||||
|
||||
tbb::parallel_for_each(first, last, [&](const LeafNode ¤t_node) {
|
||||
for (size_t i = 0; i < current_node.object_count; i++)
|
||||
{
|
||||
throw util::exception("Failed to open " + rtree_leaf_filename);
|
||||
}
|
||||
std::size_t leaf_nodes_count = leaf_node_file.tellg() / sizeof(LeafNode);
|
||||
leaf_node_file.seekg(0, std::ios::beg);
|
||||
const auto &leaf_object = current_node.objects[i];
|
||||
extractor::QueryNode *u;
|
||||
extractor::QueryNode *v;
|
||||
|
||||
LeafNode current_node;
|
||||
while (leaf_nodes_count > 0)
|
||||
{
|
||||
leaf_node_file.read(reinterpret_cast<char *>(¤t_node), sizeof(current_node));
|
||||
|
||||
for (size_t i = 0; i < current_node.object_count; i++)
|
||||
if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID)
|
||||
{
|
||||
auto &leaf_object = current_node.objects[i];
|
||||
extractor::QueryNode *u;
|
||||
extractor::QueryNode *v;
|
||||
const unsigned forward_begin =
|
||||
m_geometry_indices.at(leaf_object.forward_packed_geometry_id);
|
||||
|
||||
if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID)
|
||||
if (leaf_object.fwd_segment_position == 0)
|
||||
{
|
||||
const unsigned forward_begin =
|
||||
m_geometry_indices.at(leaf_object.forward_packed_geometry_id);
|
||||
|
||||
if (leaf_object.fwd_segment_position == 0)
|
||||
{
|
||||
u = &(internal_to_external_node_map[leaf_object.u]);
|
||||
v = &(internal_to_external_node_map[m_geometry_list[forward_begin]
|
||||
.node_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
u = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin +
|
||||
leaf_object.fwd_segment_position - 1]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin +
|
||||
leaf_object.fwd_segment_position]
|
||||
.node_id]);
|
||||
}
|
||||
const double segment_length =
|
||||
util::coordinate_calculation::greatCircleDistance(
|
||||
util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
|
||||
|
||||
auto forward_speed_iter =
|
||||
segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id));
|
||||
if (forward_speed_iter != segment_speed_lookup.end())
|
||||
{
|
||||
int new_segment_weight =
|
||||
std::max(1, static_cast<int>(std::floor(
|
||||
(segment_length * 10.) /
|
||||
(forward_speed_iter->second.first / 3.6) +
|
||||
.5)));
|
||||
m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
|
||||
.weight = new_segment_weight;
|
||||
m_geometry_datasource[forward_begin +
|
||||
leaf_object.fwd_segment_position] =
|
||||
forward_speed_iter->second.second;
|
||||
}
|
||||
u = &(internal_to_external_node_map[leaf_object.u]);
|
||||
v = &(
|
||||
internal_to_external_node_map[m_geometry_list[forward_begin].node_id]);
|
||||
}
|
||||
if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID)
|
||||
else
|
||||
{
|
||||
const unsigned reverse_begin =
|
||||
m_geometry_indices.at(leaf_object.reverse_packed_geometry_id);
|
||||
const unsigned reverse_end =
|
||||
m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1);
|
||||
u = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin +
|
||||
leaf_object.fwd_segment_position - 1]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
|
||||
.node_id]);
|
||||
}
|
||||
const double segment_length = util::coordinate_calculation::greatCircleDistance(
|
||||
util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
|
||||
|
||||
int rev_segment_position =
|
||||
(reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1;
|
||||
if (rev_segment_position == 0)
|
||||
{
|
||||
u = &(internal_to_external_node_map[leaf_object.v]);
|
||||
v = &(internal_to_external_node_map[m_geometry_list[reverse_begin]
|
||||
.node_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
u = &(internal_to_external_node_map
|
||||
[m_geometry_list[reverse_begin + rev_segment_position - 1]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[reverse_begin + rev_segment_position]
|
||||
.node_id]);
|
||||
}
|
||||
const double segment_length =
|
||||
util::coordinate_calculation::greatCircleDistance(
|
||||
util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
|
||||
|
||||
auto reverse_speed_iter =
|
||||
segment_speed_lookup.find(std::make_pair(u->node_id, v->node_id));
|
||||
if (reverse_speed_iter != segment_speed_lookup.end())
|
||||
{
|
||||
int new_segment_weight =
|
||||
std::max(1, static_cast<int>(std::floor(
|
||||
(segment_length * 10.) /
|
||||
(reverse_speed_iter->second.first / 3.6) +
|
||||
.5)));
|
||||
m_geometry_list[reverse_begin + rev_segment_position].weight =
|
||||
new_segment_weight;
|
||||
m_geometry_datasource[reverse_begin + rev_segment_position] =
|
||||
reverse_speed_iter->second.second;
|
||||
}
|
||||
auto forward_speed_iter =
|
||||
find(segment_speed_lookup, Segment{u->node_id, v->node_id});
|
||||
if (forward_speed_iter != segment_speed_lookup.end())
|
||||
{
|
||||
int new_segment_weight =
|
||||
std::max(1, static_cast<int>(std::floor(
|
||||
(segment_length * 10.) /
|
||||
(forward_speed_iter->speed_source.speed / 3.6) +
|
||||
.5)));
|
||||
m_geometry_list[forward_begin + leaf_object.fwd_segment_position].weight =
|
||||
new_segment_weight;
|
||||
m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] =
|
||||
forward_speed_iter->speed_source.source;
|
||||
}
|
||||
}
|
||||
--leaf_nodes_count;
|
||||
}
|
||||
}
|
||||
if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID)
|
||||
{
|
||||
const unsigned reverse_begin =
|
||||
m_geometry_indices.at(leaf_object.reverse_packed_geometry_id);
|
||||
const unsigned reverse_end =
|
||||
m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1);
|
||||
|
||||
// Now save out the updated compressed geometries
|
||||
{
|
||||
std::ofstream geometry_stream(geometry_filename, std::ios::binary);
|
||||
if (!geometry_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + geometry_filename + " for writing");
|
||||
int rev_segment_position =
|
||||
(reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1;
|
||||
if (rev_segment_position == 0)
|
||||
{
|
||||
u = &(internal_to_external_node_map[leaf_object.v]);
|
||||
v = &(
|
||||
internal_to_external_node_map[m_geometry_list[reverse_begin].node_id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
u = &(
|
||||
internal_to_external_node_map[m_geometry_list[reverse_begin +
|
||||
rev_segment_position - 1]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[reverse_begin + rev_segment_position].node_id]);
|
||||
}
|
||||
const double segment_length = util::coordinate_calculation::greatCircleDistance(
|
||||
util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat});
|
||||
|
||||
auto reverse_speed_iter =
|
||||
find(segment_speed_lookup, Segment{u->node_id, v->node_id});
|
||||
if (reverse_speed_iter != segment_speed_lookup.end())
|
||||
{
|
||||
int new_segment_weight =
|
||||
std::max(1, static_cast<int>(std::floor(
|
||||
(segment_length * 10.) /
|
||||
(reverse_speed_iter->speed_source.speed / 3.6) +
|
||||
.5)));
|
||||
m_geometry_list[reverse_begin + rev_segment_position].weight =
|
||||
new_segment_weight;
|
||||
m_geometry_datasource[reverse_begin + rev_segment_position] =
|
||||
reverse_speed_iter->speed_source.source;
|
||||
}
|
||||
}
|
||||
}
|
||||
const unsigned number_of_indices = m_geometry_indices.size();
|
||||
const unsigned number_of_compressed_geometries = m_geometry_list.size();
|
||||
geometry_stream.write(reinterpret_cast<const char *>(&number_of_indices),
|
||||
sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_indices[0])),
|
||||
number_of_indices * sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<const char *>(&number_of_compressed_geometries),
|
||||
sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_list[0])),
|
||||
number_of_compressed_geometries *
|
||||
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
}
|
||||
}); // parallel_for_each
|
||||
}
|
||||
|
||||
{
|
||||
const auto maybe_save_geometries = [&] {
|
||||
if (!(update_edge_weights || update_turn_penalties))
|
||||
return;
|
||||
|
||||
// Now save out the updated compressed geometries
|
||||
std::ofstream geometry_stream(geometry_filename, std::ios::binary);
|
||||
if (!geometry_stream)
|
||||
{
|
||||
throw util::exception("Failed to open " + geometry_filename + " for writing");
|
||||
}
|
||||
const unsigned number_of_indices = m_geometry_indices.size();
|
||||
const unsigned number_of_compressed_geometries = m_geometry_list.size();
|
||||
geometry_stream.write(reinterpret_cast<const char *>(&number_of_indices), sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_indices[0])),
|
||||
number_of_indices * sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<const char *>(&number_of_compressed_geometries),
|
||||
sizeof(unsigned));
|
||||
geometry_stream.write(reinterpret_cast<char *>(&(m_geometry_list[0])),
|
||||
number_of_compressed_geometries *
|
||||
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
};
|
||||
|
||||
const auto save_datasource_indexes = [&] {
|
||||
std::ofstream datasource_stream(datasource_indexes_filename, std::ios::binary);
|
||||
if (!datasource_stream)
|
||||
{
|
||||
@@ -474,9 +613,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
datasource_stream.write(reinterpret_cast<char *>(&(m_geometry_datasource[0])),
|
||||
number_of_datasource_entries * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
const auto save_datastore_names = [&] {
|
||||
std::ofstream datasource_stream(datasource_names_filename, std::ios::binary);
|
||||
if (!datasource_stream)
|
||||
{
|
||||
@@ -487,7 +626,9 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
{
|
||||
datasource_stream << name << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names);
|
||||
|
||||
// TODO: can we read this in bulk? util::DeallocatingVector isn't necessarily
|
||||
// all stored contiguously
|
||||
@@ -524,16 +665,17 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
edge_segment_input_stream.read(reinterpret_cast<char *>(&segment_weight),
|
||||
sizeof(segment_weight));
|
||||
|
||||
auto speed_iter = segment_speed_lookup.find(
|
||||
std::make_pair(previous_osm_node_id, this_osm_node_id));
|
||||
auto speed_iter =
|
||||
find(segment_speed_lookup, Segment{previous_osm_node_id, this_osm_node_id});
|
||||
if (speed_iter != segment_speed_lookup.end())
|
||||
{
|
||||
// This sets the segment weight using the same formula as the
|
||||
// EdgeBasedGraphFactory for consistency. The *why* of this formula
|
||||
// is lost in the annals of time.
|
||||
int new_segment_weight = std::max(
|
||||
1, static_cast<int>(std::floor(
|
||||
(segment_length * 10.) / (speed_iter->second.first / 3.6) + .5)));
|
||||
1,
|
||||
static_cast<int>(std::floor(
|
||||
(segment_length * 10.) / (speed_iter->speed_source.speed / 3.6) + .5)));
|
||||
new_weight += new_segment_weight;
|
||||
}
|
||||
else
|
||||
@@ -554,7 +696,8 @@ std::size_t Contractor::LoadEdgeExpandedGraph(
|
||||
sizeof(via_id));
|
||||
edge_fixed_penalties_input_stream.read(reinterpret_cast<char *>(&to_id), sizeof(to_id));
|
||||
|
||||
auto turn_iter = turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
|
||||
const auto turn_iter =
|
||||
turn_penalty_lookup.find(std::make_tuple(from_id, via_id, to_id));
|
||||
if (turn_iter != turn_penalty_lookup.end())
|
||||
{
|
||||
int new_turn_weight = static_cast<int>(turn_iter->second.first * 10);
|
||||
@@ -633,8 +776,7 @@ Contractor::WriteContractedGraph(unsigned max_node_id,
|
||||
const util::FingerPrint fingerprint = util::FingerPrint::GetValid();
|
||||
boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary);
|
||||
hsgr_output_stream.write((char *)&fingerprint, sizeof(util::FingerPrint));
|
||||
const unsigned max_used_node_id = [&contracted_edge_list]
|
||||
{
|
||||
const unsigned max_used_node_id = [&contracted_edge_list] {
|
||||
unsigned tmp_max = 0;
|
||||
for (const QueryEdge &edge : contracted_edge_list)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
@@ -13,8 +17,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using TurnType = osrm::extractor::guidance::TurnType;
|
||||
using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
|
||||
namespace TurnType = osrm::extractor::guidance::TurnType;
|
||||
namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
|
||||
using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
|
||||
|
||||
namespace osrm
|
||||
@@ -33,31 +37,12 @@ const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "s
|
||||
|
||||
// translations of TurnTypes. Not all types are exposed to the outside world.
|
||||
// invalid types should never be returned as part of the API
|
||||
const constexpr char *turn_type_names[] = {"invalid",
|
||||
"new name",
|
||||
"continue",
|
||||
"turn",
|
||||
"merge",
|
||||
"on ramp",
|
||||
"off ramp",
|
||||
"fork",
|
||||
"end of road",
|
||||
"notification",
|
||||
"roundabout",
|
||||
"roundabout",
|
||||
"rotary",
|
||||
"rotary",
|
||||
"roundabout turn",
|
||||
"roundabout turn",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid",
|
||||
"invalid"};
|
||||
const constexpr char *turn_type_names[] = {
|
||||
"invalid", "new name", "continue", "turn", "merge",
|
||||
"on ramp", "off ramp", "fork", "end of road", "notification",
|
||||
"roundabout", "roundabout", "rotary", "rotary", "roundabout turn",
|
||||
"roundabout turn", "invalid", "invalid", "invalid", "invalid",
|
||||
"invalid", "invalid", "invalid", "invalid", "invalid"};
|
||||
|
||||
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
|
||||
|
||||
@@ -70,12 +55,12 @@ inline bool isValidModifier(const guidance::StepManeuver maneuver)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string instructionTypeToString(const TurnType type)
|
||||
std::string instructionTypeToString(const TurnType::Enum type)
|
||||
{
|
||||
return turn_type_names[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
std::string instructionModifierToString(const DirectionModifier modifier)
|
||||
std::string instructionModifierToString(const DirectionModifier::Enum modifier)
|
||||
{
|
||||
return modifier_names[static_cast<std::size_t>(modifier)];
|
||||
}
|
||||
@@ -148,9 +133,7 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
|
||||
{
|
||||
util::json::Object step_maneuver;
|
||||
if (maneuver.waypoint_type == guidance::WaypointType::None)
|
||||
{
|
||||
step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type);
|
||||
}
|
||||
else
|
||||
step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type);
|
||||
|
||||
@@ -164,14 +147,40 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
|
||||
if (maneuver.exit != 0)
|
||||
step_maneuver.values["exit"] = maneuver.exit;
|
||||
|
||||
// TODO currently we need this to comply with the api.
|
||||
// We should move this to an additional entry, the moment we
|
||||
// actually compute the correct locations of the intersections
|
||||
if (!maneuver.intersections.empty() && maneuver.exit == 0)
|
||||
step_maneuver.values["exit"] = maneuver.intersections.size();
|
||||
return step_maneuver;
|
||||
}
|
||||
|
||||
util::json::Object
|
||||
makeIntersection(const guidance::Intersection &intersection)
|
||||
{
|
||||
util::json::Object result;
|
||||
util::json::Array bearings;
|
||||
util::json::Array entry;
|
||||
|
||||
bearings.values.reserve(intersection.bearings.size());
|
||||
std::copy(intersection.bearings.begin(), intersection.bearings.end(), std::back_inserter(bearings.values));
|
||||
|
||||
entry.values.reserve(intersection.entry.size());
|
||||
std::transform(intersection.entry.begin(), intersection.entry.end(),
|
||||
std::back_inserter(entry.values), [](const bool has_entry) -> util::json::Value
|
||||
{
|
||||
if (has_entry)
|
||||
return util::json::True();
|
||||
else
|
||||
return util::json::False();
|
||||
});
|
||||
|
||||
result.values["location"] = detail::coordinateToLonLat(intersection.location);
|
||||
result.values["bearings"] = bearings;
|
||||
result.values["entry"] = entry;
|
||||
if (intersection.in != guidance::Intersection::NO_INDEX)
|
||||
result.values["in"] = intersection.in;
|
||||
if (intersection.out != guidance::Intersection::NO_INDEX)
|
||||
result.values["out"] = intersection.out;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geometry)
|
||||
{
|
||||
util::json::Object route_step;
|
||||
@@ -184,6 +193,13 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo
|
||||
route_step.values["mode"] = detail::modeToString(std::move(step.mode));
|
||||
route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver));
|
||||
route_step.values["geometry"] = std::move(geometry);
|
||||
|
||||
util::json::Array intersections;
|
||||
intersections.values.reserve(step.intersections.size());
|
||||
std::transform(step.intersections.begin(), step.intersections.end(),
|
||||
std::back_inserter(intersections.values), makeIntersection);
|
||||
route_step.values["intersections"] = std::move(intersections);
|
||||
|
||||
return route_step;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
namespace osrm
|
||||
@@ -12,45 +13,8 @@ namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const WaypointType waypoint_type,
|
||||
const LegGeometry &leg_geometry)
|
||||
{
|
||||
BOOST_ASSERT(waypoint_type != WaypointType::None);
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
|
||||
double pre_turn_bearing = 0, post_turn_bearing = 0;
|
||||
Coordinate turn_coordinate;
|
||||
if (waypoint_type == WaypointType::Depart)
|
||||
{
|
||||
turn_coordinate = leg_geometry.locations.front();
|
||||
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
|
||||
post_turn_bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(waypoint_type == WaypointType::Arrive);
|
||||
turn_coordinate = leg_geometry.locations.back();
|
||||
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
|
||||
pre_turn_bearing =
|
||||
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
|
||||
}
|
||||
return StepManeuver{
|
||||
std::move(turn_coordinate),
|
||||
pre_turn_bearing,
|
||||
post_turn_bearing,
|
||||
std::move(instruction),
|
||||
waypoint_type,
|
||||
INVALID_EXIT_NR,
|
||||
{} // no intermediate intersections
|
||||
};
|
||||
}
|
||||
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index)
|
||||
std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index)
|
||||
{
|
||||
auto turn_index = leg_geometry.BackIndex(segment_index);
|
||||
BOOST_ASSERT(turn_index > 0);
|
||||
@@ -61,20 +25,27 @@ StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instr
|
||||
const auto turn_coordinate = leg_geometry.locations[turn_index];
|
||||
const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
|
||||
|
||||
const double pre_turn_bearing =
|
||||
util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate);
|
||||
const double post_turn_bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate);
|
||||
return std::make_pair<short, short>(
|
||||
std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)),
|
||||
std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
|
||||
}
|
||||
|
||||
return StepManeuver{
|
||||
std::move(turn_coordinate),
|
||||
pre_turn_bearing,
|
||||
post_turn_bearing,
|
||||
std::move(instruction),
|
||||
WaypointType::None,
|
||||
INVALID_EXIT_NR,
|
||||
{} // no intermediate intersections
|
||||
};
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry)
|
||||
{
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
const auto turn_coordinate = leg_geometry.locations.front();
|
||||
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
|
||||
return std::make_pair<short, short>(
|
||||
0, std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
|
||||
}
|
||||
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry)
|
||||
{
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
const auto turn_coordinate = leg_geometry.locations.back();
|
||||
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
|
||||
return std::make_pair<short, short>(
|
||||
std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)), 0);
|
||||
}
|
||||
} // ns detail
|
||||
} // ns engine
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include <utility>
|
||||
|
||||
using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
|
||||
using TurnType = osrm::extractor::guidance::TurnType;
|
||||
using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
|
||||
namespace TurnType = osrm::extractor::guidance::TurnType;
|
||||
namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
|
||||
using osrm::util::guidance::angularDeviation;
|
||||
using osrm::util::guidance::getTurnDirection;
|
||||
|
||||
@@ -41,23 +41,25 @@ void print(const std::vector<RouteStep> &steps)
|
||||
int segment = 0;
|
||||
for (const auto &step : steps)
|
||||
{
|
||||
const auto type =
|
||||
static_cast<std::underlying_type<TurnType>::type>(step.maneuver.instruction.type);
|
||||
const auto modifier = static_cast<std::underlying_type<DirectionModifier>::type>(
|
||||
step.maneuver.instruction.direction_modifier);
|
||||
|
||||
std::cout << "\t[" << ++segment << "]: " << type << " " << modifier
|
||||
std::cout << "\t[" << ++segment << "]: " << step.maneuver.instruction.type
|
||||
<< " " << step.maneuver.instruction.direction_modifier << " " << static_cast<int>(step.maneuver.waypoint_type)
|
||||
<< " Duration: " << step.duration << " Distance: " << step.distance
|
||||
<< " Geometry: " << step.geometry_begin << " " << step.geometry_end
|
||||
<< " exit: " << step.maneuver.exit
|
||||
<< " Intersections: " << step.maneuver.intersections.size() << " [";
|
||||
<< " Intersections: " << step.intersections.size() << " [";
|
||||
|
||||
for (const auto &intersection : step.maneuver.intersections)
|
||||
std::cout << "(" << intersection.duration << " " << intersection.distance << ")";
|
||||
for (const auto &intersection : step.intersections)
|
||||
{
|
||||
std::cout << "(bearings:";
|
||||
for( auto bearing : intersection.bearings)
|
||||
std:: cout << " " << bearing;
|
||||
std::cout << ", entry: ";
|
||||
for( auto entry : intersection.entry)
|
||||
std:: cout << " " << entry;
|
||||
std::cout << ")";
|
||||
}
|
||||
|
||||
std::cout << "] name[" << step.name_id << "]: " << step.name
|
||||
<< " Bearings: " << step.maneuver.bearing_before << " "
|
||||
<< step.maneuver.bearing_after << std::endl;
|
||||
std::cout << "] name[" << step.name_id << "]: " << step.name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +69,22 @@ RouteStep forwardInto(RouteStep destination, const RouteStep &source)
|
||||
// Overwrites turn instruction and increases exit NR
|
||||
destination.duration += source.duration;
|
||||
destination.distance += source.distance;
|
||||
|
||||
if (destination.geometry_begin < source.geometry_begin)
|
||||
{
|
||||
destination.intersections.insert(destination.intersections.end(),
|
||||
source.intersections.begin(), source.intersections.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
destination.intersections.insert(destination.intersections.begin(),
|
||||
source.intersections.begin(), source.intersections.end());
|
||||
}
|
||||
|
||||
destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin);
|
||||
destination.geometry_end = std::max(destination.geometry_end, source.geometry_end);
|
||||
destination.maneuver.exit = destination.intersections.size() - 1;
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -167,7 +183,7 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
steps[1] = forwardInto(steps[1], steps[0]);
|
||||
steps[0].duration = 0;
|
||||
steps[0].distance = 0;
|
||||
const auto exitToEnter = [](const TurnType type) {
|
||||
const auto exitToEnter = [](const TurnType::Enum type) {
|
||||
if (TurnType::ExitRotary == type)
|
||||
return TurnType::EnterRotary;
|
||||
// if we do not enter the roundabout Intersection, we cannot treat the full traversal as
|
||||
@@ -185,7 +201,10 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
// Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
|
||||
// index back to the entering location and prepare the current silent set of instructions for
|
||||
// removal.
|
||||
const auto exit_bearing = steps[step_index].maneuver.bearing_after;
|
||||
std::vector<std::size_t> intermediate_steps;
|
||||
BOOST_ASSERT(!steps[step_index].intersections.empty());
|
||||
const auto exit_intersection = steps[step_index].intersections.back();
|
||||
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||
if (step_index > 1)
|
||||
{
|
||||
// The very first route-step is head, so we cannot iterate past that one
|
||||
@@ -198,6 +217,8 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
{
|
||||
propagation_step.maneuver.exit = step.maneuver.exit;
|
||||
propagation_step.geometry_end = step.geometry_end;
|
||||
const auto entry_intersection = propagation_step.intersections.front();
|
||||
|
||||
// remember rotary name
|
||||
if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
|
||||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
|
||||
@@ -228,6 +249,7 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
// % 360;
|
||||
// All other cases are handled by first rotating both bearings to an
|
||||
// entry_bearing of 0.
|
||||
BOOST_ASSERT(!propagation_step.intersections.empty());
|
||||
const double angle = [](const double entry_bearing, const double exit_bearing) {
|
||||
const double offset = 360 - entry_bearing;
|
||||
const double rotated_exit = [](double bearing, const double offset) {
|
||||
@@ -237,7 +259,7 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
|
||||
const auto angle = 540 - rotated_exit;
|
||||
return angle > 360 ? angle - 360 : angle;
|
||||
}(propagation_step.maneuver.bearing_before, exit_bearing);
|
||||
}(util::bearing::reverseBearing(entry_intersection.bearings[entry_intersection.in]), exit_bearing);
|
||||
|
||||
propagation_step.maneuver.instruction.direction_modifier =
|
||||
::osrm::util::guidance::getTurnDirection(angle);
|
||||
@@ -268,16 +290,17 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
step.duration += by_step.duration;
|
||||
step.distance += by_step.distance;
|
||||
|
||||
// by_step comes after step -> we append at the end
|
||||
if (step.geometry_end == by_step.geometry_begin + 1)
|
||||
{
|
||||
step.geometry_end = by_step.geometry_end;
|
||||
|
||||
// if we elongate in the back, we only need to copy the intersections to the beginning.
|
||||
// the bearings remain the same, as the location of the turn doesn't change
|
||||
step.maneuver.intersections.insert(step.maneuver.intersections.end(),
|
||||
by_step.maneuver.intersections.begin(),
|
||||
by_step.maneuver.intersections.end());
|
||||
step.intersections.insert(step.intersections.end(), by_step.intersections.begin(),
|
||||
by_step.intersections.end());
|
||||
}
|
||||
// by_step comes before step -> we append at the front
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(step.maneuver.waypoint_type == WaypointType::None &&
|
||||
@@ -286,14 +309,10 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
step.geometry_begin = by_step.geometry_begin;
|
||||
|
||||
// elongating in the front changes the location of the maneuver
|
||||
step.maneuver.location = by_step.maneuver.location;
|
||||
step.maneuver.bearing_before = by_step.maneuver.bearing_before;
|
||||
step.maneuver.bearing_after = by_step.maneuver.bearing_after;
|
||||
step.maneuver.instruction = by_step.maneuver.instruction;
|
||||
step.maneuver = by_step.maneuver;
|
||||
|
||||
step.maneuver.intersections.insert(step.maneuver.intersections.begin(),
|
||||
by_step.maneuver.intersections.begin(),
|
||||
by_step.maneuver.intersections.end());
|
||||
step.intersections.insert(step.intersections.begin(), by_step.intersections.begin(),
|
||||
by_step.intersections.end());
|
||||
}
|
||||
return step;
|
||||
}
|
||||
@@ -323,8 +342,14 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
return angularDeviation(bearing_in, bearing_out) > 170;
|
||||
};
|
||||
|
||||
BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
|
||||
const auto isCollapsableInstruction = [](const TurnInstruction instruction) {
|
||||
return instruction.type == TurnType::NewName ||
|
||||
(instruction.type == TurnType::Turn &&
|
||||
instruction.direction_modifier == DirectionModifier::Straight);
|
||||
};
|
||||
// Very Short New Name
|
||||
if (TurnType::NewName == one_back_step.maneuver.instruction.type)
|
||||
if (isCollapsableInstruction(one_back_step.maneuver.instruction))
|
||||
{
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
if (one_back_step.mode == steps[two_back_index].mode)
|
||||
@@ -339,7 +364,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
}
|
||||
}
|
||||
// very short segment after turn
|
||||
else if (TurnType::NewName == current_step.maneuver.instruction.type)
|
||||
else if (isCollapsableInstruction(current_step.maneuver.instruction))
|
||||
{
|
||||
if (one_back_step.mode == current_step.mode)
|
||||
{
|
||||
@@ -353,8 +378,8 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
}
|
||||
}
|
||||
// Potential U-Turn
|
||||
else if (bearingsAreReversed(one_back_step.maneuver.bearing_before,
|
||||
current_step.maneuver.bearing_after))
|
||||
else if (bearingsAreReversed(util::bearing::reverseBearing(one_back_step.intersections.front().bearings[one_back_step.intersections.front().in]),
|
||||
current_step.intersections.front().bearings[current_step.intersections.front().out]))
|
||||
|
||||
{
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
@@ -365,7 +390,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
// additionall collapse a name-change as well
|
||||
const bool continues_with_name_change =
|
||||
(step_index + 1 < steps.size()) &&
|
||||
(TurnType::NewName == steps[step_index + 1].maneuver.instruction.type);
|
||||
isCollapsableInstruction(steps[step_index + 1].maneuver.instruction);
|
||||
const bool u_turn_with_name_change =
|
||||
collapsable(current_step) && continues_with_name_change &&
|
||||
steps[step_index + 1].name == steps[two_back_index].name;
|
||||
@@ -411,6 +436,16 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
||||
|
||||
boost::remove_erase_if(steps, not_is_valid);
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
@@ -420,7 +455,6 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
||||
// They are required for maintenance purposes. We can calculate the number
|
||||
// of exits to pass in a roundabout and the number of intersections
|
||||
// that we come across.
|
||||
|
||||
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
{
|
||||
// the steps should always include the first/last step in form of a location
|
||||
@@ -432,17 +466,6 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
bool on_roundabout = false;
|
||||
bool has_entered_roundabout = false;
|
||||
|
||||
// adds an intersection to the initial route step
|
||||
// It includes the length of the last step, until the intersection
|
||||
// Also updates the length of the respective segment
|
||||
auto addIntersection = [](RouteStep into, const RouteStep &last_step,
|
||||
const RouteStep &intersection) {
|
||||
into.maneuver.intersections.push_back(
|
||||
{last_step.duration, last_step.distance, intersection.maneuver.location});
|
||||
|
||||
return forwardInto(std::move(into), intersection);
|
||||
};
|
||||
|
||||
// count the exits forward. if enter/exit roundabout happen both, no further treatment is
|
||||
// required. We might end up with only one of them (e.g. starting within a roundabout)
|
||||
// or having a via-point in the roundabout.
|
||||
@@ -485,8 +508,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
{
|
||||
// count intersections. We cannot use exit, since intersections can follow directly
|
||||
// after a roundabout
|
||||
steps[last_valid_instruction] = addIntersection(
|
||||
std::move(steps[last_valid_instruction]), steps[step_index - 1], step);
|
||||
steps[last_valid_instruction] = elongate(steps[last_valid_instruction], step);
|
||||
step.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
}
|
||||
else if (!isSilent(instruction))
|
||||
@@ -495,6 +517,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
last_valid_instruction = step_index;
|
||||
}
|
||||
}
|
||||
|
||||
// unterminated roundabout
|
||||
// Move backwards through the instructions until the start and remove the exit number
|
||||
// A roundabout without exit translates to enter-roundabout.
|
||||
@@ -503,6 +526,16 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
fixFinalRoundabout(steps);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
|
||||
return removeNoTurnInstructions(std::move(steps));
|
||||
}
|
||||
|
||||
@@ -533,6 +566,11 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
invalidateStep(steps[1]);
|
||||
}
|
||||
}
|
||||
const auto isCollapsableInstruction = [](const TurnInstruction instruction) {
|
||||
return instruction.type == TurnType::NewName ||
|
||||
(instruction.type == TurnType::Turn &&
|
||||
instruction.direction_modifier == DirectionModifier::Straight);
|
||||
};
|
||||
|
||||
// first and last instructions are waypoints that cannot be collapsed
|
||||
for (std::size_t step_index = 2; step_index < steps.size(); ++step_index)
|
||||
@@ -551,7 +589,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
|
||||
// Due to empty segments, we can get name-changes from A->A
|
||||
// These have to be handled in post-processing
|
||||
if (TurnType::NewName == current_step.maneuver.instruction.type &&
|
||||
if (isCollapsableInstruction(current_step.maneuver.instruction) &&
|
||||
current_step.name == steps[one_back_index].name)
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
@@ -560,8 +598,8 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
// If we look at two consecutive name changes, we can check for a name oszillation.
|
||||
// A name oszillation changes from name A shortly to name B and back to A.
|
||||
// In these cases, the name change will be suppressed.
|
||||
else if (TurnType::NewName == current_step.maneuver.instruction.type &&
|
||||
TurnType::NewName == one_back_step.maneuver.instruction.type)
|
||||
else if (isCollapsableInstruction(current_step.maneuver.instruction) &&
|
||||
isCollapsableInstruction(one_back_step.maneuver.instruction))
|
||||
{
|
||||
// valid due to step_index starting at 2
|
||||
const auto &coming_from_name = steps[two_back_index].name;
|
||||
@@ -586,18 +624,29 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
collapseTurnAt(steps, two_back_index, one_back_index, step_index);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
|
||||
return removeNoTurnInstructions(std::move(steps));
|
||||
}
|
||||
|
||||
// Doing this step in post-processing provides a few challenges we cannot overcome.
|
||||
// The removal of an initial step imposes some copy overhead in the steps, moving all later
|
||||
// steps to the front. In addition, we cannot reduce the travel time that is accumulated at a
|
||||
// different location.
|
||||
// As a direct implication, we have to keep the time of the initial/final turns (which adds a
|
||||
// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
|
||||
// usually not be as relevant.
|
||||
void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
{
|
||||
// Doing this step in post-processing provides a few challenges we cannot overcome.
|
||||
// The removal of an initial step imposes some copy overhead in the steps, moving all later
|
||||
// steps to the front. In addition, we cannot reduce the travel time that is accumulated at a
|
||||
// different location.
|
||||
// As a direct implication, we have to keep the time of the initial/final turns (which adds a
|
||||
// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
|
||||
// usually not be as relevant.
|
||||
|
||||
if (steps.size() < 2 || geometry.locations.size() <= 2)
|
||||
return;
|
||||
@@ -626,6 +675,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
{
|
||||
// fixup the coordinate
|
||||
geometry.locations.erase(geometry.locations.begin());
|
||||
geometry.annotations.erase(geometry.annotations.begin());
|
||||
|
||||
// remove the initial distance value
|
||||
geometry.segment_distances.erase(geometry.segment_distances.begin());
|
||||
@@ -651,22 +701,29 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
|
||||
// update initial turn direction/bearings. Due to the duplicated first coordinate,
|
||||
// the initial bearing is invalid
|
||||
designated_depart.maneuver = detail::stepManeuverFromGeometry(
|
||||
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
|
||||
designated_depart.maneuver.waypoint_type = WaypointType::Depart;
|
||||
designated_depart.maneuver.bearing_before = 0;
|
||||
designated_depart.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
// we need to make this conform with the intersection format for the first intersection
|
||||
auto& first_intersection = designated_depart.intersections.front();
|
||||
first_intersection.bearings = {first_intersection.bearings[first_intersection.out]};
|
||||
first_intersection.entry = {true};
|
||||
first_intersection.in = Intersection::NO_INDEX;
|
||||
first_intersection.out = 0;
|
||||
|
||||
// finally remove the initial (now duplicated move)
|
||||
steps.erase(steps.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to make this at least 1 because we will substract 1
|
||||
// from all offsets at the end of the loop.
|
||||
steps.front().geometry_begin = 1;
|
||||
|
||||
// reduce all offsets by one (inplace)
|
||||
std::transform(geometry.segment_offsets.begin(), geometry.segment_offsets.end(),
|
||||
geometry.segment_offsets.begin(),
|
||||
[](const std::size_t val) { return val - 1; });
|
||||
|
||||
steps.front().maneuver = detail::stepManeuverFromGeometry(
|
||||
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
|
||||
}
|
||||
|
||||
// and update the leg geometry indices for the removed entry
|
||||
@@ -674,12 +731,31 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
--step.geometry_begin;
|
||||
--step.geometry_end;
|
||||
});
|
||||
|
||||
auto& first_step = steps.front();
|
||||
// we changed the geometry, we need to recalculate the bearing
|
||||
auto bearing = std::round(util::coordinate_calculation::bearing(
|
||||
geometry.locations[first_step.geometry_begin],
|
||||
geometry.locations[first_step.geometry_begin+1]));
|
||||
first_step.maneuver.bearing_after = bearing;
|
||||
first_step.intersections.front().bearings.front() = bearing;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
|
||||
// make sure we still have enough segments
|
||||
if (steps.size() < 2 || geometry.locations.size() == 2)
|
||||
return;
|
||||
|
||||
|
||||
BOOST_ASSERT(geometry.locations.size() >= steps.size());
|
||||
auto &next_to_last_step = *(steps.end() - 2);
|
||||
// in the end, the situation with the roundabout cannot occur. As a result, we can remove
|
||||
@@ -687,12 +763,20 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
if (next_to_last_step.distance <= 1)
|
||||
{
|
||||
geometry.locations.pop_back();
|
||||
geometry.annotations.pop_back();
|
||||
geometry.segment_offsets.pop_back();
|
||||
BOOST_ASSERT(geometry.segment_distances.back() < 1);
|
||||
geometry.segment_distances.pop_back();
|
||||
|
||||
next_to_last_step.maneuver = detail::stepManeuverFromGeometry(
|
||||
TurnInstruction::NO_TURN(), WaypointType::Arrive, geometry);
|
||||
next_to_last_step.maneuver.waypoint_type = WaypointType::Arrive;
|
||||
next_to_last_step.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
next_to_last_step.maneuver.bearing_after = 0;
|
||||
BOOST_ASSERT(next_to_last_step.intersections.size() == 1);
|
||||
auto& last_intersection = next_to_last_step.intersections.back();
|
||||
last_intersection.bearings = {last_intersection.bearings[last_intersection.in]};
|
||||
last_intersection.entry = {true};
|
||||
last_intersection.out = Intersection::NO_INDEX;
|
||||
last_intersection.in = 0;
|
||||
steps.pop_back();
|
||||
|
||||
// Because we eliminated a really short segment, it was probably
|
||||
@@ -713,17 +797,36 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
// correct steps but duplicated coordinate in the end.
|
||||
// This can happen if the last coordinate snaps to a node in the unpacked geometry
|
||||
geometry.locations.pop_back();
|
||||
geometry.annotations.pop_back();
|
||||
geometry.segment_offsets.back()--;
|
||||
// since the last geometry includes the location of arrival, the arrival instruction
|
||||
// geometry overlaps with the previous segment
|
||||
BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin + 1);
|
||||
BOOST_ASSERT(next_to_last_step.geometry_begin < next_to_last_step.geometry_end);
|
||||
next_to_last_step.geometry_end--;
|
||||
steps.back().geometry_begin--;
|
||||
steps.back().geometry_end--;
|
||||
steps.back().maneuver = detail::stepManeuverFromGeometry(TurnInstruction::NO_TURN(),
|
||||
WaypointType::Arrive, geometry);
|
||||
auto& last_step = steps.back();
|
||||
last_step.geometry_begin--;
|
||||
last_step.geometry_end--;
|
||||
BOOST_ASSERT(next_to_last_step.geometry_end == last_step.geometry_begin + 1);
|
||||
BOOST_ASSERT(last_step.geometry_begin == last_step.geometry_end-1);
|
||||
BOOST_ASSERT(next_to_last_step.geometry_end >= 2);
|
||||
// we changed the geometry, we need to recalculate the bearing
|
||||
auto bearing = std::round(util::coordinate_calculation::bearing(
|
||||
geometry.locations[next_to_last_step.geometry_end - 2],
|
||||
geometry.locations[last_step.geometry_begin]));
|
||||
last_step.maneuver.bearing_before = bearing;
|
||||
last_step.intersections.front().bearings.front() = util::bearing::reverseBearing(bearing);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
}
|
||||
|
||||
// assign relative locations to depart/arrive instructions
|
||||
@@ -760,6 +863,16 @@ std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
|
||||
: extractor::guidance::DirectionModifier::UTurn;
|
||||
|
||||
steps.back().maneuver.instruction.direction_modifier = final_modifier;
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
||||
|
||||
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
||||
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
||||
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "extractor/suffix_table.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_analysis.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
@@ -22,6 +23,7 @@
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -326,6 +328,10 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_node_info_list, *m_restriction_map,
|
||||
m_barrier_nodes, m_compressed_edge_container, name_table,
|
||||
street_name_suffix_table);
|
||||
|
||||
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
|
||||
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
progress.PrintStatus(node_u);
|
||||
@@ -340,6 +346,40 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
|
||||
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the interesction for
|
||||
// every edge
|
||||
const auto turn_classification = classifyIntersection(
|
||||
node_v, turn_analysis.getIntersection(node_u, edge_from_u), *m_node_based_graph,
|
||||
m_compressed_edge_container, m_node_info_list);
|
||||
|
||||
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
|
||||
if (0 == entry_class_hash.count(entry_class))
|
||||
{
|
||||
const auto id = static_cast<std::uint16_t>(entry_class_hash.size());
|
||||
entry_class_hash[entry_class] = id;
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry_class_hash.find(entry_class)->second;
|
||||
}
|
||||
}(turn_classification.first);
|
||||
|
||||
const auto bearing_class_id = [&](const util::guidance::BearingClass bearing_class) {
|
||||
if (0 == bearing_class_hash.count(bearing_class))
|
||||
{
|
||||
const auto id = static_cast<std::uint32_t>(bearing_class_hash.size());
|
||||
bearing_class_hash[bearing_class] = id;
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bearing_class_hash.find(bearing_class)->second;
|
||||
}
|
||||
}(turn_classification.second);
|
||||
bearing_class_by_node_based_node[node_v] = bearing_class_id;
|
||||
|
||||
for (const auto turn : possible_turns)
|
||||
{
|
||||
const double turn_angle = turn.angle;
|
||||
@@ -373,7 +413,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_from_u));
|
||||
original_edge_data_vector.emplace_back(
|
||||
m_compressed_edge_container.GetPositionForID(edge_from_u), edge_data1.name_id,
|
||||
turn_instruction, edge_data1.travel_mode);
|
||||
turn_instruction, entry_class_id, edge_data1.travel_mode);
|
||||
|
||||
++original_edges_counter;
|
||||
|
||||
@@ -469,6 +509,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
}
|
||||
}
|
||||
|
||||
util::SimpleLogger().Write() << "Created " << entry_class_hash.size() << " entry classes and "
|
||||
<< bearing_class_hash.size() << " Bearing Classes";
|
||||
|
||||
FlushVectorToStream(edge_data_file, original_edge_data_vector);
|
||||
|
||||
// Finally jump back to the empty space at the beginning and write length prefix
|
||||
@@ -493,6 +536,38 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
<< " turns over barriers";
|
||||
}
|
||||
|
||||
std::vector<util::guidance::BearingClass> EdgeBasedGraphFactory::GetBearingClasses() const
|
||||
{
|
||||
std::vector<util::guidance::BearingClass> result(bearing_class_hash.size());
|
||||
for (const auto &pair : bearing_class_hash)
|
||||
{
|
||||
BOOST_ASSERT(pair.second < result.size());
|
||||
result[pair.second] = pair.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds() const
|
||||
{
|
||||
return bearing_class_by_node_based_node;
|
||||
}
|
||||
|
||||
std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds()
|
||||
{
|
||||
return bearing_class_by_node_based_node;
|
||||
}
|
||||
|
||||
std::vector<util::guidance::EntryClass> EdgeBasedGraphFactory::GetEntryClasses() const
|
||||
{
|
||||
std::vector<util::guidance::EntryClass> result(entry_class_hash.size());
|
||||
for (const auto &pair : entry_class_hash)
|
||||
{
|
||||
BOOST_ASSERT(pair.second < result.size());
|
||||
result[pair.second] = pair.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
|
||||
{
|
||||
BOOST_ASSERT(lua_state != nullptr);
|
||||
|
||||
+72
-16
@@ -9,20 +9,19 @@
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
|
||||
#include "extractor/raster_source.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/lua_util.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
#include "util/lua_util.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
|
||||
#include "extractor/tarjan_scc.hpp"
|
||||
|
||||
@@ -41,14 +40,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -159,8 +159,7 @@ int Extractor::run()
|
||||
// parse OSM entities in parallel, store in resulting vectors
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<std::size_t>(0, osm_elements.size()),
|
||||
[&](const tbb::blocked_range<std::size_t> &range)
|
||||
{
|
||||
[&](const tbb::blocked_range<std::size_t> &range) {
|
||||
ExtractionNode result_node;
|
||||
ExtractionWay result_way;
|
||||
auto &local_context = scripting_environment.GetContex();
|
||||
@@ -269,10 +268,10 @@ int Extractor::run()
|
||||
std::vector<bool> node_is_startpoint;
|
||||
std::vector<EdgeWeight> edge_based_node_weights;
|
||||
std::vector<QueryNode> internal_to_external_node_map;
|
||||
auto graph_size = BuildEdgeExpandedGraph(main_context.state, main_context.properties,
|
||||
internal_to_external_node_map,
|
||||
edge_based_node_list, node_is_startpoint,
|
||||
edge_based_node_weights, edge_based_edge_list);
|
||||
auto graph_size = BuildEdgeExpandedGraph(
|
||||
main_context.state, main_context.properties, internal_to_external_node_map,
|
||||
edge_based_node_list, node_is_startpoint, edge_based_node_weights, edge_based_edge_list,
|
||||
config.intersection_class_data_output_path);
|
||||
|
||||
auto number_of_node_based_nodes = graph_size.first;
|
||||
auto max_edge_id = graph_size.second;
|
||||
@@ -474,7 +473,8 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
std::vector<EdgeBasedNode> &node_based_edge_list,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list)
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
const std::string &intersection_class_output_file)
|
||||
{
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
@@ -508,6 +508,11 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID();
|
||||
|
||||
const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes();
|
||||
|
||||
WriteIntersectionClassificationData(
|
||||
intersection_class_output_file, edge_based_graph_factory.GetBearingClassIds(),
|
||||
edge_based_graph_factory.GetBearingClasses(), edge_based_graph_factory.GetEntryClasses());
|
||||
|
||||
return std::make_pair(number_of_node_based_nodes, max_edge_id);
|
||||
}
|
||||
|
||||
@@ -601,5 +606,56 @@ void Extractor::WriteEdgeBasedGraph(
|
||||
|
||||
util::SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges";
|
||||
}
|
||||
|
||||
void Extractor::WriteIntersectionClassificationData(
|
||||
const std::string &output_file_name,
|
||||
const std::vector<BearingClassID> &node_based_intersection_classes,
|
||||
const std::vector<util::guidance::BearingClass> &bearing_classes,
|
||||
const std::vector<util::guidance::EntryClass> &entry_classes) const
|
||||
{
|
||||
std::ofstream file_out_stream(output_file_name.c_str(), std::ios::binary);
|
||||
if (!file_out_stream)
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Failed to open " << output_file_name
|
||||
<< " for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
util::SimpleLogger().Write() << "Writing Intersection Classification Data";
|
||||
TIMER_START(write_edges);
|
||||
util::writeFingerprint(file_out_stream);
|
||||
util::serializeVector(file_out_stream, node_based_intersection_classes);
|
||||
|
||||
// create range table for vectors:
|
||||
std::vector<unsigned> bearing_counts;
|
||||
bearing_counts.reserve(bearing_classes.size());
|
||||
std::uint64_t total_bearings = 0;
|
||||
for (const auto &bearing_class : bearing_classes){
|
||||
bearing_counts.push_back(static_cast<unsigned>(bearing_class.getAvailableBearings().size()));
|
||||
total_bearings += bearing_class.getAvailableBearings().size();
|
||||
}
|
||||
|
||||
util::RangeTable<> bearing_class_range_table(bearing_counts);
|
||||
file_out_stream << bearing_class_range_table;
|
||||
|
||||
file_out_stream << total_bearings;
|
||||
for( const auto &bearing_class : bearing_classes)
|
||||
{
|
||||
const auto &bearings = bearing_class.getAvailableBearings();
|
||||
file_out_stream.write( reinterpret_cast<const char*>(&bearings[0]), sizeof(bearings[0]) * bearings.size() );
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// This should be here, but g++4.8 does not have it...
|
||||
// static_assert(std::is_trivially_copyable<util::guidance::EntryClass>::value,
|
||||
// "EntryClass Serialization requires trivial copyable entry classes");
|
||||
|
||||
util::serializeVector(file_out_stream, entry_classes);
|
||||
TIMER_STOP(write_edges);
|
||||
util::SimpleLogger().Write() << "ok, after " << TIMER_SEC(write_edges) << "s for "
|
||||
<< node_based_intersection_classes.size() << " Indices into "
|
||||
<< bearing_classes.size() << " bearing classes and "
|
||||
<< entry_classes.size() << " entry classes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "extractor/extractor_callbacks.hpp"
|
||||
#include "extractor/extraction_containers.hpp"
|
||||
#include "extractor/extraction_node.hpp"
|
||||
#include "extractor/extraction_way.hpp"
|
||||
@@ -9,6 +8,7 @@
|
||||
#include "util/for_each_pair.hpp"
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include "extractor/extractor_callbacks.hpp"
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ std::size_t IntersectionHandler::countValid(const Intersection &intersection) co
|
||||
[](const ConnectedRoad &road) { return road.entry_allowed; });
|
||||
}
|
||||
|
||||
TurnType IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
|
||||
TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
|
||||
const ConnectedRoad &road) const
|
||||
{
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
using osrm::util::guidance::getTurnDirection;
|
||||
|
||||
@@ -79,6 +81,11 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
|
||||
return turns;
|
||||
}
|
||||
|
||||
Intersection TurnAnalysis::getIntersection(const NodeID from_nid, const EdgeID via_eid) const
|
||||
{
|
||||
return intersection_generator(from_nid, via_eid);
|
||||
}
|
||||
|
||||
// Sets basic turn types as fallback for otherwise unhandled turns
|
||||
Intersection
|
||||
TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection intersection) const
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "extractor/guidance/turn_classification.hpp"
|
||||
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct TurnPossibility
|
||||
{
|
||||
TurnPossibility(bool entry_allowed, double bearing)
|
||||
: entry_allowed(entry_allowed), bearing(std::move(bearing))
|
||||
{
|
||||
}
|
||||
|
||||
TurnPossibility() : entry_allowed(false), bearing(0) {}
|
||||
|
||||
bool entry_allowed;
|
||||
double bearing;
|
||||
};
|
||||
|
||||
std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
|
||||
classifyIntersection(NodeID nid,
|
||||
const Intersection &intersection,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
{
|
||||
if (intersection.empty())
|
||||
return {};
|
||||
|
||||
std::vector<TurnPossibility> turns;
|
||||
|
||||
const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
|
||||
|
||||
// generate a list of all turn angles between a base edge, the node and a current edge
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto eid = road.turn.eid;
|
||||
const auto edge_coordinate = getRepresentativeCoordinate(
|
||||
nid, node_based_graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
|
||||
|
||||
const double bearing =
|
||||
util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
|
||||
turns.push_back({road.entry_allowed, bearing});
|
||||
}
|
||||
|
||||
std::sort(turns.begin(), turns.end(),
|
||||
[](const TurnPossibility left, const TurnPossibility right) {
|
||||
return left.bearing < right.bearing;
|
||||
});
|
||||
|
||||
util::guidance::EntryClass entry_class;
|
||||
util::guidance::BearingClass bearing_class;
|
||||
|
||||
const bool canBeDiscretized = [&]() {
|
||||
if (turns.size() <= 1)
|
||||
return true;
|
||||
|
||||
DiscreteBearing last_discrete_bearing =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turns.back().bearing));
|
||||
for (const auto turn : turns)
|
||||
{
|
||||
const DiscreteBearing discrete_bearing =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
|
||||
if (discrete_bearing == last_discrete_bearing)
|
||||
return false;
|
||||
last_discrete_bearing = discrete_bearing;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
|
||||
// finally transfer data to the entry/bearing classes
|
||||
std::size_t number = 0;
|
||||
if (canBeDiscretized)
|
||||
{
|
||||
if(util::guidance::BearingClass::getDiscreteBearing(turns.back().bearing) <
|
||||
util::guidance::BearingClass::getDiscreteBearing(turns.front().bearing))
|
||||
{
|
||||
turns.insert(turns.begin(), turns.back());
|
||||
turns.pop_back();
|
||||
}
|
||||
for (const auto turn : turns)
|
||||
{
|
||||
if (turn.entry_allowed)
|
||||
entry_class.activate(number);
|
||||
auto discrete_bearing_class =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
|
||||
bearing_class.add(std::round(discrete_bearing_class *
|
||||
util::guidance::BearingClass::discrete_step_size));
|
||||
++number;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto turn : turns)
|
||||
{
|
||||
if (turn.entry_allowed)
|
||||
entry_class.activate(number);
|
||||
bearing_class.add(std::round(turn.bearing));
|
||||
++number;
|
||||
}
|
||||
}
|
||||
return std::make_pair(entry_class, bearing_class);
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_handler.hpp"
|
||||
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include <limits>
|
||||
@@ -78,6 +77,9 @@ Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection i
|
||||
|
||||
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
|
||||
{
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
|
||||
const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
|
||||
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
|
||||
const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
|
||||
const auto first_class =
|
||||
@@ -134,14 +136,18 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
{
|
||||
assignFork(via_edge, intersection[2], intersection[1]);
|
||||
}
|
||||
else if (isObviousOfTwo(intersection[1], intersection[2]))
|
||||
else if (isObviousOfTwo(intersection[1], intersection[2]) &&
|
||||
(second_data.name_id != in_data.name_id ||
|
||||
first_data.name_id == second_data.name_id))
|
||||
{
|
||||
intersection[1].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
|
||||
intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
|
||||
DirectionModifier::SlightLeft};
|
||||
}
|
||||
else if (isObviousOfTwo(intersection[2], intersection[1]))
|
||||
else if (isObviousOfTwo(intersection[2], intersection[1]) &&
|
||||
(first_data.name_id != in_data.name_id ||
|
||||
first_data.name_id == second_data.name_id))
|
||||
{
|
||||
intersection[2].turn.instruction =
|
||||
getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
|
||||
@@ -193,7 +199,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isObviousOfTwo(intersection[1], intersection[2]))
|
||||
if (isObviousOfTwo(intersection[1], intersection[2]) &&
|
||||
(in_data.name_id != second_data.name_id || first_data.name_id == second_data.name_id))
|
||||
{
|
||||
intersection[1].turn.instruction = getInstructionForObvious(
|
||||
3, via_edge, isThroughStreet(1, intersection), intersection[1]);
|
||||
@@ -204,7 +211,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
getTurnDirection(intersection[1].turn.angle)};
|
||||
}
|
||||
|
||||
if (isObviousOfTwo(intersection[2], intersection[1]))
|
||||
if (isObviousOfTwo(intersection[2], intersection[1]) &&
|
||||
(in_data.name_id != first_data.name_id || first_data.name_id == second_data.name_id))
|
||||
{
|
||||
intersection[2].turn.instruction = getInstructionForObvious(
|
||||
3, via_edge, isThroughStreet(2, intersection), intersection[2]);
|
||||
@@ -234,12 +242,44 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
|
||||
}
|
||||
}
|
||||
|
||||
// check whether there is a turn of the same name
|
||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
|
||||
const bool has_same_name_turn = [&]() {
|
||||
for (std::size_t i = 1; i < intersection.size(); ++i)
|
||||
{
|
||||
if (node_based_graph.GetEdgeData(intersection[i].turn.eid).name_id == in_data.name_id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
// check whether the obvious choice is actually a through street
|
||||
if (obvious_index != 0)
|
||||
{
|
||||
intersection[obvious_index].turn.instruction = getInstructionForObvious(
|
||||
intersection.size(), via_edge, isThroughStreet(obvious_index, intersection),
|
||||
intersection[obvious_index]);
|
||||
if (has_same_name_turn &&
|
||||
node_based_graph.GetEdgeData(intersection[obvious_index].turn.eid).name_id !=
|
||||
in_data.name_id &&
|
||||
intersection[obvious_index].turn.instruction.type == TurnType::NewName)
|
||||
{
|
||||
// this is a special case that is necessary to correctly handle obvious turns on
|
||||
// continuing streets. Right now osrm does not know about right of way. If a street
|
||||
// turns to the left just like:
|
||||
//
|
||||
// a
|
||||
// a
|
||||
// aaaaaaa b b
|
||||
//
|
||||
// And another road exits here, we don't want to call it a new name, even though the
|
||||
// turn is obvious and does not require steering. To correctly handle these situations
|
||||
// in turn collapsing, we use the turn + straight combination here
|
||||
intersection[obvious_index].turn.instruction.type = TurnType::Turn;
|
||||
intersection[obvious_index].turn.instruction.direction_modifier =
|
||||
DirectionModifier::Straight;
|
||||
}
|
||||
|
||||
// assign left/right turns
|
||||
intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
|
||||
@@ -302,7 +342,7 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
|
||||
}
|
||||
else
|
||||
{
|
||||
assignTrivialTurns(via_edge,intersection,1,intersection.size());
|
||||
assignTrivialTurns(via_edge, intersection, 1, intersection.size());
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
@@ -394,8 +434,7 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
|
||||
const std::size_t starting_at) const
|
||||
{
|
||||
BOOST_ASSERT(starting_at <= intersection.size());
|
||||
const auto switch_left_and_right = []( Intersection &intersection )
|
||||
{
|
||||
const auto switch_left_and_right = [](Intersection &intersection) {
|
||||
BOOST_ASSERT(!intersection.empty());
|
||||
|
||||
for (auto &road : intersection)
|
||||
@@ -410,7 +449,6 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
|
||||
intersection = assignRightTurns(via_edge, std::move(intersection), count);
|
||||
switch_left_and_right(intersection);
|
||||
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
@@ -537,18 +575,6 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
|
||||
}
|
||||
else
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING)
|
||||
<< "Reached fallback for right turns, size 3 "
|
||||
<< " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed +
|
||||
intersection[3].entry_allowed);
|
||||
for (const auto road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
util::SimpleLogger().Write(logWARNING)
|
||||
<< "\troad: " << toString(road) << " Name: " << out_data.name_id
|
||||
<< " Road Class: " << (int)out_data.road_classification.road_class;
|
||||
}
|
||||
|
||||
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,17 +64,20 @@ void RequestHandler::HandleRequest(const http::request ¤t_request, http::r
|
||||
ltime = time(nullptr);
|
||||
time_stamp = localtime(<ime);
|
||||
|
||||
// log timestamp
|
||||
util::SimpleLogger().Write()
|
||||
<< (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-"
|
||||
<< (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1) << "-"
|
||||
<< 1900 + time_stamp->tm_year << " " << (time_stamp->tm_hour < 10 ? "0" : "")
|
||||
<< time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "")
|
||||
<< time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "")
|
||||
<< time_stamp->tm_sec << " " << current_request.endpoint.to_string() << " "
|
||||
<< current_request.referrer << (0 == current_request.referrer.length() ? "- " : " ")
|
||||
<< current_request.agent << (0 == current_request.agent.length() ? "- " : " ")
|
||||
<< request_string;
|
||||
if (!std::getenv("DISABLE_ACCESS_LOGGING"))
|
||||
{
|
||||
// log timestamp
|
||||
util::SimpleLogger().Write()
|
||||
<< (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-"
|
||||
<< (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1) << "-"
|
||||
<< 1900 + time_stamp->tm_year << " " << (time_stamp->tm_hour < 10 ? "0" : "")
|
||||
<< time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "")
|
||||
<< time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "")
|
||||
<< time_stamp->tm_sec << " " << current_request.endpoint.to_string() << " "
|
||||
<< current_request.referrer << (0 == current_request.referrer.length() ? "- " : " ")
|
||||
<< current_request.agent << (0 == current_request.agent.length() ? "- " : " ")
|
||||
<< request_string;
|
||||
}
|
||||
|
||||
auto api_iterator = request_string.begin();
|
||||
auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());
|
||||
|
||||
+144
-37
@@ -1,24 +1,25 @@
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "storage/shared_barriers.hpp"
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/fingerprint.hpp"
|
||||
#include "util/io.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/shared_memory_vector_wrapper.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_barriers.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
#include "util/fingerprint.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
@@ -30,6 +31,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <string>
|
||||
@@ -49,8 +51,7 @@ void deleteRegion(const SharedDataType region)
|
||||
{
|
||||
if (SharedMemory::RegionExists(region) && !SharedMemory::Remove(region))
|
||||
{
|
||||
const std::string name = [&]
|
||||
{
|
||||
const std::string name = [&] {
|
||||
switch (region)
|
||||
{
|
||||
case CURRENT_REGIONS:
|
||||
@@ -105,20 +106,14 @@ int Storage::Run()
|
||||
|
||||
// determine segment to use
|
||||
bool segment2_in_use = SharedMemory::RegionExists(LAYOUT_2);
|
||||
const storage::SharedDataType layout_region = [&]
|
||||
{
|
||||
const storage::SharedDataType layout_region = [&] {
|
||||
return segment2_in_use ? LAYOUT_1 : LAYOUT_2;
|
||||
}();
|
||||
const storage::SharedDataType data_region = [&]
|
||||
{
|
||||
return segment2_in_use ? DATA_1 : DATA_2;
|
||||
}();
|
||||
const storage::SharedDataType previous_layout_region = [&]
|
||||
{
|
||||
const storage::SharedDataType data_region = [&] { return segment2_in_use ? DATA_1 : DATA_2; }();
|
||||
const storage::SharedDataType previous_layout_region = [&] {
|
||||
return segment2_in_use ? LAYOUT_2 : LAYOUT_1;
|
||||
}();
|
||||
const storage::SharedDataType previous_data_region = [&]
|
||||
{
|
||||
const storage::SharedDataType previous_data_region = [&] {
|
||||
return segment2_in_use ? DATA_2 : DATA_1;
|
||||
}();
|
||||
|
||||
@@ -136,7 +131,8 @@ int Storage::Run()
|
||||
boost::filesystem::ifstream name_stream(config.names_data_path, std::ios::binary);
|
||||
if (!name_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + config.names_data_path.string() + " for reading.");
|
||||
throw util::exception("Could not open " + config.names_data_path.string() +
|
||||
" for reading.");
|
||||
}
|
||||
unsigned name_blocks = 0;
|
||||
name_stream.read((char *)&name_blocks, sizeof(unsigned));
|
||||
@@ -154,7 +150,8 @@ int Storage::Run()
|
||||
boost::filesystem::ifstream edges_input_stream(config.edges_data_path, std::ios::binary);
|
||||
if (!edges_input_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + config.edges_data_path.string() + " for reading.");
|
||||
throw util::exception("Could not open " + config.edges_data_path.string() +
|
||||
" for reading.");
|
||||
}
|
||||
unsigned number_of_original_edges = 0;
|
||||
edges_input_stream.read((char *)&number_of_original_edges, sizeof(unsigned));
|
||||
@@ -168,6 +165,8 @@ int Storage::Run()
|
||||
number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<extractor::guidance::TurnInstruction>(
|
||||
SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<EntryClassID>(SharedDataLayout::ENTRY_CLASSID,
|
||||
number_of_original_edges);
|
||||
|
||||
boost::filesystem::ifstream hsgr_input_stream(config.hsgr_data_path, std::ios::binary);
|
||||
if (!hsgr_input_stream)
|
||||
@@ -250,7 +249,8 @@ int Storage::Run()
|
||||
boost::filesystem::ifstream geometry_input_stream(config.geometries_path, std::ios::binary);
|
||||
if (!geometry_input_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + config.geometries_path.string() + " for reading.");
|
||||
throw util::exception("Could not open " + config.geometries_path.string() +
|
||||
" for reading.");
|
||||
}
|
||||
unsigned number_of_geometries_indices = 0;
|
||||
unsigned number_of_compressed_geometries = 0;
|
||||
@@ -270,7 +270,8 @@ int Storage::Run()
|
||||
std::ios::binary);
|
||||
if (!geometry_datasource_input_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + config.datasource_indexes_path.string() + " for reading.");
|
||||
throw util::exception("Could not open " + config.datasource_indexes_path.string() +
|
||||
" for reading.");
|
||||
}
|
||||
std::size_t number_of_compressed_datasources = 0;
|
||||
if (geometry_datasource_input_stream)
|
||||
@@ -287,7 +288,8 @@ int Storage::Run()
|
||||
std::ios::binary);
|
||||
if (!datasource_names_input_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + config.datasource_names_path.string() + " for reading.");
|
||||
throw util::exception("Could not open " + config.datasource_names_path.string() +
|
||||
" for reading.");
|
||||
}
|
||||
std::vector<char> m_datasource_name_data;
|
||||
std::vector<std::size_t> m_datasource_name_offsets;
|
||||
@@ -310,6 +312,67 @@ int Storage::Run()
|
||||
shared_layout_ptr->SetBlockSize<std::size_t>(SharedDataLayout::DATASOURCE_NAME_LENGTHS,
|
||||
m_datasource_name_lengths.size());
|
||||
|
||||
boost::filesystem::ifstream intersection_stream(config.intersection_class_path,
|
||||
std::ios::binary);
|
||||
if (!static_cast<bool>(intersection_stream))
|
||||
throw util::exception("Could not open " + config.intersection_class_path.string() +
|
||||
" for reading.");
|
||||
|
||||
if (!util::readAndCheckFingerprint(intersection_stream))
|
||||
throw util::exception("Fingerprint of " + config.intersection_class_path.string() +
|
||||
" does not match or could not read from file");
|
||||
|
||||
std::vector<BearingClassID> bearing_class_id_table;
|
||||
if (!util::deserializeVector(intersection_stream, bearing_class_id_table))
|
||||
throw util::exception("Failed to bearing class ids read from " +
|
||||
config.names_data_path.string());
|
||||
|
||||
shared_layout_ptr->SetBlockSize<BearingClassID>(SharedDataLayout::BEARING_CLASSID,
|
||||
bearing_class_id_table.size());
|
||||
unsigned bearing_blocks = 0;
|
||||
intersection_stream.read((char *)&bearing_blocks, sizeof(unsigned));
|
||||
unsigned sum_lengths = 0;
|
||||
intersection_stream.read((char *)&sum_lengths, sizeof(unsigned));
|
||||
|
||||
shared_layout_ptr->SetBlockSize<unsigned>(SharedDataLayout::BEARING_OFFSETS, bearing_blocks);
|
||||
shared_layout_ptr->SetBlockSize<typename util::RangeTable<16, true>::BlockT>(
|
||||
SharedDataLayout::BEARING_BLOCKS, bearing_blocks);
|
||||
|
||||
std::vector<unsigned> bearing_offsets_data(bearing_blocks);
|
||||
std::vector<typename util::RangeTable<16, true>::BlockT> bearing_blocks_data(bearing_blocks);
|
||||
|
||||
if (bearing_blocks)
|
||||
{
|
||||
intersection_stream.read(reinterpret_cast<char *>(&bearing_offsets_data[0]),
|
||||
bearing_blocks * sizeof(bearing_offsets_data[0]));
|
||||
}
|
||||
|
||||
if (bearing_blocks)
|
||||
{
|
||||
intersection_stream.read(reinterpret_cast<char *>(&bearing_blocks_data[0]),
|
||||
bearing_blocks * sizeof(bearing_blocks_data[0]));
|
||||
}
|
||||
|
||||
std::uint64_t num_bearings;
|
||||
intersection_stream >> num_bearings;
|
||||
|
||||
std::vector<DiscreteBearing> bearing_class_table(num_bearings);
|
||||
intersection_stream.read(reinterpret_cast<char *>(&bearing_class_table[0]),
|
||||
sizeof(bearing_class_table[0]) * num_bearings);
|
||||
shared_layout_ptr->SetBlockSize<DiscreteBearing>(SharedDataLayout::BEARING_VALUES,
|
||||
num_bearings);
|
||||
if (!static_cast<bool>(intersection_stream))
|
||||
throw util::exception("Failed to read bearing values from " +
|
||||
config.intersection_class_path.string());
|
||||
|
||||
std::vector<util::guidance::EntryClass> entry_class_table;
|
||||
if (!util::deserializeVector(intersection_stream, entry_class_table))
|
||||
throw util::exception("Failed to read entry classes from " +
|
||||
config.intersection_class_path.string());
|
||||
|
||||
shared_layout_ptr->SetBlockSize<util::guidance::EntryClass>(SharedDataLayout::ENTRY_CLASS,
|
||||
entry_class_table.size());
|
||||
|
||||
// allocate shared memory block
|
||||
util::SimpleLogger().Write() << "allocating shared memory of "
|
||||
<< shared_layout_ptr->GetSizeOfLayout() << " bytes";
|
||||
@@ -331,7 +394,8 @@ int Storage::Run()
|
||||
file_index_path_ptr +
|
||||
shared_layout_ptr->GetBlockSize(SharedDataLayout::FILE_INDEX_PATH),
|
||||
0);
|
||||
std::copy(absolute_file_index_path.string().begin(), absolute_file_index_path.string().end(), file_index_path_ptr);
|
||||
std::copy(absolute_file_index_path.string().begin(), absolute_file_index_path.string().end(),
|
||||
file_index_path_ptr);
|
||||
|
||||
// Loading street names
|
||||
unsigned *name_offsets_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
|
||||
@@ -382,6 +446,9 @@ int Storage::Run()
|
||||
shared_layout_ptr->GetBlockPtr<extractor::guidance::TurnInstruction, true>(
|
||||
shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION);
|
||||
|
||||
EntryClassID *entry_class_id_ptr = shared_layout_ptr->GetBlockPtr<EntryClassID, true>(
|
||||
shared_memory_ptr, SharedDataLayout::ENTRY_CLASSID);
|
||||
|
||||
extractor::OriginalEdgeData current_edge_data;
|
||||
for (unsigned i = 0; i < number_of_original_edges; ++i)
|
||||
{
|
||||
@@ -390,6 +457,7 @@ int Storage::Run()
|
||||
name_id_ptr[i] = current_edge_data.name_id;
|
||||
travel_mode_ptr[i] = current_edge_data.travel_mode;
|
||||
turn_instructions_ptr[i] = current_edge_data.turn_instruction;
|
||||
entry_class_id_ptr[i] = current_edge_data.entry_classid;
|
||||
}
|
||||
edges_input_stream.close();
|
||||
|
||||
@@ -503,8 +571,7 @@ int Storage::Run()
|
||||
{
|
||||
const unsigned bucket = i / 32;
|
||||
const unsigned offset = i % 32;
|
||||
const unsigned value = [&]
|
||||
{
|
||||
const unsigned value = [&] {
|
||||
unsigned return_value = 0;
|
||||
if (0 != offset)
|
||||
{
|
||||
@@ -539,13 +606,53 @@ int Storage::Run()
|
||||
hsgr_input_stream.close();
|
||||
|
||||
// load profile properties
|
||||
auto profile_properties_ptr = shared_layout_ptr->GetBlockPtr<extractor::ProfileProperties, true>(shared_memory_ptr, SharedDataLayout::PROPERTIES);
|
||||
auto profile_properties_ptr =
|
||||
shared_layout_ptr->GetBlockPtr<extractor::ProfileProperties, true>(
|
||||
shared_memory_ptr, SharedDataLayout::PROPERTIES);
|
||||
boost::filesystem::ifstream profile_properties_stream(config.properties_path);
|
||||
if (!profile_properties_stream)
|
||||
{
|
||||
util::exception("Could not open " + config.properties_path.string() + " for reading!");
|
||||
}
|
||||
profile_properties_stream.read(reinterpret_cast<char*>(profile_properties_ptr), sizeof(extractor::ProfileProperties));
|
||||
profile_properties_stream.read(reinterpret_cast<char *>(profile_properties_ptr),
|
||||
sizeof(extractor::ProfileProperties));
|
||||
|
||||
// load intersection classes
|
||||
if (!bearing_class_id_table.empty())
|
||||
{
|
||||
auto bearing_id_ptr = shared_layout_ptr->GetBlockPtr<BearingClassID, true>(
|
||||
shared_memory_ptr, SharedDataLayout::BEARING_CLASSID);
|
||||
std::copy(bearing_class_id_table.begin(), bearing_class_id_table.end(), bearing_id_ptr);
|
||||
}
|
||||
|
||||
if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_OFFSETS) > 0)
|
||||
{
|
||||
auto *bearing_offsets_ptr = shared_layout_ptr->GetBlockPtr<unsigned, true>(
|
||||
shared_memory_ptr, SharedDataLayout::BEARING_OFFSETS);
|
||||
std::copy(bearing_offsets_data.begin(), bearing_offsets_data.end(), bearing_offsets_ptr);
|
||||
}
|
||||
|
||||
if (shared_layout_ptr->GetBlockSize(SharedDataLayout::BEARING_BLOCKS) > 0)
|
||||
{
|
||||
auto *bearing_blocks_ptr =
|
||||
shared_layout_ptr->GetBlockPtr<typename util::RangeTable<16, true>::BlockT, true>(
|
||||
shared_memory_ptr, SharedDataLayout::BEARING_BLOCKS);
|
||||
std::copy(bearing_blocks_data.begin(), bearing_blocks_data.end(), bearing_blocks_ptr);
|
||||
}
|
||||
|
||||
if (!bearing_class_table.empty())
|
||||
{
|
||||
auto bearing_class_ptr = shared_layout_ptr->GetBlockPtr<DiscreteBearing, true>(
|
||||
shared_memory_ptr, SharedDataLayout::BEARING_VALUES);
|
||||
std::copy(bearing_class_table.begin(), bearing_class_table.end(), bearing_class_ptr);
|
||||
}
|
||||
|
||||
if (!entry_class_table.empty())
|
||||
{
|
||||
auto entry_class_ptr = shared_layout_ptr->GetBlockPtr<util::guidance::EntryClass, true>(
|
||||
shared_memory_ptr, SharedDataLayout::ENTRY_CLASS);
|
||||
std::copy(entry_class_table.begin(), entry_class_table.end(), entry_class_ptr);
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
SharedMemory *data_type_memory =
|
||||
|
||||
@@ -14,8 +14,8 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base)
|
||||
geometries_path{base.string() + ".geometry"}, timestamp_path{base.string() + ".timestamp"},
|
||||
datasource_names_path{base.string() + ".datasource_names"},
|
||||
datasource_indexes_path{base.string() + ".datasource_indexes"},
|
||||
names_data_path{base.string() + ".names"},
|
||||
properties_path{base.string() + ".properties"}
|
||||
names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"},
|
||||
intersection_class_path{base.string() + ".icd"}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ bool StorageConfig::IsValid() const
|
||||
boost::filesystem::is_regular_file(datasource_names_path) &&
|
||||
boost::filesystem::is_regular_file(datasource_indexes_path) &&
|
||||
boost::filesystem::is_regular_file(names_data_path) &&
|
||||
boost::filesystem::is_regular_file(properties_path);
|
||||
boost::filesystem::is_regular_file(properties_path) &&
|
||||
boost::filesystem::is_regular_file(intersection_class_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,9 @@ int main(int argc, const char *argv[]) try
|
||||
sigaddset(&wait_mask, SIGTERM);
|
||||
pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr);
|
||||
util::SimpleLogger().Write() << "running and waiting for requests";
|
||||
if(std::getenv("SIGNAL_PARENT_WHEN_READY")) {
|
||||
kill(getppid(), SIGUSR1);
|
||||
}
|
||||
sigwait(&wait_mask, &sig);
|
||||
#else
|
||||
// Set console control handler to allow server to be stopped.
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "extractor/guidance/discrete_angle.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
bool BearingClass::operator==(const BearingClass &other) const
|
||||
{
|
||||
BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
|
||||
BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
|
||||
if (other.available_bearings.size() != available_bearings.size())
|
||||
return false;
|
||||
for (std::size_t i = 0; i < available_bearings.size(); ++i)
|
||||
if (available_bearings[i] != other.available_bearings[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BearingClass::operator<(const BearingClass &other) const
|
||||
{
|
||||
BOOST_ASSERT(std::is_sorted(available_bearings.begin(), available_bearings.end()));
|
||||
BOOST_ASSERT(std::is_sorted(other.available_bearings.begin(), other.available_bearings.end()));
|
||||
if (available_bearings.size() < other.available_bearings.size())
|
||||
return true;
|
||||
if (available_bearings.size() > other.available_bearings.size())
|
||||
return false;
|
||||
|
||||
for (std::size_t i = 0; i < available_bearings.size(); ++i)
|
||||
{
|
||||
if (available_bearings[i] < other.available_bearings[i])
|
||||
return true;
|
||||
if (available_bearings[i] > other.available_bearings[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BearingClass::add(const DiscreteBearing bearing)
|
||||
{
|
||||
available_bearings.push_back(bearing);
|
||||
}
|
||||
|
||||
const std::vector<DiscreteBearing> &BearingClass::getAvailableBearings() const
|
||||
{
|
||||
return available_bearings;
|
||||
}
|
||||
|
||||
DiscreteBearing BearingClass::getDiscreteBearing(const double bearing)
|
||||
{
|
||||
BOOST_ASSERT(0. <= bearing && bearing <= 360.);
|
||||
auto shifted_bearing = (bearing + 0.5 * discrete_step_size);
|
||||
if (shifted_bearing > 360.)
|
||||
shifted_bearing -= 360;
|
||||
return static_cast<DiscreteBearing>(shifted_bearing / discrete_step_size);
|
||||
}
|
||||
|
||||
std::size_t BearingClass::findMatchingBearing(const double bearing) const
|
||||
{
|
||||
BOOST_ASSERT(!available_bearings.empty());
|
||||
// the small size of the intersections allows a linear compare
|
||||
auto discrete_bearing = static_cast<DiscreteBearing>(bearing);
|
||||
auto max_element =
|
||||
std::max_element(available_bearings.begin(), available_bearings.end(),
|
||||
[&](const DiscreteBearing first, const DiscreteBearing second) {
|
||||
return angularDeviation(first, discrete_bearing) >
|
||||
angularDeviation(second, discrete_bearing);
|
||||
});
|
||||
|
||||
return std::distance(available_bearings.begin(), max_element);
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
EntryClass::EntryClass() : enabled_entries_flags(0) {}
|
||||
|
||||
void EntryClass::activate(std::uint32_t index)
|
||||
{
|
||||
BOOST_ASSERT(index < 8 * sizeof(FlagBaseType));
|
||||
enabled_entries_flags |= (1 << index);
|
||||
}
|
||||
|
||||
bool EntryClass::allowsEntry(std::uint32_t index) const
|
||||
{
|
||||
BOOST_ASSERT(index < 8 * sizeof(FlagBaseType));
|
||||
return 0 != (enabled_entries_flags & (1 << index));
|
||||
}
|
||||
|
||||
bool EntryClass::operator==(const EntryClass &other) const
|
||||
{
|
||||
return enabled_entries_flags == other.enabled_entries_flags;
|
||||
}
|
||||
|
||||
bool EntryClass::operator<(const EntryClass &other) const
|
||||
{
|
||||
return enabled_entries_flags < other.enabled_entries_flags;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
Copyright (c) 2015, ben-strasser
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of fast-cpp-csv-parser nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-252
@@ -1,252 +0,0 @@
|
||||
# Fast C++ Csv Parser
|
||||
|
||||
This is a small, easy-to-use and fast header-only library for reading comma separated value (CSV) files.
|
||||
|
||||
## Features
|
||||
|
||||
* Automatically rearranges columns by parsing the header line.
|
||||
* Disk I/O and CSV-parsing are overlapped using threads for efficiency.
|
||||
* Parsing features such as escaped strings can be enabled and disabled at compile time using templates. You only pay in speed for the features you actually use.
|
||||
* Can read multiple GB files in reasonable time.
|
||||
* Support for custom columns separators (i.e. Tab separated value files are supported), quote escaped strings, automatic space trimming.
|
||||
* Works with `*`nix and Windows newlines and automatically ignores UTF-8 BOMs.
|
||||
* Exception classes with enough context to format useful error messages. what() returns error messages ready to be shown to a user.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The following small example should contain most of the syntax you need to use the library.
|
||||
|
||||
```cpp
|
||||
# include "csv.h"
|
||||
|
||||
int main(){
|
||||
io::CSVReader<3> in("ram.csv");
|
||||
in.read_header(io::ignore_extra_column, "vendor", "size", "speed");
|
||||
std::string vendor; int size; double speed;
|
||||
while(in.read_row(vendor, size, speed)){
|
||||
// do stuff with the data
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
The library only needs a standard conformant C++11 compiler. It has no further dependencies. The library is completely contained inside a single header file and therefore it is sufficient to copy this file to some place on your include path. The library does not have to be explicitly build.
|
||||
|
||||
Note however, that std::future is used and some compiler (f.e. GCC) require you to link against additional libraries (i.e. -lpthread) to make it work. With GCC it is important to add -lpthread as the last item when linking, i.e. the order in
|
||||
|
||||
```
|
||||
g++ a.o b.o -o prog -lpthread
|
||||
```
|
||||
|
||||
is important.
|
||||
|
||||
Remember that the library makes use of C++11 features and therefore you have to enable support for it (f.e. add -std=C++0x or -std=gnu++0x).
|
||||
|
||||
The library was developed and tested with GCC 4.6.1
|
||||
|
||||
Note that VS2013 is not C++11 compilant and will therefore not work out of the box. See [here](https://code.google.com/p/fast-cpp-csv-parser/issues/detail?id=6) for what needs to be adjusted to make the code work.
|
||||
|
||||
## Documentation
|
||||
|
||||
The libary provides two classes:
|
||||
|
||||
* `LineReader`: A class to efficiently read large files line by line.
|
||||
* `CSVReader`: A class that efficiently reads large CSV files.
|
||||
|
||||
Note that everything is contained in the `io` namespace.
|
||||
|
||||
### `LineReader`
|
||||
|
||||
```cpp
|
||||
class LineReader{
|
||||
public:
|
||||
// Constructors
|
||||
LineReader(some_string_type file_name);
|
||||
LineReader(some_string_type file_name, std::FILE*file);
|
||||
|
||||
// Reading
|
||||
char*next_line();
|
||||
|
||||
// File Location
|
||||
void set_file_line(unsigned);
|
||||
unsigned get_file_line(unsigned)const;
|
||||
void set_file_name(some_string_type file_name);
|
||||
const char*get_truncated_file_name()const;
|
||||
};
|
||||
```
|
||||
|
||||
The constructor takes a file name and optionally a `stdio.h` file handle. If no file handle is provided the class tries to open the file and throws an `error::can_not_open_file exception` on failure. If a file handle is provided then the file name is only used to format error messages. The library will call `std::fclose` on the file handle. `some_string_type` can be a `std::string` or a `char*`.
|
||||
|
||||
Lines are read by calling the `next_line` function. It returns a pointer to a null terminated C-string that contains the line. If the end of file is reached a null pointer is returned. The newline character is not included in the string. You may modify the string as long as you do not write past the null terminator. The string stays valid until the destructor is called or until next_line is called again. Windows and `*`nix newlines are handled transparently. UTF-8 BOMs are automatically ignored and missing newlines at the end of the file are no problem.
|
||||
|
||||
**Important:** There is a limit of 2^24-1 characters per line. If this limit is exceeded a `error::line_length_limit_exceeded` exception is thrown.
|
||||
|
||||
Looping over all the lines in a file can be done in the following way.
|
||||
```cpp
|
||||
LineReader in(...);
|
||||
while(char*line = in.next_line()){
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The remaining functions are mainly used used to format error messages. The file line indicates the current position in the file, i.e., after the first `next_line` call it is 1 and after the second 2. Before the first call it is 0. The file name is truncated as internally C-strings are used to avoid `std::bad_alloc` exceptions during error reporting.
|
||||
|
||||
**Note:** It is not possible to exchange the line termination character.
|
||||
|
||||
### `CSVReader`
|
||||
|
||||
`CSVReader` uses policies. These are classes with only static members to allow core functionality to be exchanged in an efficient way.
|
||||
|
||||
```cpp
|
||||
template<
|
||||
unsigned column_count,
|
||||
class trim_policy = trim_chars<' ', '\t'>,
|
||||
class quote_policy = no_quote_escape<','>,
|
||||
class overflow_policy = throw_on_overflow,
|
||||
class comment_policy = no_comment
|
||||
>
|
||||
class CSVReader{
|
||||
public:
|
||||
// Constructors
|
||||
CSVReader(some_string_type file_name);
|
||||
CSVReader(some_string_type file_name, std::FILE*file);
|
||||
|
||||
// Parsing Header
|
||||
void read_header(ignore_column ignore_policy, some_string_type col_name1, some_string_type col_name2, ...);
|
||||
void set_header(some_string_type col_name1, some_string_type col_name2, ...);
|
||||
bool has_column(some_string_type col_name)const;
|
||||
|
||||
// Read
|
||||
bool read_row(ColType1&col1, ColType2&col2, ...);
|
||||
|
||||
// File Location
|
||||
void set_file_line(unsigned);
|
||||
unsigned get_file_line(unsigned)const;
|
||||
void set_file_name(some_string_type file_name);
|
||||
const char*get_truncated_file_name()const;
|
||||
};
|
||||
```
|
||||
|
||||
The `column_count` template parameter indicates how many columns you want to read from the CSV file. This must not necessarily coincide with the actual number of columns in the file. The three policies govern various aspects of the parsing.
|
||||
|
||||
The trim policy indicates what characters should be ignored at the begin and the end of every column. The default ignores spaces and tabs. This makes sure that
|
||||
|
||||
```
|
||||
a,b,c
|
||||
1,2,3
|
||||
```
|
||||
|
||||
is interpreted in the same way as
|
||||
|
||||
```
|
||||
a, b, c
|
||||
1 , 2, 3
|
||||
```
|
||||
|
||||
The trim_chars can take any number of template parameters. For example `trim_chars<' ', '\t', '_'> `is also valid. If no character should be trimmed use `trim_chars<>`.
|
||||
|
||||
The quote policy indicates how string should be escaped. It also specifies the column separator. The predefined policies are:
|
||||
|
||||
* `no_quote_escape<sep>` : Strings are not escaped. "`sep`" is used as column separator.
|
||||
* `double_quote_escape<sep, quote>` : Strings are escaped using quotes. Quotes are escaped using two consecutive quotes. "`sep`" is used as column separator and "`quote`" as quoting character.
|
||||
|
||||
**Important**: When combining trimming and quoting the rows are first trimmed and then unquoted. A consequence is that spaces inside the quotes will be conserved. If you want to get rid of spaces inside the quotes, you need to remove them yourself.
|
||||
|
||||
**Important**: Quoting can be quite expensive. Disable it if you do not need it.
|
||||
|
||||
The overflow policy indicates what should be done if the integers in the input are too large to fit into the variables. There following policies are predefined:
|
||||
|
||||
* `throw_on_overflow` : Throw an `error::integer_overflow` or `error::integer_underflow` exception.
|
||||
* `ignore_overflow` : Do nothing and let the overflow happen.
|
||||
* `set_to_max_on_overflow` : Set the value to `numeric_limits<...>::max()` (or to the min-pendant).
|
||||
|
||||
The comment policy allows to skip lines based on some criteria. Valid predefined policies are:
|
||||
|
||||
* `no_comment` : Do not ignore any line.
|
||||
* `empty_line_comment` : Ignore all lines that are empty or only contains spaces and tabs.
|
||||
* `single_line_comment<com1, com2, ...>` : Ignore all lines that start with com1 or com2 or ... as the first character. There may not be any space between the beginning of the line and the comment character.
|
||||
* `single_and_empty_line_comment<com1, com2, ...>` : Ignore all empty lines and single line comments.
|
||||
|
||||
Examples:
|
||||
|
||||
* `CSVReader<4, trim_chars<' '>, double_quote_escape<',','\"'> >` reads 4 columns from a normal CSV file with string escaping enabled.
|
||||
* `CSVReader<3, trim_chars<' '>, no_quote_escape<'\t'>, single_line_comment<'#'> >` reads 3 columns from a tab separated file with string escaping disabled. Lines starting with a # are ignored.
|
||||
|
||||
The constructors and the file location functions are exactly the same as for `LineReader`. See its documentation for details.
|
||||
|
||||
There are three methods that deal with headers. The `read_header` methods reads a line from the file and rearranges the columns to match that order. It also checks whether all necessary columns are present. The `set_header` method does *not* read any input. Use it if the file does not have any header. Obviously it is impossible to rearrange columns or check for their availability when using it. The order in the file and in the program must match when using `set_header`. The `has_column` method checks whether a column is present in the file. The first argument of `read_header` is a bitfield that determines how the function should react to column mismatches. The default behavior is to throw an `error::extra_column_in_header` exception if the file contains more columns than expected and an `error::missing_column_in_header` when there are not enough. This behavior can be altered using the following flags.
|
||||
|
||||
* `ignore_no_column`: The default behavior, no flags are set
|
||||
* `ignore_extra_column`: If a column with a name is in the file but not in the argument list, then it is silently ignored.
|
||||
* `ignore_missing_column`: If a column with a name is not in the file but is in the argument list, then `read_row` will not modify the corresponding variable.
|
||||
|
||||
When using `ignore_column_missing` it is a good idea to initialize the variables passed to `read_row` with a default value, for example:
|
||||
|
||||
```cpp
|
||||
// The file only contains column "a"
|
||||
CSVReader<2>in(...);
|
||||
in.read_header(ignore_missing_column, "a", "b");
|
||||
int a,b = 42;
|
||||
while(in.read_row(a,b)){
|
||||
// a contains the value from the file
|
||||
// b is left unchanged by read_row, i.e., it is 42
|
||||
}
|
||||
```
|
||||
|
||||
If only some columns are optional or their default value depends on other columns you have to use `has_column`, for example:
|
||||
|
||||
```cpp
|
||||
// The file only contains the columns "a" and "b"
|
||||
CSVReader<2>in(...);
|
||||
in.read_header(ignore_missing_column, "a", "b", "sum");
|
||||
if(!in.has_column("a") || !in.has_column("b"))
|
||||
throw my_neat_error_class();
|
||||
bool has_sum = in.has_column("sum");
|
||||
int a,b,sum;
|
||||
while(in.read_row(a,b,sum)){
|
||||
if(!has_sum)
|
||||
sum = a+b;
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Do not call `has_column` from within the read-loop. It would work correctly but significantly slowdown processing.
|
||||
|
||||
If two columns have the same name an error::duplicated_column_in_header exception is thrown. If `read_header` is called but the file is empty a `error::header_missing` exception is thrown.
|
||||
|
||||
The `read_row` function reads a line, splits it into the columns and arranges them correctly. It trims the entries and unescapes them. If requested the content is interpreted as integer or as floating point. The variables passed to read_row may be of the following types.
|
||||
|
||||
* builtin signed integer: These are `signed char`, `short`, `int`, `long` and `long long`. The input must be encoded as a base 10 ASCII number optionally preceded by a + or -. The function detects whether the integer is too large would overflow (or underflow) and behaves as indicated by overflow_policy.
|
||||
* builtin unsigned integer: Just as the signed counterparts except that a leading + or - is not allowed.
|
||||
* builtin floating point: These are `float`, `double` and `long double`. The input may have a leading + or -. The number must be base 10 encoded. The decimal point may either be a dot or a comma. (Note that a comma will only work if it is not also used as column separator or the number is escaped.) A base 10 exponent may be specified using the "1e10" syntax. The "e" may be lower- or uppercase. Examples for valid floating points are "1", "-42.42" and "+123.456E789". The input is rounded to the next floating point or infinity if it is too large or small.
|
||||
* `char`: The column content must be a single character.
|
||||
* `std::string`: The column content is assigned to the string. The std::string is filled with the trimmed and unescaped version.
|
||||
* `char*`: A pointer directly into the buffer. The string is trimmed and unescaped and null terminated. This pointer stays valid until read_row is called again or the CSVReader is destroyed. Use this for user defined types.
|
||||
|
||||
Note that there is no inherent overhead to using `char*` and then interpreting it compared to using one of the parsers directly build into `CSVReader`. The builtin number parsers are pure convenience. If you need a slightly different syntax then use `char*` and do the parsing yourself.
|
||||
|
||||
## FAQ
|
||||
|
||||
Q: The library is throwing a std::system_error with code -1. How to get it to work?
|
||||
|
||||
A: Your compiler's std::thread implementation is broken. Define CSV\_IO\_NO\_THREAD to disable threading support.
|
||||
|
||||
|
||||
Q: My values are not just ints or strings. I want to parse my customized type. Is this possible?
|
||||
|
||||
A: Read a `char*` and parse the string. At first this seems expensive but it is not as the pointer you get points directly into the memory buffer. In fact there is no inherent reason why a custom int-parser realized this way must be any slower than the int-parser build into the library. By reading a `char*` the library takes care of column reordering and quote escaping and leaves the actual parsing to you. Note that using a std::string is slower as it involves a memory copy.
|
||||
|
||||
|
||||
Q: I get lots of compiler errors when compiling the header! Please fix it. :(
|
||||
|
||||
A: Have you enabled the C++11 mode of your compiler? If you use GCC you have to add -std=c++0x to the commandline. If this does not resolve the problem, then please open a ticket.
|
||||
|
||||
|
||||
Q: The library crashes when parsing large files! Please fix it. :(
|
||||
|
||||
A: When using GCC have you linked against -lpthread? Read the installation section for details on how to do this. If this does not resolve the issue then please open a ticket. (The reason why it only crashes only on large files is that the first chuck is read synchronous and if the whole file fits into this chuck then no asynchronous call is performed.) Alternatively you can define CSV\_IO\_NO\_THREAD.
|
||||
|
||||
|
||||
Q: Does the library support UTF?
|
||||
|
||||
A: The library has basic UTF-8 support, or to be more precise it does not break when passing UTF-8 strings through it. If you read a `char*` then you get a pointer to the UTF-8 string. You will have to decode the string on your own. The separator, quoting, and commenting characters used by the library can only be ASCII characters.
|
||||
-1068
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ set(AllBoostLibrariesExceptUnitTest ${Boost_LIBRARIES})
|
||||
|
||||
find_package(Boost 1.49.0 REQUIRED COMPONENTS unit_test_framework)
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32 AND NOT Boost_USE_STATIC_LIBS)
|
||||
add_definitions(-DBOOST_TEST_DYN_LINK)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -56,29 +56,39 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture)
|
||||
{{"distance", 0.},
|
||||
{"duration", 0.},
|
||||
{"summary", ""},
|
||||
{"steps", json::Array{{json::Object{{{"duration", 0.},
|
||||
{"distance", 0.},
|
||||
{"geometry", "yw_jGupkl@??"},
|
||||
{"name", "Boulevard du Larvotto"},
|
||||
{"mode", "driving"},
|
||||
{"maneuver", json::Object{{
|
||||
{"type", "depart"},
|
||||
{"location", location},
|
||||
{"bearing_before", 0.},
|
||||
{"bearing_after", 0.},
|
||||
}}}}},
|
||||
{"steps",
|
||||
json::Array{{{json::Object{{{"duration", 0.},
|
||||
{"distance", 0.},
|
||||
{"geometry", "yw_jGupkl@??"},
|
||||
{"name", "Boulevard du Larvotto"},
|
||||
{"mode", "driving"},
|
||||
{"maneuver", json::Object{{
|
||||
{"location", location},
|
||||
{"bearing_before", 0},
|
||||
{"bearing_after", 0},
|
||||
{"type", "depart"},
|
||||
}}},
|
||||
{"intersections",
|
||||
json::Array{{json::Object{
|
||||
{{"location", location},
|
||||
{"bearings", json::Array{{0}}},
|
||||
{"entry", json::Array{{json::True()}}},
|
||||
{"out", 0}}}}}}}}},
|
||||
|
||||
json::Object{{{"duration", 0.},
|
||||
{"distance", 0.},
|
||||
{"geometry", "yw_jGupkl@"},
|
||||
{"name", "Boulevard du Larvotto"},
|
||||
{"mode", "driving"},
|
||||
{"maneuver", json::Object{{
|
||||
{"type", "arrive"},
|
||||
{"location", location},
|
||||
{"bearing_before", 0.},
|
||||
{"bearing_after", 0.},
|
||||
}}}}}}}}}}}}}}}}}}}};
|
||||
json::Object{{{"duration", 0.},
|
||||
{"distance", 0.},
|
||||
{"geometry", "yw_jGupkl@"},
|
||||
{"name", "Boulevard du Larvotto"},
|
||||
{"mode", "driving"},
|
||||
{"maneuver", json::Object{{{"location", location}, {"bearing_before", 0}, {"bearing_after", 0}, {"type", "arrive"}}}},
|
||||
{"intersections",
|
||||
json::Array{{json::Object{
|
||||
{{"location", location},
|
||||
{"bearings", json::Array{{180}}},
|
||||
{"entry", json::Array{{json::True()}}},
|
||||
{"in", 0}}}}}}
|
||||
|
||||
}}}}}}}}}}}}}}}}};
|
||||
|
||||
CHECK_EQUAL_JSON(reference, result);
|
||||
}
|
||||
@@ -161,6 +171,8 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
|
||||
const auto &steps = leg_object.values.at("steps").get<json::Array>().values;
|
||||
BOOST_CHECK(!steps.empty());
|
||||
|
||||
std::size_t step_count = 0;
|
||||
|
||||
for (const auto &step : steps)
|
||||
{
|
||||
const auto &step_object = step.get<json::Object>();
|
||||
@@ -185,25 +197,47 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates)
|
||||
|
||||
const auto &maneuver = step_object.values.at("maneuver").get<json::Object>().values;
|
||||
|
||||
const auto location = maneuver.at("location").get<json::Array>().values;
|
||||
const auto longitude = location[0].get<json::Number>().value;
|
||||
const auto latitude = location[1].get<json::Number>().value;
|
||||
BOOST_CHECK(longitude >= -180. && longitude <= 180.);
|
||||
BOOST_CHECK(latitude >= -90. && latitude <= 90.);
|
||||
|
||||
const auto bearing_before = maneuver.at("bearing_before").get<json::Number>().value;
|
||||
const auto bearing_after = maneuver.at("bearing_after").get<json::Number>().value;
|
||||
BOOST_CHECK(bearing_before >= 0. && bearing_before <= 360.);
|
||||
BOOST_CHECK(bearing_after >= 0. && bearing_after <= 360.);
|
||||
|
||||
const auto type = maneuver.at("type").get<json::String>().value;
|
||||
BOOST_CHECK(!type.empty());
|
||||
|
||||
const auto &intersections =
|
||||
step_object.values.at("intersections").get<json::Array>().values;
|
||||
|
||||
for (auto &intersection : intersections)
|
||||
{
|
||||
const auto &intersection_object = intersection.get<json::Object>().values;
|
||||
const auto location = intersection_object.at("location").get<json::Array>().values;
|
||||
const auto longitude = location[0].get<json::Number>().value;
|
||||
const auto latitude = location[1].get<json::Number>().value;
|
||||
BOOST_CHECK(longitude >= -180. && longitude <= 180.);
|
||||
BOOST_CHECK(latitude >= -90. && latitude <= 90.);
|
||||
|
||||
const auto &bearings = intersection_object.at("bearings").get<json::Array>().values;
|
||||
BOOST_CHECK(!bearings.empty());
|
||||
const auto &entries = intersection_object.at("entry").get<json::Array>().values;
|
||||
BOOST_CHECK(bearings.size() == entries.size());
|
||||
|
||||
for( const auto bearing : bearings )
|
||||
BOOST_CHECK( 0. <= bearing.get<json::Number>().value && bearing.get<json::Number>().value <= 360. );
|
||||
|
||||
if( step_count > 0 )
|
||||
{
|
||||
const auto in = intersection_object.at("in").get<json::Number>().value;
|
||||
BOOST_CHECK(in < bearings.size());
|
||||
}
|
||||
if( step_count + 1 < steps.size() )
|
||||
{
|
||||
const auto out = intersection_object.at("out").get<json::Number>().value;
|
||||
BOOST_CHECK(out < bearings.size());
|
||||
}
|
||||
}
|
||||
|
||||
// modifier is optional
|
||||
// TODO(daniel-j-h):
|
||||
|
||||
// exit is optional
|
||||
// TODO(daniel-j-h):
|
||||
++step_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
// implements all data storage when shared memory _IS_ used
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -171,6 +174,27 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade
|
||||
std::size_t GetCoreSize() const override { return 0; }
|
||||
std::string GetTimestamp() const override { return ""; }
|
||||
bool GetContinueStraightDefault() const override { return true; }
|
||||
BearingClassID GetBearingClassID(const NodeID /*id*/) const override { return 0; };
|
||||
EntryClassID GetEntryClassID(const EdgeID /*id*/) const override { return 0; }
|
||||
|
||||
util::guidance::BearingClass GetBearingClass(const BearingClassID /*bearing_class_id*/) const override
|
||||
{
|
||||
util::guidance::BearingClass result;
|
||||
result.add(0);
|
||||
result.add(90);
|
||||
result.add(180);
|
||||
result.add(270);
|
||||
return result;
|
||||
}
|
||||
|
||||
util::guidance::EntryClass GetEntryClass(const EntryClassID /*entry_class_id*/) const override
|
||||
{
|
||||
util::guidance::EntryClass result;
|
||||
result.activate(1);
|
||||
result.activate(2);
|
||||
result.activate(3);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // ns test
|
||||
} // ns osrm
|
||||
|
||||
@@ -85,6 +85,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_1.steps, result_1->steps);
|
||||
BOOST_CHECK_EQUAL(reference_1.alternatives, result_1->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_1.geometries, result_1->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_1.annotation, result_1->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_1.overview, result_1->overview);
|
||||
BOOST_CHECK_EQUAL(reference_1.continue_straight, result_1->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings);
|
||||
@@ -95,13 +96,15 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
RouteParameters reference_2{};
|
||||
reference_2.alternatives = true;
|
||||
reference_2.steps = true;
|
||||
reference_2.annotation = true;
|
||||
reference_2.coordinates = coords_1;
|
||||
auto result_2 = parseParameters<RouteParameters>(
|
||||
"1,2;3,4?steps=true&alternatives=true&geometries=polyline&overview=simplified");
|
||||
"1,2;3,4?steps=true&alternatives=true&geometries=polyline&overview=simplified&annotate=true");
|
||||
BOOST_CHECK(result_2);
|
||||
BOOST_CHECK_EQUAL(reference_2.steps, result_2->steps);
|
||||
BOOST_CHECK_EQUAL(reference_2.alternatives, result_2->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_2.geometries, result_2->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_2.annotation, result_2->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_2.overview, result_2->overview);
|
||||
BOOST_CHECK_EQUAL(reference_2.continue_straight, result_2->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings);
|
||||
@@ -109,7 +112,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates);
|
||||
CHECK_EQUAL_RANGE(reference_2.hints, result_2->hints);
|
||||
|
||||
RouteParameters reference_3{false, false, RouteParameters::GeometriesType::GeoJSON,
|
||||
RouteParameters reference_3{false, false, false, RouteParameters::GeometriesType::GeoJSON,
|
||||
RouteParameters::OverviewType::False, true};
|
||||
reference_3.coordinates = coords_1;
|
||||
auto result_3 = api::parseParameters<engine::api::RouteParameters>(
|
||||
@@ -119,6 +122,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_3.steps, result_3->steps);
|
||||
BOOST_CHECK_EQUAL(reference_3.alternatives, result_3->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_3.geometries, result_3->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_3.annotation, result_3->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_3.overview, result_3->overview);
|
||||
BOOST_CHECK_EQUAL(reference_3.continue_straight, result_3->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_3.bearings, result_3->bearings);
|
||||
@@ -137,6 +141,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
"39KAAAAHgAAACEAAAAAAAAAGAAAAE0BAABOAQAAGwAAAIAzcQBkUJsC1zNxAHBQmw"
|
||||
"IAAAEBl-Umfg==")};
|
||||
RouteParameters reference_4{false,
|
||||
false,
|
||||
false,
|
||||
RouteParameters::GeometriesType::Polyline,
|
||||
RouteParameters::OverviewType::Simplified,
|
||||
@@ -154,6 +159,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps);
|
||||
BOOST_CHECK_EQUAL(reference_4.alternatives, result_4->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_4.geometries, result_4->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_4.annotation, result_4->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_4.overview, result_4->overview);
|
||||
BOOST_CHECK_EQUAL(reference_4.continue_straight, result_4->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_4.bearings, result_4->bearings);
|
||||
@@ -165,6 +171,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
boost::none, engine::Bearing{200, 10}, engine::Bearing{100, 5},
|
||||
};
|
||||
RouteParameters reference_5{false,
|
||||
false,
|
||||
false,
|
||||
RouteParameters::GeometriesType::Polyline,
|
||||
RouteParameters::OverviewType::Simplified,
|
||||
@@ -178,6 +185,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_5.steps, result_5->steps);
|
||||
BOOST_CHECK_EQUAL(reference_5.alternatives, result_5->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_5.geometries, result_5->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_5.annotation, result_5->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_5.overview, result_5->overview);
|
||||
BOOST_CHECK_EQUAL(reference_5.continue_straight, result_5->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_5.bearings, result_5->bearings);
|
||||
@@ -196,6 +204,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_6.steps, result_6->steps);
|
||||
BOOST_CHECK_EQUAL(reference_6.alternatives, result_6->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_6.geometries, result_6->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_6.annotation, result_6->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_6.overview, result_6->overview);
|
||||
BOOST_CHECK_EQUAL(reference_6.continue_straight, result_6->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_6.bearings, result_6->bearings);
|
||||
@@ -211,6 +220,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_7.steps, result_7->steps);
|
||||
BOOST_CHECK_EQUAL(reference_7.alternatives, result_7->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_7.geometries, result_7->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_7.annotation, result_7->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_7.overview, result_7->overview);
|
||||
BOOST_CHECK_EQUAL(reference_7.continue_straight, result_7->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_7.bearings, result_7->bearings);
|
||||
@@ -247,6 +257,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
"IFAAEBl-Umfg=="),
|
||||
boost::none};
|
||||
RouteParameters reference_10{false,
|
||||
false,
|
||||
false,
|
||||
RouteParameters::GeometriesType::Polyline,
|
||||
RouteParameters::OverviewType::Simplified,
|
||||
@@ -263,6 +274,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls)
|
||||
BOOST_CHECK_EQUAL(reference_10.steps, result_10->steps);
|
||||
BOOST_CHECK_EQUAL(reference_10.alternatives, result_10->alternatives);
|
||||
BOOST_CHECK_EQUAL(reference_10.geometries, result_10->geometries);
|
||||
BOOST_CHECK_EQUAL(reference_10.annotation, result_10->annotation);
|
||||
BOOST_CHECK_EQUAL(reference_10.overview, result_10->overview);
|
||||
BOOST_CHECK_EQUAL(reference_10.continue_straight, result_10->continue_straight);
|
||||
CHECK_EQUAL_RANGE(reference_10.bearings, result_10->bearings);
|
||||
|
||||
@@ -6,17 +6,32 @@
|
||||
BOOST_AUTO_TEST_SUITE(durations_are_valid)
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::util;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(all_necessary_test)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("0"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:01"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("61"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("24:01"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:60"), true);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15M"), true);
|
||||
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid(""), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15A"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT1H25:01"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT12501"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT0125:01"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT016001"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT240000"), false);
|
||||
BOOST_CHECK_EQUAL(extractor::durationIsValid("PT24:00:00"), false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(common_durations_get_translated)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("00"), 0);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("10"), 600);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("00:01"), 60);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("00:01:01"), 61);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("01:01"), 3660);
|
||||
@@ -29,12 +44,22 @@ BOOST_AUTO_TEST_CASE(common_durations_get_translated)
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT15H"), 54000);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M"), 4500);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M1S"), 4501);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT2H25M6S"), 8706);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("P1DT2H15M1S"), 94501);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("P4D"), 345600);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT4H"), 14400);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT71M"), 4260);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT022506"), 8706);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT02:25:06"), 8706);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("P3W"), 1814400);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(iso_8601_durations_case_insensitive)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT15m"), 900);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m"), 4500);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m42s"), 4542);
|
||||
BOOST_CHECK_EQUAL(extractor::parseDuration("P2dT1h15m42s"), 177342);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
Reference in New Issue
Block a user