Compare commits
26 Commits
master
...
v5.4.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
|
f9ecdca5d5 | ||
|
accdbe92f0 | ||
|
644d082cb9 | ||
|
f948fb081b | ||
|
42445dc10c | ||
|
c1041e5a64 | ||
|
b2bc169547 | ||
|
5713460331 | ||
|
cc2e26fd52 | ||
|
c0bd9da645 | ||
|
7348e7ca55 | ||
|
519dc986f9 | ||
|
1ae2964b0f | ||
|
237e8e02fd | ||
|
865052352e | ||
|
2064d6b2ce | ||
|
6c9227d4d0 | ||
|
ef66e271c1 | ||
|
7c3b587028 | ||
|
d399014633 | ||
|
e27ce0e518 | ||
|
1a1e16c5b4 | ||
|
01e7232add | ||
|
c69f99f282 | ||
|
391163cba0 | ||
|
4cddec298f |
@ -13,6 +13,7 @@ notifications:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- 5.4
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
ccache: true
|
ccache: true
|
||||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,5 +1,25 @@
|
|||||||
|
# 5.4.0-rc.5
|
||||||
|
- Changes from 5.4.0-rc.4
|
||||||
|
- Guidance
|
||||||
|
- Improved detection of obvious name changes
|
||||||
|
- Profiles
|
||||||
|
- The defautl profile for car now excludes HOV only routes in navigation by default
|
||||||
|
- Bugfixes
|
||||||
|
- Fixed a bug that could result in endless loops in combination with sliproads
|
||||||
|
|
||||||
|
# 5.4.0-rc.4
|
||||||
|
- Changes from 5.4.0-rc.3
|
||||||
|
- Bugfixes
|
||||||
|
- Fixed a bug where roundabout intersections could result in breaking assertions when immediately exited
|
||||||
|
|
||||||
|
# 5.4.0-rc.3
|
||||||
|
- Changes from 5.4.0-rc.2
|
||||||
|
- Bugfixes
|
||||||
|
- BREAKING: Fixed a bug where some roads could be falsly identified as sliproadsi This change requires reprocessing datasets with osrm-extract and osrm-contract
|
||||||
|
- BREAKING: Fixed a bug that resulted in false names/ref/destination/pronunciation This change requires reprocessing datasets with osrm-extract and osrm-contract
|
||||||
|
|
||||||
# 5.4.0
|
# 5.4.0
|
||||||
Changes from 5.3.0
|
- Changes from 5.3.0
|
||||||
- Profiles
|
- Profiles
|
||||||
- includes library guidance.lua that offers preliminary configuration on guidance.
|
- includes library guidance.lua that offers preliminary configuration on guidance.
|
||||||
- added left_hand_driving flag in global profile properties
|
- added left_hand_driving flag in global profile properties
|
||||||
@ -20,9 +40,14 @@
|
|||||||
- Fixed an issue that could emit `invalid` as instruction when ending on a sliproad after a traffic-light
|
- Fixed an issue that could emit `invalid` as instruction when ending on a sliproad after a traffic-light
|
||||||
- Fixed an issue that would detect turning circles as sliproads
|
- Fixed an issue that would detect turning circles as sliproads
|
||||||
- Fixed a bug where post-processing instructions (e.g. left + left -> uturn) could result in false pronunciations
|
- Fixed a bug where post-processing instructions (e.g. left + left -> uturn) could result in false pronunciations
|
||||||
|
- Fixes a bug where a bearing range of zero would cause exhaustive graph traversals
|
||||||
|
- Fixes a bug where certain looped geometries could cause an infinite loop during extraction
|
||||||
|
|
||||||
|
- Infrastructure:
|
||||||
|
- Adds a feature to limit results in nearest service with a default of 100 in `osrm-routed`
|
||||||
|
|
||||||
# 5.3.0
|
# 5.3.0
|
||||||
Changes from 5.3.0-rc.3
|
- Changes from 5.3.0-rc.3
|
||||||
- Guidance
|
- Guidance
|
||||||
- Only announce `use lane` on required turns (not using all lanes to go straight)
|
- Only announce `use lane` on required turns (not using all lanes to go straight)
|
||||||
- Moved `lanes` to the intersection objects. This is BREAKING in relation to other Release Candidates but not with respect to other releases.
|
- Moved `lanes` to the intersection objects. This is BREAKING in relation to other Release Candidates but not with respect to other releases.
|
||||||
@ -30,7 +55,7 @@
|
|||||||
- Fix BREAKING: bug that could result in failure to load 'osrm.icd' files. This breaks the dataformat
|
- Fix BREAKING: bug that could result in failure to load 'osrm.icd' files. This breaks the dataformat
|
||||||
- Fix: bug that results in segfaults when `use lane` instructions are suppressed
|
- Fix: bug that results in segfaults when `use lane` instructions are suppressed
|
||||||
|
|
||||||
Changes form 5.2.7
|
- Changes form 5.2.7
|
||||||
- API
|
- API
|
||||||
- Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning
|
- Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning
|
||||||
- Introduces `lanes` to the `Intersection` object. The lane data contains both the markings at the intersection and a flag indicating if they can be chosen for the next turn
|
- Introduces `lanes` to the `Intersection` object. The lane data contains both the markings at the intersection and a flag indicating if they can be chosen for the next turn
|
||||||
@ -54,7 +79,7 @@
|
|||||||
- Fix devide by zero on updating speed data using osrm-contract
|
- Fix devide by zero on updating speed data using osrm-contract
|
||||||
|
|
||||||
# 5.3.0 RC3
|
# 5.3.0 RC3
|
||||||
Changes from 5.3.0-rc.2
|
- Changes from 5.3.0-rc.2
|
||||||
- Guidance
|
- Guidance
|
||||||
- Improved detection of obvious turns
|
- Improved detection of obvious turns
|
||||||
- Improved turn lane detection
|
- Improved turn lane detection
|
||||||
|
@ -9,7 +9,7 @@ endif()
|
|||||||
|
|
||||||
project(OSRM C CXX)
|
project(OSRM C CXX)
|
||||||
set(OSRM_VERSION_MAJOR 5)
|
set(OSRM_VERSION_MAJOR 5)
|
||||||
set(OSRM_VERSION_MINOR 3)
|
set(OSRM_VERSION_MINOR 4)
|
||||||
set(OSRM_VERSION_PATCH 0)
|
set(OSRM_VERSION_PATCH 0)
|
||||||
|
|
||||||
# these two functions build up custom variables:
|
# these two functions build up custom variables:
|
||||||
|
10
cucumber.js
10
cucumber.js
@ -1,10 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
default: '--require features --tags ~@stress --tags ~@todo',
|
default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions',
|
||||||
verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress',
|
verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
|
||||||
jenkins: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress',
|
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
|
||||||
bugs: '--require features --tags @bug',
|
all: '--strict --require features/support --require features/step_definitions'
|
||||||
todo: '--require features --tags @todo',
|
|
||||||
all: '--require features'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ http://{server}/{service}/{version}/{profile}/{coordinates}[.{format}]?option=va
|
|||||||
|
|
||||||
| Service | Description |
|
| Service | Description |
|
||||||
|-------------|-----------------------------------------------------------|
|
|-------------|-----------------------------------------------------------|
|
||||||
| [`route`](#service-route) | shortest path between given coordinates |
|
| [`route`](#service-route) | fastest path between given coordinates |
|
||||||
| [`nearest`](#service-nearest) | returns the nearest street segment for a given coordinate |
|
| [`nearest`](#service-nearest) | returns the nearest street segment for a given coordinate |
|
||||||
| [`table`](#service-table) | computes distance tables for given coordinates |
|
| [`table`](#service-table) | computes distance tables for given coordinates |
|
||||||
| [`match`](#service-match) | matches given coordinates to the road network |
|
| [`match`](#service-match) | matches given coordinates to the road network |
|
||||||
| [`trip`](#service-trip) | Compute the shortest round trip between given coordinates |
|
| [`trip`](#service-trip) | Compute the fastest round trip between given coordinates |
|
||||||
| [`tile`](#service-tile) | Return vector tiles containing debugging info |
|
| [`tile`](#service-tile) | Return vector tiles containing debugging info |
|
||||||
|
|
||||||
- `version`: Version of the protocol implemented by the service.
|
- `version`: Version of the protocol implemented by the service.
|
||||||
@ -302,7 +302,7 @@ All other fields might be undefined.
|
|||||||
## Service `trip`
|
## Service `trip`
|
||||||
|
|
||||||
The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm).
|
The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm).
|
||||||
The returned path does not have to be the shortest path, as TSP is NP-hard it is only an approximation.
|
The returned path does not have to be the fastest path, as TSP is NP-hard it is only an approximation.
|
||||||
Note that if the input coordinates can not be joined by a single trip (e.g. the coordinates are on several disconnected islands)
|
Note that if the input coordinates can not be joined by a single trip (e.g. the coordinates are on several disconnected islands)
|
||||||
multiple trips for each connected component are returned.
|
multiple trips for each connected component are returned.
|
||||||
|
|
||||||
|
@ -156,6 +156,25 @@ Feature: Car - Restricted access
|
|||||||
| primary | yes | x |
|
| primary | yes | x |
|
||||||
| primary | no | x |
|
| primary | no | x |
|
||||||
|
|
||||||
|
Scenario: Car - a way with all lanes HOV-designated is inaccessible by default (similar to hov=designated)
|
||||||
|
Then routability should be
|
||||||
|
| highway | hov:lanes:forward | hov:lanes:backward | hov:lanes | oneway | forw | backw |
|
||||||
|
| primary | designated | designated | | | | |
|
||||||
|
| primary | | designated | | | x | |
|
||||||
|
| primary | designated | | | | | x |
|
||||||
|
| primary | designated\|designated | designated\|designated | | | | |
|
||||||
|
| primary | designated\|no | designated\|no | | | x | x |
|
||||||
|
| primary | yes\|no | yes\|no | | | x | x |
|
||||||
|
| primary | | | | | x | x |
|
||||||
|
| primary | designated | | | -1 | | |
|
||||||
|
| primary | | designated | | -1 | | x |
|
||||||
|
| primary | | | designated | yes | | |
|
||||||
|
| primary | | | designated | -1 | | |
|
||||||
|
| primary | | | designated\|designated | yes | | |
|
||||||
|
| primary | | | designated\|designated | -1 | | |
|
||||||
|
| primary | | | designated\|yes | yes | x | |
|
||||||
|
| primary | | | designated\|no | -1 | | x |
|
||||||
|
|
||||||
Scenario: Car - these toll roads always work
|
Scenario: Car - these toll roads always work
|
||||||
Then routability should be
|
Then routability should be
|
||||||
| highway | toll | bothw |
|
| highway | toll | bothw |
|
||||||
|
@ -35,6 +35,23 @@ Feature: Car - Street names in instructions
|
|||||||
| a | d | My Way,My Way | ,meyeway | ,A1 |
|
| a | d | My Way,My Way | ,meyeway | ,A1 |
|
||||||
| 1 | c | Your Way,Your Way | yourewaye,yourewaye | , |
|
| 1 | c | Your Way,Your Way | yourewaye,yourewaye | , |
|
||||||
|
|
||||||
|
# See #2860
|
||||||
|
Scenario: Car - same street name but different pronunciation
|
||||||
|
Given the node map
|
||||||
|
| a | b | c |
|
||||||
|
| | d | |
|
||||||
|
| | e | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | name:pronunciation |
|
||||||
|
| abc | Houston St | hew-stun |
|
||||||
|
| bde | Houston St | how-stun |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | pronunciations |
|
||||||
|
| a | c | Houston St,Houston St | hew-stun,hew-stun |
|
||||||
|
| a | e | Houston St,Houston St,Houston St | hew-stun,how-stun,how-stun |
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
Scenario: Car - Use way type to describe unnamed ways
|
Scenario: Car - Use way type to describe unnamed ways
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -25,7 +25,7 @@ Feature: Traffic - speeds
|
|||||||
| fb | primary |
|
| fb | primary |
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
Given the extract extra arguments "--generate-edge-lookup"
|
Given the extract extra arguments "--generate-edge-lookup"
|
||||||
Given the contract extra arguments "--segment-speed-file speeds.csv"
|
Given the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||||
Given the speed file
|
Given the speed file
|
||||||
"""
|
"""
|
||||||
1,2,0
|
1,2,0
|
||||||
@ -69,7 +69,7 @@ Feature: Traffic - speeds
|
|||||||
| fb | primary |
|
| fb | primary |
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
Given the extract extra arguments "--generate-edge-lookup"
|
Given the extract extra arguments "--generate-edge-lookup"
|
||||||
Given the contract extra arguments "--segment-speed-file speeds.csv"
|
Given the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||||
Given the speed file
|
Given the speed file
|
||||||
"""
|
"""
|
||||||
1,2,0
|
1,2,0
|
||||||
@ -112,7 +112,6 @@ Feature: Traffic - speeds
|
|||||||
| fb | primary |
|
| fb | primary |
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
Given the extract extra arguments "--generate-edge-lookup"
|
Given the extract extra arguments "--generate-edge-lookup"
|
||||||
Given the contract extra arguments "--segment-speed-file speeds.csv"
|
|
||||||
Given the speed file
|
Given the speed file
|
||||||
"""
|
"""
|
||||||
1,2,-10
|
1,2,-10
|
||||||
@ -123,6 +122,6 @@ Feature: Traffic - speeds
|
|||||||
4,1,-5
|
4,1,-5
|
||||||
"""
|
"""
|
||||||
And the data has been extracted
|
And the data has been extracted
|
||||||
When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm"
|
When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}"
|
||||||
And stderr should contain "malformed"
|
And stderr should contain "malformed"
|
||||||
And it should exit with code not 0
|
And it should exit with an error
|
||||||
|
@ -58,7 +58,7 @@ Feature: Traffic - turn penalties
|
|||||||
8,11,12,23
|
8,11,12,23
|
||||||
1,4,5,-0.2
|
1,4,5,-0.2
|
||||||
"""
|
"""
|
||||||
And the contract extra arguments "--turn-penalty-file penalties.csv"
|
And the contract extra arguments "--turn-penalty-file {penalties_file}"
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | speed | time |
|
| from | to | route | speed | time |
|
||||||
| a | h | ad,dhk,dhk | 63 km/h | 11.5s +-1 |
|
| a | h | ad,dhk,dhk | 63 km/h | 11.5s +-1 |
|
||||||
@ -81,7 +81,7 @@ Feature: Traffic - turn penalties
|
|||||||
# double left - hdc penalty ever so slightly higher than imn; forces all the way around
|
# double left - hdc penalty ever so slightly higher than imn; forces all the way around
|
||||||
|
|
||||||
Scenario: Too-negative penalty clamps, but does not fail
|
Scenario: Too-negative penalty clamps, but does not fail
|
||||||
Given the contract extra arguments "--turn-penalty-file penalties.csv"
|
Given the contract extra arguments "--turn-penalty-file {penalties_file}"
|
||||||
And the profile "testbot"
|
And the profile "testbot"
|
||||||
And the turn penalty file
|
And the turn penalty file
|
||||||
"""
|
"""
|
||||||
|
@ -103,7 +103,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, |
|
| a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, |
|
||||||
| a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, |
|
| a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, |
|
||||||
|
|
||||||
@todo @bug @2654 @none
|
@todo @2654 @none
|
||||||
#https://github.com/Project-OSRM/osrm-backend/issues/2645
|
#https://github.com/Project-OSRM/osrm-backend/issues/2645
|
||||||
#http://www.openstreetmap.org/export#map=19/52.56054/13.32152
|
#http://www.openstreetmap.org/export#map=19/52.56054/13.32152
|
||||||
Scenario: Kurt-Schuhmacher-Damm
|
Scenario: Kurt-Schuhmacher-Damm
|
||||||
@ -131,7 +131,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,f | ,ksd,ksd | depart,turn left,arrive | ,left:true none:true right:false, |
|
| a,f | ,ksd,ksd | depart,turn left,arrive | ,left:true none:true right:false, |
|
||||||
| a,i | ,ksd,ksd | depart,turn right,arrive | ,left:false none:true right:true, |
|
| a,i | ,ksd,ksd | depart,turn right,arrive | ,left:false none:true right:true, |
|
||||||
|
|
||||||
@todo @bug @2650 @sliproads
|
@todo @2650 @sliproads
|
||||||
#market and haight in SF, restricted turn
|
#market and haight in SF, restricted turn
|
||||||
#http://www.openstreetmap.org/#map=19/37.77308/-122.42238
|
#http://www.openstreetmap.org/#map=19/37.77308/-122.42238
|
||||||
Scenario: Market/Haight without Through Street
|
Scenario: Market/Haight without Through Street
|
||||||
@ -177,7 +177,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, |
|
| a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, |
|
||||||
| a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, |
|
| a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, |
|
||||||
|
|
||||||
@todo @2650 @bug @sliproads
|
@todo @2650 @sliproads
|
||||||
#market and haight in SF, unrestricted
|
#market and haight in SF, unrestricted
|
||||||
#http://www.openstreetmap.org/#map=19/37.77308/-122.42238
|
#http://www.openstreetmap.org/#map=19/37.77308/-122.42238
|
||||||
Scenario: Market/Haight without Through Street
|
Scenario: Market/Haight without Through Street
|
||||||
@ -218,3 +218,19 @@ Feature: Turn Lane Guidance
|
|||||||
| a,h | ghough,market,market | depart,turn slight right,arrive | ,none:false straight:false straight:true straight:true, |
|
| a,h | ghough,market,market | depart,turn slight right,arrive | ,none:false straight:false straight:true straight:true, |
|
||||||
| a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, |
|
| a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, |
|
||||||
| a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, |
|
| a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, |
|
||||||
|
|
||||||
|
Scenario: Check sliproad handler loop's exit condition, Issue #2896
|
||||||
|
# http://www.openstreetmap.org/way/198481519
|
||||||
|
Given the node locations
|
||||||
|
| node | lat | lon |
|
||||||
|
| a | 7.6125350 | 126.5708309 |
|
||||||
|
| b | 7.6125156 | 126.5707219 |
|
||||||
|
| c | 7.6125363 | 126.5708337 |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name |
|
||||||
|
| cbac | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | turns |
|
||||||
|
| a | c | , | depart,arrive |
|
||||||
|
@ -422,7 +422,7 @@ Feature: Turn Lane Guidance
|
|||||||
| waypoints | route | turns | lanes |
|
| waypoints | route | turns | lanes |
|
||||||
| a,e | main,main,main,main | depart,use lane straight,continue right,arrive | ,left:false straight:false straight:false straight:false straight:true straight:true right:false,straight:false straight:false right:false right:true right:true, |
|
| a,e | main,main,main,main | depart,use lane straight,continue right,arrive | ,left:false straight:false straight:false straight:false straight:true straight:true right:false,straight:false straight:false right:false right:true right:true, |
|
||||||
|
|
||||||
@anticipate @todo @bug @2661
|
@anticipate @todo @2661
|
||||||
Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
|
Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | e | | |
|
| | | e | | |
|
||||||
@ -667,7 +667,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,f | abc,bdeh,feg,feg | depart,turn right,turn right,arrive | ,none:false none:false right:false right:true,left:false none:false none:false right:true, |
|
| a,f | abc,bdeh,feg,feg | depart,turn right,turn right,arrive | ,none:false none:false right:false right:true,left:false none:false none:false right:true, |
|
||||||
|
|
||||||
@anticipate
|
@anticipate
|
||||||
Scenario: Tripple Right keeping Left
|
Scenario: Triple Right keeping Left
|
||||||
Given the node map
|
Given the node map
|
||||||
| a | | | | b | | i |
|
| a | | | | b | | i |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
|
@ -264,3 +264,88 @@ Feature: Slipways and Dedicated Turn Lanes
|
|||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | bearings | route | turns |
|
| waypoints | bearings | route | turns |
|
||||||
| b,a | 90,10 270,10 | circled,circled | depart,arrive |
|
| b,a | 90,10 270,10 | circled,circled | depart,arrive |
|
||||||
|
|
||||||
|
#http://www.openstreetmap.org/#map=19/38.90597/-77.01276
|
||||||
|
Scenario: Don't falsly classify as sliproads
|
||||||
|
Given the node map
|
||||||
|
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | j | | | | | | | |
|
||||||
|
| a | b | | | | | | | | | | | | | | | | | | | | | | c | | | | | | | d |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | e | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | 1 | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | f | | | | | g | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | | | | | | | | | i | | | | | | | h |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | highway | oneway | maxspeed |
|
||||||
|
| abcd | new york | primary | yes | 35 |
|
||||||
|
| befgh | m street | secondary | yes | 35 |
|
||||||
|
| igcj | 1st street | tertiary | no | 20 |
|
||||||
|
|
||||||
|
And the nodes
|
||||||
|
| node | highway |
|
||||||
|
| c | traffic_signals |
|
||||||
|
| g | traffic_signals |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns | # |
|
||||||
|
| a,d | new york,new york | depart,arrive | this is the sinatra route |
|
||||||
|
| a,j | new york,1st street,1st street | depart,turn left,arrive | |
|
||||||
|
| a,1 | new york,m street,1st street,1st street | depart,turn right,turn left,arrive | this can false be seen as a sliproad |
|
||||||
|
|
||||||
|
# Merging into degree two loop on dedicated turn detection / 2927
|
||||||
|
Scenario: Turn Instead of Ramp
|
||||||
|
Given the node map
|
||||||
|
| | | | | | | | | | | | | | | | f |
|
||||||
|
| | | | | g | | | | | | h | | | | | |
|
||||||
|
| | | | | | | | | | | | | d | | | e |
|
||||||
|
| i | | | | c | | | | | | j | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | b | | | | | | | | | | | | | |
|
||||||
|
| | | | | | | | | | | | | | | | |
|
||||||
|
| | | a | | | | | | | | | | | | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | highway | name | oneway |
|
||||||
|
| abi | primary | road | yes |
|
||||||
|
| bcjd | primary | loop | yes |
|
||||||
|
| dhgf | primary | loop | yes |
|
||||||
|
| fed | primary | loop | yes |
|
||||||
|
|
||||||
|
And the nodes
|
||||||
|
| node | highway |
|
||||||
|
| g | traffic_signals |
|
||||||
|
| c | traffic_signals |
|
||||||
|
|
||||||
|
# We don't actually care about routes here, this is all about endless loops in turn discovery
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,i | road,road,road | depart,fork slight left,arrive |
|
||||||
|
@ -164,3 +164,172 @@ Feature: New-Name Instructions
|
|||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
| a,e | name,with-name,with-name | depart,new name straight,arrive |
|
| a,e | name,with-name,with-name | depart,new name straight,arrive |
|
||||||
| b,e | with-name,with-name | depart,arrive |
|
| b,e | with-name,with-name | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: Both Name and Ref Empty
|
||||||
|
Given the node map
|
||||||
|
| a | | b | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref |
|
||||||
|
| ab | | |
|
||||||
|
| bc | | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | , | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: Same Name, Ref Extended
|
||||||
|
Given the node map
|
||||||
|
| a | | b | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref |
|
||||||
|
| ab | A | B1 |
|
||||||
|
| bc | C | B1;B2 |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | A,C,C | depart,new name straight,arrive |
|
||||||
|
|
||||||
|
Scenario: Same Name, Ref Removed
|
||||||
|
Given the node map
|
||||||
|
| a | | b | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref |
|
||||||
|
| ab | A | B1;B2 |
|
||||||
|
| bc | C | B1 |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | A,C,C | depart,new name straight,arrive |
|
||||||
|
|
||||||
|
Scenario: Name Removed, Ref Extended
|
||||||
|
Given the node map
|
||||||
|
| a | | b | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref |
|
||||||
|
| ab | A | B1 |
|
||||||
|
| bc | | B1;B2 |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | A, | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: Name Added, Ref Removed
|
||||||
|
Given the node map
|
||||||
|
| a | | b | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref |
|
||||||
|
| ab | | B1 |
|
||||||
|
| bc | A | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | ,A,A | depart,new name straight,arrive |
|
||||||
|
|
||||||
|
Scenario: Prefix Change
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | North Central Expressway | US 75 | motorway |
|
||||||
|
| bc | Central Expressway | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | North Central Expressway,Central Expressway,Central Expressway | depart,new name straight,arrive |
|
||||||
|
|
||||||
|
Scenario: Prefix Change
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ba | North Central Expressway | US 75 | motorway |
|
||||||
|
| cb | Central Expressway | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| c,a | Central Expressway,North Central Expressway,North Central Expressway | depart,new name straight,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | Central Expressway | US 75 | motorway |
|
||||||
|
| bc | | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | Central Expressway, | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | | US 75 | motorway |
|
||||||
|
| bc | Central Expressway | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | ,Central Expressway | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | | US 75;US 69 | motorway |
|
||||||
|
| bc | | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | , | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | | US 69;US 75 | motorway |
|
||||||
|
| bc | | US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | , | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | | US 75 | motorway |
|
||||||
|
| bc | | US 75;US 69 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | , | depart,arrive |
|
||||||
|
|
||||||
|
Scenario: No Name, Same Reference
|
||||||
|
Given the node map
|
||||||
|
| a | | | | b | | | | c |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | ref | highway |
|
||||||
|
| ab | | US 75 | motorway |
|
||||||
|
| bc | | US 69;US 75 | motorway |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | , | depart,arrive |
|
||||||
|
@ -491,3 +491,23 @@ Feature: Basic Roundabout
|
|||||||
| h,a | gh,ab,ab | depart,roundabout turn left exit-3,arrive |
|
| h,a | gh,ab,ab | depart,roundabout turn left exit-3,arrive |
|
||||||
| h,d | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
|
| h,d | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
|
||||||
| h,f | gh,ef,ef | depart,roundabout turn right exit-1,arrive |
|
| h,f | gh,ef,ef | depart,roundabout turn right exit-1,arrive |
|
||||||
|
|
||||||
|
#http://www.openstreetmap.org/#map=19/41.03275/-2.18990
|
||||||
|
#at some point we probably want to recognise these situations and don't mention the roundabout at all here
|
||||||
|
Scenario: Enter And Exit Throughabout
|
||||||
|
Given the node map
|
||||||
|
| | | | | | h | | | | |
|
||||||
|
| | | | | | | | | | |
|
||||||
|
| c | b | | d | | | | e | | f |
|
||||||
|
| | | | | | | | | | |
|
||||||
|
| | a | | | | g | | | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | highway | name | junction | oneway |
|
||||||
|
| dghd | tertiary_link | | roundabout | |
|
||||||
|
| cbdef | trunk | through | | no |
|
||||||
|
| ab | residential | in | | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | turns | route |
|
||||||
|
| a,f | depart,turn right,roundabout turn straight exit-1,arrive | in,through,through,through |
|
||||||
|
@ -36,6 +36,25 @@ Feature: Basic Roundabout
|
|||||||
| h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
|
| h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
|
||||||
| h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
|
| h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
|
||||||
|
|
||||||
|
#2927
|
||||||
|
Scenario: Only Roundabout
|
||||||
|
Given the node map
|
||||||
|
| | | a | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | |
|
||||||
|
| b | | | | d |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | c | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | junction |
|
||||||
|
| abcda | roundabout |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| a,c | abcda,abcda | depart,arrive |
|
||||||
|
|
||||||
Scenario: Only Exit
|
Scenario: Only Exit
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | a | | |
|
| | | a | | |
|
||||||
|
@ -145,7 +145,6 @@ Feature: Turn Lane Guidance
|
|||||||
| a,c | road,road | depart,arrive | , |
|
| a,c | road,road | depart,arrive | , |
|
||||||
|
|
||||||
# This tests whether empty/invalid PSV tags cause osrm-extract to crash
|
# This tests whether empty/invalid PSV tags cause osrm-extract to crash
|
||||||
@bug
|
|
||||||
Scenario: Turn with Bus-Lane
|
Scenario: Turn with Bus-Lane
|
||||||
Given the node map
|
Given the node map
|
||||||
| a | | b | | c |
|
| a | | b | | c |
|
||||||
@ -181,7 +180,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,c | road,road | depart,arrive |
|
| a,c | road,road | depart,arrive |
|
||||||
|
|
||||||
#turn lanes are often drawn at the incoming road, even though the actual turn requires crossing the intersection first
|
#turn lanes are often drawn at the incoming road, even though the actual turn requires crossing the intersection first
|
||||||
@todo @bug @collapse @partition-lanes
|
@todo @collapse @partition-lanes
|
||||||
Scenario: Turn Lanes at Segregated Road
|
Scenario: Turn Lanes at Segregated Road
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | i | l | | |
|
| | | i | l | | |
|
||||||
@ -383,7 +382,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,f | road,turn,turn | depart,turn left,arrive | ,left:true right:false, |
|
| a,f | road,turn,turn | depart,turn left,arrive | ,left:true right:false, |
|
||||||
| a,g | road,right,right | depart,turn right,arrive | ,left:false right:true, |
|
| a,g | road,right,right | depart,turn right,arrive | ,left:false right:true, |
|
||||||
|
|
||||||
@todo @bug @partition-lanes @previous-lanes
|
@todo @partition-lanes @previous-lanes
|
||||||
Scenario: Narrowing Turn Lanes
|
Scenario: Narrowing Turn Lanes
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | | | g | |
|
| | | | | g | |
|
||||||
@ -428,7 +427,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,d | road,road | depart,arrive | , |
|
| a,d | road,road | depart,arrive | , |
|
||||||
| a,e | road,turn,turn | depart,turn right,arrive | ,straight:false right:true, |
|
| a,e | road,turn,turn | depart,turn right,arrive | ,straight:false right:true, |
|
||||||
|
|
||||||
@bug @todo @roundabout
|
@todo @roundabout
|
||||||
Scenario: Theodor Heuss Platz
|
Scenario: Theodor Heuss Platz
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | | i | o | | | l | |
|
| | | | i | o | | | l | |
|
||||||
@ -555,7 +554,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,e | road,road,road | depart,turn uturn,arrive | ,left:true left:false left:false straight:false straight:false, |
|
| a,e | road,road,road | depart,turn uturn,arrive | ,left:true left:false left:false straight:false straight:false, |
|
||||||
| a,g | road,straight,straight | depart,new name straight,arrive | ,left:false left:false left:false straight:true straight:true, |
|
| a,g | road,straight,straight | depart,new name straight,arrive | ,left:false left:false left:false straight:true straight:true, |
|
||||||
|
|
||||||
@bug @todo @roundabout
|
@todo @roundabout
|
||||||
Scenario: Passing Through a Roundabout
|
Scenario: Passing Through a Roundabout
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | h | | g | | |
|
| | | h | | g | | |
|
||||||
@ -620,7 +619,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,d | hwy,hwy | depart,arrive | , |
|
| a,d | hwy,hwy | depart,arrive | , |
|
||||||
| a,e | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, |
|
| a,e | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, |
|
||||||
|
|
||||||
@bug @todo
|
@todo
|
||||||
Scenario: Turning Off Ramp
|
Scenario: Turning Off Ramp
|
||||||
Given the node map
|
Given the node map
|
||||||
| | a | |
|
| | a | |
|
||||||
@ -864,7 +863,7 @@ Feature: Turn Lane Guidance
|
|||||||
| a,c | ab,bc,bc | depart,turn left,arrive | ,left:true right:false, |
|
| a,c | ab,bc,bc | depart,turn left,arrive | ,left:true right:false, |
|
||||||
|
|
||||||
# http://www.openstreetmap.org/#map=19/47.97685/7.82933&layers=D
|
# http://www.openstreetmap.org/#map=19/47.97685/7.82933&layers=D
|
||||||
@bug @todo
|
@todo
|
||||||
Scenario: Lane Parsing Issue #2706: None Assignments I
|
Scenario: Lane Parsing Issue #2706: None Assignments I
|
||||||
Given the node map
|
Given the node map
|
||||||
| | f | | | j | |
|
| | f | | | j | |
|
||||||
@ -904,7 +903,7 @@ Feature: Turn Lane Guidance
|
|||||||
# Note: at the moment we don't care about routes, we care about the extract process triggering assertions
|
# Note: at the moment we don't care about routes, we care about the extract process triggering assertions
|
||||||
|
|
||||||
# https://www.openstreetmap.org/#map=19/47.99257/7.83276&layers=D
|
# https://www.openstreetmap.org/#map=19/47.99257/7.83276&layers=D
|
||||||
@bug @todo
|
@todo
|
||||||
Scenario: Lane Parsing Issue #2706: None Assignments II
|
Scenario: Lane Parsing Issue #2706: None Assignments II
|
||||||
Given the node map
|
Given the node map
|
||||||
| | k | l | |
|
| | k | l | |
|
||||||
@ -939,7 +938,7 @@ Feature: Turn Lane Guidance
|
|||||||
| i,e ||||
|
| i,e ||||
|
||||||
# Note: at the moment we don't care about routes, we care about the extract process triggering assertions
|
# Note: at the moment we don't care about routes, we care about the extract process triggering assertions
|
||||||
|
|
||||||
@bug @todo
|
@todo
|
||||||
Scenario: Lane Parsing Issue #2706: None Assignments III - Minimal reproduction recipe
|
Scenario: Lane Parsing Issue #2706: None Assignments III - Minimal reproduction recipe
|
||||||
Given the node map
|
Given the node map
|
||||||
| | | l | |
|
| | | l | |
|
||||||
|
31
features/lib/hash.js
Normal file
31
features/lib/hash.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const d3 = require('d3-queue');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
hashOfFiles: (paths, cb) => {
|
||||||
|
let queue = d3.queue();
|
||||||
|
for (let i = 0; i < paths.length; ++i) {
|
||||||
|
queue.defer(fs.readFile, paths[i]);
|
||||||
|
}
|
||||||
|
queue.awaitAll((err, results) => {
|
||||||
|
if (err) return cb(err);
|
||||||
|
let checksum = crypto.createHash('md5');
|
||||||
|
for (let i = 0; i < results.length; ++i) {
|
||||||
|
checksum.update(results[i]);
|
||||||
|
}
|
||||||
|
cb(null, checksum.digest('hex'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
hashOfFile: (path, cb) => {
|
||||||
|
fs.readFile(path, (err, result) => {
|
||||||
|
if (err) return cb(err);
|
||||||
|
let checksum = crypto.createHash('md5');
|
||||||
|
checksum.update(result);
|
||||||
|
cb(null, checksum.digest('hex'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,11 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var builder = require('xmlbuilder');
|
const builder = require('xmlbuilder');
|
||||||
|
const ensureDecimal = require('./utils').ensureDecimal;
|
||||||
var ensureDecimal = (i) => {
|
|
||||||
if (parseInt(i) === i) return i.toFixed(1);
|
|
||||||
else return i;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
constructor () {
|
constructor () {
|
169
features/lib/osrm_loader.js
Normal file
169
features/lib/osrm_loader.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const util = require('util');
|
||||||
|
const Timeout = require('node-timeout');
|
||||||
|
const tryConnect = require('../lib/try_connect');
|
||||||
|
const errorReason = require('./utils').errorReason;
|
||||||
|
|
||||||
|
class OSRMBaseLoader{
|
||||||
|
constructor (scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.child = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
launch (callback) {
|
||||||
|
var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Launching osrm-routed timed out.') });
|
||||||
|
|
||||||
|
var runLaunch = (cb) => {
|
||||||
|
this.osrmUp(() => { this.waitForConnection(cb); });
|
||||||
|
};
|
||||||
|
|
||||||
|
runLaunch(limit((e) => { if (e) callback(e); else callback(); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown (callback) {
|
||||||
|
if (!this.osrmIsRunning()) return callback();
|
||||||
|
|
||||||
|
var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Shutting down osrm-routed timed out.')});
|
||||||
|
|
||||||
|
this.osrmDown(limit(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
osrmIsRunning () {
|
||||||
|
return this.child && !this.child.killed;
|
||||||
|
}
|
||||||
|
|
||||||
|
osrmDown (callback) {
|
||||||
|
if (this.osrmIsRunning()) {
|
||||||
|
this.child.on('exit', (code, signal) => {callback();});
|
||||||
|
this.child.kill();
|
||||||
|
} else callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForConnection (callback) {
|
||||||
|
var retryCount = 0;
|
||||||
|
let retry = (err) => {
|
||||||
|
if (err) {
|
||||||
|
if (retryCount < 10) {
|
||||||
|
retryCount++;
|
||||||
|
setTimeout(() => { tryConnect(this.scope.OSRM_PORT, retry); }, 10);
|
||||||
|
} else {
|
||||||
|
callback(new Error("Could not connect to osrm-routed after ten retries."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tryConnect(this.scope.OSRM_PORT, retry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OSRMDirectLoader extends OSRMBaseLoader {
|
||||||
|
constructor (scope) {
|
||||||
|
super(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
load (inputFile, callback) {
|
||||||
|
this.inputFile = inputFile;
|
||||||
|
this.shutdown(() => {
|
||||||
|
this.launch(callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
osrmUp (callback) {
|
||||||
|
if (this.osrmIsRunning()) return callback(new Error("osrm-routed already running!"));
|
||||||
|
|
||||||
|
this.child = this.scope.runBin('osrm-routed', util.format("%s -p %d", this.inputFile, this.scope.OSRM_PORT), this.scope.environment, (err) => {
|
||||||
|
if (err) {
|
||||||
|
throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OSRMDatastoreLoader extends OSRMBaseLoader {
|
||||||
|
constructor (scope) {
|
||||||
|
super(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
load (inputFile, callback) {
|
||||||
|
this.inputFile = inputFile;
|
||||||
|
|
||||||
|
this.loadData((err) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
if (!this.osrmIsRunning()) this.launch(callback);
|
||||||
|
else {
|
||||||
|
this.scope.setupOutputLog(this.child, fs.createWriteStream(this.scope.scenarioLogFile, {'flags': 'a'}));
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData (callback) {
|
||||||
|
this.scope.runBin('osrm-datastore', this.inputFile, this.scope.environment, (err) => {
|
||||||
|
if (err) return callback(new Error('*** osrm-datastore exited with ' + err.code + ': ' + err));
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
osrmUp (callback) {
|
||||||
|
if (this.osrmIsRunning()) return callback();
|
||||||
|
|
||||||
|
this.child = this.scope.runBin('osrm-routed', util.format('--shared-memory=1 -p %d', this.scope.OSRM_PORT), this.scope.environment, (err) => {
|
||||||
|
if (err) {
|
||||||
|
throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// we call the callback here, becuase we don't want to wait for the child process to finish
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OSRMLoader {
|
||||||
|
constructor (scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.sharedLoader = new OSRMDatastoreLoader(this.scope);
|
||||||
|
this.directLoader = new OSRMDirectLoader(this.scope);
|
||||||
|
this.method = scope.DEFAULT_LOAD_METHOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
load (inputFile, callback) {
|
||||||
|
if (this.method === 'datastore') {
|
||||||
|
this.directLoader.shutdown((err) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
this.loader = this.sharedLoader;
|
||||||
|
this.sharedLoader.load(inputFile, callback);
|
||||||
|
});
|
||||||
|
} else if (this.method === 'directly') {
|
||||||
|
this.sharedLoader.shutdown((err) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
this.loader = this.directLoader;
|
||||||
|
this.directLoader.load(inputFile, callback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(new Error('*** Unknown load method ' + method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadMethod (method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown (callback) {
|
||||||
|
if (!this.loader) return callback();
|
||||||
|
|
||||||
|
this.loader.shutdown(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
up () {
|
||||||
|
return this.loader ? this.loader.osrmIsRunning() : false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = OSRMLoader;
|
54
features/lib/table_diff.js
Normal file
54
features/lib/table_diff.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('util');
|
||||||
|
var path = require('path');
|
||||||
|
var fs = require('fs');
|
||||||
|
var chalk = require('chalk');
|
||||||
|
|
||||||
|
var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\');
|
||||||
|
|
||||||
|
module.exports = function (expected, actual) {
|
||||||
|
let headers = expected.raw()[0];
|
||||||
|
let expected_keys = expected.hashes();
|
||||||
|
let diff = [];
|
||||||
|
let hasErrors = false;
|
||||||
|
|
||||||
|
var good = 0, bad = 0;
|
||||||
|
|
||||||
|
expected_keys.forEach((row, i) => {
|
||||||
|
var rowError = false;
|
||||||
|
|
||||||
|
for (var j in row) {
|
||||||
|
if (unescapeStr(row[j]) != actual[i][j]) {
|
||||||
|
rowError = true;
|
||||||
|
hasErrors = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowError) {
|
||||||
|
bad++;
|
||||||
|
diff.push(Object.assign({}, row, {c_status: 'undefined'}));
|
||||||
|
diff.push(Object.assign({}, actual[i], {c_status: 'comment'}));
|
||||||
|
} else {
|
||||||
|
good++;
|
||||||
|
diff.push(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasErrors) return null;
|
||||||
|
|
||||||
|
var s = ['Tables were not identical:'];
|
||||||
|
s.push(headers.map(key => ' ' + key).join(' | '));
|
||||||
|
diff.forEach((row) => {
|
||||||
|
var rowString = '| ';
|
||||||
|
headers.forEach((header) => {
|
||||||
|
if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | ');
|
||||||
|
else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | ');
|
||||||
|
else rowString += chalk.red('(+) ' + row[header] + ' | ');
|
||||||
|
});
|
||||||
|
s.push(rowString);
|
||||||
|
});
|
||||||
|
|
||||||
|
return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534';
|
||||||
|
};
|
13
features/lib/try_connect.js
Normal file
13
features/lib/try_connect.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const net = require('net');
|
||||||
|
const Timeout = require('node-timeout');
|
||||||
|
|
||||||
|
module.exports = function tryConnect(port, callback) {
|
||||||
|
net.connect({ port: port, host: '127.0.0.1' })
|
||||||
|
.on('connect', () => { callback(); })
|
||||||
|
.on('error', () => {
|
||||||
|
callback(new Error('Could not connect.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
17
features/lib/utils.js
Normal file
17
features/lib/utils.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
ensureDecimal: (i) => {
|
||||||
|
if (parseInt(i) === i) return i.toFixed(1);
|
||||||
|
else return i;
|
||||||
|
},
|
||||||
|
|
||||||
|
errorReason: (err) => {
|
||||||
|
return err.signal ?
|
||||||
|
util.format('killed by signal %s', err.signal) :
|
||||||
|
util.format('exited with code %d', err.code);
|
||||||
|
}
|
||||||
|
};
|
@ -1,8 +1,7 @@
|
|||||||
@prepare @options @files
|
@prepare @options @files
|
||||||
Feature: osrm-contract command line options: datasources
|
Feature: osrm-contract command line options: datasources
|
||||||
# expansions:
|
# expansions:
|
||||||
# {extracted_base} => path to current extracted input file
|
# {processed_file} => path to .osrm file
|
||||||
# {profile} => path to current profile script
|
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
@ -24,7 +23,6 @@ Feature: osrm-contract command line options: datasources
|
|||||||
And the data has been extracted
|
And the data has been extracted
|
||||||
|
|
||||||
Scenario: osrm-contract - Passing base file
|
Scenario: osrm-contract - Passing base file
|
||||||
When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm"
|
When I run "osrm-contract --segment-speed-file {speeds_file} {processed_file}"
|
||||||
Then stderr should be empty
|
Then datasource names should contain "lua profile,25_osrmcontract_passing_base_file_speeds"
|
||||||
And datasource names should contain "lua profile,speeds"
|
And it should exit successfully
|
||||||
And it should exit with code 0
|
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
@prepare @options @files
|
@prepare @options @files
|
||||||
Feature: osrm-contract command line options: files
|
Feature: osrm-contract command line options: files
|
||||||
# expansions:
|
|
||||||
# {extracted_base} => path to current extracted input file
|
|
||||||
# {profile} => path to current profile script
|
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
And the node map
|
And the node map
|
||||||
@ -14,12 +10,11 @@ Feature: osrm-contract command line options: files
|
|||||||
And the data has been extracted
|
And the data has been extracted
|
||||||
|
|
||||||
Scenario: osrm-contract - Passing base file
|
Scenario: osrm-contract - Passing base file
|
||||||
When I run "osrm-contract {extracted_base}.osrm"
|
When I run "osrm-contract {processed_file}"
|
||||||
Then stderr should be empty
|
Then it should exit successfully
|
||||||
And it should exit with code 0
|
|
||||||
|
|
||||||
Scenario: osrm-contract - Missing input file
|
Scenario: osrm-contract - Missing input file
|
||||||
When I run "osrm-contract over-the-rainbow.osrm"
|
When I try to run "osrm-contract over-the-rainbow.osrm"
|
||||||
And stderr should contain "over-the-rainbow.osrm"
|
And stderr should contain "over-the-rainbow.osrm"
|
||||||
And stderr should contain "not found"
|
And stderr should contain "not found"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Feature: osrm-contract command line options: help
|
Feature: osrm-contract command line options: help
|
||||||
|
|
||||||
Scenario: osrm-contract - Help should be shown when no options are passed
|
Scenario: osrm-contract - Help should be shown when no options are passed
|
||||||
When I run "osrm-contract"
|
When I try to run "osrm-contract"
|
||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain "osrm-contract <input.osrm> [options]:"
|
And stdout should contain "osrm-contract <input.osrm> [options]:"
|
||||||
And stdout should contain "Options:"
|
And stdout should contain "Options:"
|
||||||
@ -13,7 +13,7 @@ Feature: osrm-contract command line options: help
|
|||||||
And stdout should contain "--core"
|
And stdout should contain "--core"
|
||||||
And stdout should contain "--level-cache"
|
And stdout should contain "--level-cache"
|
||||||
And stdout should contain "--segment-speed-file"
|
And stdout should contain "--segment-speed-file"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
|
||||||
Scenario: osrm-contract - Help, short
|
Scenario: osrm-contract - Help, short
|
||||||
When I run "osrm-contract -h"
|
When I run "osrm-contract -h"
|
||||||
@ -27,7 +27,7 @@ Feature: osrm-contract command line options: help
|
|||||||
And stdout should contain "--core"
|
And stdout should contain "--core"
|
||||||
And stdout should contain "--level-cache"
|
And stdout should contain "--level-cache"
|
||||||
And stdout should contain "--segment-speed-file"
|
And stdout should contain "--segment-speed-file"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-contract - Help, long
|
Scenario: osrm-contract - Help, long
|
||||||
When I run "osrm-contract --help"
|
When I run "osrm-contract --help"
|
||||||
@ -41,4 +41,4 @@ Feature: osrm-contract command line options: help
|
|||||||
And stdout should contain "--core"
|
And stdout should contain "--core"
|
||||||
And stdout should contain "--level-cache"
|
And stdout should contain "--level-cache"
|
||||||
And stdout should contain "--segment-speed-file"
|
And stdout should contain "--segment-speed-file"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -5,8 +5,8 @@ Feature: osrm-contract command line options: invalid options
|
|||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
|
|
||||||
Scenario: osrm-contract - Non-existing option
|
Scenario: osrm-contract - Non-existing option
|
||||||
When I run "osrm-contract --fly-me-to-the-moon"
|
When I try to run "osrm-contract --fly-me-to-the-moon"
|
||||||
Then stdout should be empty
|
Then stdout should be empty
|
||||||
And stderr should contain "option"
|
And stderr should contain "option"
|
||||||
And stderr should contain "fly-me-to-the-moon"
|
And stderr should contain "fly-me-to-the-moon"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
@ -12,11 +12,11 @@ Feature: osrm-contract command line options: version
|
|||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-contract - Version, long
|
Scenario: osrm-contract - Version, long
|
||||||
When I run "osrm-contract --version"
|
When I run "osrm-contract --version"
|
||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -14,17 +14,15 @@ Feature: osrm-extract command line options: files
|
|||||||
And the data has been saved to disk
|
And the data has been saved to disk
|
||||||
|
|
||||||
Scenario: osrm-extract - Passing base file
|
Scenario: osrm-extract - Passing base file
|
||||||
When I run "osrm-extract {osm_base}.osm --profile {profile}"
|
When I run "osrm-extract {osm_file} --profile {profile_file}"
|
||||||
Then stderr should be empty
|
Then it should exit successfully
|
||||||
And it should exit with code 0
|
|
||||||
|
|
||||||
Scenario: osrm-extract - Order of options should not matter
|
Scenario: osrm-extract - Order of options should not matter
|
||||||
When I run "osrm-extract --profile {profile} {osm_base}.osm"
|
When I run "osrm-extract --profile {profile_file} {osm_file}"
|
||||||
Then stderr should be empty
|
Then it should exit successfully
|
||||||
And it should exit with code 0
|
|
||||||
|
|
||||||
Scenario: osrm-extract - Missing input file
|
Scenario: osrm-extract - Missing input file
|
||||||
When I run "osrm-extract over-the-rainbow.osrm --profile {profile}"
|
When I try to run "osrm-extract over-the-rainbow.osrm --profile {profile_file}"
|
||||||
And stderr should contain "over-the-rainbow.osrm"
|
And stderr should contain "over-the-rainbow.osrm"
|
||||||
And stderr should contain "not found"
|
And stderr should contain "not found"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
@ -16,7 +16,7 @@ Feature: osrm-extract command line options: help
|
|||||||
And stdout should contain "--threads"
|
And stdout should contain "--threads"
|
||||||
And stdout should contain "--generate-edge-lookup"
|
And stdout should contain "--generate-edge-lookup"
|
||||||
And stdout should contain "--small-component-size"
|
And stdout should contain "--small-component-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-extract - Help, short
|
Scenario: osrm-extract - Help, short
|
||||||
When I run "osrm-extract -h"
|
When I run "osrm-extract -h"
|
||||||
@ -30,7 +30,7 @@ Feature: osrm-extract command line options: help
|
|||||||
And stdout should contain "--threads"
|
And stdout should contain "--threads"
|
||||||
And stdout should contain "--generate-edge-lookup"
|
And stdout should contain "--generate-edge-lookup"
|
||||||
And stdout should contain "--small-component-size"
|
And stdout should contain "--small-component-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-extract - Help, long
|
Scenario: osrm-extract - Help, long
|
||||||
When I run "osrm-extract --help"
|
When I run "osrm-extract --help"
|
||||||
@ -44,4 +44,4 @@ Feature: osrm-extract command line options: help
|
|||||||
And stdout should contain "--threads"
|
And stdout should contain "--threads"
|
||||||
And stdout should contain "--generate-edge-lookup"
|
And stdout should contain "--generate-edge-lookup"
|
||||||
And stdout should contain "--small-component-size"
|
And stdout should contain "--small-component-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -5,8 +5,8 @@ Feature: osrm-extract command line options: invalid options
|
|||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
|
|
||||||
Scenario: osrm-extract - Non-existing option
|
Scenario: osrm-extract - Non-existing option
|
||||||
When I run "osrm-extract --fly-me-to-the-moon"
|
When I try to run "osrm-extract --fly-me-to-the-moon"
|
||||||
Then stdout should be empty
|
Then stdout should be empty
|
||||||
And stderr should contain "option"
|
And stderr should contain "option"
|
||||||
And stderr should contain "fly-me-to-the-moon"
|
And stderr should contain "fly-me-to-the-moon"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
@ -12,11 +12,11 @@ Feature: osrm-extract command line options: version
|
|||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-extract - Version, long
|
Scenario: osrm-extract - Version, long
|
||||||
When I run "osrm-extract --version"
|
When I run "osrm-extract --version"
|
||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -29,4 +29,4 @@ Feature: osrm-routed command line options: files
|
|||||||
And stdout should contain /^\[info\] loaded plugin: viaroute/
|
And stdout should contain /^\[info\] loaded plugin: viaroute/
|
||||||
And stdout should contain /^\[info\] trial run/
|
And stdout should contain /^\[info\] trial run/
|
||||||
And stdout should contain /^\[info\] shutdown completed/
|
And stdout should contain /^\[info\] shutdown completed/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -21,7 +21,7 @@ Feature: osrm-routed command line options: help
|
|||||||
And stdout should contain "--max-trip-size"
|
And stdout should contain "--max-trip-size"
|
||||||
And stdout should contain "--max-table-size"
|
And stdout should contain "--max-table-size"
|
||||||
And stdout should contain "--max-matching-size"
|
And stdout should contain "--max-matching-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-routed - Help, short
|
Scenario: osrm-routed - Help, short
|
||||||
When I run "osrm-routed -h"
|
When I run "osrm-routed -h"
|
||||||
@ -40,7 +40,7 @@ Feature: osrm-routed command line options: help
|
|||||||
And stdout should contain "--max-trip-size"
|
And stdout should contain "--max-trip-size"
|
||||||
And stdout should contain "--max-table-size"
|
And stdout should contain "--max-table-size"
|
||||||
And stdout should contain "--max-matching-size"
|
And stdout should contain "--max-matching-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-routed - Help, long
|
Scenario: osrm-routed - Help, long
|
||||||
When I run "osrm-routed --help"
|
When I run "osrm-routed --help"
|
||||||
@ -59,4 +59,4 @@ Feature: osrm-routed command line options: help
|
|||||||
And stdout should contain "--max-table-size"
|
And stdout should contain "--max-table-size"
|
||||||
And stdout should contain "--max-table-size"
|
And stdout should contain "--max-table-size"
|
||||||
And stdout should contain "--max-matching-size"
|
And stdout should contain "--max-matching-size"
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -5,14 +5,14 @@ Feature: osrm-routed command line options: invalid options
|
|||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
|
|
||||||
Scenario: osrm-routed - Non-existing option
|
Scenario: osrm-routed - Non-existing option
|
||||||
When I run "osrm-routed --fly-me-to-the-moon"
|
When I try to run "osrm-routed --fly-me-to-the-moon"
|
||||||
Then stdout should be empty
|
Then stdout should be empty
|
||||||
And stderr should contain "unrecognised"
|
And stderr should contain "unrecognised"
|
||||||
And stderr should contain "fly-me-to-the-moon"
|
And stderr should contain "fly-me-to-the-moon"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
|
||||||
Scenario: osrm-routed - Missing file
|
Scenario: osrm-routed - Missing file
|
||||||
When I run "osrm-routed over-the-rainbow.osrm"
|
When I try to run "osrm-routed over-the-rainbow.osrm"
|
||||||
Then stderr should contain "over-the-rainbow.osrm"
|
Then stderr should contain "over-the-rainbow.osrm"
|
||||||
And stderr should contain "not found"
|
And stderr should contain "not found"
|
||||||
And it should exit with code 1
|
And it should exit with an error
|
||||||
|
@ -12,11 +12,11 @@ Feature: osrm-routed command line options: version
|
|||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
|
||||||
Scenario: osrm-routed - Version, long
|
Scenario: osrm-routed - Version, long
|
||||||
When I run "osrm-routed --version"
|
When I run "osrm-routed --version"
|
||||||
Then stderr should be empty
|
Then stderr should be empty
|
||||||
And stdout should contain 1 line
|
And stdout should contain 1 line
|
||||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||||
And it should exit with code 0
|
And it should exit successfully
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
@raster @extract
|
@raster @extract
|
||||||
Feature: osrm-extract with a profile containing raster source
|
Feature: osrm-extract with a profile containing raster source
|
||||||
# expansions:
|
|
||||||
# {osm_base} => path to current input file
|
|
||||||
# {profile} => path to current profile script
|
|
||||||
|
|
||||||
Scenario: osrm-extract on a valid profile
|
Scenario: osrm-extract on a valid profile
|
||||||
Given the profile "rasterbot"
|
Given the profile "rasterbot"
|
||||||
And the node map
|
And the node map
|
||||||
@ -11,8 +7,15 @@ Feature: osrm-extract with a profile containing raster source
|
|||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| ab |
|
| ab |
|
||||||
|
And the raster source
|
||||||
|
"""
|
||||||
|
0 0 0 0
|
||||||
|
0 0 0 250
|
||||||
|
0 0 250 500
|
||||||
|
0 0 0 250
|
||||||
|
0 0 0 0
|
||||||
|
"""
|
||||||
And the data has been saved to disk
|
And the data has been saved to disk
|
||||||
When I run "osrm-extract {osm_base}.osm -p {profile}"
|
When I run "osrm-extract {osm_file} -p {profile_file}"
|
||||||
Then stderr should be empty
|
Then stdout should contain "source loader"
|
||||||
And stdout should contain "source loader"
|
And it should exit successfully
|
||||||
And it should exit with code 0
|
|
||||||
|
@ -32,8 +32,8 @@ Feature: Raster - weights
|
|||||||
|
|
||||||
Scenario: Weighting not based on raster sources
|
Scenario: Weighting not based on raster sources
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
When I run "osrm-extract {osm_base}.osm -p {profile}"
|
When I run "osrm-extract {osm_file} -p {profile_file}"
|
||||||
And I run "osrm-contract {osm_base}.osm"
|
And I run "osrm-contract {processed_file}"
|
||||||
And I route I should get
|
And I route I should get
|
||||||
| from | to | route | speed |
|
| from | to | route | speed |
|
||||||
| a | b | ab,ab | 36 km/h |
|
| a | b | ab,ab | 36 km/h |
|
||||||
@ -44,9 +44,9 @@ Feature: Raster - weights
|
|||||||
|
|
||||||
Scenario: Weighting based on raster sources
|
Scenario: Weighting based on raster sources
|
||||||
Given the profile "rasterbot"
|
Given the profile "rasterbot"
|
||||||
When I run "osrm-extract {osm_base}.osm -p {profile}"
|
When I run "osrm-extract {osm_file} -p {profile_file}"
|
||||||
Then stdout should contain "evaluating segment"
|
Then stdout should contain "evaluating segment"
|
||||||
And I run "osrm-contract {osm_base}.osm"
|
And I run "osrm-contract {processed_file}"
|
||||||
And I route I should get
|
And I route I should get
|
||||||
| from | to | route | speed |
|
| from | to | route | speed |
|
||||||
| a | b | ab,ab | 8 km/h |
|
| a | b | ab,ab | 8 km/h |
|
||||||
@ -62,9 +62,9 @@ Feature: Raster - weights
|
|||||||
|
|
||||||
Scenario: Weighting based on raster sources
|
Scenario: Weighting based on raster sources
|
||||||
Given the profile "rasterbotinterp"
|
Given the profile "rasterbotinterp"
|
||||||
When I run "osrm-extract {osm_base}.osm -p {profile}"
|
When I run "osrm-extract {osm_file} -p {profile_file}"
|
||||||
Then stdout should contain "evaluating segment"
|
Then stdout should contain "evaluating segment"
|
||||||
And I run "osrm-contract {osm_base}.osm"
|
And I run "osrm-contract {processed_file}"
|
||||||
And I route I should get
|
And I route I should get
|
||||||
| from | to | route | speed |
|
| from | to | route | speed |
|
||||||
| a | b | ab,ab | 8 km/h |
|
| a | b | ab,ab | 8 km/h |
|
||||||
|
@ -2,19 +2,23 @@ var util = require('util');
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var d3 = require('d3-queue');
|
var d3 = require('d3-queue');
|
||||||
var OSM = require('../support/build_osm');
|
var OSM = require('../lib/osm');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.Given(/^the profile "([^"]*)"$/, (profile, callback) => {
|
this.Given(/^the profile "([^"]*)"$/, (profile, callback) => {
|
||||||
this.setProfile(profile, callback);
|
this.profile = profile;
|
||||||
|
this.profileFile = path.join(this.PROFILES_PATH, this.profile + '.lua');
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the extract extra arguments "(.*?)"$/, (args, callback) => {
|
this.Given(/^the extract extra arguments "(.*?)"$/, (args, callback) => {
|
||||||
this.setExtractArgs(args, callback);
|
this.extractArgs = this.expandOptions(args);
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the contract extra arguments "(.*?)"$/, (args, callback) => {
|
this.Given(/^the contract extra arguments "(.*?)"$/, (args, callback) => {
|
||||||
this.setContractArgs(args, callback);
|
this.contractArgs = this.expandOptions(args);
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^a grid size of ([0-9.]+) meters$/, (meters, callback) => {
|
this.Given(/^a grid size of ([0-9.]+) meters$/, (meters, callback) => {
|
||||||
@ -228,58 +232,46 @@ module.exports = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the raster source$/, (data, callback) => {
|
this.Given(/^the raster source$/, (data, callback) => {
|
||||||
this.updateFingerprintExtract(data);
|
// TODO: Don't overwrite if it exists
|
||||||
fs.writeFile(path.resolve(this.TEST_FOLDER, 'rastersource.asc'), data, callback);
|
fs.writeFile(this.rasterCacheFile, data, callback);
|
||||||
|
// we need this to pass it to the profiles
|
||||||
|
this.environment = Object.assign({OSRM_RASTER_SOURCE: this.rasterCacheFile}, this.environment);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the speed file$/, (data, callback) => {
|
this.Given(/^the speed file$/, (data, callback) => {
|
||||||
this.updateFingerprintContract(data);
|
// TODO: Don't overwrite if it exists
|
||||||
fs.writeFile(path.resolve(this.TEST_FOLDER, 'speeds.csv'), data, callback);
|
fs.writeFile(this.speedsCacheFile, data, callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the turn penalty file$/, (data, callback) => {
|
this.Given(/^the turn penalty file$/, (data, callback) => {
|
||||||
this.updateFingerprintContract(data);
|
// TODO: Don't overwrite if it exists
|
||||||
fs.writeFile(path.resolve(this.TEST_FOLDER, 'penalties.csv'), data, callback);
|
fs.writeFile(this.penaltiesCacheFile, data, callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the data has been saved to disk$/, (callback) => {
|
this.Given(/^the data has been saved to disk$/, (callback) => {
|
||||||
try {
|
this.reprocess(callback);
|
||||||
this.reprocess(callback);
|
|
||||||
} catch(e) {
|
|
||||||
this.processError = e;
|
|
||||||
callback(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the data has been extracted$/, (callback) => {
|
this.Given(/^the data has been extracted$/, (callback) => {
|
||||||
this.osmData.populate(() => {
|
this.reprocess(callback);
|
||||||
this.writeAndExtract((err) => {
|
|
||||||
if (err) this.processError = err;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the data has been contracted$/, (callback) => {
|
this.Given(/^the data has been contracted$/, (callback) => {
|
||||||
this.reprocess((err) => {
|
this.reprocess(callback);
|
||||||
if (err) this.processError = err;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^osrm\-routed is stopped$/, (callback) => {
|
this.Given(/^osrm\-routed is stopped$/, (callback) => {
|
||||||
this.OSRMLoader.shutdown((err) => {
|
this.OSRMLoader.shutdown(callback);
|
||||||
if (err) this.processError = err;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^data is loaded directly/, () => {
|
this.Given(/^data is loaded directly/, (callback) => {
|
||||||
this.loadMethod = 'directly';
|
this.osrmLoader.setLoadMethod('directly');
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^data is loaded with datastore$/, () => {
|
this.Given(/^data is loaded with datastore$/, (callback) => {
|
||||||
this.loadMethod = 'datastore';
|
this.osrmLoader.setLoadMethod('datastore');
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Given(/^the HTTP method "([^"]*)"$/, (method, callback) => {
|
this.Given(/^the HTTP method "([^"]*)"$/, (method, callback) => {
|
||||||
|
@ -53,8 +53,6 @@ module.exports = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var testRow = (row, ri, cb) => {
|
var testRow = (row, ri, cb) => {
|
||||||
var ok = true;
|
|
||||||
|
|
||||||
for (var k in result[ri]) {
|
for (var k in result[ri]) {
|
||||||
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
|
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
|
||||||
result[ri][k] = row[k];
|
result[ri][k] = row[k];
|
||||||
@ -62,15 +60,9 @@ module.exports = function () {
|
|||||||
result[ri][k] = '';
|
result[ri][k] = '';
|
||||||
} else {
|
} else {
|
||||||
result[ri][k] = result[ri][k].toString();
|
result[ri][k] = result[ri][k].toString();
|
||||||
ok = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
var failed = { attempt: 'distance_matrix', query: this.query, response: response };
|
|
||||||
this.logFail(row, result[ri], [failed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
result[ri][''] = row[''];
|
result[ri][''] = row[''];
|
||||||
cb(null, result[ri]);
|
cb(null, result[ri]);
|
||||||
};
|
};
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
var util = require('util');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.Before((scenario, callback) => {
|
|
||||||
this.scenarioTitle = scenario.getName();
|
|
||||||
|
|
||||||
this.loadMethod = this.DEFAULT_LOAD_METHOD;
|
|
||||||
this.queryParams = {};
|
|
||||||
var d = new Date();
|
|
||||||
this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds());
|
|
||||||
this.resetData();
|
|
||||||
this.hasLoggedPreprocessInfo = false;
|
|
||||||
this.hasLoggedScenarioInfo = false;
|
|
||||||
this.setGridSize(this.DEFAULT_GRID_SIZE);
|
|
||||||
this.setOrigin(this.DEFAULT_ORIGIN);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
@ -157,7 +157,6 @@ module.exports = function () {
|
|||||||
} else {
|
} else {
|
||||||
got.matchings = encodedResult;
|
got.matchings = encodedResult;
|
||||||
row.matchings = extendedTarget;
|
row.matchings = extendedTarget;
|
||||||
this.logFail(row, got, { matching: { query: this.query, response: res } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cb(null, got);
|
cb(null, got);
|
||||||
|
@ -22,24 +22,16 @@ module.exports = function () {
|
|||||||
|
|
||||||
var got = { in: row.in, out: row.out };
|
var got = { in: row.in, out: row.out };
|
||||||
|
|
||||||
var ok = true;
|
|
||||||
|
|
||||||
Object.keys(row).forEach((key) => {
|
Object.keys(row).forEach((key) => {
|
||||||
if (key === 'out') {
|
if (key === 'out') {
|
||||||
if (this.FuzzyMatch.matchLocation(coord, outNode)) {
|
if (this.FuzzyMatch.matchLocation(coord, outNode)) {
|
||||||
got[key] = row[key];
|
got[key] = row[key];
|
||||||
} else {
|
} else {
|
||||||
row[key] = util.format('%s [%d,%d]', row[key], outNode.lat, outNode.lon);
|
row[key] = util.format('%s [%d,%d]', row[key], outNode.lat, outNode.lon);
|
||||||
ok = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
var failed = { attempt: 'nearest', query: this.query, response: response };
|
|
||||||
this.logFail(row, got, [failed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, got);
|
cb(null, got);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2,36 +2,58 @@ var assert = require('assert');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => {
|
this.resetOptionsOutput = () => {
|
||||||
this.runBin('osrm-routed', options, () => {
|
this.stdout = null;
|
||||||
callback();
|
this.stderr = null;
|
||||||
|
this.exitCode = null;
|
||||||
|
this.termSignal = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.runAndSafeOutput = (binary, options, callback) => {
|
||||||
|
this.runBin(binary, this.expandOptions(options), this.environment, (err, stdout, stderr) => {
|
||||||
|
this.stdout = stdout;
|
||||||
|
this.stderr = stderr;
|
||||||
|
this.exitCode = err && err.code || 0;
|
||||||
|
this.termSignal = err && err.signal || '';
|
||||||
|
callback(err);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => {
|
||||||
|
this.runAndSafeOutput('osrm-routed', options, callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.When(/^I run "osrm\-extract\s?(.*?)"$/, (options, callback) => {
|
this.When(/^I run "osrm\-extract\s?(.*?)"$/, (options, callback) => {
|
||||||
this.runBin('osrm-extract', options, () => {
|
this.runAndSafeOutput('osrm-extract', options, callback);
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.When(/^I run "osrm\-contract\s?(.*?)"$/, (options, callback) => {
|
this.When(/^I run "osrm\-contract\s?(.*?)"$/, (options, callback) => {
|
||||||
this.runBin('osrm-contract', options, () => {
|
this.runAndSafeOutput('osrm-contract', options, callback);
|
||||||
callback();
|
});
|
||||||
});
|
|
||||||
|
this.When(/^I try to run "osrm\-routed\s?(.*?)"$/, (options, callback) => {
|
||||||
|
this.runAndSafeOutput('osrm-routed', options, () => { callback(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.When(/^I try to run "osrm\-extract\s?(.*?)"$/, (options, callback) => {
|
||||||
|
this.runAndSafeOutput('osrm-extract', options, () => { callback(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.When(/^I try to run "osrm\-contract\s?(.*?)"$/, (options, callback) => {
|
||||||
|
this.runAndSafeOutput('osrm-contract', options, () => { callback(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.When(/^I run "osrm\-datastore\s?(.*?)"$/, (options, callback) => {
|
this.When(/^I run "osrm\-datastore\s?(.*?)"$/, (options, callback) => {
|
||||||
this.runBin('osrm-datastore', options, () => {
|
this.runAndSafeOutput('osrm-datastore', options, callback);
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Then(/^it should exit with code (\d+)$/, (code) => {
|
this.Then(/^it should exit successfully$/, () => {
|
||||||
assert.equal(this.exitCode, parseInt(code));
|
assert.equal(this.exitCode, 0);
|
||||||
|
assert.equal(this.termSignal, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Then(/^it should exit with code not (\d+)$/, (code) => {
|
this.Then(/^it should exit with an error$/, () => {
|
||||||
assert.notEqual(this.exitCode, parseInt(code));
|
assert.ok(this.exitCode !== 0 || this.termSignal);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Then(/^stdout should contain "(.*?)"$/, (str) => {
|
this.Then(/^stdout should contain "(.*?)"$/, (str) => {
|
||||||
@ -65,7 +87,7 @@ module.exports = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.Then(/^datasource names should contain "(.+)"$/, (expectedData) => {
|
this.Then(/^datasource names should contain "(.+)"$/, (expectedData) => {
|
||||||
var actualData = fs.readFileSync(this.osmData.extractedFile + '.osrm.datasource_names', {encoding:'UTF-8'}).trim().split('\n').join(',');
|
var actualData = fs.readFileSync(this.processedCacheFile + '.datasource_names', {encoding:'UTF-8'}).trim().split('\n').join(',');
|
||||||
assert.equal(actualData, expectedData);
|
assert.equal(actualData, expectedData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ module.exports = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.Then(/^"([^"]*)" should return code (\d+)$/, (binary, code) => {
|
this.Then(/^"([^"]*)" should return code (\d+)$/, (binary, code) => {
|
||||||
assert.ok(this.processError instanceof this.OSRMError);
|
assert.ok(this.processError instanceof Error);
|
||||||
assert.equal(this.processError.process, binary);
|
assert.equal(this.processError.process, binary);
|
||||||
assert.equal(parseInt(this.processError.code), parseInt(code));
|
assert.equal(parseInt(this.processError.code), parseInt(code));
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ module.exports = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reprocessAndLoadData((e) => {
|
this.reprocessAndLoadData((e) => {
|
||||||
if (e) callback(e);
|
if (e) return callback(e);
|
||||||
var testRow = (row, i, cb) => {
|
var testRow = (row, i, cb) => {
|
||||||
var outputRow = row;
|
var outputRow = row;
|
||||||
|
|
||||||
@ -41,10 +41,6 @@ module.exports = function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (outputRow != row) {
|
|
||||||
this.logFail(row, outputRow, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, outputRow);
|
cb(null, outputRow);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -116,7 +112,7 @@ module.exports = function () {
|
|||||||
sq.defer(parseRes, key);
|
sq.defer(parseRes, key);
|
||||||
});
|
});
|
||||||
|
|
||||||
sq.awaitAll(() => { cb(null, result); });
|
sq.awaitAll((err) => { cb(err, result); });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -85,23 +85,14 @@ module.exports = function () {
|
|||||||
} else {
|
} else {
|
||||||
got.trips = encodedResult;
|
got.trips = encodedResult;
|
||||||
got.trips = extendedTarget;
|
got.trips = extendedTarget;
|
||||||
this.logFail(row, got, { trip: { query: this.query, response: res }});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = true;
|
|
||||||
|
|
||||||
for (var key in row) {
|
for (var key in row) {
|
||||||
if (this.FuzzyMatch.match(got[key], row[key])) {
|
if (this.FuzzyMatch.match(got[key], row[key])) {
|
||||||
got[key] = row[key];
|
got[key] = row[key];
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
this.logFail(row, got, { trip: { query: this.query, response: res }});
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, got);
|
cb(null, got);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
184
features/support/cache.js
Normal file
184
features/support/cache.js
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const d3 = require('d3-queue');
|
||||||
|
const fs = require('fs');
|
||||||
|
const util = require('util');
|
||||||
|
const path = require('path');
|
||||||
|
const mkdirp = require('mkdirp');
|
||||||
|
const hash = require('../lib/hash');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
this.initializeCache = (callback) => {
|
||||||
|
this.getOSRMHash((err, osrmHash) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
this.osrmHash = osrmHash;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// computes all paths for every feature
|
||||||
|
this.setupFeatures = (features, callback) => {
|
||||||
|
this.featureIDs = {};
|
||||||
|
this.featureCacheDirectories = {};
|
||||||
|
this.featureProcessedCacheDirectories = {};
|
||||||
|
let queue = d3.queue();
|
||||||
|
|
||||||
|
function initializeFeature(feature, callback) {
|
||||||
|
let uri = feature.getUri();
|
||||||
|
|
||||||
|
// setup cache for feature data
|
||||||
|
hash.hashOfFile(uri, (err, hash) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
// shorten uri to be realtive to 'features/'
|
||||||
|
let featurePath = path.relative(path.resolve('./features'), uri);
|
||||||
|
// bicycle/bollards/{HASH}/
|
||||||
|
let featureID = path.join(featurePath, hash);
|
||||||
|
let featureCacheDirectory = this.getFeatureCacheDirectory(featureID);
|
||||||
|
let featureProcessedCacheDirectory = this.getFeatureProcessedCacheDirectory(featureCacheDirectory, this.osrmHash);
|
||||||
|
this.featureIDs[uri] = featureID;
|
||||||
|
this.featureCacheDirectories[uri] = featureCacheDirectory;
|
||||||
|
this.featureProcessedCacheDirectories[uri] = featureProcessedCacheDirectory;
|
||||||
|
|
||||||
|
d3.queue(1)
|
||||||
|
.defer(mkdirp, featureProcessedCacheDirectory)
|
||||||
|
.defer(this.cleanupFeatureCache.bind(this), featureCacheDirectory, hash)
|
||||||
|
.defer(this.cleanupProcessedFeatureCache.bind(this), featureProcessedCacheDirectory, this.osrmHash)
|
||||||
|
.awaitAll(callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < features.length; ++i) {
|
||||||
|
queue.defer(initializeFeature.bind(this), features[i]);
|
||||||
|
}
|
||||||
|
queue.awaitAll(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.cleanupProcessedFeatureCache = (directory, osrmHash, callback) => {
|
||||||
|
let parentPath = path.resolve(path.join(directory, '..'));
|
||||||
|
fs.readdir(parentPath, (err, files) => {
|
||||||
|
let q = d3.queue();
|
||||||
|
function runStats(path, callback) {
|
||||||
|
fs.stat(path, (err, stat) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
callback(null, {file: path, stat: stat});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
files.map(f => { q.defer(runStats, path.join(parentPath, f)); });
|
||||||
|
q.awaitAll((err, results) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
let q = d3.queue();
|
||||||
|
results.forEach(r => {
|
||||||
|
if (r.stat.isDirectory() && r.file.search(osrmHash) < 0) {
|
||||||
|
q.defer(rimraf, r.file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
q.awaitAll(callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.cleanupFeatureCache = (directory, featureHash, callback) => {
|
||||||
|
let parentPath = path.resolve(path.join(directory, '..'));
|
||||||
|
fs.readdir(parentPath, (err, files) => {
|
||||||
|
let q = d3.queue();
|
||||||
|
files.filter(name => { return name !== featureHash;})
|
||||||
|
.map((f) => { q.defer(rimraf, path.join(parentPath, f)); });
|
||||||
|
q.awaitAll(callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setupFeatureCache = (feature) => {
|
||||||
|
let uri = feature.getUri();
|
||||||
|
this.featureID = this.featureIDs[uri];
|
||||||
|
this.featureCacheDirectory = this.featureCacheDirectories[uri];
|
||||||
|
this.featureProcessedCacheDirectory = this.featureProcessedCacheDirectories[uri];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setupScenarioCache = (scenarioID) => {
|
||||||
|
this.scenarioCacheFile = this.getScenarioCacheFile(this.featureCacheDirectory, scenarioID);
|
||||||
|
this.processedCacheFile = this.getProcessedCacheFile(this.featureProcessedCacheDirectory, scenarioID);
|
||||||
|
this.inputCacheFile = this.getInputCacheFile(this.featureProcessedCacheDirectory, scenarioID);
|
||||||
|
this.rasterCacheFile = this.getRasterCacheFile(this.featureProcessedCacheDirectory, scenarioID);
|
||||||
|
this.speedsCacheFile = this.getSpeedsCacheFile(this.featureProcessedCacheDirectory, scenarioID);
|
||||||
|
this.penaltiesCacheFile = this.getPenaltiesCacheFile(this.featureProcessedCacheDirectory, scenarioID);
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns a hash of all OSRM code side dependencies
|
||||||
|
this.getOSRMHash = (callback) => {
|
||||||
|
let dependencies = [
|
||||||
|
this.OSRM_EXTRACT_PATH,
|
||||||
|
this.OSRM_CONTRACT_PATH,
|
||||||
|
this.LIB_OSRM_EXTRACT_PATH,
|
||||||
|
this.LIB_OSRM_CONTRACT_PATH
|
||||||
|
];
|
||||||
|
|
||||||
|
var addLuaFiles = (directory, callback) => {
|
||||||
|
fs.readdir(path.normalize(directory), (err, files) => {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(directory + '/' + f));
|
||||||
|
Array.prototype.push.apply(dependencies, luaFiles);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: we need a serialized queue here to ensure that the order of the files
|
||||||
|
// passed is stable. Otherwise the hash will not be stable
|
||||||
|
d3.queue(1)
|
||||||
|
.defer(addLuaFiles, this.PROFILES_PATH)
|
||||||
|
.defer(addLuaFiles, this.PROFILES_PATH + '/lib')
|
||||||
|
.awaitAll(hash.hashOfFiles.bind(hash, dependencies, callback));
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/bicycle/bollards/{HASH}/
|
||||||
|
this.getFeatureCacheDirectory = (featureID) => {
|
||||||
|
return path.join(this.CACHE_PATH, featureID);
|
||||||
|
};
|
||||||
|
|
||||||
|
// converts the scenario titles in file prefixes
|
||||||
|
this.getScenarioID = (scenario) => {
|
||||||
|
let name = scenario.getName().toLowerCase().replace(/[\/\-'=,\(\)]/g, '').replace(/\s/g, '_').replace(/__/g, '_').replace(/\.\./g, '.');
|
||||||
|
return util.format('%d_%s', scenario.getLine(), name);
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{scenario}_raster.asc
|
||||||
|
this.getRasterCacheFile = (featureCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureCacheDirectory, scenarioID) + '_raster.asc';
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{scenario}_speeds.csv
|
||||||
|
this.getSpeedsCacheFile = (featureCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureCacheDirectory, scenarioID) + '_speeds.csv';
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{scenario}_penalties.csv
|
||||||
|
this.getPenaltiesCacheFile = (featureCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureCacheDirectory, scenarioID) + '_penalties.csv';
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{scenario}.osm
|
||||||
|
this.getScenarioCacheFile = (featureCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureCacheDirectory, scenarioID) + '.osm';
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{osrm_hash}/
|
||||||
|
this.getFeatureProcessedCacheDirectory = (featureCacheDirectory, osrmHash) => {
|
||||||
|
return path.join(featureCacheDirectory, osrmHash);
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{osrm_hash}/{scenario}.osrm
|
||||||
|
this.getProcessedCacheFile = (featureProcessedCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureProcessedCacheDirectory, scenarioID) + '.osrm';
|
||||||
|
};
|
||||||
|
|
||||||
|
// test/cache/{feature_path}/{feature_hash}/{osrm_hash}/{scenario}.osm
|
||||||
|
this.getInputCacheFile = (featureProcessedCacheDirectory, scenarioID) => {
|
||||||
|
return path.join(featureProcessedCacheDirectory, scenarioID) + '.osm';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
@ -1,127 +0,0 @@
|
|||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var util = require('util');
|
|
||||||
var d3 = require('d3-queue');
|
|
||||||
var OSM = require('./build_osm');
|
|
||||||
var classes = require('./data_classes');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.initializeOptions = (callback) => {
|
|
||||||
this.profile = this.profile || this.DEFAULT_SPEEDPROFILE;
|
|
||||||
|
|
||||||
this.OSMDB = this.OSMDB || new OSM.DB();
|
|
||||||
|
|
||||||
this.nameNodeHash = this.nameNodeHash || {};
|
|
||||||
|
|
||||||
this.locationHash = this.locationHash || {};
|
|
||||||
|
|
||||||
this.nameWayHash = this.nameWayHash || {};
|
|
||||||
|
|
||||||
this.osmData = new classes.osmData(this);
|
|
||||||
|
|
||||||
this.OSRMLoader = this._OSRMLoader();
|
|
||||||
|
|
||||||
this.PREPROCESS_LOG_FILE = path.resolve(this.TEST_FOLDER, 'preprocessing.log');
|
|
||||||
|
|
||||||
this.LOG_FILE = path.resolve(this.TEST_FOLDER, 'fail.log');
|
|
||||||
|
|
||||||
this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT;
|
|
||||||
|
|
||||||
this.DESTINATION_REACHED = 15; // OSRM instruction code
|
|
||||||
|
|
||||||
this.shortcutsHash = this.shortcutsHash || {};
|
|
||||||
|
|
||||||
var hashLuaLib = (cb) => {
|
|
||||||
fs.readdir(path.normalize(this.PROFILES_PATH + '/lib/'), (err, files) => {
|
|
||||||
if (err) cb(err);
|
|
||||||
var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(this.PROFILES_PATH + '/lib/' + f));
|
|
||||||
this.hashOfFiles(luaFiles, hash => {
|
|
||||||
this.luaLibHash = hash;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var hashProfile = (cb) => {
|
|
||||||
this.hashProfile((hash) => {
|
|
||||||
this.profileHash = hash;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var hashExtract = (cb) => {
|
|
||||||
var files = [ util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE),
|
|
||||||
util.format('%s/libosrm_extract%s', this.BIN_PATH, this.LIB) ];
|
|
||||||
this.hashOfFiles(files, (hash) => {
|
|
||||||
this.binExtractHash = hash;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var hashContract = (cb) => {
|
|
||||||
var files = [ util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE),
|
|
||||||
util.format('%s/libosrm_contract%s', this.BIN_PATH, this.LIB) ];
|
|
||||||
this.hashOfFiles(files, (hash) => {
|
|
||||||
this.binContractHash = hash;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var hashRouted = (cb) => {
|
|
||||||
var files = [ util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE),
|
|
||||||
util.format('%s/libosrm%s', this.BIN_PATH, this.LIB) ];
|
|
||||||
this.hashOfFiles(files, (hash) => {
|
|
||||||
this.binRoutedHash = hash;
|
|
||||||
this.fingerprintRoute = this.hashString(this.binRoutedHash);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
d3.queue()
|
|
||||||
.defer(hashLuaLib)
|
|
||||||
.defer(hashProfile)
|
|
||||||
.defer(hashExtract)
|
|
||||||
.defer(hashContract)
|
|
||||||
.defer(hashRouted)
|
|
||||||
.awaitAll(() => {
|
|
||||||
this.AfterConfiguration(() => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateFingerprintExtract = (str) => {
|
|
||||||
this.fingerprintExtract = this.hashString([this.fingerprintExtract, str].join('-'));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateFingerprintContract = (str) => {
|
|
||||||
this.fingerprintContract = this.hashString([this.fingerprintContract, str].join('-'));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setProfile = (profile, cb) => {
|
|
||||||
var lastProfile = this.profile;
|
|
||||||
if (profile !== lastProfile) {
|
|
||||||
this.profile = profile;
|
|
||||||
this.hashProfile((hash) => {
|
|
||||||
this.profileHash = hash;
|
|
||||||
this.updateFingerprintExtract(this.profileHash);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.updateFingerprintExtract(this.profileHash);
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setExtractArgs = (args, callback) => {
|
|
||||||
this.extractArgs = args;
|
|
||||||
this.updateFingerprintExtract(args);
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setContractArgs = (args, callback) => {
|
|
||||||
this.contractArgs = args;
|
|
||||||
this.updateFingerprintContract(args);
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,11 +1,14 @@
|
|||||||
var fs = require('fs');
|
'use strict';
|
||||||
var path = require('path');
|
|
||||||
var util = require('util');
|
|
||||||
var exec = require('child_process').exec;
|
|
||||||
var d3 = require('d3-queue');
|
|
||||||
|
|
||||||
var OSM = require('./build_osm');
|
const fs = require('fs');
|
||||||
var classes = require('./data_classes');
|
const util = require('util');
|
||||||
|
const d3 = require('d3-queue');
|
||||||
|
|
||||||
|
const OSM = require('../lib/osm');
|
||||||
|
const classes = require('./data_classes');
|
||||||
|
const tableDiff = require('../lib/table_diff');
|
||||||
|
const ensureDecimal = require('../lib/utils').ensureDecimal;
|
||||||
|
const errorReason = require('../lib/utils').errorReason;
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.setGridSize = (meters) => {
|
this.setGridSize = (meters) => {
|
||||||
@ -94,13 +97,8 @@ module.exports = function () {
|
|||||||
q.awaitAll(callback);
|
q.awaitAll(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ensureDecimal = (i) => {
|
|
||||||
if (parseInt(i) === i) return i.toFixed(1);
|
|
||||||
else return i;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tableCoordToLonLat = (ci, ri) => {
|
this.tableCoordToLonLat = (ci, ri) => {
|
||||||
return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(this.ensureDecimal);
|
return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(ensureDecimal);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addOSMNode = (name, lon, lat, id) => {
|
this.addOSMNode = (name, lon, lat, id) => {
|
||||||
@ -132,10 +130,6 @@ module.exports = function () {
|
|||||||
return this.nameWayHash[s.toString()] || this.nameWayHash[s.toString().split('').reverse().join('')];
|
return this.nameWayHash[s.toString()] || this.nameWayHash[s.toString().split('').reverse().join('')];
|
||||||
};
|
};
|
||||||
|
|
||||||
this.resetData = () => {
|
|
||||||
this.resetOSM();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.makeOSMId = () => {
|
this.makeOSMId = () => {
|
||||||
this.osmID = this.osmID + 1;
|
this.osmID = this.osmID + 1;
|
||||||
return this.osmID;
|
return this.osmID;
|
||||||
@ -143,206 +137,88 @@ module.exports = function () {
|
|||||||
|
|
||||||
this.resetOSM = () => {
|
this.resetOSM = () => {
|
||||||
this.OSMDB.clear();
|
this.OSMDB.clear();
|
||||||
this.osmData.reset();
|
|
||||||
this.nameNodeHash = {};
|
this.nameNodeHash = {};
|
||||||
this.locationHash = {};
|
this.locationHash = {};
|
||||||
|
this.shortcutsHash = {};
|
||||||
this.nameWayHash = {};
|
this.nameWayHash = {};
|
||||||
this.osmID = 0;
|
this.osmID = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.writeOSM = (callback) => {
|
this.writeOSM = (callback) => {
|
||||||
fs.exists(this.DATA_FOLDER, (exists) => {
|
fs.exists(this.scenarioCacheFile, (exists) => {
|
||||||
var mkDirFn = exists ? (cb) => { cb(); } : fs.mkdir.bind(fs.mkdir, this.DATA_FOLDER);
|
if (exists) callback();
|
||||||
mkDirFn((err) => {
|
else {
|
||||||
if (err) return callback(err);
|
this.OSMDB.toXML((xml) => {
|
||||||
var osmPath = path.resolve(this.DATA_FOLDER, util.format('%s.osm', this.osmData.osmFile));
|
fs.writeFile(this.scenarioCacheFile, xml, callback);
|
||||||
fs.exists(osmPath, (exists) => {
|
|
||||||
if (!exists) fs.writeFile(osmPath, this.osmData.str, callback);
|
|
||||||
else callback();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isExtracted = (callback) => {
|
|
||||||
fs.exists(util.format('%s.osrm', this.osmData.extractedFile), (core) => {
|
|
||||||
if (!core) return callback(false);
|
|
||||||
fs.exists(util.format('%s.osrm.names', this.osmData.extractedFile), (names) => {
|
|
||||||
if (!names) return callback(false);
|
|
||||||
fs.exists(util.format('%s.osrm.restrictions', this.osmData.extractedFile), (restrictions) => {
|
|
||||||
return callback(restrictions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isContracted = (callback) => {
|
|
||||||
fs.exists(util.format('%s.osrm.hsgr', this.osmData.contractedFile), callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.writeTimestamp = (callback) => {
|
|
||||||
fs.writeFile(util.format('%s.osrm.timestamp', this.osmData.contractedFile), this.OSM_TIMESTAMP, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.writeInputData = (callback) => {
|
|
||||||
this.writeOSM((err) => {
|
|
||||||
if (err) return callback(err);
|
|
||||||
this.writeTimestamp(callback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.extractData = (callback) => {
|
|
||||||
this.logPreprocessInfo();
|
|
||||||
this.log(util.format('== Extracting %s.osm...', this.osmData.osmFile), 'preprocess');
|
|
||||||
var cmd = util.format('%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1',
|
|
||||||
this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE);
|
|
||||||
this.log(cmd);
|
|
||||||
process.chdir(this.TEST_FOLDER);
|
|
||||||
exec(cmd, (err) => {
|
|
||||||
if (err) {
|
|
||||||
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
|
|
||||||
process.chdir('../');
|
|
||||||
return callback(this.ExtractError(err.code, util.format('osrm-extract exited with code %d', err.code)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var q = d3.queue();
|
|
||||||
|
|
||||||
var rename = (file, cb) => {
|
|
||||||
this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.osmFile, file, this.osmData.extractedFile, file), 'preprocess');
|
|
||||||
fs.rename([this.osmData.osmFile, file].join('.'), [this.osmData.extractedFile, file].join('.'), (err) => {
|
|
||||||
if (err) return cb(this.FileError(null, 'failed to rename data file after extracting'));
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameIfExists = (file, cb) => {
|
|
||||||
fs.stat([this.osmData.osmFile, file].join('.'), (doesNotExistErr, exists) => {
|
|
||||||
if (exists) rename(file, cb);
|
|
||||||
else cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
['osrm', 'osrm.ebg', 'osrm.edges', 'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.icd',
|
|
||||||
'osrm.names', 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach(file => {
|
|
||||||
q.defer(rename, file);
|
|
||||||
});
|
|
||||||
|
|
||||||
['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => {
|
|
||||||
q.defer(renameIfExists, file);
|
|
||||||
});
|
|
||||||
|
|
||||||
q.awaitAll((err) => {
|
|
||||||
this.log('Finished extracting ' + this.osmData.extractedFile, 'preprocess');
|
|
||||||
process.chdir('../');
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.contractData = (callback) => {
|
this.linkOSM = (callback) => {
|
||||||
this.logPreprocessInfo();
|
fs.exists(this.inputCacheFile, (exists) => {
|
||||||
this.log(util.format('== Contracting %s.osm...', this.osmData.extractedFile), 'preprocess');
|
if (exists) callback();
|
||||||
var cmd = util.format('%s/osrm-contract %s %s.osrm >>%s 2>&1',
|
else {
|
||||||
this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE);
|
fs.link(this.scenarioCacheFile, this.inputCacheFile, callback);
|
||||||
this.log(cmd);
|
|
||||||
process.chdir(this.TEST_FOLDER);
|
|
||||||
exec(cmd, (err) => {
|
|
||||||
if (err) {
|
|
||||||
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
|
|
||||||
process.chdir('../');
|
|
||||||
return callback(this.ContractError(err.code, util.format('osrm-contract exited with code %d', err.code)));
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var rename = (file, cb) => {
|
this.extractData = (p, callback) => {
|
||||||
this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess');
|
let stamp = p.processedCacheFile + '.extract';
|
||||||
fs.rename([this.osmData.extractedFile, file].join('.'), [this.osmData.contractedFile, file].join('.'), (err) => {
|
fs.exists(stamp, (exists) => {
|
||||||
if (err) return cb(this.FileError(null, 'failed to rename data file after contracting.'));
|
if (exists) return callback();
|
||||||
cb();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameIfExists = (file, cb) => {
|
this.runBin('osrm-extract', util.format('%s --profile %s %s', p.extractArgs, p.profileFile, p.inputCacheFile), p.environment, (err) => {
|
||||||
fs.stat([this.osmData.extractedFile, file].join('.'), (doesNotExistErr, exists) => {
|
if (err) {
|
||||||
if (exists) rename(file, cb);
|
return callback(new Error(util.format('osrm-extract %s: %s', errorReason(err), err.cmd)));
|
||||||
else cb();
|
}
|
||||||
});
|
fs.writeFile(stamp, 'ok', callback);
|
||||||
};
|
|
||||||
|
|
||||||
var copy = (file, cb) => {
|
|
||||||
this.log(util.format('Copying %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess');
|
|
||||||
fs.createReadStream([this.osmData.extractedFile, file].join('.'))
|
|
||||||
.pipe(fs.createWriteStream([this.osmData.contractedFile, file].join('.'))
|
|
||||||
.on('finish', cb)
|
|
||||||
)
|
|
||||||
.on('error', () => {
|
|
||||||
return cb(this.FileError(null, 'failed to copy data after contracting.'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var q = d3.queue();
|
|
||||||
|
|
||||||
['osrm', 'osrm.core', 'osrm.datasource_indexes', 'osrm.datasource_names', 'osrm.ebg','osrm.edges',
|
|
||||||
'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.hsgr', 'osrm.icd','osrm.level', 'osrm.names',
|
|
||||||
'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach((file) => {
|
|
||||||
q.defer(rename, file);
|
|
||||||
});
|
|
||||||
|
|
||||||
['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => {
|
|
||||||
q.defer(renameIfExists, file);
|
|
||||||
});
|
|
||||||
|
|
||||||
[].forEach((file) => {
|
|
||||||
q.defer(copy, file);
|
|
||||||
});
|
|
||||||
|
|
||||||
q.awaitAll((err) => {
|
|
||||||
this.log('Finished contracting ' + this.osmData.contractedFile, 'preprocess');
|
|
||||||
process.chdir('../');
|
|
||||||
callback(err);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var noop = (cb) => cb();
|
this.contractData = (p, callback) => {
|
||||||
|
let stamp = p.processedCacheFile + '.contract';
|
||||||
|
fs.exists(stamp, (exists) => {
|
||||||
|
if (exists) return callback();
|
||||||
|
|
||||||
|
this.runBin('osrm-contract', util.format('%s %s', p.contractArgs, p.processedCacheFile), p.environment, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(new Error(util.format('osrm-contract %s: %s', errorReason(err), err)));
|
||||||
|
}
|
||||||
|
fs.writeFile(stamp, 'ok', callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.extractAndContract = (callback) => {
|
||||||
|
// a shallow copy of scenario parameters to avoid data inconsistency
|
||||||
|
// if a cucumber timeout occurs during deferred jobs
|
||||||
|
let p = {extractArgs: this.extractArgs, contractArgs: this.contractArgs,
|
||||||
|
profileFile: this.profileFile, inputCacheFile: this.inputCacheFile,
|
||||||
|
processedCacheFile: this.processedCacheFile, environment: this.environment};
|
||||||
|
let queue = d3.queue(1);
|
||||||
|
queue.defer(this.extractData.bind(this), p);
|
||||||
|
queue.defer(this.contractData.bind(this), p);
|
||||||
|
queue.awaitAll(callback);
|
||||||
|
};
|
||||||
|
|
||||||
this.reprocess = (callback) => {
|
this.reprocess = (callback) => {
|
||||||
this.osmData.populate(() => {
|
let queue = d3.queue(1);
|
||||||
this.isContracted((isContracted) => {
|
queue.defer(this.writeOSM.bind(this));
|
||||||
if (!isContracted) {
|
queue.defer(this.linkOSM.bind(this));
|
||||||
this.writeAndExtract((e) => {
|
queue.defer(this.extractAndContract.bind(this));
|
||||||
if (e) return callback(e);
|
queue.awaitAll(callback);
|
||||||
this.contractData((e) => {
|
|
||||||
if (e) return callback(e);
|
|
||||||
this.logPreprocessDone();
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess');
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.writeAndExtract = (callback) => {
|
|
||||||
this.writeInputData((e) => {
|
|
||||||
if (e) return callback(e);
|
|
||||||
this.isExtracted((isExtracted) => {
|
|
||||||
var extractFn = isExtracted ? noop : this.extractData;
|
|
||||||
if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess');
|
|
||||||
extractFn((e) => {
|
|
||||||
callback(e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.reprocessAndLoadData = (callback) => {
|
this.reprocessAndLoadData = (callback) => {
|
||||||
this.reprocess((e) => {
|
let queue = d3.queue(1);
|
||||||
if (e) return callback(e);
|
queue.defer(this.writeOSM.bind(this));
|
||||||
this.OSRMLoader.load(util.format('%s.osrm', this.osmData.contractedFile), callback);
|
queue.defer(this.linkOSM.bind(this));
|
||||||
});
|
queue.defer(this.extractAndContract.bind(this));
|
||||||
|
queue.defer(this.osrmLoader.load.bind(this.osrmLoader), this.processedCacheFile);
|
||||||
|
queue.awaitAll(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.processRowsAndDiff = (table, fn, callback) => {
|
this.processRowsAndDiff = (table, fn, callback) => {
|
||||||
@ -352,7 +228,9 @@ module.exports = function () {
|
|||||||
|
|
||||||
q.awaitAll((err, actual) => {
|
q.awaitAll((err, actual) => {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
this.diffTables(table, actual, {}, callback);
|
let diff = tableDiff(table, actual);
|
||||||
|
if (diff) callback(new Error(diff));
|
||||||
|
else callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
const util = require('util');
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Location: class {
|
Location: class {
|
||||||
@ -11,43 +10,6 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
osmData: class {
|
|
||||||
constructor (scope) {
|
|
||||||
this.scope = scope;
|
|
||||||
this.str = null;
|
|
||||||
this.hash = null;
|
|
||||||
this.fingerprintOSM = null;
|
|
||||||
this.osmFile = null;
|
|
||||||
this.extractedFile = null;
|
|
||||||
this.contractedFile = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
populate (callback) {
|
|
||||||
this.scope.OSMDB.toXML((str) => {
|
|
||||||
this.str = str;
|
|
||||||
|
|
||||||
this.hash = this.scope.hashString(str);
|
|
||||||
this.fingerprintOSM = this.scope.hashString(this.hash);
|
|
||||||
|
|
||||||
this.osmFile = path.resolve(this.scope.DATA_FOLDER, this.fingerprintOSM);
|
|
||||||
|
|
||||||
this.extractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract].join('_'));
|
|
||||||
this.contractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract, this.scope.fingerprintContract].join('_'));
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reset () {
|
|
||||||
this.str = null;
|
|
||||||
this.hash = null;
|
|
||||||
this.fingerprintOSM = null;
|
|
||||||
this.osmFile = null;
|
|
||||||
this.extractedFile = null;
|
|
||||||
this.contractedFile = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
FuzzyMatch: class {
|
FuzzyMatch: class {
|
||||||
match (got, want) {
|
match (got, want) {
|
||||||
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
|
||||||
|
@ -1,32 +1,46 @@
|
|||||||
var path = require('path');
|
'use strict';
|
||||||
var util = require('util');
|
|
||||||
var fs = require('fs');
|
|
||||||
var exec = require('child_process').exec;
|
|
||||||
var d3 = require('d3-queue');
|
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const util = require('util');
|
||||||
|
const fs = require('fs');
|
||||||
|
const d3 = require('d3-queue');
|
||||||
|
const child_process = require('child_process');
|
||||||
|
const tryConnect = require('../lib/try_connect');
|
||||||
|
|
||||||
|
// Sets up all constants that are valid for all features
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.initializeEnv = (callback) => {
|
this.initializeEnv = (callback) => {
|
||||||
this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000;
|
|
||||||
this.TIMEOUT = process.env.CUCUMBER_TIMEOUT && parseInt(process.env.CUCUMBER_TIMEOUT) || 5000;
|
this.TIMEOUT = process.env.CUCUMBER_TIMEOUT && parseInt(process.env.CUCUMBER_TIMEOUT) || 5000;
|
||||||
|
// set cucumber default timeout
|
||||||
this.setDefaultTimeout(this.TIMEOUT);
|
this.setDefaultTimeout(this.TIMEOUT);
|
||||||
this.ROOT_FOLDER = process.cwd();
|
this.ROOT_PATH = process.cwd();
|
||||||
|
|
||||||
|
this.TEST_PATH = path.resolve(this.ROOT_PATH, 'test');
|
||||||
|
this.CACHE_PATH = path.resolve(this.TEST_PATH, 'cache');
|
||||||
|
this.LOGS_PATH = path.resolve(this.TEST_PATH, 'logs');
|
||||||
|
|
||||||
|
this.PROFILES_PATH = path.resolve(this.ROOT_PATH, 'profiles');
|
||||||
|
this.FIXTURES_PATH = path.resolve(this.ROOT_PATH, 'unit_tests/fixtures');
|
||||||
|
this.BIN_PATH = process.env.OSRM_BUILD_DIR && process.env.OSRM_BUILD_DIR || path.resolve(this.ROOT_PATH, 'build');
|
||||||
|
var stxxl_config = path.resolve(this.ROOT_PATH, 'test/.stxxl');
|
||||||
|
if (!fs.existsSync(stxxl_config)) {
|
||||||
|
return callback(new Error('*** '+stxxl_config+ 'does not exist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.DEFAULT_ENVIRONMENT = Object.assign({STXXLCFG: stxxl_config}, process.env);
|
||||||
|
this.DEFAULT_PROFILE = 'bicycle';
|
||||||
|
this.DEFAULT_INPUT_FORMAT = 'osm';
|
||||||
|
this.DEFAULT_LOAD_METHOD = 'datastore';
|
||||||
|
this.DEFAULT_ORIGIN = [1,1];
|
||||||
this.OSM_USER = 'osrm';
|
this.OSM_USER = 'osrm';
|
||||||
this.OSM_GENERATOR = 'osrm-test';
|
this.OSM_GENERATOR = 'osrm-test';
|
||||||
this.OSM_UID = 1;
|
this.OSM_UID = 1;
|
||||||
this.TEST_FOLDER = path.resolve(this.ROOT_FOLDER, 'test');
|
|
||||||
this.DATA_FOLDER = path.resolve(this.TEST_FOLDER, 'cache');
|
|
||||||
this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z';
|
this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z';
|
||||||
this.DEFAULT_SPEEDPROFILE = 'bicycle';
|
|
||||||
this.WAY_SPACING = 100;
|
this.WAY_SPACING = 100;
|
||||||
this.DEFAULT_GRID_SIZE = 100; // meters
|
this.DEFAULT_GRID_SIZE = 100; // meters
|
||||||
this.PROFILES_PATH = path.resolve(this.ROOT_FOLDER, 'profiles');
|
|
||||||
this.FIXTURES_PATH = path.resolve(this.ROOT_FOLDER, 'unit_tests/fixtures');
|
this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000;
|
||||||
this.BIN_PATH = process.env.OSRM_BUILD_DIR && process.env.OSRM_BUILD_DIR || path.resolve(this.ROOT_FOLDER, 'build');
|
this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT;
|
||||||
this.DEFAULT_INPUT_FORMAT = 'osm';
|
|
||||||
this.DEFAULT_ORIGIN = [1,1];
|
|
||||||
this.DEFAULT_LOAD_METHOD = 'datastore';
|
|
||||||
this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log');
|
|
||||||
this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log');
|
|
||||||
|
|
||||||
// TODO make sure this works on win
|
// TODO make sure this works on win
|
||||||
if (process.platform.match(/indows.*/)) {
|
if (process.platform.match(/indows.*/)) {
|
||||||
@ -37,36 +51,49 @@ module.exports = function () {
|
|||||||
} else {
|
} else {
|
||||||
this.TERMSIGNAL = 'SIGTERM';
|
this.TERMSIGNAL = 'SIGTERM';
|
||||||
this.EXE = '';
|
this.EXE = '';
|
||||||
this.LIB = '.so';
|
// TODO autodetect if this was build with shared or static libraries
|
||||||
|
this.LIB = process.env.BUILD_SHARED_LIBS && '.so' || '.a';
|
||||||
this.QQ = '';
|
this.QQ = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.OSRM_EXTRACT_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-extract', this.EXE));
|
||||||
|
this.OSRM_CONTRACT_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-contract', this.EXE));
|
||||||
|
this.OSRM_ROUTED_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-routed', this.EXE));
|
||||||
|
this.LIB_OSRM_EXTRACT_PATH = util.format('%s/libosrm_extract%s', this.BIN_PATH, this.LIB),
|
||||||
|
this.LIB_OSRM_CONTRACT_PATH = util.format('%s/libosrm_contract%s', this.BIN_PATH, this.LIB),
|
||||||
|
this.LIB_OSRM_PATH = util.format('%s/libosrm%s', this.BIN_PATH, this.LIB);
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.info(util.format('Node Version', process.version));
|
console.info(util.format('Node Version', process.version));
|
||||||
if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests');
|
if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests');
|
||||||
|
|
||||||
fs.exists(this.TEST_FOLDER, (exists) => {
|
fs.exists(this.TEST_PATH, (exists) => {
|
||||||
if (!exists) throw new Error(util.format('*** Test folder %s doesn\'t exist.', this.TEST_FOLDER));
|
if (exists)
|
||||||
callback();
|
return callback();
|
||||||
|
else
|
||||||
|
return callback(new Error('*** Test folder doesn\'t exist.'));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.verifyOSRMIsNotRunning = () => {
|
this.getProfilePath = (profile) => {
|
||||||
if (this.OSRMLoader.up()) {
|
return path.resolve(this.PROFILES_PATH, profile + '.lua');
|
||||||
throw new Error('*** osrm-routed is already running.');
|
};
|
||||||
}
|
|
||||||
|
this.verifyOSRMIsNotRunning = (callback) => {
|
||||||
|
tryConnect(this.OSRM_PORT, (err) => {
|
||||||
|
if (!err) return callback(new Error('*** osrm-routed is already running.'));
|
||||||
|
else callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.verifyExistenceOfBinaries = (callback) => {
|
this.verifyExistenceOfBinaries = (callback) => {
|
||||||
var verify = (bin, cb) => {
|
var verify = (binPath, cb) => {
|
||||||
var binPath = path.resolve(util.format('%s/%s%s', this.BIN_PATH, bin, this.EXE));
|
|
||||||
fs.exists(binPath, (exists) => {
|
fs.exists(binPath, (exists) => {
|
||||||
if (!exists) throw new Error(util.format('%s is missing. Build failed?', binPath));
|
if (!exists) return cb(new Error(util.format('%s is missing. Build failed?', binPath)));
|
||||||
var helpPath = util.format('%s --help > /dev/null 2>&1', binPath);
|
var helpPath = util.format('%s --help > /dev/null 2>&1', binPath);
|
||||||
exec(helpPath, (err) => {
|
child_process.exec(helpPath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
|
return cb(new Error(util.format('*** %s exited with code %d', helpPath, err.code)));
|
||||||
throw new Error(util.format('*** %s exited with code %d', helpPath, err.code));
|
|
||||||
}
|
}
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
@ -74,23 +101,12 @@ module.exports = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var q = d3.queue();
|
var q = d3.queue();
|
||||||
['osrm-extract', 'osrm-contract', 'osrm-routed'].forEach(bin => { q.defer(verify, bin); });
|
[this.OSRM_EXTRACT_PATH, this.OSRM_CONTRACT_PATH, this.OSRM_ROUTED_PATH].forEach(bin => { q.defer(verify, bin); });
|
||||||
q.awaitAll(() => {
|
q.awaitAll(callback);
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.AfterConfiguration = (callback) => {
|
|
||||||
this.clearLogFiles(() => {
|
|
||||||
this.verifyOSRMIsNotRunning();
|
|
||||||
this.verifyExistenceOfBinaries(() => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on('exit', () => {
|
process.on('exit', () => {
|
||||||
if (this.OSRMLoader.loader) this.OSRMLoader.shutdown(() => {});
|
this.osrmLoader.shutdown(() => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var util = require('util');
|
|
||||||
var path = require('path');
|
|
||||||
var fs = require('fs');
|
|
||||||
var chalk = require('chalk');
|
|
||||||
|
|
||||||
var OSRMError = class extends Error {
|
|
||||||
constructor (process, code, msg, log, lines) {
|
|
||||||
super(msg);
|
|
||||||
this.process = process;
|
|
||||||
this.code = code;
|
|
||||||
this.msg = msg;
|
|
||||||
this.lines = lines;
|
|
||||||
this.log = log;
|
|
||||||
}
|
|
||||||
|
|
||||||
extract (callback) {
|
|
||||||
this.logTail(this.log, this.lines, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// toString (callback) {
|
|
||||||
// this.extract((tail) => {
|
|
||||||
// callback(util.format('*** %s\nLast %s from %s:\n%s\n', this.msg, this.lines, this.log, tail));
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
logTail (logPath, n, callback) {
|
|
||||||
var expanded = path.resolve(this.TEST_FOLDER, logPath);
|
|
||||||
fs.exists(expanded, (exists) => {
|
|
||||||
if (exists) {
|
|
||||||
fs.readFile(expanded, (err, data) => {
|
|
||||||
var lines = data.toString().trim().split('\n');
|
|
||||||
callback(lines
|
|
||||||
.slice(lines.length - n)
|
|
||||||
.map(line => util.format(' %s', line))
|
|
||||||
.join('\n'));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(util.format('File %s does not exist!', expanded));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
OSRMError: OSRMError,
|
|
||||||
|
|
||||||
FileError: class extends OSRMError {
|
|
||||||
constructor (logFile, code, msg) {
|
|
||||||
super ('fileutil', code, msg, logFile, 5);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
LaunchError: class extends OSRMError {
|
|
||||||
constructor (logFile, launchProcess, code, msg) {
|
|
||||||
super (launchProcess, code, msg, logFile, 5);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ExtractError: class extends OSRMError {
|
|
||||||
constructor (logFile, code, msg) {
|
|
||||||
super('osrm-extract', code, msg, logFile, 3);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ContractError: class extends OSRMError {
|
|
||||||
constructor (logFile, code, msg) {
|
|
||||||
super('osrm-contract', code, msg, logFile, 3);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
RoutedError: class extends OSRMError {
|
|
||||||
constructor (logFile, msg) {
|
|
||||||
super('osrm-routed', null, msg, logFile, 3);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
TableDiffError: class extends Error {
|
|
||||||
constructor (expected, actual) {
|
|
||||||
super();
|
|
||||||
this.headers = expected.raw()[0];
|
|
||||||
this.expected = expected.hashes();
|
|
||||||
this.actual = actual;
|
|
||||||
this.diff = [];
|
|
||||||
this.hasErrors = false;
|
|
||||||
|
|
||||||
var good = 0, bad = 0;
|
|
||||||
|
|
||||||
this.expected.forEach((row, i) => {
|
|
||||||
var rowError = false;
|
|
||||||
|
|
||||||
for (var j in row) {
|
|
||||||
if (unescapeStr(row[j]) != actual[i][j]) {
|
|
||||||
rowError = true;
|
|
||||||
this.hasErrors = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rowError) {
|
|
||||||
bad++;
|
|
||||||
this.diff.push(Object.assign({}, row, {c_status: 'undefined'}));
|
|
||||||
this.diff.push(Object.assign({}, actual[i], {c_status: 'comment'}));
|
|
||||||
} else {
|
|
||||||
good++;
|
|
||||||
this.diff.push(row);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get string () {
|
|
||||||
if (!this.hasErrors) return null;
|
|
||||||
|
|
||||||
var s = ['Tables were not identical:'];
|
|
||||||
s.push(this.headers.map(key => ' ' + key).join(' | '));
|
|
||||||
this.diff.forEach((row) => {
|
|
||||||
var rowString = '| ';
|
|
||||||
this.headers.forEach((header) => {
|
|
||||||
if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | ');
|
|
||||||
else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | ');
|
|
||||||
else rowString += chalk.red('(+) ' + row[header] + ' | ');
|
|
||||||
});
|
|
||||||
s.push(rowString);
|
|
||||||
});
|
|
||||||
|
|
||||||
return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
var exceptions = require('./exception_classes');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.OSRMError = exceptions.OSRMError,
|
|
||||||
|
|
||||||
this.FileError = (code, msg) => new (exceptions.FileError.bind(exceptions.FileError, this.PREPROCESS_LOG_FILE))(code, msg);
|
|
||||||
|
|
||||||
this.LaunchError = (code, launchProcess, msg) => new (exceptions.LaunchError.bind(exceptions.LaunchError, this.ERROR_LOG_FILE))(code, launchProcess, msg);
|
|
||||||
|
|
||||||
this.ExtractError = (code, msg) => new (exceptions.ExtractError.bind(exceptions.ExtractError, this.PREPROCESS_LOG_FILE))(code, msg);
|
|
||||||
|
|
||||||
this.ContractError = (code, msg) => new (exceptions.ContractError.bind(exceptions.ContractError, this.PREPROCESS_LOG_FILE))(code, msg);
|
|
||||||
|
|
||||||
this.RoutedError = (msg) => new (exceptions.RoutedError.bind(exceptions.RoutedError, this.OSRM_ROUTED_LOG_FILE))(msg);
|
|
||||||
};
|
|
@ -1,43 +0,0 @@
|
|||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var crypto = require('crypto');
|
|
||||||
var d3 = require('d3-queue');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.hashOfFiles = (paths, cb) => {
|
|
||||||
paths = Array.isArray(paths) ? paths : [paths];
|
|
||||||
var shasum = crypto.createHash('sha1'), hashedFiles = false;
|
|
||||||
|
|
||||||
var q = d3.queue(1);
|
|
||||||
|
|
||||||
var addFile = (path, cb) => {
|
|
||||||
fs.readFile(path, (err, data) => {
|
|
||||||
if (err && err.code === 'ENOENT') cb(); // ignore non-existing files
|
|
||||||
else if (err) cb(err);
|
|
||||||
else {
|
|
||||||
shasum.update(data);
|
|
||||||
hashedFiles = true;
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
paths.forEach(path => { q.defer(addFile, path); });
|
|
||||||
|
|
||||||
q.awaitAll(err => {
|
|
||||||
if (err) throw new Error('*** Error reading files:', err);
|
|
||||||
if (!hashedFiles) throw new Error('*** No files found: [' + paths.join(', ') + ']');
|
|
||||||
cb(shasum.digest('hex'));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hashProfile = (cb) => {
|
|
||||||
this.hashOfFiles(path.resolve(this.PROFILES_PATH, this.profile + '.lua'), cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hashString = (str) => {
|
|
||||||
return crypto.createHash('sha1').update(str).digest('hex');
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
@ -1,36 +1,61 @@
|
|||||||
var util = require('util');
|
'use strict';
|
||||||
|
|
||||||
|
var d3 = require('d3-queue');
|
||||||
|
var path = require('path');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
|
var rimraf = require('rimraf');
|
||||||
|
var OSM = require('../lib/osm');
|
||||||
|
var OSRMLoader = require('../lib/osrm_loader');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.BeforeFeatures((features, callback) => {
|
this.registerHandler('BeforeFeatures', {timeout: 30000}, (features, callback) => {
|
||||||
this.pid = null;
|
this.osrmLoader = new OSRMLoader(this);
|
||||||
this.initializeEnv(() => {
|
this.OSMDB = new OSM.DB();
|
||||||
this.initializeOptions(callback);
|
|
||||||
});
|
let queue = d3.queue(1);
|
||||||
|
queue.defer(this.initializeEnv.bind(this));
|
||||||
|
queue.defer(this.verifyOSRMIsNotRunning.bind(this));
|
||||||
|
queue.defer(this.verifyExistenceOfBinaries.bind(this));
|
||||||
|
queue.defer(this.initializeCache.bind(this));
|
||||||
|
queue.defer(this.setupFeatures.bind(this, features));
|
||||||
|
queue.awaitAll(callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Before((scenario, callback) => {
|
this.BeforeFeature((feature, callback) => {
|
||||||
this.scenarioTitle = scenario.getName();
|
this.profile = this.DEFAULT_PROFILE;
|
||||||
|
this.profileFile = path.join(this.PROFILES_PATH, this.profile + '.lua');
|
||||||
this.loadMethod = this.DEFAULT_LOAD_METHOD;
|
this.setupFeatureCache(feature);
|
||||||
this.queryParams = {};
|
|
||||||
var d = new Date();
|
|
||||||
this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds());
|
|
||||||
this.resetData();
|
|
||||||
this.hasLoggedPreprocessInfo = false;
|
|
||||||
this.hasLoggedScenarioInfo = false;
|
|
||||||
this.setGridSize(this.DEFAULT_GRID_SIZE);
|
|
||||||
this.setOrigin(this.DEFAULT_ORIGIN);
|
|
||||||
this.fingerprintExtract = this.hashString([this.luaLibHash, this.binExtractHash].join('-'));
|
|
||||||
this.fingerprintContract = this.hashString(this.binContractHash);
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.Before((scenario, callback) => {
|
||||||
|
this.osrmLoader.setLoadMethod(this.DEFAULT_LOAD_METHOD);
|
||||||
|
this.setGridSize(this.DEFAULT_GRID_SIZE);
|
||||||
|
this.setOrigin(this.DEFAULT_ORIGIN);
|
||||||
|
this.queryParams = {};
|
||||||
|
this.extractArgs = '';
|
||||||
|
this.contractArgs = '';
|
||||||
|
this.environment = Object.assign(this.DEFAULT_ENVIRONMENT);
|
||||||
|
this.resetOSM();
|
||||||
|
|
||||||
|
this.scenarioID = this.getScenarioID(scenario);
|
||||||
|
this.setupScenarioCache(this.scenarioID);
|
||||||
|
|
||||||
|
// setup output logging
|
||||||
|
let logDir = path.join(this.LOGS_PATH, this.featureID);
|
||||||
|
this.scenarioLogFile = path.join(logDir, this.scenarioID) + '.log';
|
||||||
|
d3.queue(1)
|
||||||
|
.defer(mkdirp, logDir)
|
||||||
|
.defer(rimraf, this.scenarioLogFile)
|
||||||
|
.awaitAll(callback);
|
||||||
|
});
|
||||||
|
|
||||||
this.After((scenario, callback) => {
|
this.After((scenario, callback) => {
|
||||||
this.setExtractArgs('', () => {
|
this.resetOptionsOutput();
|
||||||
this.setContractArgs('', () => {
|
callback();
|
||||||
if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback);
|
});
|
||||||
else callback();
|
|
||||||
});
|
this.AfterFeatures((features, callback) => {
|
||||||
});
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,9 @@ module.exports = function () {
|
|||||||
return paramString;
|
return paramString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME this needs to be simplified!
|
||||||
|
// - remove usage of node-timeout
|
||||||
|
// - replace with node's native timout mechanism
|
||||||
this.sendRequest = (baseUri, parameters, callback) => {
|
this.sendRequest = (baseUri, parameters, callback) => {
|
||||||
var limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } });
|
var limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } });
|
||||||
|
|
||||||
@ -28,9 +31,9 @@ module.exports = function () {
|
|||||||
|
|
||||||
request(this.query, (err, res, body) => {
|
request(this.query, (err, res, body) => {
|
||||||
if (err && err.code === 'ECONNREFUSED') {
|
if (err && err.code === 'ECONNREFUSED') {
|
||||||
throw new Error('*** osrm-routed is not running.');
|
return cb(new Error('*** osrm-routed is not running.'));
|
||||||
} else if (err && err.statusCode === 408) {
|
} else if (err && err.statusCode === 408) {
|
||||||
throw new Error();
|
return cb(new Error());
|
||||||
}
|
}
|
||||||
|
|
||||||
return cb(err, res, body);
|
return cb(err, res, body);
|
||||||
@ -40,11 +43,10 @@ module.exports = function () {
|
|||||||
runRequest(limit((err, res, body) => {
|
runRequest(limit((err, res, body) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.statusCode === 408)
|
if (err.statusCode === 408)
|
||||||
return callback(this.RoutedError('*** osrm-routed did not respond'));
|
return callback(new Error('*** osrm-routed did not respond'));
|
||||||
else if (err.code === 'ECONNREFUSED')
|
else if (err.code === 'ECONNREFUSED')
|
||||||
return callback(this.RoutedError('*** osrm-routed is not running'));
|
return callback(new Error('*** osrm-routed is not running'));
|
||||||
}
|
}
|
||||||
//console.log(body+"\n");
|
|
||||||
return callback(err, res, body);
|
return callback(err, res, body);
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
var launchClasses = require('./launch_classes');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this._OSRMLoader = () => new (launchClasses._OSRMLoader.bind(launchClasses._OSRMLoader, this))();
|
|
||||||
};
|
|
@ -1,164 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var spawn = require('child_process').spawn;
|
|
||||||
var util = require('util');
|
|
||||||
var net = require('net');
|
|
||||||
var Timeout = require('node-timeout');
|
|
||||||
|
|
||||||
var OSRMBaseLoader = class {
|
|
||||||
constructor (scope) {
|
|
||||||
this.scope = scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
launch (callback) {
|
|
||||||
var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') });
|
|
||||||
|
|
||||||
var runLaunch = (cb) => {
|
|
||||||
this.osrmUp(() => { this.waitForConnection(cb); });
|
|
||||||
};
|
|
||||||
|
|
||||||
runLaunch(limit((e) => { if (e) callback(e); else callback(); }));
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown (callback) {
|
|
||||||
var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')});
|
|
||||||
|
|
||||||
var runShutdown = (cb) => {
|
|
||||||
this.osrmDown(cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
runShutdown(limit((e) => { if (e) callback(e); else callback(); }));
|
|
||||||
}
|
|
||||||
|
|
||||||
osrmIsRunning () {
|
|
||||||
return !!this.scope.pid && this.child && !this.child.killed;
|
|
||||||
}
|
|
||||||
|
|
||||||
osrmDown (callback) {
|
|
||||||
if (this.scope.pid) {
|
|
||||||
process.kill(this.scope.pid, this.scope.TERMSIGNAL);
|
|
||||||
this.waitForShutdown(callback);
|
|
||||||
this.scope.pid = null;
|
|
||||||
} else callback(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForConnection (callback) {
|
|
||||||
var retryCount = 0;
|
|
||||||
var connectWithRetry = () => {
|
|
||||||
net.connect({ port: this.scope.OSRM_PORT, host: '127.0.0.1' })
|
|
||||||
.on('connect', () => { callback(); })
|
|
||||||
.on('error', () => {
|
|
||||||
if (retryCount < 2) {
|
|
||||||
retryCount++;
|
|
||||||
setTimeout(connectWithRetry, 100);
|
|
||||||
} else {
|
|
||||||
callback(new Error('Could not connect to osrm-routed after three retires'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
connectWithRetry();
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForShutdown (callback) {
|
|
||||||
var check = () => {
|
|
||||||
if (!this.osrmIsRunning()) return callback();
|
|
||||||
};
|
|
||||||
setTimeout(check, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var OSRMDirectLoader = class extends OSRMBaseLoader {
|
|
||||||
constructor (scope) {
|
|
||||||
super(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
load (inputFile, callback) {
|
|
||||||
this.inputFile = inputFile;
|
|
||||||
this.shutdown(() => {
|
|
||||||
this.launch(callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
osrmUp (callback) {
|
|
||||||
if (this.scope.pid) return callback();
|
|
||||||
var writeToLog = (data) => {
|
|
||||||
fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
|
|
||||||
};
|
|
||||||
|
|
||||||
var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)]);
|
|
||||||
this.scope.pid = child.pid;
|
|
||||||
child.stdout.on('data', writeToLog);
|
|
||||||
child.stderr.on('data', writeToLog);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var OSRMDatastoreLoader = class extends OSRMBaseLoader {
|
|
||||||
constructor (scope) {
|
|
||||||
super(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
load (inputFile, callback) {
|
|
||||||
this.inputFile = inputFile;
|
|
||||||
this.loadData((err) => {
|
|
||||||
if (err) return callback(err);
|
|
||||||
if (!this.scope.pid) return this.launch(callback);
|
|
||||||
else callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData (callback) {
|
|
||||||
this.scope.runBin('osrm-datastore', this.inputFile, (err) => {
|
|
||||||
if (err) return callback(this.scope.LaunchError(this.exitCode, 'datastore', err));
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
osrmUp (callback) {
|
|
||||||
if (this.scope.pid) return callback();
|
|
||||||
var writeToLog = (data) => {
|
|
||||||
fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
|
|
||||||
};
|
|
||||||
|
|
||||||
var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)]);
|
|
||||||
this.child = child;
|
|
||||||
this.scope.pid = child.pid;
|
|
||||||
child.stdout.on('data', writeToLog);
|
|
||||||
child.stderr.on('data', writeToLog);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
_OSRMLoader: class {
|
|
||||||
constructor (scope) {
|
|
||||||
this.scope = scope;
|
|
||||||
this.loader = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
load (inputFile, callback) {
|
|
||||||
var method = this.scope.loadMethod;
|
|
||||||
if (method === 'datastore') {
|
|
||||||
this.loader = new OSRMDatastoreLoader(this.scope);
|
|
||||||
this.loader.load(inputFile, callback);
|
|
||||||
} else if (method === 'directly') {
|
|
||||||
this.loader = new OSRMDirectLoader(this.scope);
|
|
||||||
this.loader.load(inputFile, callback);
|
|
||||||
} else {
|
|
||||||
callback(new Error('*** Unknown load method ' + method));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown (callback) {
|
|
||||||
this.loader.shutdown(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
up () {
|
|
||||||
return this.loader ? this.loader.osrmIsRunning() : false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,90 +0,0 @@
|
|||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.clearLogFiles = (callback) => {
|
|
||||||
// emptying existing files, rather than deleting and writing new ones makes it
|
|
||||||
// easier to use tail -f from the command line
|
|
||||||
fs.writeFile(this.OSRM_ROUTED_LOG_FILE, '', err => {
|
|
||||||
if (err) throw err;
|
|
||||||
fs.writeFile(this.PREPROCESS_LOG_FILE, '', err => {
|
|
||||||
if (err) throw err;
|
|
||||||
fs.writeFile(this.LOG_FILE, '', err => {
|
|
||||||
if (err) throw err;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var log = this.log = (s, type) => {
|
|
||||||
s = s || '';
|
|
||||||
type = type || null;
|
|
||||||
var file = type === 'preprocess' ? this.PREPROCESS_LOG_FILE : this.LOG_FILE;
|
|
||||||
fs.appendFile(file, s + '\n', err => {
|
|
||||||
if (err) throw err;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logScenarioFailInfo = () => {
|
|
||||||
if (this.hasLoggedScenarioInfo) return;
|
|
||||||
|
|
||||||
log('=========================================');
|
|
||||||
log('Failed scenario: ' + this.scenarioTitle);
|
|
||||||
log('Time: ' + this.scenarioTime);
|
|
||||||
log('Fingerprint osm stage: ' + this.osmData.fingerprintOSM);
|
|
||||||
log('Fingerprint extract stage: ' + this.fingerprintExtract);
|
|
||||||
log('Fingerprint contract stage: ' + this.fingerprintContract);
|
|
||||||
log('Fingerprint route stage: ' + this.fingerprintRoute);
|
|
||||||
log('Profile: ' + this.profile);
|
|
||||||
log();
|
|
||||||
log('```xml'); // so output can be posted directly to github comment fields
|
|
||||||
log(this.osmData.str.trim());
|
|
||||||
log('```');
|
|
||||||
log();
|
|
||||||
log();
|
|
||||||
|
|
||||||
this.hasLoggedScenarioInfo = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logFail = (expected, got, attempts) => {
|
|
||||||
this.logScenarioFailInfo();
|
|
||||||
log('== ');
|
|
||||||
log('Expected: ' + JSON.stringify(expected));
|
|
||||||
log('Got: ' + JSON.stringify(got));
|
|
||||||
log();
|
|
||||||
['route','forw','backw'].forEach((direction) => {
|
|
||||||
if (attempts[direction]) {
|
|
||||||
log('Direction: ' + direction);
|
|
||||||
log('Query: ' + attempts[direction].query);
|
|
||||||
log('Response: ' + attempts[direction].response.body);
|
|
||||||
log();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logPreprocessInfo = () => {
|
|
||||||
if (this.hasLoggedPreprocessInfo) return;
|
|
||||||
log('=========================================', 'preprocess');
|
|
||||||
log('Preprocessing data for scenario: ' + this.scenarioTitle, 'preprocess');
|
|
||||||
log('Time: ' + this.scenarioTime, 'preprocess');
|
|
||||||
log('', 'preprocess');
|
|
||||||
log('== OSM data:', 'preprocess');
|
|
||||||
log('```xml', 'preprocess'); // so output can be posted directly to github comment fields
|
|
||||||
log(this.osmData.str, 'preprocess');
|
|
||||||
log('```', 'preprocess');
|
|
||||||
log('', 'preprocess');
|
|
||||||
log('== Profile:', 'preprocess');
|
|
||||||
log(this.profile, 'preprocess');
|
|
||||||
log('', 'preprocess');
|
|
||||||
this.hasLoggedPreprocessInfo = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logPreprocess = (str) => {
|
|
||||||
this.logPreprocessInfo();
|
|
||||||
log(str, 'preprocess');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logPreprocessDone = () => {
|
|
||||||
log('Done with preprocessing at ' + new Date(), 'preprocess');
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,7 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Timeout = require('node-timeout');
|
const Timeout = require('node-timeout');
|
||||||
var request = require('request');
|
const request = require('request');
|
||||||
|
const ensureDecimal = require('../lib/utils').ensureDecimal;
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.requestPath = (service, params, callback) => {
|
this.requestPath = (service, params, callback) => {
|
||||||
@ -42,7 +43,7 @@ module.exports = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var encodeWaypoints = (waypoints) => {
|
var encodeWaypoints = (waypoints) => {
|
||||||
return waypoints.map(w => [w.lon, w.lat].map(this.ensureDecimal).join(','));
|
return waypoints.map(w => [w.lon, w.lat].map(ensureDecimal).join(','));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.requestRoute = (waypoints, bearings, userParams, callback) => {
|
this.requestRoute = (waypoints, bearings, userParams, callback) => {
|
||||||
|
@ -1,40 +1,52 @@
|
|||||||
var fs = require('fs');
|
'use strict';
|
||||||
var util = require('util');
|
|
||||||
var exec = require('child_process').exec;
|
const fs = require('fs');
|
||||||
|
const util = require('util');
|
||||||
|
const child_process = require('child_process');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.runBin = (bin, options, callback) => {
|
// replaces placeholders for in user supplied commands
|
||||||
var opts = options.slice();
|
this.expandOptions = (options) => {
|
||||||
|
let opts = options.slice();
|
||||||
|
let table = {
|
||||||
|
'{osm_file}': this.inputCacheFile,
|
||||||
|
'{processed_file}': this.processedCacheFile,
|
||||||
|
'{profile_file}': this.profileFile,
|
||||||
|
'{rastersource_file}': this.rasterCacheFile,
|
||||||
|
'{speeds_file}': this.speedsCacheFile,
|
||||||
|
'{penalties_file}': this.penaltiesCacheFile
|
||||||
|
};
|
||||||
|
|
||||||
if (opts.match('{osm_base}')) {
|
for (let k in table) {
|
||||||
if (!this.osmData.osmFile) throw new Error('*** {osm_base} is missing');
|
opts = opts.replace(k, table[k]);
|
||||||
opts = opts.replace('{osm_base}', this.osmData.osmFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.match('{extracted_base}')) {
|
return opts;
|
||||||
if (!this.osmData.extractedFile) throw new Error('*** {extracted_base} is missing');
|
};
|
||||||
opts = opts.replace('{extracted_base}', this.osmData.extractedFile);
|
|
||||||
|
this.setupOutputLog = (process, log) => {
|
||||||
|
if (process.logFunc) {
|
||||||
|
process.stdout.removeListener('data', process.logFunc);
|
||||||
|
process.stderr.removeListener('data', process.logFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.match('{contracted_base}')) {
|
process.logFunc = (message) => { log.write(message); };
|
||||||
if (!this.osmData.contractedFile) throw new Error('*** {contracted_base} is missing');
|
process.stdout.on('data', process.logFunc);
|
||||||
opts = opts.replace('{contracted_base}', this.osmData.contractedFile);
|
process.stderr.on('data', process.logFunc);
|
||||||
}
|
};
|
||||||
|
|
||||||
if (opts.match('{profile}')) {
|
this.runBin = (bin, options, env, callback) => {
|
||||||
opts = opts.replace('{profile}', [this.PROFILES_PATH, this.profile + '.lua'].join('/'));
|
let cmd = util.format('%s%s/%s%s%s', this.QQ, this.BIN_PATH, bin, this.EXE, this.QQ);
|
||||||
}
|
let opts = options.split(' ').filter((x) => { return x && x.length > 0; });
|
||||||
|
let log = fs.createWriteStream(this.scenarioLogFile, {'flags': 'a'});
|
||||||
var cmd = util.format('%s%s/%s%s%s %s 2>%s', this.QQ, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE);
|
log.write(util.format('*** running %s %s\n', cmd, options));
|
||||||
process.chdir(this.TEST_FOLDER);
|
// we need to set a large maxbuffer here because we have long running processes like osrm-routed
|
||||||
exec(cmd, (err, stdout, stderr) => {
|
// with lots of log output
|
||||||
this.stdout = stdout.toString();
|
let child = child_process.execFile(cmd, opts, {maxBuffer: 1024 * 1024 * 1000, env: env}, callback);
|
||||||
fs.readFile(this.ERROR_LOG_FILE, (e, data) => {
|
child.on('exit', function(code) {
|
||||||
this.stderr = data ? data.toString() : '';
|
log.write(util.format('*** %s exited with code %d\n', bin, code));
|
||||||
this.exitCode = err && err.code || 0;
|
}.bind(this));
|
||||||
process.chdir('../');
|
this.setupOutputLog(child, log);
|
||||||
callback(err, stdout, stderr);
|
return child;
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,7 @@ module.exports = function () {
|
|||||||
if (headers.has('distance')) {
|
if (headers.has('distance')) {
|
||||||
if (row.distance.length) {
|
if (row.distance.length) {
|
||||||
if (!row.distance.match(/\d+m/))
|
if (!row.distance.match(/\d+m/))
|
||||||
throw new Error('*** Distance must be specified in meters. (ex: 250m)');
|
return cb(new Error('*** Distance must be specified in meters. (ex: 250m)'));
|
||||||
got.distance = instructions ? util.format('%dm', distance) : '';
|
got.distance = instructions ? util.format('%dm', distance) : '';
|
||||||
} else {
|
} else {
|
||||||
got.distance = '';
|
got.distance = '';
|
||||||
@ -102,7 +102,7 @@ module.exports = function () {
|
|||||||
|
|
||||||
if (headers.has('time')) {
|
if (headers.has('time')) {
|
||||||
if (!row.time.match(/\d+s/))
|
if (!row.time.match(/\d+s/))
|
||||||
throw new Error('*** Time must be specied in seconds. (ex: 60s)');
|
return cb(new Error('*** Time must be specied in seconds. (ex: 60s)'));
|
||||||
got.time = instructions ? util.format('%ds', time) : '';
|
got.time = instructions ? util.format('%ds', time) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ module.exports = function () {
|
|||||||
if (headers.has('speed')) {
|
if (headers.has('speed')) {
|
||||||
if (row.speed !== '' && instructions) {
|
if (row.speed !== '' && instructions) {
|
||||||
if (!row.speed.match(/\d+ km\/h/))
|
if (!row.speed.match(/\d+ km\/h/))
|
||||||
throw new Error('*** Speed must be specied in km/h. (ex: 50 km/h)');
|
cb(new Error('*** Speed must be specied in km/h. (ex: 50 km/h)'));
|
||||||
var speed = time > 0 ? Math.round(3.6*distance/time) : null;
|
var speed = time > 0 ? Math.round(3.6*distance/time) : null;
|
||||||
got.speed = util.format('%d km/h', speed);
|
got.speed = util.format('%d km/h', speed);
|
||||||
} else {
|
} else {
|
||||||
@ -139,20 +139,12 @@ module.exports = function () {
|
|||||||
putValue('destinations', destinations);
|
putValue('destinations', destinations);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ok = true;
|
|
||||||
|
|
||||||
for (var key in row) {
|
for (var key in row) {
|
||||||
if (this.FuzzyMatch.match(got[key], row[key])) {
|
if (this.FuzzyMatch.match(got[key], row[key])) {
|
||||||
got[key] = row[key];
|
got[key] = row[key];
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
this.logFail(row, got, { route: { query: this.query, response: res }});
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(null, got);
|
cb(null, got);
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('request failed to return valid body'));
|
cb(new Error('request failed to return valid body'));
|
||||||
@ -189,11 +181,11 @@ module.exports = function () {
|
|||||||
|
|
||||||
if (row.from && row.to) {
|
if (row.from && row.to) {
|
||||||
var fromNode = this.findNodeByName(row.from);
|
var fromNode = this.findNodeByName(row.from);
|
||||||
if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"'), row.from);
|
if (!fromNode) return cb(new Error(util.format('*** unknown from-node "%s"'), row.from));
|
||||||
waypoints.push(fromNode);
|
waypoints.push(fromNode);
|
||||||
|
|
||||||
var toNode = this.findNodeByName(row.to);
|
var toNode = this.findNodeByName(row.to);
|
||||||
if (!toNode) throw new Error(util.format('*** unknown to-node "%s"'), row.to);
|
if (!toNode) return cb(new Error(util.format('*** unknown to-node "%s"'), row.to));
|
||||||
waypoints.push(toNode);
|
waypoints.push(toNode);
|
||||||
|
|
||||||
got.from = row.from;
|
got.from = row.from;
|
||||||
@ -202,13 +194,13 @@ module.exports = function () {
|
|||||||
} else if (row.waypoints) {
|
} else if (row.waypoints) {
|
||||||
row.waypoints.split(',').forEach((n) => {
|
row.waypoints.split(',').forEach((n) => {
|
||||||
var node = this.findNodeByName(n.trim());
|
var node = this.findNodeByName(n.trim());
|
||||||
if (!node) throw new Error('*** unknown waypoint node "%s"', n.trim());
|
if (!node) return cb(new Error('*** unknown waypoint node "%s"', n.trim()));
|
||||||
waypoints.push(node);
|
waypoints.push(node);
|
||||||
});
|
});
|
||||||
got.waypoints = row.waypoints;
|
got.waypoints = row.waypoints;
|
||||||
this.requestRoute(waypoints, bearings, params, afterRequest);
|
this.requestRoute(waypoints, bearings, params, afterRequest);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('*** no waypoints');
|
return cb(new Error('*** no waypoints'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
var DifferentError = require('./exception_classes').TableDiffError;
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
this.diffTables = (expected, actual, options, callback) => {
|
|
||||||
// this is a temp workaround while waiting for https://github.com/cucumber/cucumber-js/issues/534
|
|
||||||
|
|
||||||
var error = new DifferentError(expected, actual);
|
|
||||||
|
|
||||||
return callback(error.string);
|
|
||||||
};
|
|
||||||
};
|
|
@ -11,8 +11,8 @@ Feature: Handle bad data in a graceful manner
|
|||||||
Given the ways
|
Given the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
|
|
||||||
When the data has been contracted
|
When I try to run "osrm-extract {osm_file} --profile {profile_file}"
|
||||||
Then "osrm-extract" should return code 1
|
Then it should exit with an error
|
||||||
|
|
||||||
Scenario: Only dead-end oneways
|
Scenario: Only dead-end oneways
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
@routing @testbot @bug
|
|
||||||
Feature: Known bugs
|
|
||||||
|
|
||||||
Background:
|
|
||||||
Given the profile "testbot"
|
|
@ -124,7 +124,7 @@ Feature: Basic Map Matching
|
|||||||
1,2,36
|
1,2,36
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the contract extra arguments "--segment-speed-file speeds.csv"
|
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||||
|
|
||||||
When I match I should get
|
When I match I should get
|
||||||
| trace | matchings | annotation |
|
| trace | matchings | annotation |
|
||||||
|
@ -26,7 +26,7 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap
|
|||||||
1,2,5,0,comment
|
1,2,5,0,comment
|
||||||
3,4,7,-20
|
3,4,7,-20
|
||||||
"""
|
"""
|
||||||
And the contract extra arguments "--turn-penalty-file penalties.csv"
|
And the contract extra arguments "--turn-penalty-file {penalties_file}"
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | speed | time |
|
| from | to | route | speed | time |
|
||||||
| a | e | ab,be,be | 36 km/h | 40s +-1 |
|
| a | e | ab,be,be | 36 km/h | 40s +-1 |
|
||||||
|
@ -173,7 +173,7 @@ Feature: Via points
|
|||||||
| c,d,a | abc,bd,bd,bd,abc,abc |
|
| c,d,a | abc,bd,bd,bd,abc,abc |
|
||||||
|
|
||||||
# See issue #2349
|
# See issue #2349
|
||||||
@bug @todo
|
@todo
|
||||||
Scenario: Via point at a dead end with oneway
|
Scenario: Via point at a dead end with oneway
|
||||||
Given the node map
|
Given the node map
|
||||||
| a | b | c |
|
| a | b | c |
|
||||||
|
@ -51,6 +51,7 @@ namespace engine
|
|||||||
* - Route
|
* - Route
|
||||||
* - Table
|
* - Table
|
||||||
* - Match
|
* - Match
|
||||||
|
* - Nearest
|
||||||
*
|
*
|
||||||
* In addition, shared memory can be used for datasets loaded with osrm-datastore.
|
* In addition, shared memory can be used for datasets loaded with osrm-datastore.
|
||||||
*
|
*
|
||||||
@ -65,6 +66,7 @@ struct EngineConfig final
|
|||||||
int max_locations_viaroute = -1;
|
int max_locations_viaroute = -1;
|
||||||
int max_locations_distance_table = -1;
|
int max_locations_distance_table = -1;
|
||||||
int max_locations_map_matching = -1;
|
int max_locations_map_matching = -1;
|
||||||
|
int max_results_nearest = -1;
|
||||||
bool use_shared_memory = true;
|
bool use_shared_memory = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||||
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||||
|
|
||||||
#include "engine/datafacade/datafacade_base.hpp"
|
|
||||||
#include "engine/guidance/leg_geometry.hpp"
|
#include "engine/guidance/leg_geometry.hpp"
|
||||||
#include "engine/guidance/route_step.hpp"
|
#include "engine/guidance/route_step.hpp"
|
||||||
#include "engine/phantom_node.hpp"
|
#include "engine/phantom_node.hpp"
|
||||||
|
@ -15,9 +15,12 @@ namespace plugins
|
|||||||
class NearestPlugin final : public BasePlugin
|
class NearestPlugin final : public BasePlugin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NearestPlugin(datafacade::BaseDataFacade &facade);
|
explicit NearestPlugin(datafacade::BaseDataFacade &facade, const int max_results);
|
||||||
|
|
||||||
Status HandleRequest(const api::NearestParameters ¶ms, util::json::Object &result);
|
Status HandleRequest(const api::NearestParameters ¶ms, util::json::Object &result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int max_results;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,6 @@ class ExtractionContainers
|
|||||||
void WriteNodes(std::ofstream &file_out_stream) const;
|
void WriteNodes(std::ofstream &file_out_stream) const;
|
||||||
void WriteRestrictions(const std::string &restrictions_file_name) const;
|
void WriteRestrictions(const std::string &restrictions_file_name) const;
|
||||||
void WriteEdges(std::ofstream &file_out_stream) const;
|
void WriteEdges(std::ofstream &file_out_stream) const;
|
||||||
void
|
|
||||||
WriteTurnLaneMasks(const std::string &file_name,
|
|
||||||
const stxxl::vector<std::uint32_t> &turn_lane_offsets,
|
|
||||||
const stxxl::vector<guidance::TurnLaneType::Mask> &turn_lane_masks) const;
|
|
||||||
void WriteCharData(const std::string &file_name);
|
void WriteCharData(const std::string &file_name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -18,14 +18,17 @@ class Way;
|
|||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
template <> struct hash<std::tuple<std::string, std::string, std::string>>
|
template <> struct hash<std::tuple<std::string, std::string, std::string, std::string>>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const std::tuple<std::string, std::string, std::string> &mk) const noexcept
|
std::size_t
|
||||||
|
operator()(const std::tuple<std::string, std::string, std::string, std::string> &mk) const
|
||||||
|
noexcept
|
||||||
{
|
{
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
boost::hash_combine(seed, std::get<0>(mk));
|
boost::hash_combine(seed, std::get<0>(mk));
|
||||||
boost::hash_combine(seed, std::get<1>(mk));
|
boost::hash_combine(seed, std::get<1>(mk));
|
||||||
boost::hash_combine(seed, std::get<2>(mk));
|
boost::hash_combine(seed, std::get<2>(mk));
|
||||||
|
boost::hash_combine(seed, std::get<3>(mk));
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -51,8 +54,9 @@ struct ExtractionWay;
|
|||||||
class ExtractorCallbacks
|
class ExtractorCallbacks
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// used to deduplicate street names and street destinations: actually maps to name ids
|
// used to deduplicate street names, refs, destinations, pronunciation: actually maps to name
|
||||||
using MapKey = std::tuple<std::string, std::string, std::string>;
|
// ids
|
||||||
|
using MapKey = std::tuple<std::string, std::string, std::string, std::string>;
|
||||||
using MapVal = unsigned;
|
using MapVal = unsigned;
|
||||||
std::unordered_map<MapKey, MapVal> string_map;
|
std::unordered_map<MapKey, MapVal> string_map;
|
||||||
guidance::LaneDescriptionMap lane_description_map;
|
guidance::LaneDescriptionMap lane_description_map;
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include "extractor/compressed_edge_container.hpp"
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
#include "extractor/suffix_table.hpp"
|
|
||||||
|
|
||||||
#include "extractor/guidance/discrete_angle.hpp"
|
#include "extractor/guidance/discrete_angle.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
@ -26,7 +25,6 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/tokenizer.hpp>
|
#include <boost/tokenizer.hpp>
|
||||||
|
|
||||||
@ -151,122 +149,6 @@ getRepresentativeCoordinate(const NodeID from_node,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::pair<std::string, std::string> getPrefixAndSuffix(const std::string &data)
|
|
||||||
{
|
|
||||||
const auto suffix_pos = data.find_last_of(' ');
|
|
||||||
if (suffix_pos == std::string::npos)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const auto prefix_pos = data.find_first_of(' ');
|
|
||||||
auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1));
|
|
||||||
boost::to_lower(result.first);
|
|
||||||
boost::to_lower(result.second);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// FIXME, handle in profile to begin with?
|
|
||||||
// this uses the encoding of references in the profile, which is very BAD
|
|
||||||
// Input for this function should be a struct separating streetname, suffix (e.g. road,
|
|
||||||
// boulevard, North, West ...), and a list of references
|
|
||||||
std::string from_name;
|
|
||||||
std::string from_ref;
|
|
||||||
std::string to_name;
|
|
||||||
std::string to_ref;
|
|
||||||
|
|
||||||
// Split from the format "{name} ({ref})" -> name, ref
|
|
||||||
auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) {
|
|
||||||
const auto ref_begin = name.find_first_of('(');
|
|
||||||
if (ref_begin != std::string::npos)
|
|
||||||
{
|
|
||||||
if (ref_begin != 0)
|
|
||||||
out_name = name.substr(0, ref_begin - 1);
|
|
||||||
const auto ref_end = name.find_first_of(')');
|
|
||||||
out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out_name = name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
split(from, from_name, from_ref);
|
|
||||||
split(to, to_name, to_ref);
|
|
||||||
|
|
||||||
// check similarity of names
|
|
||||||
const auto names_are_empty = from_name.empty() && to_name.empty();
|
|
||||||
const auto name_is_contained =
|
|
||||||
boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
|
|
||||||
|
|
||||||
const auto checkForPrefixOrSuffixChange = [](
|
|
||||||
const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
|
|
||||||
|
|
||||||
const auto first_prefix_and_suffixes = getPrefixAndSuffix(first);
|
|
||||||
const auto second_prefix_and_suffixes = getPrefixAndSuffix(second);
|
|
||||||
// reverse strings, get suffices and reverse them to get prefixes
|
|
||||||
const auto checkTable = [&](const std::string &str) {
|
|
||||||
return str.empty() || suffix_table.isSuffix(str);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto getOffset = [](const std::string &str) -> std::size_t {
|
|
||||||
if (str.empty())
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return str.length() + 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool is_prefix_change = [&]() -> bool {
|
|
||||||
if (!checkTable(first_prefix_and_suffixes.first))
|
|
||||||
return false;
|
|
||||||
if (!checkTable(second_prefix_and_suffixes.first))
|
|
||||||
return false;
|
|
||||||
return !first.compare(getOffset(first_prefix_and_suffixes.first),
|
|
||||||
std::string::npos,
|
|
||||||
second,
|
|
||||||
getOffset(second_prefix_and_suffixes.first),
|
|
||||||
std::string::npos);
|
|
||||||
}();
|
|
||||||
|
|
||||||
const bool is_suffix_change = [&]() -> bool {
|
|
||||||
if (!checkTable(first_prefix_and_suffixes.second))
|
|
||||||
return false;
|
|
||||||
if (!checkTable(second_prefix_and_suffixes.second))
|
|
||||||
return false;
|
|
||||||
return !first.compare(0,
|
|
||||||
first.length() - getOffset(first_prefix_and_suffixes.second),
|
|
||||||
second,
|
|
||||||
0,
|
|
||||||
second.length() - getOffset(second_prefix_and_suffixes.second));
|
|
||||||
}();
|
|
||||||
|
|
||||||
return is_prefix_change || is_suffix_change;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table);
|
|
||||||
const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change;
|
|
||||||
const auto name_is_removed = !from_name.empty() && to_name.empty();
|
|
||||||
// references are contained in one another
|
|
||||||
const auto refs_are_empty = from_ref.empty() && to_ref.empty();
|
|
||||||
const auto ref_is_contained =
|
|
||||||
from_ref.empty() || to_ref.empty() ||
|
|
||||||
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
|
|
||||||
const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
|
|
||||||
|
|
||||||
const auto obvious_change =
|
|
||||||
(names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) ||
|
|
||||||
(names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) ||
|
|
||||||
(names_are_equal && ref_is_removed) || is_suffix_change;
|
|
||||||
|
|
||||||
return !obvious_change;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection
|
// To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection
|
||||||
// handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand
|
// handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand
|
||||||
// turn and vice versa.
|
// turn and vice versa.
|
||||||
|
@ -25,6 +25,7 @@ class SuffixTable final
|
|||||||
private:
|
private:
|
||||||
std::unordered_set<std::string> suffix_set;
|
std::unordered_set<std::string> suffix_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace extractor */
|
} /* namespace extractor */
|
||||||
} /* namespace osrm */
|
} /* namespace osrm */
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ inline bool CheckInBounds(const int A, const int B, const int range)
|
|||||||
|
|
||||||
if (range >= 180)
|
if (range >= 180)
|
||||||
return true;
|
return true;
|
||||||
if (range <= 0)
|
if (range < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Map both bearings into positive modulo 360 space
|
// Map both bearings into positive modulo 360 space
|
||||||
|
@ -12,8 +12,13 @@
|
|||||||
#include "util/simple_logger.hpp"
|
#include "util/simple_logger.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
namespace util
|
namespace util
|
||||||
@ -113,7 +118,7 @@ inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruct
|
|||||||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
|
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
|
||||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
|
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
|
||||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
|
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
|
||||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
|
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
|
inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
|
||||||
@ -131,6 +136,128 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc
|
|||||||
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
|
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name Change Logic
|
||||||
|
// Used both during Extraction as well as during Post-Processing
|
||||||
|
|
||||||
|
inline std::pair<std::string, std::string> getPrefixAndSuffix(const std::string &data)
|
||||||
|
{
|
||||||
|
const auto suffix_pos = data.find_last_of(' ');
|
||||||
|
if (suffix_pos == std::string::npos)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto prefix_pos = data.find_first_of(' ');
|
||||||
|
auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1));
|
||||||
|
boost::to_lower(result.first);
|
||||||
|
boost::to_lower(result.second);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: there is an overload without suffix checking below.
|
||||||
|
// (that's the reason we template the suffix table here)
|
||||||
|
template <typename SuffixTable>
|
||||||
|
inline bool requiresNameAnnounced(const std::string &from_name,
|
||||||
|
const std::string &from_ref,
|
||||||
|
const std::string &to_name,
|
||||||
|
const std::string &to_ref,
|
||||||
|
const SuffixTable &suffix_table)
|
||||||
|
{
|
||||||
|
// first is empty and the second is not
|
||||||
|
if ((from_name.empty() && from_ref.empty()) && !(to_name.empty() && to_ref.empty()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// FIXME, handle in profile to begin with?
|
||||||
|
// Input for this function should be a struct separating streetname, suffix (e.g. road,
|
||||||
|
// boulevard, North, West ...), and a list of references
|
||||||
|
|
||||||
|
// check similarity of names
|
||||||
|
const auto names_are_empty = from_name.empty() && to_name.empty();
|
||||||
|
const auto name_is_contained =
|
||||||
|
boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
|
||||||
|
|
||||||
|
const auto checkForPrefixOrSuffixChange = [](
|
||||||
|
const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
|
||||||
|
|
||||||
|
const auto first_prefix_and_suffixes = getPrefixAndSuffix(first);
|
||||||
|
const auto second_prefix_and_suffixes = getPrefixAndSuffix(second);
|
||||||
|
|
||||||
|
// reverse strings, get suffices and reverse them to get prefixes
|
||||||
|
const auto checkTable = [&](const std::string &str) {
|
||||||
|
return str.empty() || suffix_table.isSuffix(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto getOffset = [](const std::string &str) -> std::size_t {
|
||||||
|
if (str.empty())
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return str.length() + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool is_prefix_change = [&]() -> bool {
|
||||||
|
if (!checkTable(first_prefix_and_suffixes.first))
|
||||||
|
return false;
|
||||||
|
if (!checkTable(second_prefix_and_suffixes.first))
|
||||||
|
return false;
|
||||||
|
return !first.compare(getOffset(first_prefix_and_suffixes.first),
|
||||||
|
std::string::npos,
|
||||||
|
second,
|
||||||
|
getOffset(second_prefix_and_suffixes.first),
|
||||||
|
std::string::npos);
|
||||||
|
}();
|
||||||
|
|
||||||
|
const bool is_suffix_change = [&]() -> bool {
|
||||||
|
if (!checkTable(first_prefix_and_suffixes.second))
|
||||||
|
return false;
|
||||||
|
if (!checkTable(second_prefix_and_suffixes.second))
|
||||||
|
return false;
|
||||||
|
return !first.compare(0,
|
||||||
|
first.length() - getOffset(first_prefix_and_suffixes.second),
|
||||||
|
second,
|
||||||
|
0,
|
||||||
|
second.length() - getOffset(second_prefix_and_suffixes.second));
|
||||||
|
}();
|
||||||
|
|
||||||
|
return is_prefix_change || is_suffix_change;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table);
|
||||||
|
const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change;
|
||||||
|
const auto name_is_removed = !from_name.empty() && to_name.empty();
|
||||||
|
// references are contained in one another
|
||||||
|
const auto refs_are_empty = from_ref.empty() && to_ref.empty();
|
||||||
|
const auto ref_is_contained =
|
||||||
|
from_ref.empty() || to_ref.empty() ||
|
||||||
|
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
|
||||||
|
const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
|
||||||
|
|
||||||
|
const auto obvious_change =
|
||||||
|
(names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) ||
|
||||||
|
(names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) ||
|
||||||
|
(names_are_equal && ref_is_removed) || is_suffix_change;
|
||||||
|
|
||||||
|
const auto needs_announce =
|
||||||
|
// " (Ref)" -> "Name "
|
||||||
|
(from_name.empty() && !from_ref.empty() && !to_name.empty() && to_ref.empty());
|
||||||
|
|
||||||
|
return !obvious_change || needs_announce;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overload without suffix checking
|
||||||
|
inline bool requiresNameAnnounced(const std::string &from_name,
|
||||||
|
const std::string &from_ref,
|
||||||
|
const std::string &to_name,
|
||||||
|
const std::string &to_ref)
|
||||||
|
{
|
||||||
|
// Dummy since we need to provide a SuffixTable but do not have the data for it.
|
||||||
|
// (Guidance Post-Processing does not keep the suffix table around at the moment)
|
||||||
|
struct NopSuffixTable final
|
||||||
|
{
|
||||||
|
NopSuffixTable(){}
|
||||||
|
bool isSuffix(const std::string &) const { return false; }
|
||||||
|
} static const table;
|
||||||
|
|
||||||
|
return requiresNameAnnounced(from_name, from_ref, to_name, to_ref, table);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
@ -23,7 +23,13 @@ class NameTable
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
NameTable(const std::string &filename);
|
NameTable(const std::string &filename);
|
||||||
|
|
||||||
|
// This class provides a limited view over all the string data we serialize out.
|
||||||
|
// The following functions are a subset of what is available.
|
||||||
|
// See the data facades for they provide full access to this serialized string data.
|
||||||
|
// (at time of writing this: get{Name,Ref,Pronunciation,Destinations}ForID(name_id);)
|
||||||
std::string GetNameForID(const unsigned name_id) const;
|
std::string GetNameForID(const unsigned name_id) const;
|
||||||
|
std::string GetRefForID(const unsigned name_id) const;
|
||||||
};
|
};
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
#include <boost/iostreams/device/mapped_file.hpp>
|
#include <boost/iostreams/device/mapped_file.hpp>
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"cucumber": "^1.2.1",
|
"cucumber": "^1.2.1",
|
||||||
"d3-queue": "^2.0.3",
|
"d3-queue": "^2.0.3",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
"node-timeout": "0.0.4",
|
"node-timeout": "0.0.4",
|
||||||
"polyline": "^0.2.0",
|
"polyline": "^0.2.0",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
|
"rimraf": "^2.5.4",
|
||||||
"xmlbuilder": "^4.2.1"
|
"xmlbuilder": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -238,6 +238,10 @@ function way_function (way, result)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- default to driving mode, may get overwritten below
|
||||||
|
result.forward_mode = mode.driving
|
||||||
|
result.backward_mode = mode.driving
|
||||||
|
|
||||||
-- we dont route over areas
|
-- we dont route over areas
|
||||||
local area = way:get_value_by_key("area")
|
local area = way:get_value_by_key("area")
|
||||||
if ignore_areas and area and "yes" == area then
|
if ignore_areas and area and "yes" == area then
|
||||||
@ -245,10 +249,58 @@ function way_function (way, result)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- respect user-preference for HOV-only ways
|
-- respect user-preference for HOV-only ways
|
||||||
local hov = way:get_value_by_key("hov")
|
if ignore_hov_ways then
|
||||||
if ignore_hov_ways and hov and "designated" == hov then
|
local hov = way:get_value_by_key("hov")
|
||||||
return
|
if hov and "designated" == hov then
|
||||||
end
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- also respect user-preference for HOV-only ways when all lanes are HOV-designated
|
||||||
|
local function has_all_designated_hov_lanes(lanes)
|
||||||
|
local all = true
|
||||||
|
for lane in lanes:gmatch("(%w+)") do
|
||||||
|
if lane and lane ~= "designated" then
|
||||||
|
all = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return all
|
||||||
|
end
|
||||||
|
|
||||||
|
local hov_lanes = way:get_value_by_key("hov:lanes")
|
||||||
|
local hov_lanes_forward = way:get_value_by_key("hov:lanes:forward")
|
||||||
|
local hov_lanes_backward = way:get_value_by_key("hov:lanes:backward")
|
||||||
|
|
||||||
|
local hov_all_designated = hov_lanes and hov_lanes ~= ""
|
||||||
|
and has_all_designated_hov_lanes(hov_lanes)
|
||||||
|
|
||||||
|
local hov_all_designated_forward = hov_lanes_forward and hov_lanes_forward ~= ""
|
||||||
|
and has_all_designated_hov_lanes(hov_lanes_forward)
|
||||||
|
|
||||||
|
local hov_all_designated_backward = hov_lanes_backward and hov_lanes_backward ~= ""
|
||||||
|
and has_all_designated_hov_lanes(hov_lanes_backward)
|
||||||
|
|
||||||
|
-- forward/backward lane depend on a way's direction
|
||||||
|
local oneway = way:get_value_by_key("oneway")
|
||||||
|
local reverse = oneway and oneway == "-1"
|
||||||
|
|
||||||
|
if hov_all_designated or hov_all_designated_forward then
|
||||||
|
if reverse then
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
else
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if hov_all_designated_backward then
|
||||||
|
if reverse then
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
else
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- hov handling
|
||||||
|
|
||||||
-- respect user-preference for toll=yes ways
|
-- respect user-preference for toll=yes ways
|
||||||
local toll = way:get_value_by_key("toll")
|
local toll = way:get_value_by_key("toll")
|
||||||
@ -278,9 +330,6 @@ function way_function (way, result)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
result.forward_mode = mode.driving
|
|
||||||
result.backward_mode = mode.driving
|
|
||||||
|
|
||||||
-- handling ferries and piers
|
-- handling ferries and piers
|
||||||
local route_speed = speed_profile[route]
|
local route_speed = speed_profile[route]
|
||||||
if (route_speed and route_speed > 0) then
|
if (route_speed and route_speed > 0) then
|
||||||
|
@ -21,8 +21,12 @@ function way_function (way, result)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function source_function ()
|
function source_function ()
|
||||||
|
local path = os.getenv('OSRM_RASTER_SOURCE')
|
||||||
|
if not path then
|
||||||
|
path = "rastersource.asc"
|
||||||
|
end
|
||||||
raster_source = sources:load(
|
raster_source = sources:load(
|
||||||
"../test/rastersource.asc",
|
path,
|
||||||
0, -- lon_min
|
0, -- lon_min
|
||||||
0.1, -- lon_max
|
0.1, -- lon_max
|
||||||
0, -- lat_min
|
0, -- lat_min
|
||||||
|
@ -21,8 +21,12 @@ function way_function (way, result)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function source_function ()
|
function source_function ()
|
||||||
|
local path = os.getenv('OSRM_RASTER_SOURCE')
|
||||||
|
if not path then
|
||||||
|
path = "rastersource.asc"
|
||||||
|
end
|
||||||
raster_source = sources:load(
|
raster_source = sources:load(
|
||||||
"../test/rastersource.asc",
|
path,
|
||||||
0, -- lon_min
|
0, -- lon_min
|
||||||
0.1, -- lon_max
|
0.1, -- lon_max
|
||||||
0, -- lat_min
|
0, -- lat_min
|
||||||
|
@ -235,7 +235,8 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo
|
|||||||
route_step.values["distance"] = std::round(step.distance * 10) / 10.;
|
route_step.values["distance"] = std::round(step.distance * 10) / 10.;
|
||||||
route_step.values["duration"] = std::round(step.duration * 10) / 10.;
|
route_step.values["duration"] = std::round(step.duration * 10) / 10.;
|
||||||
route_step.values["name"] = std::move(step.name);
|
route_step.values["name"] = std::move(step.name);
|
||||||
if (!step.ref.empty()) route_step.values["ref"] = std::move(step.ref);
|
if (!step.ref.empty())
|
||||||
|
route_step.values["ref"] = std::move(step.ref);
|
||||||
if (!step.pronunciation.empty())
|
if (!step.pronunciation.empty())
|
||||||
route_step.values["pronunciation"] = std::move(step.pronunciation);
|
route_step.values["pronunciation"] = std::move(step.pronunciation);
|
||||||
if (!step.destinations.empty())
|
if (!step.destinations.empty())
|
||||||
|
@ -146,7 +146,7 @@ Engine::Engine(EngineConfig &config)
|
|||||||
|
|
||||||
route_plugin = create<ViaRoutePlugin>(*query_data_facade, config.max_locations_viaroute);
|
route_plugin = create<ViaRoutePlugin>(*query_data_facade, config.max_locations_viaroute);
|
||||||
table_plugin = create<TablePlugin>(*query_data_facade, config.max_locations_distance_table);
|
table_plugin = create<TablePlugin>(*query_data_facade, config.max_locations_distance_table);
|
||||||
nearest_plugin = create<NearestPlugin>(*query_data_facade);
|
nearest_plugin = create<NearestPlugin>(*query_data_facade, config.max_results_nearest);
|
||||||
trip_plugin = create<TripPlugin>(*query_data_facade, config.max_locations_trip);
|
trip_plugin = create<TripPlugin>(*query_data_facade, config.max_locations_trip);
|
||||||
match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching);
|
match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching);
|
||||||
tile_plugin = create<TilePlugin>(*query_data_facade);
|
tile_plugin = create<TilePlugin>(*query_data_facade);
|
||||||
|
@ -15,11 +15,15 @@ bool EngineConfig::IsValid() const
|
|||||||
storage_config.datasource_names_path.empty() &&
|
storage_config.datasource_names_path.empty() &&
|
||||||
storage_config.datasource_indexes_path.empty() && storage_config.names_data_path.empty();
|
storage_config.datasource_indexes_path.empty() && storage_config.names_data_path.empty();
|
||||||
|
|
||||||
const bool limits_valid =
|
const auto unlimited_or_more_than = [](const int v, const int limit) {
|
||||||
(max_locations_distance_table == -1 || max_locations_distance_table > 2) &&
|
return v == -1 || v > limit;
|
||||||
(max_locations_map_matching == -1 || max_locations_map_matching > 2) &&
|
};
|
||||||
(max_locations_trip == -1 || max_locations_trip > 2) &&
|
|
||||||
(max_locations_viaroute == -1 || max_locations_viaroute > 2);
|
const bool limits_valid = unlimited_or_more_than(max_locations_distance_table, 2) &&
|
||||||
|
unlimited_or_more_than(max_locations_map_matching, 2) &&
|
||||||
|
unlimited_or_more_than(max_locations_trip, 2) &&
|
||||||
|
unlimited_or_more_than(max_locations_viaroute, 2) &&
|
||||||
|
unlimited_or_more_than(max_results_nearest, 0);
|
||||||
|
|
||||||
return ((use_shared_memory && all_path_are_empty) || storage_config.IsValid()) && limits_valid;
|
return ((use_shared_memory && all_path_are_empty) || storage_config.IsValid()) && limits_valid;
|
||||||
}
|
}
|
||||||
|
@ -83,18 +83,6 @@ bool isCollapsableInstruction(const TurnInstruction instruction)
|
|||||||
|
|
||||||
bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
|
bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
|
||||||
|
|
||||||
double nameSegmentLength(std::size_t at, const std::vector<RouteStep> &steps)
|
|
||||||
{
|
|
||||||
double result = steps[at].distance;
|
|
||||||
while (at + 1 < steps.size() && steps[at + 1].name_id == steps[at].name_id)
|
|
||||||
{
|
|
||||||
++at;
|
|
||||||
result += steps[at].distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidate a step and set its content to nothing
|
// invalidate a step and set its content to nothing
|
||||||
void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
|
void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); }
|
||||||
|
|
||||||
@ -129,6 +117,28 @@ double turn_angle(const double entry_bearing, const double exit_bearing)
|
|||||||
return angle > 360 ? angle - 360 : angle;
|
return angle > 360 ? angle - 360 : angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if name change happens the user wants to know about.
|
||||||
|
// Treats e.g. "Name (Ref)" -> "Name" changes still as same name.
|
||||||
|
bool isNoticeableNameChange(const RouteStep &lhs, const RouteStep &rhs)
|
||||||
|
{
|
||||||
|
// TODO: at some point we might want to think about pronunciation here.
|
||||||
|
// Also rotary_name is not handled at the moment.
|
||||||
|
return util::guidance::requiresNameAnnounced(lhs.name, lhs.ref, rhs.name, rhs.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
double nameSegmentLength(std::size_t at, const std::vector<RouteStep> &steps)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(at < steps.size());
|
||||||
|
|
||||||
|
double result = steps[at].distance;
|
||||||
|
while (at + 1 < steps.size() && !isNoticeableNameChange(steps[at], steps[at + 1]))
|
||||||
|
{
|
||||||
|
at += 1;
|
||||||
|
result += steps[at].distance;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
RouteStep forwardInto(RouteStep destination, const RouteStep &source)
|
RouteStep forwardInto(RouteStep destination, const RouteStep &source)
|
||||||
{
|
{
|
||||||
@ -203,6 +213,8 @@ bool setUpRoundabout(RouteStep &step)
|
|||||||
instruction.type == TurnType::EnterRoundaboutAtExit ||
|
instruction.type == TurnType::EnterRoundaboutAtExit ||
|
||||||
instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
|
instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
|
||||||
{
|
{
|
||||||
|
// Here we consider an actual entry, not an exit. We simply have to count the additional
|
||||||
|
// exit
|
||||||
step.maneuver.exit = 1;
|
step.maneuver.exit = 1;
|
||||||
// prevent futher special case handling of these two.
|
// prevent futher special case handling of these two.
|
||||||
if (instruction.type == TurnType::EnterRotaryAtExit)
|
if (instruction.type == TurnType::EnterRotaryAtExit)
|
||||||
@ -215,6 +227,7 @@ bool setUpRoundabout(RouteStep &step)
|
|||||||
|
|
||||||
if (leavesRoundabout(instruction))
|
if (leavesRoundabout(instruction))
|
||||||
{
|
{
|
||||||
|
// This set-up, even though it looks the same, is actually looking at entering AND exiting
|
||||||
step.maneuver.exit = 1; // count the otherwise missing exit
|
step.maneuver.exit = 1; // count the otherwise missing exit
|
||||||
|
|
||||||
// prevent futher special case handling of these two.
|
// prevent futher special case handling of these two.
|
||||||
@ -406,12 +419,16 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
|||||||
if (compatible(one_back_step, current_step))
|
if (compatible(one_back_step, current_step))
|
||||||
{
|
{
|
||||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||||
|
|
||||||
if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
|
if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
|
||||||
TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
|
TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
|
||||||
current_step.name_id != steps[two_back_index].name_id)
|
isNoticeableNameChange(steps[two_back_index], current_step))
|
||||||
|
{
|
||||||
|
|
||||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||||
|
}
|
||||||
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
|
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
|
||||||
current_step.name_id == steps[two_back_index].name_id)
|
!isNoticeableNameChange(steps[two_back_index], current_step))
|
||||||
{
|
{
|
||||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||||
|
|
||||||
@ -457,7 +474,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
|||||||
{
|
{
|
||||||
BOOST_ASSERT(two_back_index < steps.size());
|
BOOST_ASSERT(two_back_index < steps.size());
|
||||||
// the simple case is a u-turn that changes directly into the in-name again
|
// the simple case is a u-turn that changes directly into the in-name again
|
||||||
const bool direct_u_turn = steps[two_back_index].name_id == current_step.name_id;
|
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
|
||||||
|
|
||||||
// however, we might also deal with a dual-collapse scenario in which we have to
|
// however, we might also deal with a dual-collapse scenario in which we have to
|
||||||
// additionall collapse a name-change as welll
|
// additionall collapse a name-change as welll
|
||||||
@ -468,7 +485,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
|||||||
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
|
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
|
||||||
const bool u_turn_with_name_change =
|
const bool u_turn_with_name_change =
|
||||||
continues_with_name_change &&
|
continues_with_name_change &&
|
||||||
steps[next_step_index].name_id == steps[two_back_index].name_id;
|
!isNoticeableNameChange(steps[two_back_index], steps[next_step_index]);
|
||||||
|
|
||||||
if (direct_u_turn || u_turn_with_name_change)
|
if (direct_u_turn || u_turn_with_name_change)
|
||||||
{
|
{
|
||||||
@ -792,9 +809,8 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
|||||||
// Turn Types in the response depend on whether we find the same road name
|
// Turn Types in the response depend on whether we find the same road name
|
||||||
// (sliproad indcating a u-turn) or if we are turning onto a different road, in
|
// (sliproad indcating a u-turn) or if we are turning onto a different road, in
|
||||||
// which case we use a turn.
|
// which case we use a turn.
|
||||||
if (steps[getPreviousIndex(one_back_index)].name_id ==
|
if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index)],
|
||||||
steps[step_index].name_id &&
|
steps[step_index]))
|
||||||
steps[step_index].name_id != EMPTY_NAMEID)
|
|
||||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||||
else
|
else
|
||||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||||
@ -828,7 +844,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
|||||||
// These have to be handled in post-processing
|
// These have to be handled in post-processing
|
||||||
else if (isCollapsableInstruction(current_step.maneuver.instruction) &&
|
else if (isCollapsableInstruction(current_step.maneuver.instruction) &&
|
||||||
current_step.maneuver.instruction.type != TurnType::Suppressed &&
|
current_step.maneuver.instruction.type != TurnType::Suppressed &&
|
||||||
steps[getPreviousNameIndex(step_index)].name_id == current_step.name_id &&
|
!isNoticeableNameChange(steps[getPreviousNameIndex(step_index)], current_step) &&
|
||||||
// canCollapseAll is also checking for compatible(step,step+1) for all indices
|
// canCollapseAll is also checking for compatible(step,step+1) for all indices
|
||||||
canCollapseAll(getPreviousNameIndex(step_index) + 1, next_step_index))
|
canCollapseAll(getPreviousNameIndex(step_index) + 1, next_step_index))
|
||||||
{
|
{
|
||||||
@ -853,8 +869,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
|||||||
const auto two_back_index = getPreviousIndex(one_back_index);
|
const auto two_back_index = getPreviousIndex(one_back_index);
|
||||||
BOOST_ASSERT(two_back_index < steps.size());
|
BOOST_ASSERT(two_back_index < steps.size());
|
||||||
// valid, since one_back is collapsable or a turn and therefore not depart:
|
// valid, since one_back is collapsable or a turn and therefore not depart:
|
||||||
const auto &coming_from_name_id = steps[two_back_index].name_id;
|
if (!isNoticeableNameChange(steps[two_back_index], current_step))
|
||||||
if (current_step.name_id == coming_from_name_id)
|
|
||||||
{
|
{
|
||||||
if (compatible(one_back_step, steps[two_back_index]))
|
if (compatible(one_back_step, steps[two_back_index]))
|
||||||
{
|
{
|
||||||
@ -886,7 +901,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
|||||||
else if (step_index + 2 < steps.size() &&
|
else if (step_index + 2 < steps.size() &&
|
||||||
current_step.maneuver.instruction.type == TurnType::NewName &&
|
current_step.maneuver.instruction.type == TurnType::NewName &&
|
||||||
steps[next_step_index].maneuver.instruction.type == TurnType::NewName &&
|
steps[next_step_index].maneuver.instruction.type == TurnType::NewName &&
|
||||||
one_back_step.name_id == steps[next_step_index].name_id)
|
!isNoticeableNameChange(one_back_step, steps[next_step_index]))
|
||||||
{
|
{
|
||||||
if (compatible(steps[step_index], steps[next_step_index]))
|
if (compatible(steps[step_index], steps[next_step_index]))
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
#include <boost/numeric/conversion/cast.hpp>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
@ -16,13 +17,25 @@ namespace engine
|
|||||||
namespace plugins
|
namespace plugins
|
||||||
{
|
{
|
||||||
|
|
||||||
NearestPlugin::NearestPlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {}
|
NearestPlugin::NearestPlugin(datafacade::BaseDataFacade &facade, const int max_results_)
|
||||||
|
: BasePlugin{facade}, max_results{max_results_}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms,
|
Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms,
|
||||||
util::json::Object &json_result)
|
util::json::Object &json_result)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(params.IsValid());
|
BOOST_ASSERT(params.IsValid());
|
||||||
|
|
||||||
|
if (max_results > 0 &&
|
||||||
|
(boost::numeric_cast<std::int64_t>(params.number_of_results) > max_results))
|
||||||
|
{
|
||||||
|
return Error("TooBig",
|
||||||
|
"Number of results " + std::to_string(params.number_of_results) +
|
||||||
|
" is higher than current maximum (" + std::to_string(max_results) + ")",
|
||||||
|
json_result);
|
||||||
|
}
|
||||||
|
|
||||||
if (!CheckAllCoordinates(params.coordinates))
|
if (!CheckAllCoordinates(params.coordinates))
|
||||||
return Error("InvalidOptions", "Coordinates are invalid", json_result);
|
return Error("InvalidOptions", "Coordinates are invalid", json_result);
|
||||||
|
|
||||||
|
@ -110,7 +110,8 @@ ExtractionContainers::ExtractionContainers()
|
|||||||
// Check if stxxl can be instantiated
|
// Check if stxxl can be instantiated
|
||||||
stxxl::vector<unsigned> dummy_vector;
|
stxxl::vector<unsigned> dummy_vector;
|
||||||
|
|
||||||
// Insert three empty strings offsets for name, destination and pronunciation
|
// Insert three empty strings offsets for name, ref, destination and pronunciation
|
||||||
|
name_offsets.push_back(0);
|
||||||
name_offsets.push_back(0);
|
name_offsets.push_back(0);
|
||||||
name_offsets.push_back(0);
|
name_offsets.push_back(0);
|
||||||
name_offsets.push_back(0);
|
name_offsets.push_back(0);
|
||||||
|
@ -34,8 +34,8 @@ namespace TurnLaneType = guidance::TurnLaneType;
|
|||||||
ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
|
ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
|
||||||
: external_memory(extraction_containers)
|
: external_memory(extraction_containers)
|
||||||
{
|
{
|
||||||
// we reserved 0, 1, 2 for the empty case
|
// we reserved 0, 1, 2, 3 for the empty case
|
||||||
string_map[MapKey("", "", "")] = 0;
|
string_map[MapKey("", "", "", "")] = 0;
|
||||||
lane_description_map[TurnLaneDescription()] = 0;
|
lane_description_map[TurnLaneDescription()] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
|
|
||||||
// FIXME this need to be moved into the profiles
|
// FIXME this need to be moved into the profiles
|
||||||
const guidance::RoadClassification road_classification = parsed_way.road_classification;
|
const guidance::RoadClassification road_classification = parsed_way.road_classification;
|
||||||
const auto laneStringToDescription = [](std::string lane_string) -> TurnLaneDescription {
|
const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription {
|
||||||
if (lane_string.empty())
|
if (lane_string.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
|
|
||||||
// convert the lane description into an ID and, if necessary, remembr the description in the
|
// convert the lane description into an ID and, if necessary, remembr the description in the
|
||||||
// description_map
|
// description_map
|
||||||
const auto requestId = [&](std::string lane_string) {
|
const auto requestId = [&](const std::string &lane_string) {
|
||||||
if (lane_string.empty())
|
if (lane_string.empty())
|
||||||
return INVALID_LANE_DESCRIPTIONID;
|
return INVALID_LANE_DESCRIPTIONID;
|
||||||
TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));
|
TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));
|
||||||
@ -233,15 +233,16 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deduplicates street names, destination names and refs based on the string_map map.
|
// Deduplicates street names, refs, destinations, pronunciation based on the string_map.
|
||||||
// In case we do not already store the name, inserts (name, id) tuple and return id.
|
// In case we do not already store the key, inserts (key, id) tuple and return id.
|
||||||
// Otherwise fetches the id based on the name and returns it without insertion.
|
// Otherwise fetches the id based on the name and returns it without insertion.
|
||||||
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
|
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
|
||||||
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
|
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
|
||||||
|
|
||||||
const constexpr auto MAX_STRING_LENGTH = 255u;
|
const constexpr auto MAX_STRING_LENGTH = 255u;
|
||||||
// Get the unique identifier for the street name, destination, and ref
|
// Get the unique identifier for the street name, destination, and ref
|
||||||
const auto name_iterator = string_map.find(MapKey(parsed_way.name, parsed_way.destinations, parsed_way.ref));
|
const auto name_iterator = string_map.find(
|
||||||
|
MapKey(parsed_way.name, parsed_way.destinations, parsed_way.ref, parsed_way.pronunciation));
|
||||||
unsigned name_id = EMPTY_NAMEID;
|
unsigned name_id = EMPTY_NAMEID;
|
||||||
if (string_map.end() == name_iterator)
|
if (string_map.end() == name_iterator)
|
||||||
{
|
{
|
||||||
@ -256,7 +257,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
name_id = external_memory.name_offsets.size() - 1;
|
name_id = external_memory.name_offsets.size() - 1;
|
||||||
|
|
||||||
external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length +
|
external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length +
|
||||||
destinations_length + pronunciation_length);
|
destinations_length + pronunciation_length +
|
||||||
|
ref_length);
|
||||||
|
|
||||||
std::copy(parsed_way.name.c_str(),
|
std::copy(parsed_way.name.c_str(),
|
||||||
parsed_way.name.c_str() + name_length,
|
parsed_way.name.c_str() + name_length,
|
||||||
@ -278,7 +280,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
std::back_inserter(external_memory.name_char_data));
|
std::back_inserter(external_memory.name_char_data));
|
||||||
external_memory.name_offsets.push_back(external_memory.name_char_data.size());
|
external_memory.name_offsets.push_back(external_memory.name_char_data.size());
|
||||||
|
|
||||||
auto k = MapKey{parsed_way.name, parsed_way.destinations, parsed_way.ref};
|
auto k = MapKey{
|
||||||
|
parsed_way.name, parsed_way.destinations, parsed_way.ref, parsed_way.pronunciation};
|
||||||
auto v = MapVal{name_id};
|
auto v = MapVal{name_id};
|
||||||
string_map.emplace(std::move(k), std::move(v));
|
string_map.emplace(std::move(k), std::move(v));
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#include "extractor/guidance/intersection_generator.hpp"
|
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
|
#include "extractor/guidance/intersection_generator.hpp"
|
||||||
#include "extractor/guidance/toolkit.hpp"
|
#include "extractor/guidance/toolkit.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <boost/range/algorithm/count_if.hpp>
|
#include <boost/range/algorithm/count_if.hpp>
|
||||||
@ -268,8 +269,11 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection,
|
|||||||
return becomes_narrower;
|
return becomes_narrower;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const bool is_y_arm_first = isValidYArm(first_index, second_index);
|
||||||
|
const bool is_y_arm_second = isValidYArm(second_index, first_index);
|
||||||
|
|
||||||
// Only merge valid y-arms
|
// Only merge valid y-arms
|
||||||
if (!isValidYArm(first_index, second_index) || !isValidYArm(second_index, first_index))
|
if (!is_y_arm_first || !is_y_arm_second)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (angle_between < 60)
|
if (angle_between < 60)
|
||||||
@ -577,10 +581,14 @@ IntersectionGenerator::GetActualNextIntersection(const NodeID starting_node,
|
|||||||
// to prevent endless loops
|
// to prevent endless loops
|
||||||
const auto termination_node = node_based_graph.GetTarget(via_edge);
|
const auto termination_node = node_based_graph.GetTarget(via_edge);
|
||||||
|
|
||||||
while (result.size() == 2 &&
|
// using a maximum lookahead, we make sure not to end up in some form of loop
|
||||||
node_based_graph.GetEdgeData(via_edge).IsCompatibleTo(
|
std::unordered_set<NodeID> visited_nodes;
|
||||||
node_based_graph.GetEdgeData(result[1].turn.eid)))
|
while (visited_nodes.count(node_at_intersection) == 0 &&
|
||||||
|
(result.size() == 2 &&
|
||||||
|
node_based_graph.GetEdgeData(via_edge).IsCompatibleTo(
|
||||||
|
node_based_graph.GetEdgeData(result[1].turn.eid))))
|
||||||
{
|
{
|
||||||
|
visited_nodes.insert(node_at_intersection);
|
||||||
node_at_intersection = node_based_graph.GetTarget(incoming_edge);
|
node_at_intersection = node_based_graph.GetTarget(incoming_edge);
|
||||||
incoming_edge = result[1].turn.eid;
|
incoming_edge = result[1].turn.eid;
|
||||||
result = GetConnectedRoads(node_at_intersection, incoming_edge);
|
result = GetConnectedRoads(node_at_intersection, incoming_edge);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
#include "util/guidance/toolkit.hpp"
|
#include "util/guidance/toolkit.hpp"
|
||||||
|
#include "util/guidance/toolkit.hpp"
|
||||||
#include "util/simple_logger.hpp"
|
#include "util/simple_logger.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -90,9 +91,11 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
|
|||||||
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||||
if (in_data.name_id != out_data.name_id &&
|
if (in_data.name_id != out_data.name_id &&
|
||||||
requiresNameAnnounced(name_table.GetNameForID(in_data.name_id),
|
util::guidance::requiresNameAnnounced(name_table.GetNameForID(in_data.name_id),
|
||||||
name_table.GetNameForID(out_data.name_id),
|
name_table.GetRefForID(in_data.name_id),
|
||||||
street_name_suffix_table))
|
name_table.GetNameForID(out_data.name_id),
|
||||||
|
name_table.GetRefForID(out_data.name_id),
|
||||||
|
street_name_suffix_table))
|
||||||
{
|
{
|
||||||
// obvious turn onto a through street is a merge
|
// obvious turn onto a through street is a merge
|
||||||
if (through_street)
|
if (through_street)
|
||||||
@ -597,17 +600,18 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
|||||||
if (deviation_ratio < DISTINCTION_RATIO / 1.5)
|
if (deviation_ratio < DISTINCTION_RATIO / 1.5)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// in comparison to another continuing road, we need a better distinction. This prevents
|
/* in comparison to another continuing road, we need a better distinction. This prevents
|
||||||
// situations where the turn is probably less obvious. An example are places that have a
|
situations where the turn is probably less obvious. An example are places that have a
|
||||||
// road with the same name entering/exiting:
|
road with the same name entering/exiting:
|
||||||
//
|
|
||||||
// d
|
d
|
||||||
// /
|
/
|
||||||
// /
|
/
|
||||||
// a -- b
|
a -- b
|
||||||
// \
|
\
|
||||||
// \
|
\
|
||||||
// c
|
c
|
||||||
|
*/
|
||||||
|
|
||||||
if (turn_data.name_id == continue_data.name_id &&
|
if (turn_data.name_id == continue_data.name_id &&
|
||||||
deviation_ratio < 1.5 * DISTINCTION_RATIO)
|
deviation_ratio < 1.5 * DISTINCTION_RATIO)
|
||||||
|
@ -222,48 +222,46 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
|
|||||||
std::unordered_set<unsigned> roundabout_name_ids;
|
std::unordered_set<unsigned> roundabout_name_ids;
|
||||||
std::unordered_set<unsigned> connected_names;
|
std::unordered_set<unsigned> connected_names;
|
||||||
|
|
||||||
const auto getNextOnRoundabout =
|
const auto getNextOnRoundabout = [this, &roundabout_name_ids, &connected_names](
|
||||||
[this, &roundabout_name_ids, &connected_names](const NodeID node) {
|
const NodeID node) {
|
||||||
EdgeID continue_edge = SPECIAL_EDGEID;
|
EdgeID continue_edge = SPECIAL_EDGEID;
|
||||||
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
|
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
|
||||||
|
{
|
||||||
|
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
||||||
|
if (!edge_data.reversed && edge_data.roundabout)
|
||||||
{
|
{
|
||||||
const auto &edge_data = node_based_graph.GetEdgeData(edge);
|
if (SPECIAL_EDGEID != continue_edge)
|
||||||
if (!edge_data.reversed && edge_data.roundabout)
|
|
||||||
{
|
{
|
||||||
if (SPECIAL_EDGEID != continue_edge)
|
// fork in roundabout
|
||||||
{
|
return SPECIAL_EDGEID;
|
||||||
// fork in roundabout
|
|
||||||
return SPECIAL_EDGEID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EMPTY_NAMEID != edge_data.name_id)
|
|
||||||
{
|
|
||||||
bool add = true;
|
|
||||||
for (auto name_id : roundabout_name_ids)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!requiresNameAnnounced(name_table.GetNameForID(name_id),
|
|
||||||
name_table.GetNameForID(edge_data.name_id),
|
|
||||||
street_name_suffix_table))
|
|
||||||
{
|
|
||||||
add = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (add)
|
|
||||||
roundabout_name_ids.insert(edge_data.name_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue_edge = edge;
|
|
||||||
}
|
}
|
||||||
else if (!edge_data.roundabout)
|
|
||||||
|
if (EMPTY_NAMEID != edge_data.name_id)
|
||||||
{
|
{
|
||||||
// remember all connected road names
|
|
||||||
connected_names.insert(edge_data.name_id);
|
const auto announce = [&](unsigned id) {
|
||||||
|
return util::guidance::requiresNameAnnounced(
|
||||||
|
name_table.GetNameForID(id),
|
||||||
|
name_table.GetRefForID(id),
|
||||||
|
name_table.GetNameForID(edge_data.name_id),
|
||||||
|
name_table.GetRefForID(edge_data.name_id),
|
||||||
|
street_name_suffix_table);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::all_of(begin(roundabout_name_ids), end(roundabout_name_ids), announce))
|
||||||
|
roundabout_name_ids.insert(edge_data.name_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue_edge = edge;
|
||||||
}
|
}
|
||||||
return continue_edge;
|
else if (!edge_data.roundabout)
|
||||||
};
|
{
|
||||||
|
// remember all connected road names
|
||||||
|
connected_names.insert(edge_data.name_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return continue_edge;
|
||||||
|
};
|
||||||
// the roundabout radius has to be the same for all locations we look at it from
|
// the roundabout radius has to be the same for all locations we look at it from
|
||||||
// to guarantee this, we search the full roundabout for its vertices
|
// to guarantee this, we search the full roundabout for its vertices
|
||||||
// and select the three smallest ids
|
// and select the three smallest ids
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "extractor/guidance/sliproad_handler.hpp"
|
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
#include "extractor/guidance/intersection_scenario_three_way.hpp"
|
#include "extractor/guidance/intersection_scenario_three_way.hpp"
|
||||||
|
#include "extractor/guidance/sliproad_handler.hpp"
|
||||||
#include "extractor/guidance/toolkit.hpp"
|
#include "extractor/guidance/toolkit.hpp"
|
||||||
|
|
||||||
#include "util/guidance/toolkit.hpp"
|
#include "util/guidance/toolkit.hpp"
|
||||||
@ -37,9 +37,9 @@ SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_gener
|
|||||||
// included for interface reasons only
|
// included for interface reasons only
|
||||||
bool SliproadHandler::canProcess(const NodeID /*nid*/,
|
bool SliproadHandler::canProcess(const NodeID /*nid*/,
|
||||||
const EdgeID /*via_eid*/,
|
const EdgeID /*via_eid*/,
|
||||||
const Intersection & /*intersection*/) const
|
const Intersection &intersection) const
|
||||||
{
|
{
|
||||||
return true;
|
return intersection.size() > 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intersection SliproadHandler::
|
Intersection SliproadHandler::
|
||||||
@ -56,13 +56,30 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
|||||||
auto intersection = intersection_generator(at_node, road.turn.eid);
|
auto intersection = intersection_generator(at_node, road.turn.eid);
|
||||||
auto in_edge = road.turn.eid;
|
auto in_edge = road.turn.eid;
|
||||||
// skip over traffic lights
|
// skip over traffic lights
|
||||||
while (intersection.size() == 2)
|
// to prevent ending up in an endless loop, we remember all visited nodes. This is
|
||||||
|
// necessary, since merging of roads can actually create enterable loops of degree two
|
||||||
|
std::unordered_set<NodeID> visited_nodes;
|
||||||
|
auto node = at_node;
|
||||||
|
while (intersection.size() == 2 && visited_nodes.count(node) == 0)
|
||||||
{
|
{
|
||||||
const auto node = node_based_graph.GetTarget(in_edge);
|
visited_nodes.insert(node);
|
||||||
|
node = node_based_graph.GetTarget(in_edge);
|
||||||
|
if (node == at_node)
|
||||||
|
{
|
||||||
|
// we ended up in a loop without exit
|
||||||
|
output_node = SPECIAL_NODEID;
|
||||||
|
intersection.clear();
|
||||||
|
return intersection;
|
||||||
|
}
|
||||||
in_edge = intersection[1].turn.eid;
|
in_edge = intersection[1].turn.eid;
|
||||||
output_node = node_based_graph.GetTarget(in_edge);
|
output_node = node_based_graph.GetTarget(in_edge);
|
||||||
intersection = intersection_generator(node, in_edge);
|
intersection = intersection_generator(node, in_edge);
|
||||||
}
|
}
|
||||||
|
if (intersection.size() <= 2)
|
||||||
|
{
|
||||||
|
output_node = SPECIAL_NODEID;
|
||||||
|
intersection.clear();
|
||||||
|
}
|
||||||
return intersection;
|
return intersection;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,11 +94,15 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
|||||||
// a one-sided sliproad, however, the non-sliproad can be considered `obvious`. Here we
|
// a one-sided sliproad, however, the non-sliproad can be considered `obvious`. Here we
|
||||||
// assume that this could be the case and check for a potential sliproad/non-sliproad
|
// assume that this could be the case and check for a potential sliproad/non-sliproad
|
||||||
// situation.
|
// situation.
|
||||||
NodeID intersection_node_one, intersection_node_two;
|
NodeID intersection_node_one = SPECIAL_NODEID, intersection_node_two = SPECIAL_NODEID;
|
||||||
const auto intersection_following_index_one = findNextIntersectionForRoad(
|
const auto intersection_following_index_one = findNextIntersectionForRoad(
|
||||||
intersection_node_id, intersection[1], intersection_node_one);
|
intersection_node_id, intersection[1], intersection_node_one);
|
||||||
const auto intersection_following_index_two = findNextIntersectionForRoad(
|
const auto intersection_following_index_two = findNextIntersectionForRoad(
|
||||||
intersection_node_id, intersection[2], intersection_node_two);
|
intersection_node_id, intersection[2], intersection_node_two);
|
||||||
|
// in case of broken roads, we return
|
||||||
|
if (intersection_following_index_one.empty() ||
|
||||||
|
intersection_following_index_two.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
// In case of loops at the end of the road, we will arrive back at the intersection
|
// In case of loops at the end of the road, we will arrive back at the intersection
|
||||||
// itself. If that is the case, the road is obviously not a sliproad.
|
// itself. If that is the case, the road is obviously not a sliproad.
|
||||||
@ -128,7 +149,6 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
|||||||
return intersection;
|
return intersection;
|
||||||
|
|
||||||
const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
|
const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
|
||||||
|
|
||||||
// check whether the continue road is valid
|
// check whether the continue road is valid
|
||||||
const auto check_valid = [this, source_edge_data](const ConnectedRoad &road) {
|
const auto check_valid = [this, source_edge_data](const ConnectedRoad &road) {
|
||||||
const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid);
|
const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||||
@ -152,6 +172,9 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
|||||||
const auto next_road_next_intersection =
|
const auto next_road_next_intersection =
|
||||||
findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node);
|
findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node);
|
||||||
|
|
||||||
|
if (next_road_next_intersection.empty())
|
||||||
|
return intersection;
|
||||||
|
|
||||||
// If we are at a traffic loop at the end of a road, don't consider it a sliproad
|
// If we are at a traffic loop at the end of a road, don't consider it a sliproad
|
||||||
if (intersection_node_id == next_intersection_node)
|
if (intersection_node_id == next_intersection_node)
|
||||||
return intersection;
|
return intersection;
|
||||||
@ -181,6 +204,22 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection)
|
|||||||
return intersection;
|
return intersection;
|
||||||
}(intersection_node_id);
|
}(intersection_node_id);
|
||||||
|
|
||||||
|
const auto link_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||||
|
// Check if the road continues here
|
||||||
|
const bool is_through_street =
|
||||||
|
!target_intersection.empty() &&
|
||||||
|
target_intersection.end() !=
|
||||||
|
std::find_if(target_intersection.begin() + 1,
|
||||||
|
target_intersection.end(),
|
||||||
|
[this, &link_data](const ConnectedRoad &road) {
|
||||||
|
return node_based_graph.GetEdgeData(road.turn.eid).name_id ==
|
||||||
|
link_data.name_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the sliproad candidate is a through street, we cannot handle it as a sliproad
|
||||||
|
if (is_through_street)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (const auto &candidate_road : target_intersection)
|
for (const auto &candidate_road : target_intersection)
|
||||||
{
|
{
|
||||||
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
|
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
|
||||||
|
@ -94,7 +94,8 @@ Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Handle sliproads
|
// Handle sliproads
|
||||||
intersection = sliproad_handler(from_nid, via_eid, std::move(intersection));
|
if (sliproad_handler.canProcess(from_nid, via_eid, intersection))
|
||||||
|
intersection = sliproad_handler(from_nid, via_eid, std::move(intersection));
|
||||||
|
|
||||||
// Turn On Ramps Into Off Ramps, if we come from a motorway-like road
|
// Turn On Ramps Into Off Ramps, if we come from a motorway-like road
|
||||||
if (node_based_graph.GetEdgeData(via_eid).road_classification.IsMotorwayClass())
|
if (node_based_graph.GetEdgeData(via_eid).road_classification.IsMotorwayClass())
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#include "util/debug.hpp"
|
|
||||||
|
|
||||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||||
#include "extractor/guidance/turn_lane_types.hpp"
|
#include "extractor/guidance/turn_lane_types.hpp"
|
||||||
#include "util/simple_logger.hpp"
|
#include "util/simple_logger.hpp"
|
||||||
|
@ -63,7 +63,8 @@ inline unsigned generateServerProgramOptions(const int argc,
|
|||||||
int &max_locations_trip,
|
int &max_locations_trip,
|
||||||
int &max_locations_viaroute,
|
int &max_locations_viaroute,
|
||||||
int &max_locations_distance_table,
|
int &max_locations_distance_table,
|
||||||
int &max_locations_map_matching)
|
int &max_locations_map_matching,
|
||||||
|
int &max_results_nearest)
|
||||||
{
|
{
|
||||||
using boost::program_options::value;
|
using boost::program_options::value;
|
||||||
using boost::filesystem::path;
|
using boost::filesystem::path;
|
||||||
@ -100,7 +101,10 @@ inline unsigned generateServerProgramOptions(const int argc,
|
|||||||
"Max. locations supported in distance table query") //
|
"Max. locations supported in distance table query") //
|
||||||
("max-matching-size",
|
("max-matching-size",
|
||||||
value<int>(&max_locations_map_matching)->default_value(100),
|
value<int>(&max_locations_map_matching)->default_value(100),
|
||||||
"Max. locations supported in map matching query");
|
"Max. locations supported in map matching query") //
|
||||||
|
("max-nearest-size",
|
||||||
|
value<int>(&max_results_nearest)->default_value(100),
|
||||||
|
"Max. results supported in nearest query");
|
||||||
|
|
||||||
// hidden options, will be allowed on command line, but will not be shown to the user
|
// hidden options, will be allowed on command line, but will not be shown to the user
|
||||||
boost::program_options::options_description hidden_options("Hidden options");
|
boost::program_options::options_description hidden_options("Hidden options");
|
||||||
@ -189,7 +193,8 @@ int main(int argc, const char *argv[]) try
|
|||||||
config.max_locations_trip,
|
config.max_locations_trip,
|
||||||
config.max_locations_viaroute,
|
config.max_locations_viaroute,
|
||||||
config.max_locations_distance_table,
|
config.max_locations_distance_table,
|
||||||
config.max_locations_map_matching);
|
config.max_locations_map_matching,
|
||||||
|
config.max_results_nearest);
|
||||||
if (init_result == INIT_OK_DO_NOT_START_ENGINE)
|
if (init_result == INIT_OK_DO_NOT_START_ENGINE)
|
||||||
{
|
{
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
@ -22,6 +22,9 @@ NameTable::NameTable(const std::string &filename)
|
|||||||
|
|
||||||
name_stream >> m_name_table;
|
name_stream >> m_name_table;
|
||||||
|
|
||||||
|
if (!name_stream)
|
||||||
|
throw exception("Unable to deserialize RangeTable for NameTable");
|
||||||
|
|
||||||
unsigned number_of_chars = 0;
|
unsigned number_of_chars = 0;
|
||||||
name_stream.read(reinterpret_cast<char *>(&number_of_chars), sizeof(number_of_chars));
|
name_stream.read(reinterpret_cast<char *>(&number_of_chars), sizeof(number_of_chars));
|
||||||
if (!name_stream)
|
if (!name_stream)
|
||||||
@ -64,5 +67,23 @@ std::string NameTable::GetNameForID(const unsigned name_id) const
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string NameTable::GetRefForID(const unsigned name_id) const
|
||||||
|
{
|
||||||
|
// Way string data is stored in blocks based on `name_id` as follows:
|
||||||
|
//
|
||||||
|
// | name | destination | pronunciation | ref |
|
||||||
|
// ^ ^
|
||||||
|
// [range)
|
||||||
|
// ^ name_id + 3
|
||||||
|
//
|
||||||
|
// `name_id + offset` gives us the range of chars.
|
||||||
|
//
|
||||||
|
// Offset 0 is name, 1 is destination, 2 is pronunciation, 3 is ref.
|
||||||
|
// See datafacades and extractor callbacks for details.
|
||||||
|
const constexpr auto OFFSET_REF = 3u;
|
||||||
|
return GetNameForID(name_id + OFFSET_REF);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
18
taginfo.json
18
taginfo.json
@ -62,6 +62,24 @@
|
|||||||
"object_types": [ "way" ],
|
"object_types": [ "way" ],
|
||||||
"description": "Roads with hov=designated are ignored by default."
|
"description": "Roads with hov=designated are ignored by default."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "hov:lanes",
|
||||||
|
"value": "designated",
|
||||||
|
"object_types": [ "way" ],
|
||||||
|
"description": "Roads with hov:lanes all-designated are ignored by default."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "hov:lanes:forward",
|
||||||
|
"value": "designated",
|
||||||
|
"object_types": [ "way" ],
|
||||||
|
"description": "Roads with hov:lanes:forward all-designated are ignored by default."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "hov:lanes:backward",
|
||||||
|
"value": "designated",
|
||||||
|
"object_types": [ "way" ],
|
||||||
|
"description": "Roads with hov:lanes:backward all-designated are ignored by default."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "toll",
|
"key": "toll",
|
||||||
"value": "yes",
|
"value": "yes",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "args.hpp"
|
#include "args.hpp"
|
||||||
|
|
||||||
#include "osrm/match_parameters.hpp"
|
#include "osrm/match_parameters.hpp"
|
||||||
|
#include "osrm/nearest_parameters.hpp"
|
||||||
#include "osrm/route_parameters.hpp"
|
#include "osrm/route_parameters.hpp"
|
||||||
#include "osrm/table_parameters.hpp"
|
#include "osrm/table_parameters.hpp"
|
||||||
#include "osrm/trip_parameters.hpp"
|
#include "osrm/trip_parameters.hpp"
|
||||||
@ -136,4 +137,33 @@ BOOST_AUTO_TEST_CASE(test_match_limits)
|
|||||||
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_nearest_limits)
|
||||||
|
{
|
||||||
|
const auto args = get_args();
|
||||||
|
BOOST_REQUIRE_EQUAL(args.size(), 1);
|
||||||
|
|
||||||
|
using namespace osrm;
|
||||||
|
|
||||||
|
EngineConfig config;
|
||||||
|
config.storage_config = {args[0]};
|
||||||
|
config.use_shared_memory = false;
|
||||||
|
config.max_results_nearest = 2;
|
||||||
|
|
||||||
|
OSRM osrm{config};
|
||||||
|
|
||||||
|
NearestParameters params;
|
||||||
|
params.coordinates.emplace_back(util::FloatLongitude{}, util::FloatLatitude{});
|
||||||
|
params.number_of_results = 10000;
|
||||||
|
|
||||||
|
json::Object result;
|
||||||
|
|
||||||
|
const auto rc = osrm.Nearest(params, result);
|
||||||
|
|
||||||
|
BOOST_CHECK(rc == Status::Error);
|
||||||
|
|
||||||
|
// Make sure we're not accidentally hitting a guard code path before
|
||||||
|
const auto code = result.values["code"].get<json::String>().value;
|
||||||
|
BOOST_CHECK(code == "TooBig"); // per the New-Server API spec
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user