profiles api v2

This commit is contained in:
Emil Tin 2017-05-18 14:27:28 +02:00 committed by Patrick Niklaus
parent 5ece65cade
commit e413b25cd9
41 changed files with 1858 additions and 1208 deletions

View File

@ -1,5 +1,19 @@
# 5.10.0
- Changes from 5.9:
- Changes from 5.9:
- Profiles:
- New version 2 profile API which cleans up a number of things and makes it easier to for profiles to include each other. Profiles using the old version 0 and 1 APIs are still supported.
- New required `setup()` function that must return a configuration hash. Storing configuration in globals is deprecated.
- Passes the config hash returned in `setup()` as an argument to `process_node/way/segment/turn`.
- Properties are now set in `.properties` in the config hash returend by setup().
- initialize raster sources in `setup()` instead of in a separate callback.
- Renames the `sources` helper to `raster`.
- Renames `way_functions` to `process_way` (same for node, segment and turn).
- Removes `get_restrictions()`. Instead set `.restrictions` in the config hash in `setup()`.
- Removes `get_name_suffix_list()`. Instead set `.suffix_list` in the config hash in `setup()`.
- Renames `Handlers` to `WayHandlers`.
- Pass functions instead of strings to `WayHandlers.run()`, so it's possible to mix in your own functions.
- Reorders arguments to `WayHandlers` functions to match `process_way()`.
- Profiles must return a hash of profile functions. This makes it easier for profiles to include each other.
# 5.9.0
- Changes from 5.8:

View File

@ -1,31 +1,97 @@
OSRM supports "profiles". Configurations representing different routing behaviours for (typically) different transport modes. A profile describes whether or not we route along a particular type of way, or over a particular node in the OpenStreetMap data, and also how quickly we'll be travelling when we do. This feeds into the way the routing graph is created and thus influences the output routes.
# OSRM profiles
OSRM supports "profiles". Profiles representing routing behavior for different transport modes like car, bike and foot. You can also create profiles for variations like a fastest/shortest car profile or fastest/safest/greenest bicycles profile.
A profile describes whether or not it's possible to route along a particular type of way, whether we can pass a particular node, and how quickly we'll be traveling when we do. This feeds into the way the routing graph is created and thus influences the output routes.
## Available profiles
Out-of-the-box OSRM comes with profiles for car, bicycle and foot. You can easily modify these or create new ones if you like.
Out-of-the-box OSRM comes with several different profiles, including car, bicycle and foot.
Profiles have a 'lua' extension, and are places in 'profiles' directory.
Profile configuration files have a 'lua' extension, and are found under the 'profiles' subdirectory.
Alternatively commands will take a lua profile specified with an explicit -p param, for example:
When running OSRM preprocessing commands you specify the profile with the --profile (or the shorthand -p) option, for example:
`osrm-extract -p ../profiles/car.lua planet-latest.osm.pbf`
`osrm-extract --profile ../profiles/car.lua planet-latest.osm.pbf`
And then **you will need to extract and contract again** (A change to the profile will typically affect the extract step as well as the contract step. See [Processing Flow](https://github.com/Project-OSRM/osrm-backend/wiki/Processing-Flow))
## Processing flow
It's important to understand that profiles are used when preprocessing the OSM data, NOT at query time when routes are computed.
## lua scripts?
This means that after modifying a profile **you will need to extract, contract and reload the data again** and to see changes in the routing results. See [Processing Flow](https://github.com/Project-OSRM/osrm-backend/wiki/Processing-Flow) for more.
Profiles are not just configuration files. They are scripts written in the "lua" scripting language ( http://www.lua.org ) The reason for this, is that OpenStreetMap data is not sufficiently straightforward, to simply define tag mappings. Lua scripting offers a powerful way of coping with the complexity of different node,way,relation,tag combinations found within OpenStreetMap data.
## Profiles are written in LUA
Profiles are not just configuration files. They are scripts written in the [LUA scripting language](http://www.lua.org). The reason for this is that OpenStreetMap data is complex, and it's not possible to simply define tag mappings. LUA scripting offers a powerful way to handle all the possible tag combinations found in OpenStreetMap nodes and ways.
## Basic structure of a profile
## Basic structure of profiles
A profile will process every node and way in the OSM input data to determine what ways are routable in which direction, at what speed, etc.
You can understand these lua scripts enough to make interesting modifications, without needing to get to grips with how they work completely.
A profile will typically:
Towards the top of the file, a profile (such as [car.lua](../profiles/car.lua)) will typically define various configurations as global variables. A lot of these are look-up hashes of one sort or another.
- Define api version (required)
- Require library files (optional)
- Define setup function (required)
- Define process functions (some are required)
- Return functions table (required)
As you scroll down the file you'll see local variables, and then local functions, and finally...
A profile can also define various local functions it needs.
`way_function` and `node_function` are the important functions which are called when extracting OpenStreetMap data with `osrm-extract`.
Looking at [car.lua](../profiles/car.lua) as an example, at the top of the file the api version is defined and then required library files are included.
The following global properties can be set in your profile:
Then follows the `setup` functions, which is called once when the profile is loaded. It returns a big hash table of configurations, specifying things like what speed to use for different way types. The configurations are used later in the various processing functions. Many adjustments can be done just be modifying this configuration table.
The setup function is also where you can do other setup, like loading elevation data source if you want to consider that when processing ways.
Then comes the `process_node` and `process_way` functions, which are called for each OSM node and way when extracting OpenStreetMap data with `osrm-extract`.
The `process_turn` function processes every possible turn in the network, and sets a penalty depending on the angle and turn of the movement.
Profiles can also define a `process_segment` function to handle differences in speed along an OSM way, for example to handle elevation. As you can see, this is not currently used in the car profile.
At the end of the file, a table if returned with references to the setup and processing functions the profile has defined.
## Understanding speed, weight and rate
When computing a route from A to B there can be different measure of what is the best route. That's why there's a need for different profiles.
Because speeds very on different types of roads, the shortest and the fastest route are typically different. But there are many other possible preferences. For example a user might prefer a bicycle route that follow parks or other green areas, even though both duration and distance are a bit longer.
To handle this, OSRM doesn't simply choose the ways with the highest speed. Instead it uses the concept of `weight` and `rate`. The rate is an abstract measure that you can assign to ways as you like to make some ways preferable to others. Routing will prefer ways with high rate.
The weight of a way normally computed as length / rate. The weight can be thought of as the resistance or cost when passing the way. Routing will prefer ways with low weight.
You can also set the weight of a way to a fixed value, In this case it's not calculated based on the length or rate, and the rate is ignored.
You should set the speed to you best estimate of the actual speed that will be used on a particular way. This will result in the best estimated travel times.
If you want to prefer certain ways due to other factors than the speed, adjust the rate accordingly. If you adjust the speed, the time time estimation will be skewed.
If you set the same rate on all ways, the result will be shortest path routing.
If you set rate = speed on all ways, the result will be fastest path routing.
If you want to prioritize certain street, increase the rate on these.
## Elements
### api_version
A profile should set api_version at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If api_version is not defined, 0 will be assumed. The current api version is 2.
### Library files
The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks.
File | Notes
------------------|------------------------------
way_handlers.lua | Functions for processing way tags
tags.lua | Functions for general parsing of OSM tags
set.lua | Defines the Set helper for handling sets of values
sequence.lua | Defines the Sequence helper for handling sequences of values
access.lua | Function for finding relevant access tags
destination.lua | Function for finding relevant destination tags
destination.lua | Function for determining maximum speed
guidance.lua | Function for processing guidance attributes
They all return a table of functions when you use `require` to load them. You can either store this table and reference it's functions later, of if you need only a single you can store that directly.
### setup()
The `setup` function is called once when the profile is loaded and must return a table of configurations. It's also where you can do other global setup, like loading data sources that are used during processing.
Note that processing of data is parallelized and several unconnected LUA interpreters will be running at the same time. The `setup` function will be called once for each. Each LUA iinterpreter will have it's own set of globals.
The following global properties can be set under `properties` in the hash you return in the `setup` function:
Attribute | Type | Notes
-------------------------------------|----------|----------------------------------------------------------------------------
@ -36,17 +102,40 @@ use_turn_restrictions | Boolean | Are turn instructions followed
continue_straight_at_waypoint | Boolean | Must the route continue straight on at a via point, or are U-turns allowed? (default `true`)
max_speed_for_map_matching | Float | Maximum vehicle speed to be assumed in matching (in m/s)
max_turn_weight | Float | Maximum turn penalty weight
force_split_edges | Boolean | True value forces a split of forward and backward edges of extracted ways and guarantees that `segment_function` will be called for all segments (default `false`)
force_split_edges | Boolean | True value forces a split of forward and backward edges of extracted ways and guarantees that `process_segment` will be called for all segments (default `false`)
## way_function
### process_node(profile, node, result)
Process an OSM node to determine whether this node is a barrier or can be passed and whether passing it incurs a delay.
Given an OpenStreetMap way, the `way_function` will either return nothing (meaning we are not going to route over this way at all), or it will set up a result hash to be returned. The most important thing it will do is set the value of `result.forward_speed` and `result.backward_speed` as a suitable integer value representing the speed for traversing the way.
Argument | Description
---------|-------------------------------------------------------
profile | The configuration table you returned in `setup`.
node | The input node to process (read-only).
result | The output that you will modify.
All other calculations stem from that, including the returned timings in driving directions, but also, less directly, it feeds into the actual routing decisions the engine will take (a way with a slow traversal speed, may be less favoured than a way with fast traversal speed, but it depends how long it is, and... what it connects to in the rest of the network graph)
The following attributes can be set on `result`:
Using the power of the scripting language you wouldn't typically see something as simple as a `result.forward_speed = 20` line within the `way_function`. Instead a `way_function` will examine the tagging (e.g. `way:get_value_by_key("highway")` and many others), process this information in various ways, calling other local functions, referencing the global variables and look-up hashes, before arriving at the result.
Attribute | Type | Notes
----------------|---------|---------------------------------------------------------
barrier | Boolean | Is it an impassable barrier?
traffic_lights | Boolean | Is it a traffic light (incurs delay in `process_turn`)?
The following attributes can be set on the result in `way_function`:
## process_way(profile, way, result)
Given an OpenStreetMap way, the `process_way` function will either return nothing (meaning we are not going to route over this way at all), or it will set up a result hash.
Argument | Description
---------|-------------------------------------------------------
profile | The configuration table you returned in `setup`.
node | The input way to process (read-only).
result | The output that you will modify.
Importantly it will set `result.forward_mode` and `result.backward_mode` to indicate the travel mode in each direction, as well as set `result.forward_speed` and `result.backward_speed` to integer values representing the speed for traversing the way.
It will also set a number of other attributes on `result`.
Using the power of the scripting language you wouldn't typically see something as simple as a `result.forward_speed = 20` line within the `process_way` function. Instead `process_way` will examine the tag set on the way, process this information in various ways, calling other local functions and referencing the configuration in `profile`, etc, before arriving at the result.
The following attributes can be set on the result in `process_way`:
Attribute | Type | Notes
----------------------------------------|----------|--------------------------------------------------------------------------
@ -60,7 +149,7 @@ forward_classes | Table | Mark this way as being of a
backward_classes | Table | " "
duration | Float | Alternative setter for duration of the whole way in both directions
weight | Float | Alternative setter for weight of the whole way in both directions
turn_lanes_forward | String | Directions for individual lanes (normalised OSM `turn:lanes` value)
turn_lanes_forward | String | Directions for individual lanes (normalized OSM `turn:lanes` value)
turn_lanes_backward | String | " "
forward_restricted | Boolean | Is this a restricted access road? (e.g. private, or deliveries only; used to enable high turn penalty, so that way is only chosen for start/end of route)
backward_restricted | Boolean | " "
@ -78,32 +167,19 @@ road_classification.road_priority_class | Enum | Guidance: order in priority
road_classification.may_be_ignored | Boolean | Guidance: way is non-highway
road_classification.num_lanes | Unsigned | Guidance: total number of lanes in way
### Guidance
### process_segment(profile, segment)
The `process_segment` function is called for every segment of OSM ways. A segment is a straight line between two OSM nodes.
The guidance parameters in profiles are currently a work in progress. They can and will change.
Please be aware of this when using guidance configuration possibilities.
On OpenStreetMap way cannot have different tags on different parts of a way. Instead you would split the way into several smaller ways. However many ways are long. For example, many ways pass hills without any change in tags.
Guidance uses road classes to decide on when/if to emit specific instructions and to discover which road is obvious when following a route.
Classification uses three flags and a priority-category.
The flags indicate whether a road is a motorway (required for on/off ramps), a link type (the ramps itself, if also a motorway) and whether a road may be omittted in considerations (is considered purely for connectivity).
The priority-category influences the decision which road is considered the obvious choice and which roads can be seen as fork.
Forks can be emitted between roads of similar priority category only. Obvious choices follow a major priority road, if the priority difference is large.
Processing each segment of an OSM way makes it possible to have different speeds on different parts of a way based on external data like data about elevation, pollution, noise or scenic value and adjust weight and duration of the segment.
## node_function
In the `process_segment` you don't have access to OSM tags. Instead you use the geographical location of the start and end point of the way to lookup other data source, like elevation data. See [rasterbot.lua](../profiles/rasterbot.lua) for an example.
The following attributes can be set on the result in `node_function`:
Attribute | Type | Notes
----------------|---------|-------------------------------------------------------
barrier | Boolean | Is it an impassable barrier?
traffic_lights | Boolean | Is it a traffic light (incurs delay in `turn_function`)?
## segment_function
The following attributes can be read and set on the result in `segment_function`:
The following attributes can be read and set on the result in `process_segment`:
Attribute | Read/write? | Type | Notes
-------------------|-------------|---------|------------------------------------------------------
-------------------|-------------|---------|----------------------------------------
source.lon | Read | Float | Co-ordinates of segment start
source.lat | Read | Float | " "
target.lon | Read | Float | Co-ordinates of segment end
@ -112,17 +188,88 @@ target.distance | Read | Float | Length of segment
weight | Read/write | Float | Routing weight for this segment
duration | Read/write | Float | Duration for this segment
## turn_function
### process_turn(profile, turn)
The `process_turn` function is called for every possible turn in the network. Based on the angle and type of turn you assign the weight and duration of the movement.
The following attributes can be read and set on the result in `turn_function`:
The following attributes can be read and set on the result in `process_turn`:
Attribute | Read/write? | Type | Notes
-------------------|-------------|---------|------------------------------------------------------
direction_modifier | Read | Enum | Geometry of turn. Defined in `include/extractor/guidance/turn_instruction.hpp`
turn_type | Read | Enum | Priority of turn. Defined in `include/extractor/guidance/turn_instruction.hpp`
has_traffic_light | Read | Boolean | Is a traffic light present at this turn?
source_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `way_function`)
target_restricted | Read | Boolean | Is it to a restricted access road? (See definition in `way_function`)
source_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
target_restricted | Read | Boolean | Is it to a restricted access road? (See definition in `process_way`)
angle | Read | Float | Angle of turn in degrees (`0-360`: `0`=u-turn, `180`=straight on)
duration | Read/write | Float | Penalty to be applied for this turn (duration in deciseconds)
weight | Read/write | Float | Penalty to be applied for this turn (routing weight)
## Guidance
The guidance parameters in profiles are currently a work in progress. They can and will change.
Please be aware of this when using guidance configuration possibilities.
Guidance uses road classes to decide on when/if to emit specific instructions and to discover which road is obvious when following a route.
Classification uses three flags and a priority-category.
The flags indicate whether a road is a motorway (required for on/off ramps), a link type (the ramps itself, if also a motorway) and whether a road may be omitted in considerations (is considered purely for connectivity).
The priority-category influences the decision which road is considered the obvious choice and which roads can be seen as fork.
Forks can be emitted between roads of similar priority category only. Obvious choices follow a major priority road, if the priority difference is large.
### Using raster data
OSRM has build-in support for loading an interpolating raster data in ASCII format. This can be used e.g. for factoring in elevation when computing routes.
Use `raster:load()` in your `setup` function to load data and store the source in your configuration hash:
```lua
function setup()
return {
raster_source = raster:load(
"rastersource.asc", -- file to load
0, -- longitude min
0.1, -- longitude max
0, -- latitude min
0.1, -- latitude max
5, -- number of rows
4 -- number of columns
)
}
end
```
The input data must an ASCII file with rows of integers. e.g.:
```
0 0 0 0
0 0 0 250
0 0 250 500
0 0 0 250
0 0 0 0
```
In your `segment_function` you can then access the raster source and use `raster:query()` to query to find the nearest data point, or `raster:interpolate()` to interpolate a value based on nearby data points.
You must check whether the result is valid before use it.
Example:
```lua
function process_segment (profile, segment)
local sourceData = raster:query(profile.raster_source, segment.source.lon, segment.source.lat)
local targetData = raster:query(profile.raster_source, segment.target.lon, segment.target.lat)
local invalid = sourceData.invalid_data()
if sourceData.datum ~= invalid and targetData.datum ~= invalid then
-- use values to adjust weight and duration
[...]
end
```
See [rasterbot.lua](../profiles/rasterbot.lua) and [rasterbotinterp.lua](../profiles/rasterbotinterp.lua) for examples.
### Helper functions
There are a few helper functions defined in the global scope that profiles can use:
durationIsValid
parseDuration
trimLaneString
applyAccessTokens
canonicalizeStringList

View File

@ -2,10 +2,9 @@
Feature: Bicycle - Route around alleys
Background:
Given the profile file
Given the profile file "bicycle" initialized with
"""
require 'bicycle'
properties.weight_name = 'cyclability'
profile.properties.weight_name = 'cyclability'
"""
Scenario: Bicycle - Avoid taking alleys

View File

@ -4,7 +4,7 @@ Feature: Bicycle - Handle cycling
Background:
Given the profile "bicycle"
Scenario: Bicycle - Use a ferry route
Scenario: Bicycle - Use a movable bridge
Given the node map
"""
a b c

View File

@ -2,10 +2,9 @@
Feature: Bicycle - Adds penalties to unsafe roads
Background:
Given the profile file
Given the profile file "bicycle" initialized with
"""
require 'bicycle'
properties.weight_name = 'cyclability'
profile.properties.weight_name = 'cyclability'
"""
Scenario: Bike - Apply penalties to ways without cycleways

View File

@ -7,10 +7,9 @@ Feature: Turn Penalties
Scenario: Bicycle - Turn penalties on cyclability
Given the profile file
Given the profile file "bicycle" initialized with
"""
require 'bicycle'
properties.weight_name = 'cyclability'
profile.properties.weight_name = 'cyclability'
"""
Given the node map

View File

@ -1,18 +1,11 @@
@routing @testbot @sidebias
Feature: Testbot - side bias
Background:
Given the profile file
Scenario: Left-hand bias
Given the profile file "car" initialized with
"""
require 'testbot'
properties.left_hand_driving = true
"""
Scenario: Left hand bias
Given the profile file "car" extended with
"""
properties.left_hand_driving = true
profile.turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
profile.left_hand_driving = true
profile.turn_bias = 1/1.075
"""
Given the node map
"""
@ -31,11 +24,11 @@ Feature: Testbot - side bias
| d | a | bd,ab,ab | 24s +-1 |
| d | c | bd,bc,bc | 27s +-1 |
Scenario: Right hand bias
Given the profile file "car" extended with
Scenario: Right-hand bias
Given the profile file "car" initialized with
"""
properties.left_hand_driving = false
profile.turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
profile.left_hand_driving = true
profile.turn_bias = 1.075
"""
And the node map
"""
@ -56,6 +49,11 @@ Feature: Testbot - side bias
| d | c | bd,bc,bc | 24s +-1 |
Scenario: Roundabout exit counting for left sided driving
Given the profile file "testbot" initialized with
"""
profile.left_hand_driving = true
profile.turn_bias = 1/1.075
"""
And a grid size of 10 meters
And the node map
"""

View File

@ -1,11 +1,9 @@
@routing @car @weight
Feature: Car - weights
Background: Use specific speeds
Given the profile "car"
Scenario: Only routes down service road when that's the destination
Given the node map
Given the profile "car"
And the node map
"""
a--b--c
|
@ -25,7 +23,8 @@ Feature: Car - weights
| a | d | abc,bdf,bdf | 18 km/h | 71.7 |
Scenario: Does not jump off the highway to go down service road
Given the node map
Given the profile "car"
And the node map
"""
a
|
@ -63,10 +62,9 @@ Feature: Car - weights
| a | e | ab,be,be | 14 km/h | 112 |
Scenario: Distance weights
Given the profile file "car" extended with
Given the profile file "car" initialized with
"""
api_version = 1
properties.weight_name = 'distance'
profile.properties.weight_name = 'distance'
"""
Given the node map

View File

@ -582,9 +582,9 @@ Feature: Turn Lane Guidance
@anticipate
Scenario: No Lanes for Roundabouts, see #2626
Given the profile file "car" extended with
Given the profile file "car" initialized with
"""
properties.left_hand_driving = true
profile.left_hand_driving = true
"""
And the node map
"""

View File

@ -3,11 +3,10 @@ Feature: Basic Roundabout
Background:
Given a grid size of 10 meters
Given the profile file
"""
require 'car'
properties.left_hand_driving = true
"""
Given the profile file "car" initialized with
"""
profile.properties.left_hand_driving = true
"""
Scenario: Roundabout exit counting for left sided driving
And a grid size of 10 meters

View File

@ -33,15 +33,25 @@ Feature: osrm-contract command line option: edge-weight-updates-over-factor
Scenario: Logging using weigts as durations for non-duration profile
Given the profile file "testbot" extended with
Given the profile file
"""
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.weight = 1
result.duration = 1
end
return functions
"""
And the data has been saved to disk

View File

@ -12,15 +12,20 @@ Feature: osrm-extract lua ways:get_nodes()
And the data has been saved to disk
Scenario: osrm-extract - Passing base file
Given the profile file "testbot" extended with
Given the profile file
"""
function way_function(way, result)
functions = require('testbot')
function way_function(profile, way, result)
for _, node in ipairs(way:get_nodes()) do
print('node id ' .. node:id())
end
result.forward_mode = mode.driving
result.forward_speed = 1
end
functions.process_way = way_function
return functions
"""
When I run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully

View File

@ -0,0 +1,40 @@
Feature: Invalid profile API versions
Background:
Given a grid size of 100 meters
Scenario: Profile API version too low
Given the profile file
"""
api_version = -1
"""
And the node map
"""
ab
"""
And the ways
| nodes |
| ab |
And the data has been saved to disk
When I try to run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit with an error
And stderr should contain "Invalid profile API version"
Scenario: Profile API version too high
Given the profile file
"""
api_version = 3
"""
And the node map
"""
ab
"""
And the ways
| nodes |
| ab |
And the data has been saved to disk
When I try to run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit with an error
And stderr should contain "Invalid profile API version"

View File

@ -1,84 +1,93 @@
Feature: Profile API version 0
Background:
Given a grid size of 100 meters
Scenario: Not-defined API version
Scenario: Profile api version 0
Given the profile file
"""
function way_function(way, result)
result.forward_mode = mode.driving
result.forward_speed = 1
end
"""
And the node map
"""
ab
"""
And the ways
| nodes |
| ab |
And the data has been saved to disk
When I try to run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully
And stderr should not contain "Invalid profile API version"
Scenario: Out-bound API version
Given the profile file
"""
api_version = 2
"""
And the node map
"""
ab
"""
And the ways
| nodes |
| ab |
And the data has been saved to disk
When I try to run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit with an error
And stderr should contain "Invalid profile API version"
Scenario: Basic profile function calls and property values
Given the profile file
"""
api_version = 0
-- set profile properties
properties.u_turn_penalty = 20
properties.traffic_signal_penalty = 2
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.left_hand_driving = false
properties.weight_name = 'duration'
function node_function (node, result)
print ('node_function ' .. node:id())
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function turn_function (angle)
print('turn_function ' .. angle)
return angle == 0 and 0 or 42
end
function segment_function (source, target, distance, weight)
print ('segment_function ' .. source.lon .. ' ' .. source.lat)
end
"""
"""
api_version = 0
-- set profile properties
properties.u_turn_penalty = 20
properties.traffic_signal_penalty = 2
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.left_hand_driving = false
properties.weight_name = 'duration'
function node_function (node, result)
print ('node_function ' .. node:id())
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function turn_function (angle)
print('turn_function ' .. angle)
return angle == 0 and 0 or 42
end
function segment_function (source, target, distance, weight)
print ('segment_function ' .. source.lon .. ' ' .. source.lat)
end
"""
And the node map
"""
a
b c d
e
"""
And the ways
| nodes |
| ac |
| cb |
| cd |
| ce |
And the data has been saved to disk
When I run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully
And stdout should contain "node_function"
And stdout should contain "way_function"
And stdout should contain "turn_function"
And stdout should contain "segment_function"
When I route I should get
| from | to | route | time |
| a | b | ac,cb,cb | 24.2s |
| a | d | ac,cd,cd | 24.2s |
| a | e | ac,ce | 20s |
Scenario: Profile version undefined, assume version 0
Given the profile file
"""
-- set profile properties
properties.u_turn_penalty = 20
properties.traffic_signal_penalty = 2
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.left_hand_driving = false
properties.weight_name = 'duration'
function node_function (node, result)
print ('node_function ' .. node:id())
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function turn_function (angle)
print('turn_function ' .. angle)
return angle == 0 and 0 or 42
end
function segment_function (source, target, distance, weight)
print ('segment_function ' .. source.lon .. ' ' .. source.lat)
end
"""
And the node map
"""
a

View File

@ -6,41 +6,42 @@ Feature: Profile API version 1
Scenario: Basic profile function calls and property values
Given the profile file
"""
api_version = 1
api_version = 1
-- set profile properties
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.weight_name = 'test_version1'
properties.weight_precision = 2
-- set profile properties
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.weight_name = 'test_version1'
properties.weight_precision = 2
assert(properties.max_turn_weight == 327.67)
assert(properties.max_turn_weight == 327.67)
function node_function (node, result)
print ('node_function ' .. node:id())
end
function node_function (node, result)
print(node, result)
print ('node_function ' .. node:id())
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.weight = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.weight = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function turn_function (turn)
print('turn_function', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light)
turn.weight = turn.angle == 0 and 0 or 4.2
turn.duration = turn.weight
end
function turn_function (turn)
print('turn_function', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light)
turn.weight = turn.angle == 0 and 0 or 4.2
turn.duration = turn.weight
end
function segment_function (segment)
print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat)
end
"""
function segment_function (segment)
print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat)
end
"""
And the node map
"""
a
@ -67,3 +68,165 @@ end
| a | b | ac,cb,cb | 19.2s |
| a | d | ac,cd,cd | 19.2s |
| a | e | ac,ce | 20s |
Scenario: Basic profile function calls and property values
Given the profile file
"""
api_version = 1
-- set profile properties
properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.weight_name = 'test_version1'
properties.weight_precision = 2
assert(properties.max_turn_weight == 327.67)
function node_function (node, result)
print(node, result)
print ('node_function ' .. node:id())
end
function way_function(way, result)
result.name = way:get_value_by_key('name')
result.weight = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name)
end
function turn_function (turn)
print('turn_function', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light)
turn.weight = turn.angle == 0 and 0 or 4.2
turn.duration = turn.weight
end
function segment_function (segment)
print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat)
end
"""
And the node map
"""
a
bcd
e
"""
And the ways
| nodes |
| ac |
| cb |
| cd |
| ce |
And the data has been saved to disk
When I run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully
And stdout should contain "node_function"
And stdout should contain "way_function"
And stdout should contain "turn_function"
And stdout should contain "segment_function"
When I route I should get
| from | to | route | time |
| a | b | ac,cb,cb | 19.2s |
| a | d | ac,cd,cd | 19.2s |
| a | e | ac,ce | 20s |
Scenario: Weighting based on raster sources
Given the profile file
"""
api_version = 1
properties.force_split_edges = true
function source_function()
local path = os.getenv('OSRM_RASTER_SOURCE')
if not path then
path = 'rastersource.asc'
end
raster_source = sources:load(
path,
0, -- lon_min
0.1, -- lon_max
0, -- lat_min
0.1, -- lat_max
5, -- nrows
4 -- ncols
)
end
function way_function (way, result)
result.name = way:get_value_by_key('name')
result.forward_mode = mode.cycling
result.backward_mode = mode.cycling
result.forward_speed = 15
result.backward_speed = 15
end
function segment_function (segment)
local sourceData = sources:query(raster_source, segment.source.lon, segment.source.lat)
local targetData = sources:query(raster_source, segment.target.lon, segment.target.lat)
io.write('evaluating segment: ' .. sourceData.datum .. ' ' .. targetData.datum .. '\n')
local invalid = sourceData.invalid_data()
local scaled_weight = segment.weight
local scaled_duration = segment.duration
if sourceData.datum ~= invalid and targetData.datum ~= invalid then
local slope = (targetData.datum - sourceData.datum) / segment.distance
scaled_weight = scaled_weight / (1.0 - (slope * 5.0))
scaled_duration = scaled_duration / (1.0 - (slope * 5.0))
io.write(' slope: ' .. slope .. '\n')
io.write(' was weight: ' .. segment.weight .. '\n')
io.write(' new weight: ' .. scaled_weight .. '\n')
io.write(' was duration: ' .. segment.duration .. '\n')
io.write(' new duration: ' .. scaled_duration .. '\n')
end
segment.weight = scaled_weight
segment.duration = scaled_duration
end
"""
And the node locations
| node | lat | lon |
| a | 0.1 | 0.1 |
| b | 0.05 | 0.1 |
| c | 0.0 | 0.1 |
| d | 0.05 | 0.03 |
| e | 0.05 | 0.066 |
| f | 0.075 | 0.066 |
And the ways
| nodes | highway |
| ab | primary |
| ad | primary |
| bc | primary |
| dc | primary |
| de | primary |
| eb | primary |
| df | primary |
| fb | primary |
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
When I route I should get
| from | to | route | speed |
| a | b | ab,ab | 8 km/h |
| b | a | ab,ab | 22 km/h |
| a | c | ab,bc,bc | 12 km/h |
| b | c | bc,bc | 22 km/h |
| a | d | ad,ad | 15 km/h |
| d | c | dc,dc | 15 km/h |
| d | e | de,de | 10 km/h |
| e | b | eb,eb | 10 km/h |
| d | f | df,df | 15 km/h |
| f | b | fb,fb | 7 km/h |
| d | b | de,eb,eb | 10 km/h |

View File

@ -0,0 +1,87 @@
Feature: Profile API version 2
Background:
Given a grid size of 100 meters
Scenario: Basic profile function calls and property values
Given the profile file
"""
api_version = 2
Set = require('lib/set')
Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
find_access_tag = require("lib/access").find_access_tag
limit = require("lib/maxspeed").limit
function setup()
return {
properties = {
max_speed_for_map_matching = 180/3.6,
use_turn_restrictions = true,
continue_straight_at_waypoint = true,
weight_name = 'test_version2',
weight_precision = 2
}
}
end
function process_node(profile, node, result)
print ('process_node ' .. node:id())
end
function process_way(profile, way, result)
result.name = way:get_value_by_key('name')
result.weight = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 36
result.backward_speed = 36
print ('process_way ' .. way:id() .. ' ' .. result.name)
end
function process_turn (profile, turn)
print('process_turn', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light)
turn.weight = turn.angle == 0 and 0 or 4.2
turn.duration = turn.weight
end
function process_segment (profile, segment)
print ('process_segment ' .. segment.source.lon .. ' ' .. segment.source.lat)
end
return {
setup = setup,
process_node = process_node,
process_way = process_way,
process_segment = process_segment,
process_turn = process_turn
}
"""
And the node map
"""
a
bcd
e
"""
And the ways
| nodes |
| ac |
| cb |
| cd |
| ce |
And the data has been saved to disk
When I run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully
And stdout should contain "process_node"
And stdout should contain "process_way"
And stdout should contain "process_turn"
And stdout should contain "process_segment"
When I route I should get
| from | to | route | time |
| a | b | ac,cb,cb | 19.2s |
| a | d | ac,cd,cd | 19.2s |
| a | e | ac,ce | 20s |

View File

@ -248,17 +248,20 @@ module.exports = function () {
fs.writeFile(this.penaltiesCacheFile, data, callback);
});
this.Given(/^the profile file(?: "([^"]*)" extended with)?$/, (profile, data, callback) => {
this.Given(/^the profile file(?: "([^"]*)" initialized with)?$/, (profile, data, callback) => {
const lua_profiles_path = this.PROFILES_PATH.split(path.sep).join('/');
let text = 'package.path = "' + lua_profiles_path + '/?.lua;" .. package.path\n';
if (profile == null) {
text += data + '\n';
} else {
text += 'local f = assert(io.open("' + lua_profiles_path + '/' + profile + '.lua", "r"))\n';
text += 'local s = f:read("*all") .. [[\n' + data + '\n]]\n';
text += 'f:close()\n';
text += 'local m = assert(loadstring and loadstring(s) or load(s))\n';
text += 'm()\n';
text += 'local functions = require("' + profile + '")\n';
text += 'functions.setup_parent = functions.setup\n';
text += 'functions.setup = function()\n';
text += 'local profile = functions.setup_parent()\n';
text += data + '\n';
text += 'return profile\n';
text += 'end\n';
text += 'return functions\n';
}
this.profileFile = this.profileCacheFile;
// TODO: Don't overwrite if it exists

View File

@ -221,15 +221,24 @@ Feature: Basic Distance Matrix
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
Scenario: Testbot - Travel time matrix based on segment durations
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
function segment_function (segment)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 2
segment.duration = 11
end
return functions
"""
And the node map
@ -254,16 +263,25 @@ Feature: Basic Distance Matrix
Scenario: Testbot - Travel time matrix for alternative loop paths
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_precision = 3
function segment_function (segment)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
profile.weight_precision = 3
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 777
segment.duration = 3
end
return functions
"""
And the node map
"""

View File

@ -1,29 +1,27 @@
@routing @testbot @nil
Feature: Testbot - Check assigning nil values
Scenario: Assign nil values to all way strings
Given the profile file "testbot" extended with
Given the profile file
"""
function way_function (way, result)
result.name = "name"
result.ref = "ref"
result.destinations = "destinations"
result.pronunciation = "pronunciation"
result.turn_lanes_forward = "turn_lanes_forward"
result.turn_lanes_backward = "turn_lanes_backward"
functions = require('testbot')
result.name = nil
result.ref = nil
result.destinations = nil
result.exits = nil
result.pronunciation = nil
result.turn_lanes_forward = nil
result.turn_lanes_backward = nil
function way_function(profile, way, result)
result.name = nil
result.ref = nil
result.destinations = nil
result.exits = nil
result.pronunciation = nil
result.turn_lanes_forward = nil
result.turn_lanes_backward = nil
result.forward_speed = 10
result.backward_speed = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 10
result.backward_speed = 10
result.forward_mode = mode.driving
result.backward_mode = mode.driving
end
functions.process_way = way_function
return functions
"""
Given the node map
"""

View File

@ -39,13 +39,17 @@ Feature: Projection to nearest point on road
Scenario: Projection results negative duration
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
function segment_function (segment)
functions = require('testbot')
function segment_function(profile, segment)
segment.weight = 5.5
segment.duration = 2.8
end
functions.process_segment = segment_function
return functions
"""
Given the node locations

View File

@ -84,12 +84,11 @@ Feature: Traffic - speeds
Scenario: Weighting based on speed file weights, ETA based on file durations
Given the profile file "testbot" extended with
Given the profile file "testbot" initialized with
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_precision = 2
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_precision = 2
"""
And the contract extra arguments "--segment-speed-file {speeds_file}"
And the customize extra arguments "--segment-speed-file {speeds_file}"

View File

@ -8,12 +8,11 @@ Feature: Traffic - speeds edge cases
And the ways
| nodes | highway |
| ab | primary |
And the profile file "testbot" extended with
And the profile file "testbot" initialized with
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_precision = 2
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_precision = 2
"""
And the contract extra arguments "--segment-speed-file {speeds_file}"
And the customize extra arguments "--segment-speed-file {speeds_file}"

View File

@ -1,12 +1,9 @@
@routing @testbot @turn_penalty
Feature: Turn Penalties
Background:
Scenario: Turns should incur a delay that depend on the angle
Given the profile "turnbot"
Given a grid size of 200 meters
Scenario: Turns should incur a delay that depend on the angle
Given the node map
"""
c d e

View File

@ -60,18 +60,27 @@ Feature: Weight tests
Scenario: Step weights -- way_function: fail if no weight or weight_per_meter property
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.forward_speed = 42
result.backward_speed = 42
end
return functions
"""
And the node map
"""
@ -87,18 +96,27 @@ Feature: Weight tests
And it should exit with an error
Scenario: Step weights -- way_function: second way wins
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.duration = 42
result.weight = 35
end
return functions
"""
Given the node map
@ -119,19 +137,28 @@ Feature: Weight tests
| h,a | , | 140m +-1 | 35,0 | 42s,0s |
Scenario: Step weights -- way_function: higher weight_per_meter is preferred
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.duration = 42
result.forward_rate = 1
result.backward_rate = 0.5
end
return functions
"""
Given the node map
@ -155,22 +182,32 @@ Feature: Weight tests
| h,f | , | 40m | 80,0 | 12s,0s |
Scenario: Step weights -- segment_function
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.weight = 42
result.duration = 3
end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 1
segment.duration = 11
end
return functions
"""
Given the node map
@ -195,28 +232,39 @@ Feature: Weight tests
Scenario: Step weights -- segment_function and turn_function with weight precision
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
properties.weight_precision = 3
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
profile.properties.weight_precision = 3
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.weight = 42
result.duration = 3
end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 1.11
segment.duration = 100
end
function turn_function (turn)
functions.process_turn = function(profile, turn)
print (turn.angle)
turn.weight = 2 + turn.angle / 100
turn.duration = turn.angle
end
return functions
"""
Given the node map
@ -241,22 +289,32 @@ Feature: Weight tests
@traffic @speed
Scenario: Step weights -- segment_function with speed and turn updates
Given the profile file "testbot" extended with
Given the profile file
"""
api_version = 1
properties.traffic_signal_penalty = 0
properties.u_turn_penalty = 0
properties.weight_name = 'steps'
function way_function(way, result)
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.properties.traffic_signal_penalty = 0
profile.properties.u_turn_penalty = 0
profile.properties.weight_name = 'steps'
return profile
end
functions.process_way = function(profile, way, result)
result.forward_mode = mode.driving
result.backward_mode = mode.driving
result.weight = 42
result.duration = 3
end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 10
segment.duration = 11
end
return functions
"""
And the node map
@ -289,10 +347,9 @@ Feature: Weight tests
@traffic @speed
Scenario: Step weights -- segment_function with speed and turn updates with fallback to durations
Given the profile file "testbot" extended with
Given the profile file "testbot" initialized with
"""
api_version = 1
properties.weight_precision = 3
profile.properties.weight_precision = 3
"""
And the node map

View File

@ -125,10 +125,10 @@ class RasterSource
int _ymax);
};
class SourceContainer
class RasterContainer
{
public:
SourceContainer() = default;
RasterContainer() = default;
int LoadRasterSource(const std::string &path_string,
double xmin,

View File

@ -54,7 +54,6 @@ class ScriptingEnvironment
virtual std::vector<std::string> GetNameSuffixList() = 0;
virtual std::vector<std::string> GetRestrictions() = 0;
virtual void SetupSources() = 0;
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
virtual void ProcessSegment(ExtractionSegment &segment) = 0;

View File

@ -23,7 +23,7 @@ struct LuaScriptingContext final
void ProcessWay(const osmium::Way &, ExtractionWay &result);
ProfileProperties properties;
SourceContainer sources;
RasterContainer raster_sources;
sol::state state;
bool has_turn_penalty_function;
@ -37,6 +37,7 @@ struct LuaScriptingContext final
sol::function segment_function;
int api_version;
sol::table profile_table;
};
/**
@ -50,7 +51,7 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
{
public:
static const constexpr int SUPPORTED_MIN_API_VERSION = 0;
static const constexpr int SUPPORTED_MAX_API_VERSION = 1;
static const constexpr int SUPPORTED_MAX_API_VERSION = 2;
explicit Sol2ScriptingEnvironment(const std::string &file_name);
~Sol2ScriptingEnvironment() override = default;
@ -59,9 +60,10 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
LuaScriptingContext &GetSol2Context();
std::vector<std::string> GetStringListFromTable(const std::string &table_name);
std::vector<std::string> GetStringListFromFunction(const std::string &function_name);
std::vector<std::string> GetNameSuffixList() override;
std::vector<std::string> GetRestrictions() override;
void SetupSources() override;
void ProcessTurn(ExtractionTurn &turn) override;
void ProcessSegment(ExtractionSegment &segment) override;

View File

@ -1,199 +1,196 @@
api_version = 1
-- Bicycle profile
local find_access_tag = require("lib/access").find_access_tag
local Set = require('lib/set')
local Sequence = require('lib/sequence')
local Handlers = require("lib/handlers")
local next = next -- bind to local for speed
local limit = require("lib/maxspeed").limit
-- these need to be global because they are accesed externaly
properties.max_speed_for_map_matching = 110/3.6 -- kmph -> m/s
properties.use_turn_restrictions = false
properties.continue_straight_at_waypoint = false
properties.weight_name = 'duration'
--properties.weight_name = 'cyclability'
api_version = 2
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
Set = require('lib/set')
Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
find_access_tag = require("lib/access").find_access_tag
limit = require("lib/maxspeed").limit
function setup()
local default_speed = 15
local walking_speed = 6
local default_speed = 15
local walking_speed = 6
return {
properties = {
u_turn_penalty = 20,
traffic_light_penalty = 2,
--weight_name = 'cyclability',
weight_name = 'duration',
process_call_tagless_node = false,
max_speed_for_map_matching = 110/3.6, -- kmph -> m/s
use_turn_restrictions = false,
continue_straight_at_waypoint = false
},
local profile = {
default_mode = mode.cycling,
default_speed = 15,
oneway_handling = true,
traffic_light_penalty = 2,
u_turn_penalty = 20,
turn_penalty = 6,
turn_bias = 1.4,
use_public_transport = true,
default_mode = mode.cycling,
default_speed = default_speed,
walking_speed = walking_speed,
oneway_handling = true,
turn_penalty = 6,
turn_bias = 1.4,
use_public_transport = true,
allowed_start_modes = Set {
mode.cycling,
mode.pushing_bike
},
allowed_start_modes = Set {
mode.cycling,
mode.pushing_bike
},
barrier_whitelist = Set {
'sump_buster',
'bus_trap',
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'block'
},
barrier_whitelist = Set {
'sump_buster',
'bus_trap',
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'block'
},
access_tag_whitelist = Set {
'yes',
'permissive',
'designated'
},
access_tag_whitelist = Set {
'yes',
'permissive',
'designated'
},
access_tag_blacklist = Set {
'no',
'private',
'agricultural',
'forestry',
'delivery'
},
access_tag_blacklist = Set {
'no',
'private',
'agricultural',
'forestry',
'delivery'
},
restricted_access_tag_list = Set { },
restricted_access_tag_list = Set { },
restricted_highway_whitelist = Set { },
restricted_highway_whitelist = Set { },
access_tags_hierarchy = Sequence {
'bicycle',
'vehicle',
'access'
},
access_tags_hierarchy = Sequence {
'bicycle',
'vehicle',
'access'
},
restrictions = Set {
'bicycle'
},
restrictions = Set {
'bicycle'
},
cycleway_tags = Set {
'track',
'lane',
'opposite',
'opposite_lane',
'opposite_track',
'share_busway',
'sharrow',
'shared',
'shared_lane'
},
cycleway_tags = Set {
'track',
'lane',
'opposite',
'opposite_lane',
'opposite_track',
'share_busway',
'sharrow',
'shared',
'shared_lane'
},
-- reduce the driving speed by 30% for unsafe roads
-- only used for cyclability metric
unsafe_highway_list = {
primary = 0.7,
secondary = 0.75,
tertiary = 0.8,
primary_link = 0.7,
secondary_link = 0.75,
tertiary_link = 0.8,
},
-- reduce the driving speed by 30% for unsafe roads
-- only used for cyclability metric
unsafe_highway_list = {
primary = 0.7,
secondary = 0.75,
tertiary = 0.8,
primary_link = 0.7,
secondary_link = 0.75,
tertiary_link = 0.8,
},
service_penalties = {
alley = 0.5,
},
service_penalties = {
alley = 0.5,
},
bicycle_speeds = {
cycleway = default_speed,
primary = default_speed,
primary_link = default_speed,
secondary = default_speed,
secondary_link = default_speed,
tertiary = default_speed,
tertiary_link = default_speed,
residential = default_speed,
unclassified = default_speed,
living_street = default_speed,
road = default_speed,
service = default_speed,
track = 12,
path = 12
},
bicycle_speeds = {
cycleway = default_speed,
primary = default_speed,
primary_link = default_speed,
secondary = default_speed,
secondary_link = default_speed,
tertiary = default_speed,
tertiary_link = default_speed,
residential = default_speed,
unclassified = default_speed,
living_street = default_speed,
road = default_speed,
service = default_speed,
track = 12,
path = 12
},
pedestrian_speeds = {
footway = walking_speed,
pedestrian = walking_speed,
steps = 2
},
pedestrian_speeds = {
footway = walking_speed,
pedestrian = walking_speed,
steps = 2
},
railway_speeds = {
train = 10,
railway = 10,
subway = 10,
light_rail = 10,
monorail = 10,
tram = 10
},
railway_speeds = {
train = 10,
railway = 10,
subway = 10,
light_rail = 10,
monorail = 10,
tram = 10
},
platform_speeds = {
platform = walking_speed
},
platform_speeds = {
platform = walking_speed
},
amenity_speeds = {
parking = 10,
parking_entrance = 10
},
amenity_speeds = {
parking = 10,
parking_entrance = 10
},
man_made_speeds = {
pier = walking_speed
},
man_made_speeds = {
pier = walking_speed
},
route_speeds = {
ferry = 5
},
route_speeds = {
ferry = 5
},
bridge_speeds = {
movable = 5
},
bridge_speeds = {
movable = 5
},
surface_speeds = {
asphalt = default_speed,
["cobblestone:flattened"] = 10,
paving_stones = 10,
compacted = 10,
cobblestone = 6,
unpaved = 6,
fine_gravel = 6,
gravel = 6,
pebblestone = 6,
ground = 6,
dirt = 6,
earth = 6,
grass = 6,
mud = 3,
sand = 3,
sett = 10
},
surface_speeds = {
asphalt = default_speed,
["cobblestone:flattened"] = 10,
paving_stones = 10,
compacted = 10,
cobblestone = 6,
unpaved = 6,
fine_gravel = 6,
gravel = 6,
pebblestone = 6,
ground = 6,
dirt = 6,
earth = 6,
grass = 6,
mud = 3,
sand = 3,
sett = 10
},
tracktype_speeds = {
},
tracktype_speeds = {
},
smoothness_speeds = {
},
smoothness_speeds = {
},
avoid = Set {
'impassable',
'construction',
'proposed'
avoid = Set {
'impassable',
'construction'
}
}
}
end
local function parse_maxspeed(source)
if not source then
@ -209,13 +206,7 @@ local function parse_maxspeed(source)
return n
end
function get_restrictions(vector)
for i,v in ipairs(profile.restrictions) do
vector:Add(v)
end
end
function node_function (node, result)
function process_node (profile, node, result)
-- parse access and barrier tags
local highway = node:get_value_by_key("highway")
local is_crossing = highway and highway == "crossing"
@ -243,41 +234,8 @@ function node_function (node, result)
end
end
function way_function (way, result)
-- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
-- to increase performance, prefetching and intial tag check
-- is done in directly instead of via a handler.
-- in general we should try to abort as soon as
-- possible if the way is not routable, to avoid doing
-- unnecessary work. this implies we should check things that
-- commonly forbids access early, and handle edge cases later.
-- data table for storing intermediate values during processing
local data = {
-- prefetch tags
highway = way:get_value_by_key('highway'),
}
local handlers = Sequence {
-- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry
'handle_default_mode',
-- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible
'handle_blocked_ways',
}
if Handlers.run(handlers,way,result,data,profile) == false then
return
end
-- initial routability check, filters out buildings, boundaries, etc
function handle_bicycle_tags(profile,way,result,data)
-- initial routability check, filters out buildings, boundaries, etc
local route = way:get_value_by_key("route")
local man_made = way:get_value_by_key("man_made")
local railway = way:get_value_by_key("railway")
@ -293,13 +251,13 @@ function way_function (way, result)
(not public_transport or public_transport=='') and
(not bridge or bridge=='')
then
return
return false
end
-- access
local access = find_access_tag(way, profile.access_tags_hierarchy)
if access and profile.access_tag_blacklist[access] then
return
return false
end
-- other tags
@ -371,8 +329,8 @@ function way_function (way, result)
way_type_allows_pushing = true
elseif access and profile.access_tag_whitelist[access] then
-- unknown way, but valid access tag
result.forward_speed = default_speed
result.backward_speed = default_speed
result.forward_speed = profile.default_speed
result.backward_speed = profile.default_speed
way_type_allows_pushing = true
end
@ -447,18 +405,18 @@ function way_function (way, result)
push_backward_speed = profile.man_made_speeds[man_made]
else
if foot == 'yes' then
push_forward_speed = walking_speed
push_forward_speed = profile.walking_speed
if not implied_oneway then
push_backward_speed = walking_speed
push_backward_speed = profile.walking_speed
end
elseif foot_forward == 'yes' then
push_forward_speed = walking_speed
push_forward_speed = profile.walking_speed
elseif foot_backward == 'yes' then
push_backward_speed = walking_speed
push_backward_speed = profile.walking_speed
elseif way_type_allows_pushing then
push_forward_speed = walking_speed
push_forward_speed = profile.walking_speed
if not implied_oneway then
push_backward_speed = walking_speed
push_backward_speed = profile.walking_speed
end
end
end
@ -481,8 +439,8 @@ function way_function (way, result)
if bicycle == "dismount" then
result.forward_mode = mode.pushing_bike
result.backward_mode = mode.pushing_bike
result.forward_speed = walking_speed
result.backward_speed = walking_speed
result.forward_speed = profile.walking_speed
result.backward_speed = profile.walking_speed
end
@ -502,7 +460,7 @@ function way_function (way, result)
-- convert duration into cyclability
if properties.weight_name == 'cyclability' then
if profile.properties.weight_name == 'cyclability' then
local safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1
local forward_is_unsafe = is_unsafe and not has_cycleway_right
@ -534,29 +492,59 @@ function way_function (way, result)
result.weight = result.duration / forward_penalty
end
end
end
function process_way (profile, way, result)
-- the initial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
-- to increase performance, prefetching and initial tag check
-- is done directly instead of via a handler.
local handlers = Sequence {
-- compute speed taking into account way type, maxspeed tags, etc.
'handle_surface',
-- in general we should try to abort as soon as
-- possible if the way is not routable, to avoid doing
-- unnecessary work. this implies we should check things that
-- commonly forbids access early, and handle edge cases later.
-- handle turn lanes and road classification, used for guidance
'handle_classification',
-- data table for storing intermediate values during processing
-- handle various other flags
'handle_roundabouts',
-- handle allowed start/end modes
'handle_startpoint',
-- set name, ref and pronunciation
'handle_names'
local data = {
-- prefetch tags
highway = way:get_value_by_key('highway'),
}
Handlers.run(handlers,way,result,data,profile)
local handlers = Sequence {
-- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry
WayHandlers.default_mode,
-- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible
WayHandlers.blocked_ways,
-- our main handler
handle_bicycle_tags,
-- compute speed taking into account way type, maxspeed tags, etc.
WayHandlers.surface,
-- handle turn lanes and road classification, used for guidance
WayHandlers.classification,
-- handle allowed start/end modes
WayHandlers.startpoint,
-- handle roundabouts
WayHandlers.roundabouts,
-- set name, ref and pronunciation
WayHandlers.names
}
WayHandlers.run(profile,way,result,data,handlers)
end
function turn_function(turn)
function process_turn(profile, turn)
-- compute turn penalty as angle^2, with a left/right bias
local normalized_angle = turn.angle / 90.0
if normalized_angle >= 0.0 then
@ -566,17 +554,20 @@ function turn_function(turn)
end
if turn.direction_modifier == direction_modifier.uturn then
turn.duration = turn.duration + profile.u_turn_penalty
turn.duration = turn.duration + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.traffic_light_penalty
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
if properties.weight_name == 'cyclability' then
turn.weight = turn.duration
-- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then
turn.weight = turn.weight + 3000
end
if profile.properties.weight_name == 'cyclability' then
turn.weight = turn.duration
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -1,282 +1,268 @@
-- Car profile
api_version = 1
api_version = 2
local find_access_tag = require("lib/access").find_access_tag
local Set = require('lib/set')
local Sequence = require('lib/sequence')
local Handlers = require("lib/handlers")
local next = next -- bind to local for speed
Set = require('lib/set')
Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
find_access_tag = require("lib/access").find_access_tag
limit = require("lib/maxspeed").limit
-- set profile properties
properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s
properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true
properties.left_hand_driving = false
-- For routing based on duration, but weighted for preferring certain roads
properties.weight_name = 'routability'
-- For shortest duration without penalties for accessibility
--properties.weight_name = 'duration'
-- For shortest distance without penalties for accessibility
--properties.weight_name = 'distance'
function setup()
local use_left_hand_driving = false
return {
properties = {
max_speed_for_map_matching = 180/3.6, -- 180kmph -> m/s
left_hand_driving = use_left_hand_driving,
-- For routing based on duration, but weighted for preferring certain roads
weight_name = 'routability',
-- For shortest duration without penalties for accessibility
-- weight_name = 'duration',
-- For shortest distance without penalties for accessibility
-- weight_name = 'distance',
process_call_tagless_node = false,
u_turn_penalty = 20,
continue_straight_at_waypoint = true,
use_turn_restrictions = true,
traffic_light_penalty = 2,
},
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
default_mode = mode.driving,
default_speed = 10,
oneway_handling = true,
side_road_multiplier = 0.8,
turn_penalty = 7.5,
speed_reduction = 0.8,
-- Note: this biases right-side driving.
-- Should be inverted for left-driving countries.
turn_bias = use_left_hand_driving and 1/1.075 or 1.075,
local profile = {
default_mode = mode.driving,
default_speed = 10,
oneway_handling = true,
-- a list of suffixes to suppress in name change instructions
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
side_road_multiplier = 0.8,
turn_penalty = 7.5,
speed_reduction = 0.8,
traffic_light_penalty = 2,
u_turn_penalty = 20,
barrier_whitelist = Set {
'cattle_grid',
'border_control',
'checkpoint',
'toll_booth',
'sally_port',
'gate',
'lift_gate',
'no',
'entrance'
},
-- Note: this biases right-side driving.
-- Should be inverted for left-driving countries.
turn_bias = properties.left_hand_driving and 1/1.075 or 1.075,
access_tag_whitelist = Set {
'yes',
'motorcar',
'motor_vehicle',
'vehicle',
'permissive',
'designated',
'hov'
},
-- a list of suffixes to suppress in name change instructions
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
access_tag_blacklist = Set {
'no',
'agricultural',
'forestry',
'emergency',
'psv',
'customers',
'private',
'delivery',
'destination'
},
barrier_whitelist = Set {
'cattle_grid',
'border_control',
'checkpoint',
'toll_booth',
'sally_port',
'gate',
'lift_gate',
'no',
'entrance'
},
restricted_access_tag_list = Set {
'private',
'delivery',
'destination',
'customers',
},
access_tag_whitelist = Set {
'yes',
'motorcar',
'motor_vehicle',
'vehicle',
'permissive',
'designated',
'hov'
},
access_tags_hierarchy = Sequence {
'motorcar',
'motor_vehicle',
'vehicle',
'access'
},
access_tag_blacklist = Set {
'no',
'agricultural',
'forestry',
'emergency',
'psv',
'customers',
'private',
'delivery',
'destination'
},
service_tag_forbidden = Set {
'emergency_access'
},
restricted_access_tag_list = Set {
'private',
'delivery',
'destination',
'customers',
},
restrictions = Sequence {
'motorcar',
'motor_vehicle',
'vehicle'
},
access_tags_hierarchy = Sequence {
'motorcar',
'motor_vehicle',
'vehicle',
'access'
},
avoid = Set {
'area',
-- 'toll', -- uncomment this to avoid tolls
'reversible',
'impassable',
'hov_lanes',
'steps',
'construction',
'proposed'
},
service_tag_forbidden = Set {
'emergency_access'
},
speeds = Sequence {
highway = {
motorway = 90,
motorway_link = 45,
trunk = 85,
trunk_link = 40,
primary = 65,
primary_link = 30,
secondary = 55,
secondary_link = 25,
tertiary = 40,
tertiary_link = 20,
unclassified = 25,
residential = 25,
living_street = 10,
service = 15
}
},
restrictions = Sequence {
'motorcar',
'motor_vehicle',
'vehicle'
},
service_penalties = {
alley = 0.5,
parking = 0.5,
parking_aisle = 0.5,
driveway = 0.5,
["drive-through"] = 0.5,
["drive-thru"] = 0.5
},
avoid = Set {
'area',
-- 'toll', -- uncomment this to avoid tolls
'reversible',
'impassable',
'hov_lanes',
'steps',
'construction',
'proposed'
},
restricted_highway_whitelist = Set {
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'residential',
'living_street',
},
speeds = Sequence {
highway = {
motorway = 90,
motorway_link = 45,
trunk = 85,
trunk_link = 40,
primary = 65,
primary_link = 30,
secondary = 55,
secondary_link = 25,
tertiary = 40,
tertiary_link = 20,
unclassified = 25,
residential = 25,
living_street = 10,
service = 15
route_speeds = {
ferry = 5,
shuttle_train = 10
},
bridge_speeds = {
movable = 5
},
-- surface/trackype/smoothness
-- values were estimated from looking at the photos at the relevant wiki pages
-- max speed for surfaces
surface_speeds = {
asphalt = nil, -- nil mean no limit. removing the line has the same effect
concrete = nil,
["concrete:plates"] = nil,
["concrete:lanes"] = nil,
paved = nil,
cement = 80,
compacted = 80,
fine_gravel = 80,
paving_stones = 60,
metal = 60,
bricks = 60,
grass = 40,
wood = 40,
sett = 40,
grass_paver = 40,
gravel = 40,
unpaved = 40,
ground = 40,
dirt = 40,
pebblestone = 40,
tartan = 40,
cobblestone = 30,
clay = 30,
earth = 20,
stone = 20,
rocky = 20,
sand = 20,
mud = 10
},
-- max speed for tracktypes
tracktype_speeds = {
grade1 = 60,
grade2 = 40,
grade3 = 30,
grade4 = 25,
grade5 = 20
},
-- max speed for smoothnesses
smoothness_speeds = {
intermediate = 80,
bad = 40,
very_bad = 20,
horrible = 10,
very_horrible = 5,
impassable = 0
},
-- http://wiki.openstreetmap.org/wiki/Speed_limits
maxspeed_table_default = {
urban = 50,
rural = 90,
trunk = 110,
motorway = 130
},
-- List only exceptions
maxspeed_table = {
["ch:rural"] = 80,
["ch:trunk"] = 100,
["ch:motorway"] = 120,
["de:living_street"] = 7,
["ru:living_street"] = 20,
["ru:urban"] = 60,
["ua:urban"] = 60,
["at:rural"] = 100,
["de:rural"] = 100,
["at:trunk"] = 100,
["cz:trunk"] = 0,
["ro:trunk"] = 100,
["cz:motorway"] = 0,
["de:motorway"] = 0,
["ru:motorway"] = 110,
["gb:nsl_single"] = (60*1609)/1000,
["gb:nsl_dual"] = (70*1609)/1000,
["gb:motorway"] = (70*1609)/1000,
["uk:nsl_single"] = (60*1609)/1000,
["uk:nsl_dual"] = (70*1609)/1000,
["uk:motorway"] = (70*1609)/1000,
["nl:rural"] = 80,
["nl:trunk"] = 100,
["none"] = 140
}
},
service_penalties = {
alley = 0.5,
parking = 0.5,
parking_aisle = 0.5,
driveway = 0.5,
["drive-through"] = 0.5,
["drive-thru"] = 0.5
},
restricted_highway_whitelist = Set {
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'residential',
'living_street',
},
route_speeds = {
ferry = 5,
shuttle_train = 10
},
bridge_speeds = {
movable = 5
},
-- surface/trackype/smoothness
-- values were estimated from looking at the photos at the relevant wiki pages
-- max speed for surfaces
surface_speeds = {
asphalt = nil, -- nil mean no limit. removing the line has the same effect
concrete = nil,
["concrete:plates"] = nil,
["concrete:lanes"] = nil,
paved = nil,
cement = 80,
compacted = 80,
fine_gravel = 80,
paving_stones = 60,
metal = 60,
bricks = 60,
grass = 40,
wood = 40,
sett = 40,
grass_paver = 40,
gravel = 40,
unpaved = 40,
ground = 40,
dirt = 40,
pebblestone = 40,
tartan = 40,
cobblestone = 30,
clay = 30,
earth = 20,
stone = 20,
rocky = 20,
sand = 20,
mud = 10
},
-- max speed for tracktypes
tracktype_speeds = {
grade1 = 60,
grade2 = 40,
grade3 = 30,
grade4 = 25,
grade5 = 20
},
-- max speed for smoothnesses
smoothness_speeds = {
intermediate = 80,
bad = 40,
very_bad = 20,
horrible = 10,
very_horrible = 5,
impassable = 0
},
-- http://wiki.openstreetmap.org/wiki/Speed_limits
maxspeed_table_default = {
urban = 50,
rural = 90,
trunk = 110,
motorway = 130
},
-- List only exceptions
maxspeed_table = {
["ch:rural"] = 80,
["ch:trunk"] = 100,
["ch:motorway"] = 120,
["de:living_street"] = 7,
["ru:living_street"] = 20,
["ru:urban"] = 60,
["ua:urban"] = 60,
["at:rural"] = 100,
["de:rural"] = 100,
["at:trunk"] = 100,
["cz:trunk"] = 0,
["ro:trunk"] = 100,
["cz:motorway"] = 0,
["de:motorway"] = 0,
["ru:motorway"] = 110,
["gb:nsl_single"] = (60*1609)/1000,
["gb:nsl_dual"] = (70*1609)/1000,
["gb:motorway"] = (70*1609)/1000,
["uk:nsl_single"] = (60*1609)/1000,
["uk:nsl_dual"] = (70*1609)/1000,
["uk:motorway"] = (70*1609)/1000,
["nl:rural"] = 80,
["nl:trunk"] = 100,
["none"] = 140
}
}
function get_name_suffix_list(vector)
for index,suffix in ipairs(profile.suffix_list) do
vector:Add(suffix)
end
end
function get_restrictions(vector)
for i,v in ipairs(profile.restrictions) do
vector:Add(v)
end
end
function node_function (node, result)
function process_node (profile, node, result)
-- parse access and barrier tags
local access = find_access_tag(node, profile.access_tags_hierarchy)
if access then
@ -303,7 +289,7 @@ function node_function (node, result)
end
end
function way_function(way, result)
function process_way(profile, way, result)
-- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
@ -335,61 +321,61 @@ function way_function(way, result)
handlers = Sequence {
-- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry
'handle_default_mode',
WayHandlers.default_mode,
-- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible
'handle_blocked_ways',
WayHandlers.blocked_ways,
-- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle
'handle_access',
WayHandlers.access,
-- check whether forward/backward directions are routable
'handle_oneway',
WayHandlers.oneway,
-- check a road's destination
'handle_destinations',
WayHandlers.destinations,
-- check whether we're using a special transport mode
'handle_ferries',
'handle_movables',
WayHandlers.ferries,
WayHandlers.movables,
-- handle service road restrictions
'handle_service',
WayHandlers.service,
-- handle hov
'handle_hov',
WayHandlers.hov,
-- compute speed taking into account way type, maxspeed tags, etc.
'handle_speed',
'handle_surface',
'handle_maxspeed',
'handle_penalties',
WayHandlers.speed,
WayHandlers.surface,
WayHandlers.maxspeed,
WayHandlers.penalties,
-- compute class labels
'handle_classes',
WayHandlers.classes,
-- handle turn lanes and road classification, used for guidance
'handle_turn_lanes',
'handle_classification',
WayHandlers.turn_lanes,
WayHandlers.classification,
-- handle various other flags
'handle_roundabouts',
'handle_startpoint',
WayHandlers.roundabouts,
WayHandlers.startpoint,
-- set name, ref and pronunciation
'handle_names',
WayHandlers.names,
-- set weight properties of the way
'handle_weights'
WayHandlers.weights
}
Handlers.run(handlers,way,result,data,profile)
WayHandlers.run(profile,way,result,data,handlers)
end
function turn_function (turn)
function process_turn (profile, turn)
-- Use a sigmoid function to return a penalty that maxes out at turn_penalty
-- over the space of 0-180 degrees. Values here were chosen by fitting
-- the function to some turn penalty samples from real driving.
@ -397,7 +383,7 @@ function turn_function (turn)
local turn_bias = profile.turn_bias
if turn.has_traffic_light then
turn.duration = profile.traffic_light_penalty
turn.duration = profile.properties.traffic_light_penalty
end
if turn.turn_type ~= turn_type.no_turn then
@ -408,21 +394,28 @@ function turn_function (turn)
end
if turn.direction_modifier == direction_modifier.u_turn then
turn.duration = turn.duration + profile.u_turn_penalty
turn.duration = turn.duration + profile.properties.u_turn_penalty
end
end
-- for distance based routing we don't want to have penalties based on turn angle
if properties.weight_name == 'distance' then
if profile.properties.weight_name == 'distance' then
turn.weight = 0
else
turn.weight = turn.duration
end
if properties.weight_name == 'routability' then
if profile.properties.weight_name == 'routability' then
-- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then
turn.weight = properties.max_turn_weight;
turn.weight = constants.max_turn_weight
end
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -1,149 +1,143 @@
-- Foot profile
api_version = 1
api_version = 2
local find_access_tag = require("lib/access").find_access_tag
local Set = require('lib/set')
local Sequence = require('lib/sequence')
local Handlers = require("lib/handlers")
local next = next -- bind to local for speed
Set = require('lib/set')
Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
find_access_tag = require("lib/access").find_access_tag
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s
properties.use_turn_restrictions = false
properties.continue_straight_at_waypoint = false
properties.weight_name = 'duration'
--properties.weight_name = 'routability'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local walking_speed = 5
local profile = {
default_mode = mode.walking,
default_speed = walking_speed,
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
traffic_light_penalty = 2,
u_turn_penalty = 2,
barrier_whitelist = Set {
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'kerb',
'block'
},
access_tag_whitelist = Set {
'yes',
'foot',
'permissive',
'designated'
},
access_tag_blacklist = Set {
'no',
'agricultural',
'forestry',
'private',
'delivery',
},
restricted_access_tag_list = Set { },
restricted_highway_whitelist = Set { },
access_tags_hierarchy = Sequence {
'foot',
'access'
},
restrictions = Sequence {
'foot'
},
-- list of suffixes to suppress in name change instructions
suffix_list = Set {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
avoid = Set {
'impassable',
'construction',
'proposed'
},
speeds = Sequence {
highway = {
primary = walking_speed,
primary_link = walking_speed,
secondary = walking_speed,
secondary_link = walking_speed,
tertiary = walking_speed,
tertiary_link = walking_speed,
unclassified = walking_speed,
residential = walking_speed,
road = walking_speed,
living_street = walking_speed,
service = walking_speed,
track = walking_speed,
path = walking_speed,
steps = walking_speed,
pedestrian = walking_speed,
footway = walking_speed,
pier = walking_speed,
function setup()
local walking_speed = 5
return {
properties = {
weight_name = 'duration',
max_speed_for_map_matching = 40/3.6, -- kmph -> m/s
call_tagless_node_function = false,
traffic_light_penalty = 2,
u_turn_penalty = 2,
continue_straight_at_waypoint = false,
use_turn_restrictions = false,
},
railway = {
platform = walking_speed
default_mode = mode.walking,
default_speed = walking_speed,
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
barrier_whitelist = Set {
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'kerb',
'block'
},
amenity = {
parking = walking_speed,
parking_entrance= walking_speed
access_tag_whitelist = Set {
'yes',
'foot',
'permissive',
'designated'
},
man_made = {
pier = walking_speed
access_tag_blacklist = Set {
'no',
'agricultural',
'forestry',
'private',
'delivery',
},
leisure = {
track = walking_speed
restricted_access_tag_list = Set { },
restricted_highway_whitelist = Set { },
access_tags_hierarchy = Sequence {
'foot',
'access'
},
restrictions = Sequence {
'foot'
},
-- list of suffixes to suppress in name change instructions
suffix_list = Set {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
avoid = Set {
'impassable'
},
speeds = Sequence {
highway = {
primary = walking_speed,
primary_link = walking_speed,
secondary = walking_speed,
secondary_link = walking_speed,
tertiary = walking_speed,
tertiary_link = walking_speed,
unclassified = walking_speed,
residential = walking_speed,
road = walking_speed,
living_street = walking_speed,
service = walking_speed,
track = walking_speed,
path = walking_speed,
steps = walking_speed,
pedestrian = walking_speed,
footway = walking_speed,
pier = walking_speed,
},
railway = {
platform = walking_speed
},
amenity = {
parking = walking_speed,
parking_entrance= walking_speed
},
man_made = {
pier = walking_speed
},
leisure = {
track = walking_speed
}
},
route_speeds = {
ferry = 5
},
bridge_speeds = {
},
surface_speeds = {
fine_gravel = walking_speed*0.75,
gravel = walking_speed*0.75,
pebblestone = walking_speed*0.75,
mud = walking_speed*0.5,
sand = walking_speed*0.5
},
tracktype_speeds = {
},
smoothness_speeds = {
}
},
route_speeds = {
ferry = 5
},
bridge_speeds = {
},
surface_speeds = {
fine_gravel = walking_speed*0.75,
gravel = walking_speed*0.75,
pebblestone = walking_speed*0.75,
mud = walking_speed*0.5,
sand = walking_speed*0.5
},
tracktype_speeds = {
},
smoothness_speeds = {
}
}
end
function node_function (node, result)
function process_node (profile, node, result)
-- parse access and barrier tags
local access = find_access_tag(node, profile.access_tags_hierarchy)
if access then
@ -171,7 +165,7 @@ function node_function (node, result)
end
-- main entry point for processsing a way
function way_function(way, result)
function process_way(profile, way, result)
-- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
@ -208,59 +202,66 @@ function way_function(way, result)
local handlers = Sequence {
-- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry
'handle_default_mode',
WayHandlers.default_mode,
-- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible
'handle_blocked_ways',
WayHandlers.blocked_ways,
-- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle
'handle_access',
WayHandlers.access,
-- check whether forward/backward directons are routable
'handle_oneway',
WayHandlers.oneway,
-- check whether forward/backward directons are routable
'handle_destinations',
WayHandlers.destinations,
-- check whether we're using a special transport mode
'handle_ferries',
'handle_movables',
WayHandlers.ferries,
WayHandlers.movables,
-- compute speed taking into account way type, maxspeed tags, etc.
'handle_speed',
'handle_surface',
WayHandlers.speed,
WayHandlers.surface,
-- handle turn lanes and road classification, used for guidance
'handle_classification',
WayHandlers.classification,
-- handle various other flags
'handle_roundabouts',
'handle_startpoint',
WayHandlers.roundabouts,
WayHandlers.startpoint,
-- set name, ref and pronunciation
'handle_names'
WayHandlers.names
}
Handlers.run(handlers,way,result,data,profile)
WayHandlers.run(profile,way,result,data,handlers)
end
function turn_function (turn)
function process_turn (profile, turn)
turn.duration = 0.
if turn.direction_modifier == direction_modifier.u_turn then
turn.duration = turn.duration + profile.u_turn_penalty
turn.duration = turn.duration + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = profile.traffic_light_penalty
turn.duration = profile.properties.traffic_light_penalty
end
if properties.weight_name == 'routability' then
if profile.properties.weight_name == 'routability' then
-- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then
turn.weight = turn.weight + 3000
end
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -90,7 +90,8 @@ function Debug.report_tag_fetches()
end
function Debug.load_profile(profile)
require(profile)
Debug.functions = require(profile)
Debug.profile = Debug.functions.setup()
end
function Debug.reset_tag_fetch_counts()
@ -115,7 +116,7 @@ function Debug.register_tag_fetch(k)
end
function Debug.way_function(way,result)
function Debug.process_way(way,result)
-- setup result table
result.road_classification = {}
@ -132,8 +133,8 @@ function Debug.way_function(way,result)
-- reset tag counts
Debug:reset_tag_fetch_counts()
-- call the global method defined in the profile file
way_function(way,result)
-- call the way processsing function
Debug.functions.process_way(Debug.profile,way,result)
end
return Debug

View File

@ -1,7 +1,7 @@
-- Sequence of items
-- Ordered, but have to loop through items to check for inclusion.
-- Currently the same as a table.
-- Adds the convenience function append() to append to the sequnce.
function Sequence(source)
return source

View File

@ -9,25 +9,26 @@ local set_classification = require("lib/guidance").set_classification
local get_destination = require("lib/destination").get_destination
local Tags = require('lib/tags')
Handlers = {}
WayHandlers = {}
-- check that way has at least one tag that could imply routability-
-- we store the checked tags in data, to avoid fetching again later
function Handlers.handle_tag_prefetch(way,result,data,profile)
function WayHandlers.tag_prefetch(profile,way,result,data)
for key,v in pairs(profile.prefetch) do
data[key] = way:get_value_by_key( key )
end
return next(data) ~= nil
end
-- set default mode
function Handlers.handle_default_mode(way,result,data,profile)
function WayHandlers.default_mode(profile,way,result,data)
result.forward_mode = profile.default_mode
result.backward_mode = profile.default_mode
end
-- handles name, including ref and pronunciation
function Handlers.handle_names(way,result,data,profile)
function WayHandlers.names(profile,way,result,data)
-- parse the remaining tags
local name = way:get_value_by_key("name")
local pronunciation = way:get_value_by_key("name:pronunciation")
@ -53,7 +54,7 @@ function Handlers.handle_names(way,result,data,profile)
end
-- junctions
function Handlers.handle_roundabouts(way,result,data,profile)
function WayHandlers.roundabouts(profile,way,result,data)
local junction = way:get_value_by_key("junction");
if junction == "roundabout" then
@ -69,7 +70,7 @@ function Handlers.handle_roundabouts(way,result,data,profile)
end
-- determine if this way can be used as a start/end point for routing
function Handlers.handle_startpoint(way,result,data,profile)
function WayHandlers.startpoint(profile,way,result,data)
-- if profile specifies set of allowed start modes, then check for that
-- otherwise require default mode
if profile.allowed_start_modes then
@ -82,7 +83,7 @@ function Handlers.handle_startpoint(way,result,data,profile)
end
-- handle turn lanes
function Handlers.handle_turn_lanes(way,result,data,profile)
function WayHandlers.turn_lanes(profile,way,result,data)
local forward, backward = get_turn_lanes(way,data)
if forward then
@ -95,12 +96,12 @@ function Handlers.handle_turn_lanes(way,result,data,profile)
end
-- set the road classification based on guidance globals configuration
function Handlers.handle_classification(way,result,data,profile)
function WayHandlers.classification(profile,way,result,data)
set_classification(data.highway,result,way)
end
-- handle destination tags
function Handlers.handle_destinations(way,result,data,profile)
function WayHandlers.destinations(profile,way,result,data)
if data.is_forward_oneway or data.is_reverse_oneway then
local destination = get_destination(way, data.is_forward_oneway)
result.destinations = canonicalizeStringList(destination, ",")
@ -108,7 +109,7 @@ function Handlers.handle_destinations(way,result,data,profile)
end
-- handling ferries and piers
function Handlers.handle_ferries(way,result,data,profile)
function WayHandlers.ferries(profile,way,result,data)
local route = data.route
if route then
local route_speed = profile.route_speeds[route]
@ -126,7 +127,7 @@ function Handlers.handle_ferries(way,result,data,profile)
end
-- handling movable bridges
function Handlers.handle_movables(way,result,data,profile)
function WayHandlers.movables(profile,way,result,data)
local bridge = data.bridge
if bridge then
local bridge_speed = profile.bridge_speeds[bridge]
@ -148,7 +149,7 @@ function Handlers.handle_movables(way,result,data,profile)
end
-- service roads
function Handlers.handle_service(way,result,data,profile)
function WayHandlers.service(profile,way,result,data)
local service = way:get_value_by_key("service")
if service then
-- Set don't allow access to certain service roads
@ -161,7 +162,7 @@ function Handlers.handle_service(way,result,data,profile)
end
-- all lanes restricted to hov vehicles?
function Handlers.has_all_designated_hov_lanes(lanes)
function WayHandlers.has_all_designated_hov_lanes(lanes)
if not lanes then
return false
end
@ -176,7 +177,7 @@ function Handlers.has_all_designated_hov_lanes(lanes)
end
-- handle high occupancy vehicle tags
function Handlers.handle_hov(way,result,data,profile)
function WayHandlers.hov(profile,way,result,data)
-- respect user-preference for HOV
if not profile.avoid.hov_lanes then
return
@ -189,11 +190,11 @@ function Handlers.handle_hov(way,result,data,profile)
end
data.hov_lanes_forward, data.hov_lanes_backward = Tags.get_forward_backward_by_key(way,data,'hov:lanes')
local all_hov_forward = Handlers.has_all_designated_hov_lanes(data.hov_lanes_forward)
local all_hov_backward = Handlers.has_all_designated_hov_lanes(data.hov_lanes_backward)
local all_hov_forward = WayHandlers.has_all_designated_hov_lanes(data.hov_lanes_forward)
local all_hov_backward = WayHandlers.has_all_designated_hov_lanes(data.hov_lanes_backward)
-- in this case we will use turn penalties instead of filtering out
if properties.weight_name == 'routability' then
if profile.properties.weight_name == 'routability' then
if (all_hov_forward) then
result.forward_restricted = true
end
@ -213,7 +214,7 @@ function Handlers.handle_hov(way,result,data,profile)
end
-- check accessibility by traversing our access tag hierarchy
function Handlers.handle_access(way,result,data,profile)
function WayHandlers.access(profile,way,result,data)
data.forward_access, data.backward_access =
Tags.get_forward_backward_by_set(way,data,profile.access_tags_hierarchy)
@ -242,7 +243,7 @@ function Handlers.handle_access(way,result,data,profile)
end
-- handle speed (excluding maxspeed)
function Handlers.handle_speed(way,result,data,profile)
function WayHandlers.speed(profile,way,result,data)
if result.forward_speed ~= -1 then
return -- abort if already set, eg. by a route
end
@ -278,7 +279,7 @@ function Handlers.handle_speed(way,result,data,profile)
end
-- add class information
function Handlers.handle_classes(way,result,data,profile)
function WayHandlers.classes(profile,way,result,data)
local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll")
local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
@ -310,7 +311,7 @@ function Handlers.handle_classes(way,result,data,profile)
end
-- reduce speed on bad surfaces
function Handlers.handle_surface(way,result,data,profile)
function WayHandlers.surface(profile,way,result,data)
local surface = way:get_value_by_key("surface")
local tracktype = way:get_value_by_key("tracktype")
local smoothness = way:get_value_by_key("smoothness")
@ -330,7 +331,7 @@ function Handlers.handle_surface(way,result,data,profile)
end
-- scale speeds to get better average driving times
function Handlers.handle_penalties(way,result,data,profile)
function WayHandlers.penalties(profile,way,result,data)
-- heavily penalize a way tagged with all HOV lanes
-- in order to only route over them if there is no other option
local service_penalty = 1.0
@ -375,7 +376,7 @@ function Handlers.handle_penalties(way,result,data,profile)
local forward_penalty = math.min(service_penalty, width_penalty, alternating_penalty, sideroad_penalty)
local backward_penalty = math.min(service_penalty, width_penalty, alternating_penalty, sideroad_penalty)
if properties.weight_name == 'routability' then
if profile.properties.weight_name == 'routability' then
if result.forward_speed > 0 then
result.forward_rate = (result.forward_speed * forward_penalty) / 3.6
end
@ -389,11 +390,11 @@ function Handlers.handle_penalties(way,result,data,profile)
end
-- maxspeed and advisory maxspeed
function Handlers.handle_maxspeed(way,result,data,profile)
function WayHandlers.maxspeed(profile,way,result,data)
local keys = Sequence { 'maxspeed:advisory', 'maxspeed' }
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
forward = Handlers.parse_maxspeed(forward,profile)
backward = Handlers.parse_maxspeed(backward,profile)
forward = WayHandlers.parse_maxspeed(forward,profile)
backward = WayHandlers.parse_maxspeed(backward,profile)
if forward and forward > 0 then
result.forward_speed = forward * profile.speed_reduction
@ -404,7 +405,7 @@ function Handlers.handle_maxspeed(way,result,data,profile)
end
end
function Handlers.parse_maxspeed(source,profile)
function WayHandlers.parse_maxspeed(source,profile)
if not source then
return 0
end
@ -429,7 +430,7 @@ function Handlers.parse_maxspeed(source,profile)
end
-- handle oneways tags
function Handlers.handle_oneway(way,result,data,profile)
function WayHandlers.oneway(profile,way,result,data)
if not profile.oneway_handling then
return
end
@ -476,8 +477,8 @@ function Handlers.handle_oneway(way,result,data,profile)
end
end
function Handlers.handle_weights(way,result,data,profile)
if properties.weight_name == 'distance' then
function WayHandlers.weights(profile,way,result,data)
if profile.properties.weight_name == 'distance' then
result.weight = -1
-- set weight rates to 1 for the distance weight, edge weights are distance / rate
if (result.forward_mode ~= mode.inaccessible and result.forward_speed > 0) then
@ -490,7 +491,7 @@ function Handlers.handle_weights(way,result,data,profile)
end
-- handle various that can block access
function Handlers.handle_blocked_ways(way,result,data,profile)
function WayHandlers.blocked_ways(profile,way,result,data)
-- areas
if profile.avoid.area and way:get_value_by_key("area") == "yes" then
@ -554,27 +555,27 @@ end
-- Call a sequence of handlers, aborting in case a handler returns false. Example:
--
-- handlers = Sequence {
-- 'handle_tag_prefetch',
-- 'handle_default_mode',
-- 'handle_blocked_ways',
-- 'handle_access',
-- 'handle_speed',
-- 'handle_names'
-- WayHandlers.tag_prefetch,
-- WayHandlers.default_mode,
-- WayHandlers.blocked_ways,
-- WayHandlers.access,
-- WayHandlers.speed,
-- WayHandlers.names
-- }
--
-- Handlers.run(handlers,way,result,data,profile)
-- WayHandlers.run(handlers,way,result,data,profile)
--
-- Each method in the list will be called on the Handlers object.
-- All handlers must accept the parameteres (way,result,data,profile) and return false
-- Each method in the list will be called on the WayHandlers object.
-- All handlers must accept the parameteres (profile,way,result,data) and return false
-- if the handler chain should be aborted.
-- To ensure the correct order of method calls, use a Sequence of handler names.
function Handlers.run(handlers,way,result,data,profile)
function WayHandlers.run(profile,way,result,data,handlers)
for i,handler in ipairs(handlers) do
if Handlers[handler](way,result,data,profile) == false then
if handler(profile,way,result,data) == false then
return false
end
end
end
return Handlers
return WayHandlers

View File

@ -1,19 +1,30 @@
api_version = 1
-- Rasterbot profile
properties.force_split_edges = true
api_version = 2
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
function setup()
local raster_path = os.getenv('OSRM_RASTER_SOURCE') or "rastersource.asc"
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
return {
properties = {
force_split_edges = true,
process_call_tagless_node = false,
},
function node_function (node, result)
raster_source = raster:load(
raster_path,
0, -- lon_min
0.1, -- lon_max
0, -- lat_min
0.1, -- lat_max
5, -- nrows
4 -- ncols
)
}
end
function way_function (way, result)
-- Minimalist process_ways in order to test source_ and process_segments
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local name = way:get_value_by_key("name")
@ -28,25 +39,9 @@ function way_function (way, result)
result.backward_speed = 15
end
function source_function ()
local path = os.getenv('OSRM_RASTER_SOURCE')
if not path then
path = "rastersource.asc"
end
raster_source = sources:load(
path,
0, -- lon_min
0.1, -- lon_max
0, -- lat_min
0.1, -- lat_max
5, -- nrows
4 -- ncols
)
end
function segment_function (segment)
local sourceData = sources:query(raster_source, segment.source.lon, segment.source.lat)
local targetData = sources:query(raster_source, segment.target.lon, segment.target.lat)
function process_segment (profile, segment)
local sourceData = raster:query(profile.raster_source, segment.source.lon, segment.source.lat)
local targetData = raster:query(profile.raster_source, segment.target.lon, segment.target.lat)
io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n")
local invalid = sourceData.invalid_data()
local scaled_weight = segment.weight
@ -66,3 +61,9 @@ function segment_function (segment)
segment.weight = scaled_weight
segment.duration = scaled_duration
end
return {
setup = setup,
process_way = process_way,
process_segment = process_segment
}

View File

@ -1,50 +1,10 @@
api_version = 1
-- Rasterbot profile
-- Rasterbot with interpolation profile
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
functions = require('rasterbot')
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
function node_function (node, result)
end
function way_function (way, result)
local highway = way:get_value_by_key("highway")
local name = way:get_value_by_key("name")
if name then
result.name = name
end
result.forward_mode = mode.cycling
result.backward_mode = mode.cycling
result.forward_speed = 15
result.backward_speed = 15
end
function source_function ()
local path = os.getenv('OSRM_RASTER_SOURCE')
if not path then
path = "rastersource.asc"
end
raster_source = sources:load(
path,
0, -- lon_min
0.1, -- lon_max
0, -- lat_min
0.1, -- lat_max
5, -- nrows
4 -- ncols
)
end
function segment_function (segment)
local sourceData = sources:interpolate(raster_source, segment.source.lon, segment.source.lat)
local targetData = sources:interpolate(raster_source, segment.target.lon, segment.target.lat)
functions.process_segment = function(profile, segment)
local sourceData = raster:interpolate(profile.raster_source, segment.source.lon, segment.source.lat)
local targetData = raster:interpolate(profile.raster_source, segment.target.lon, segment.target.lat)
io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n")
local invalid = sourceData.invalid_data()
local scaled_weight = segment.weight
@ -64,3 +24,5 @@ function segment_function (segment)
segment.weight = scaled_weight
segment.duration = scaled_duration
end
return functions

View File

@ -1,49 +1,35 @@
api_version = 1
-- Testbot profile
-- Moves at fixed, well-known speeds, practical for testing speed and travel times:
-- Primary road: 36km/h = 36000m/3600s = 100m/10s
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
speed_profile = {
["primary"] = 36,
["secondary"] = 18,
["tertiary"] = 12,
["steps"] = 6,
["default"] = 24
}
api_version = 2
-- these settings are read directly by osrm
function setup()
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
uturn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
properties.continue_straight_at_waypoint = true
properties.use_turn_restrictions = true
properties.max_speed_for_map_matching = 30/3.6 --km -> m/s
properties.weight_name = 'duration'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local uturn_penalty = 20
local traffic_light_penalty = 7 -- seconds
function limit_speed(speed, limits)
-- don't use ipairs(), since it stops at the first nil value
for i=1, #limits do
limit = limits[i]
if limit ~= nil and limit > 0 then
if limit < speed then
return limit -- stop at first speedlimit that's smaller than speed
end
end
end
return speed
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6,
}
}
end
function node_function (node, result)
function process_node (profile, node, result)
local traffic_signal = node:get_value_by_key("highway")
if traffic_signal and traffic_signal == "traffic_signals" then
@ -52,7 +38,7 @@ function node_function (node, result)
end
end
function way_function (way, result)
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
@ -75,7 +61,7 @@ function way_function (way, result)
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = speed_profile[highway] or speed_profile['default']
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
@ -122,12 +108,19 @@ function way_function (way, result)
end
end
function turn_function (turn)
function process_turn (profile, turn)
if turn.direction_modifier == direction_modifier.uturn then
turn.duration = uturn_penalty
turn.weight = uturn_penalty
turn.duration = profile.properties.uturn_penalty
turn.weight = profile.properties.uturn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + traffic_light_penalty
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -1,10 +1,10 @@
api_version = 1
-- Testbot, with turn penalty
-- Used for testing turn penalties
require 'testbot'
functions = require 'testbot'
function turn_function (turn)
turn.duration = 20 * math.abs(turn.angle) / 180
functions.process_turn = function(profile, turn)
turn.duration = 20 * math.abs(turn.angle) / 180
end
return functions

View File

@ -224,9 +224,6 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
turn_lane_map,
scripting_environment.GetProfileProperties());
// setup raster sources
scripting_environment.SetupSources();
std::string generator = header.get("generator");
if (generator.empty())
{

View File

@ -79,7 +79,7 @@ RasterDatum RasterSource::GetRasterInterpolate(const int lon, const int lat) con
}
// Load raster source into memory
int SourceContainer::LoadRasterSource(const std::string &path_string,
int RasterContainer::LoadRasterSource(const std::string &path_string,
double xmin,
double xmax,
double ymin,
@ -125,7 +125,7 @@ int SourceContainer::LoadRasterSource(const std::string &path_string,
}
// External function for looking up nearest data point from a specified source
RasterDatum SourceContainer::GetRasterDataFromSource(unsigned int source_id, double lon, double lat)
RasterDatum RasterContainer::GetRasterDataFromSource(unsigned int source_id, double lon, double lat)
{
if (LoadedSources.size() < source_id + 1)
{
@ -146,7 +146,7 @@ RasterDatum SourceContainer::GetRasterDataFromSource(unsigned int source_id, dou
// External function for looking up interpolated data from a specified source
RasterDatum
SourceContainer::GetRasterInterpolateFromSource(unsigned int source_id, double lon, double lat)
RasterContainer::GetRasterInterpolateFromSource(unsigned int source_id, double lon, double lat)
{
if (LoadedSources.size() < source_id + 1)
{

View File

@ -216,15 +216,13 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
"sharp_left",
extractor::guidance::DirectionModifier::SharpLeft);
context.state.new_usertype<SourceContainer>("sources",
context.state.new_usertype<RasterContainer>("raster",
"load",
&SourceContainer::LoadRasterSource,
&RasterContainer::LoadRasterSource,
"query",
&SourceContainer::GetRasterDataFromSource,
&RasterContainer::GetRasterDataFromSource,
"interpolate",
&SourceContainer::GetRasterInterpolateFromSource);
context.state.new_enum("constants", "precision", COORDINATE_PRECISION);
&RasterContainer::GetRasterInterpolateFromSource);
context.state.new_usertype<ProfileProperties>(
"ProfileProperties",
@ -421,8 +419,10 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
context.state.new_usertype<RasterDatum>(
"RasterDatum", "datum", &RasterDatum::datum, "invalid_data", &RasterDatum::get_invalid);
// the "properties" global is only used in v1 of the api, but we don't know
// the version until we have read the file. so we have to declare it in any case.
// we will then clear it for v2 profiles after reading the file
context.state["properties"] = &context.properties;
context.state["sources"] = &context.sources;
//
// end of register block
@ -430,24 +430,15 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
util::luaAddScriptFolderToLoadPath(context.state.lua_state(), file_name.c_str());
context.state.script_file(file_name);
// cache references to functions for faster execution
context.turn_function = context.state["turn_function"];
context.node_function = context.state["node_function"];
context.way_function = context.state["way_function"];
context.segment_function = context.state["segment_function"];
context.has_turn_penalty_function = context.turn_function.valid();
context.has_node_function = context.node_function.valid();
context.has_way_function = context.way_function.valid();
context.has_segment_function = context.segment_function.valid();
sol::optional<sol::table> function_table = context.state.script_file(file_name);
// Check profile API version
auto maybe_version = context.state.get<sol::optional<int>>("api_version");
if (maybe_version)
{
context.api_version = *maybe_version;
else
{
context.api_version = 0;
}
if (context.api_version < SUPPORTED_MIN_API_VERSION ||
@ -459,14 +450,140 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
" are supported." + SOURCE_REF);
}
// Assert that version-dependent properties were not changed by profile
util::Log() << "Using profile api version " << context.api_version;
// version-dependent parts of the api
switch (context.api_version)
{
case 2:
{
// clear global not used in v2
context.state["properties"] = sol::nullopt;
// check function table
if (function_table == sol::nullopt)
throw util::exception("Profile must return a function table.");
// setup helpers
context.state["raster"] = &context.raster_sources;
// set constants
context.state.new_enum("constants",
"precision",
COORDINATE_PRECISION,
"max_turn_weight",
std::numeric_limits<TurnPenalty>::max());
// call initialize function
sol::function setup_function = function_table.value()["setup"];
if (!setup_function.valid())
throw util::exception("Profile must have an setup() function.");
sol::optional<sol::table> profile_table = setup_function();
if (profile_table == sol::nullopt)
throw util::exception("Profile setup() must return a table.");
else
context.profile_table = profile_table.value();
// store functions
context.turn_function = function_table.value()["process_turn"];
context.node_function = function_table.value()["process_node"];
context.way_function = function_table.value()["process_way"];
context.segment_function = function_table.value()["process_segment"];
context.has_turn_penalty_function = context.turn_function.valid();
context.has_node_function = context.node_function.valid();
context.has_way_function = context.way_function.valid();
context.has_segment_function = context.segment_function.valid();
// read properties from 'profile.properties' table
sol::table properties = context.profile_table["properties"];
if (properties.valid())
{
sol::optional<std::string> weight_name = properties["weight_name"];
if (weight_name != sol::nullopt)
context.properties.SetWeightName(weight_name.value());
sol::optional<std::int32_t> traffic_signal_penalty =
properties["traffic_signal_penalty"];
if (traffic_signal_penalty != sol::nullopt)
context.properties.SetTrafficSignalPenalty(traffic_signal_penalty.value());
sol::optional<std::int32_t> u_turn_penalty = properties["u_turn_penalty"];
if (u_turn_penalty != sol::nullopt)
context.properties.SetUturnPenalty(u_turn_penalty.value());
sol::optional<double> max_speed_for_map_matching =
properties["max_speed_for_map_matching"];
if (max_speed_for_map_matching != sol::nullopt)
context.properties.SetMaxSpeedForMapMatching(max_speed_for_map_matching.value());
sol::optional<bool> continue_straight_at_waypoint =
properties["continue_straight_at_waypoint"];
if (continue_straight_at_waypoint != sol::nullopt)
context.properties.continue_straight_at_waypoint =
continue_straight_at_waypoint.value();
sol::optional<bool> use_turn_restrictions = properties["use_turn_restrictions"];
if (use_turn_restrictions != sol::nullopt)
context.properties.use_turn_restrictions = use_turn_restrictions.value();
sol::optional<bool> left_hand_driving = properties["left_hand_driving"];
if (left_hand_driving != sol::nullopt)
context.properties.left_hand_driving = left_hand_driving.value();
sol::optional<unsigned> weight_precision = properties["weight_precision"];
if (weight_precision != sol::nullopt)
context.properties.weight_precision = weight_precision.value();
sol::optional<bool> force_split_edges = properties["force_split_edges"];
if (force_split_edges != sol::nullopt)
context.properties.force_split_edges = force_split_edges.value();
}
break;
}
case 1:
{
// cache references to functions for faster execution
context.turn_function = context.state["turn_function"];
context.node_function = context.state["node_function"];
context.way_function = context.state["way_function"];
context.segment_function = context.state["segment_function"];
context.has_turn_penalty_function = context.turn_function.valid();
context.has_node_function = context.node_function.valid();
context.has_way_function = context.way_function.valid();
context.has_segment_function = context.segment_function.valid();
// setup helpers
context.state["sources"] = &context.raster_sources;
// set constants
context.state.new_enum("constants", "precision", COORDINATE_PRECISION);
BOOST_ASSERT(context.properties.GetUturnPenalty() == 0);
BOOST_ASSERT(context.properties.GetTrafficSignalPenalty() == 0);
// call source_function if present
sol::function source_function = context.state["source_function"];
if (source_function.valid())
{
source_function();
}
break;
}
case 0:
// cache references to functions for faster execution
context.turn_function = context.state["turn_function"];
context.node_function = context.state["node_function"];
context.way_function = context.state["way_function"];
context.segment_function = context.state["segment_function"];
context.has_turn_penalty_function = context.turn_function.valid();
context.has_node_function = context.node_function.valid();
context.has_way_function = context.way_function.valid();
context.has_segment_function = context.segment_function.valid();
BOOST_ASSERT(context.properties.GetWeightName() == "duration");
break;
}
@ -542,48 +659,62 @@ void Sol2ScriptingEnvironment::ProcessElements(
}
}
std::vector<std::string> Sol2ScriptingEnvironment::GetNameSuffixList()
std::vector<std::string>
Sol2ScriptingEnvironment::GetStringListFromFunction(const std::string &function_name)
{
auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr);
std::vector<std::string> suffixes_vector;
sol::function get_name_suffix_list = context.state["get_name_suffix_list"];
if (get_name_suffix_list.valid())
std::vector<std::string> strings;
sol::function function = context.state[function_name];
if (function.valid())
{
get_name_suffix_list(suffixes_vector);
function(strings);
}
return strings;
}
return suffixes_vector;
std::vector<std::string>
Sol2ScriptingEnvironment::GetStringListFromTable(const std::string &table_name)
{
auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr);
std::vector<std::string> strings;
sol::table table = context.profile_table[table_name];
if (table.valid())
{
for (auto &&pair : table)
{
strings.push_back(pair.second.as<std::string>());
};
}
return strings;
}
std::vector<std::string> Sol2ScriptingEnvironment::GetNameSuffixList()
{
auto &context = GetSol2Context();
switch (context.api_version)
{
case 2:
return Sol2ScriptingEnvironment::GetStringListFromTable("suffix_list");
case 1:
return Sol2ScriptingEnvironment::GetStringListFromFunction("get_name_suffix_list");
default:
return {};
}
}
std::vector<std::string> Sol2ScriptingEnvironment::GetRestrictions()
{
auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr);
std::vector<std::string> restrictions;
sol::function get_restrictions = context.state["get_restrictions"];
if (get_restrictions.valid())
switch (context.api_version)
{
get_restrictions(restrictions);
}
return restrictions;
}
void Sol2ScriptingEnvironment::SetupSources()
{
auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr);
sol::function source_function = context.state["source_function"];
if (source_function.valid())
{
source_function();
case 2:
return Sol2ScriptingEnvironment::GetStringListFromTable("restrictions");
case 1:
return Sol2ScriptingEnvironment::GetStringListFromFunction("get_restrictions");
default:
return {};
}
}
@ -593,6 +724,21 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
switch (context.api_version)
{
case 2:
if (context.has_turn_penalty_function)
{
context.turn_function(context.profile_table, turn);
// Turn weight falls back to the duration value in deciseconds
// or uses the extracted unit-less weight value
if (context.properties.fallback_to_duration)
turn.weight = turn.duration;
else
// cap turn weight to max turn weight, which depend on weight precision
turn.weight = std::min(turn.weight, context.properties.GetMaxTurnWeight());
}
break;
case 1:
if (context.has_turn_penalty_function)
{
@ -644,6 +790,9 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment)
{
switch (context.api_version)
{
case 2:
context.segment_function(context.profile_table, segment);
break;
case 1:
context.segment_function(segment);
break;
@ -660,14 +809,32 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node, ExtractionNode &
{
BOOST_ASSERT(state.lua_state() != nullptr);
node_function(node, result);
switch (api_version)
{
case 2:
node_function(profile_table, node, result);
break;
case 1:
case 0:
node_function(node, result);
break;
}
}
void LuaScriptingContext::ProcessWay(const osmium::Way &way, ExtractionWay &result)
{
BOOST_ASSERT(state.lua_state() != nullptr);
way_function(way, result);
switch (api_version)
{
case 2:
way_function(profile_table, way, result);
break;
case 1:
case 0:
way_function(way, result);
break;
}
}
}
}

View File

@ -22,7 +22,7 @@ int normalize(double coord) { return static_cast<int>(coord * COORDINATE_PRECISI
BOOST_AUTO_TEST_CASE(raster_test)
{
SourceContainer sources;
RasterContainer sources;
int source_id =
sources.LoadRasterSource(OSRM_FIXTURES_DIR "/raster_data.asc", 1, 1.09, 1, 1.09, 10, 10);
BOOST_CHECK_EQUAL(source_id, 0);