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 # 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 # 5.9.0
- Changes from 5.8: - 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 ## 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. When running OSRM preprocessing commands you specify the profile with the --profile (or the shorthand -p) option, for example:
Alternatively commands will take a lua profile specified with an explicit -p param, 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 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`) 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_speed_for_map_matching | Float | Maximum vehicle speed to be assumed in matching (in m/s)
max_turn_weight | Float | Maximum turn penalty weight 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 Attribute | Type | Notes
----------------------------------------|----------|-------------------------------------------------------------------------- ----------------------------------------|----------|--------------------------------------------------------------------------
@ -60,7 +149,7 @@ forward_classes | Table | Mark this way as being of a
backward_classes | Table | " " backward_classes | Table | " "
duration | Float | Alternative setter for duration of the whole way in both directions 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 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 | " " 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) 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 | " " 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.may_be_ignored | Boolean | Guidance: way is non-highway
road_classification.num_lanes | Unsigned | Guidance: total number of lanes in way 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. 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.
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. 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.
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.
## 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`: The following attributes can be read and set on the result in `process_segment`:
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`:
Attribute | Read/write? | Type | Notes Attribute | Read/write? | Type | Notes
-------------------|-------------|---------|------------------------------------------------------ -------------------|-------------|---------|----------------------------------------
source.lon | Read | Float | Co-ordinates of segment start source.lon | Read | Float | Co-ordinates of segment start
source.lat | Read | Float | " " source.lat | Read | Float | " "
target.lon | Read | Float | Co-ordinates of segment end 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 weight | Read/write | Float | Routing weight for this segment
duration | Read/write | Float | Duration 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 Attribute | Read/write? | Type | Notes
-------------------|-------------|---------|------------------------------------------------------ -------------------|-------------|---------|------------------------------------------------------
direction_modifier | Read | Enum | Geometry of turn. Defined in `include/extractor/guidance/turn_instruction.hpp` 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` 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? 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`) 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 `way_function`) 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) 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) 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) 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 Feature: Bicycle - Route around alleys
Background: Background:
Given the profile file Given the profile file "bicycle" initialized with
""" """
require 'bicycle' profile.properties.weight_name = 'cyclability'
properties.weight_name = 'cyclability'
""" """
Scenario: Bicycle - Avoid taking alleys Scenario: Bicycle - Avoid taking alleys

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -582,9 +582,9 @@ Feature: Turn Lane Guidance
@anticipate @anticipate
Scenario: No Lanes for Roundabouts, see #2626 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 And the node map
""" """

View File

@ -3,11 +3,10 @@ Feature: Basic Roundabout
Background: Background:
Given a grid size of 10 meters Given a grid size of 10 meters
Given the profile file Given the profile file "car" initialized with
""" """
require 'car' profile.properties.left_hand_driving = true
properties.left_hand_driving = true """
"""
Scenario: Roundabout exit counting for left sided driving Scenario: Roundabout exit counting for left sided driving
And a grid size of 10 meters 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 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' local functions = require('testbot')
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.weight = 1 result.weight = 1
result.duration = 1 result.duration = 1
end end
return functions
""" """
And the data has been saved to disk 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 And the data has been saved to disk
Scenario: osrm-extract - Passing base file 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 for _, node in ipairs(way:get_nodes()) do
print('node id ' .. node:id()) print('node id ' .. node:id())
end end
result.forward_mode = mode.driving result.forward_mode = mode.driving
result.forward_speed = 1 result.forward_speed = 1
end end
functions.process_way = way_function
return functions
""" """
When I run "osrm-extract --profile {profile_file} {osm_file}" When I run "osrm-extract --profile {profile_file} {osm_file}"
Then it should exit successfully 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,36 @@
Feature: Profile API version 0 Feature: Profile API version 0
Background: Scenario: Profile api version 0
Given a grid size of 100 meters
Scenario: Not-defined API version
Given the profile file Given the profile file
""" """
function way_function(way, result) api_version = 0
result.forward_mode = mode.driving -- set profile properties
result.forward_speed = 1 properties.u_turn_penalty = 20
end properties.traffic_signal_penalty = 2
""" properties.max_speed_for_map_matching = 180/3.6
And the node map properties.use_turn_restrictions = true
""" properties.continue_straight_at_waypoint = true
ab properties.left_hand_driving = false
""" properties.weight_name = 'duration'
And the ways function node_function (node, result)
| nodes | print ('node_function ' .. node:id())
| ab | end
And the data has been saved to disk function way_function(way, result)
result.name = way:get_value_by_key('name')
When I try to run "osrm-extract --profile {profile_file} {osm_file}" result.forward_mode = mode.driving
Then it should exit successfully result.backward_mode = mode.driving
And stderr should not contain "Invalid profile API version" result.forward_speed = 36
result.backward_speed = 36
Scenario: Out-bound API version print ('way_function ' .. way:id() .. ' ' .. result.name)
Given the profile file end
""" function turn_function (angle)
api_version = 2 print('turn_function ' .. angle)
""" return angle == 0 and 0 or 42
And the node map end
""" function segment_function (source, target, distance, weight)
ab print ('segment_function ' .. source.lon .. ' ' .. source.lat)
""" end
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
"""
And the node map And the node map
""" """
a a
@ -105,3 +57,60 @@ end
| a | b | ac,cb,cb | 24.2s | | a | b | ac,cb,cb | 24.2s |
| a | d | ac,cd,cd | 24.2s | | a | d | ac,cd,cd | 24.2s |
| a | e | ac,ce | 20s | | 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
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 |

View File

@ -6,41 +6,42 @@ Feature: Profile API version 1
Scenario: Basic profile function calls and property values Scenario: Basic profile function calls and property values
Given the profile file Given the profile file
""" """
api_version = 1 api_version = 1
-- set profile properties -- set profile properties
properties.max_speed_for_map_matching = 180/3.6 properties.max_speed_for_map_matching = 180/3.6
properties.use_turn_restrictions = true properties.use_turn_restrictions = true
properties.continue_straight_at_waypoint = true properties.continue_straight_at_waypoint = true
properties.weight_name = 'test_version1' properties.weight_name = 'test_version1'
properties.weight_precision = 2 properties.weight_precision = 2
assert(properties.max_turn_weight == 327.67) assert(properties.max_turn_weight == 327.67)
function node_function (node, result) function node_function (node, result)
print ('node_function ' .. node:id()) print(node, result)
end print ('node_function ' .. node:id())
end
function way_function(way, result) function way_function(way, result)
result.name = way:get_value_by_key('name') result.name = way:get_value_by_key('name')
result.weight = 10 result.weight = 10
result.forward_mode = mode.driving result.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.forward_speed = 36 result.forward_speed = 36
result.backward_speed = 36 result.backward_speed = 36
print ('way_function ' .. way:id() .. ' ' .. result.name) print ('way_function ' .. way:id() .. ' ' .. result.name)
end end
function turn_function (turn) function turn_function (turn)
print('turn_function', turn.angle, turn.turn_type, turn.direction_modifier, turn.has_traffic_light) 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.weight = turn.angle == 0 and 0 or 4.2
turn.duration = turn.weight turn.duration = turn.weight
end end
function segment_function (segment) function segment_function (segment)
print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat) print ('segment_function ' .. segment.source.lon .. ' ' .. segment.source.lat)
end end
""" """
And the node map And the node map
""" """
a a
@ -67,3 +68,165 @@ end
| a | b | ac,cb,cb | 19.2s | | a | b | ac,cb,cb | 19.2s |
| a | d | ac,cd,cd | 19.2s | | a | d | ac,cd,cd | 19.2s |
| a | e | ac,ce | 20s | | 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); 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('/'); const lua_profiles_path = this.PROFILES_PATH.split(path.sep).join('/');
let text = 'package.path = "' + lua_profiles_path + '/?.lua;" .. package.path\n'; let text = 'package.path = "' + lua_profiles_path + '/?.lua;" .. package.path\n';
if (profile == null) { if (profile == null) {
text += data + '\n'; text += data + '\n';
} else { } else {
text += 'local f = assert(io.open("' + lua_profiles_path + '/' + profile + '.lua", "r"))\n'; text += 'local functions = require("' + profile + '")\n';
text += 'local s = f:read("*all") .. [[\n' + data + '\n]]\n'; text += 'functions.setup_parent = functions.setup\n';
text += 'f:close()\n'; text += 'functions.setup = function()\n';
text += 'local m = assert(loadstring and loadstring(s) or load(s))\n'; text += 'local profile = functions.setup_parent()\n';
text += 'm()\n'; text += data + '\n';
text += 'return profile\n';
text += 'end\n';
text += 'return functions\n';
} }
this.profileFile = this.profileCacheFile; this.profileFile = this.profileCacheFile;
// TODO: Don't overwrite if it exists // 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 | | 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
Scenario: Testbot - Travel time matrix based on segment durations Scenario: Testbot - Travel time matrix based on segment durations
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
function segment_function (segment) 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.weight = 2
segment.duration = 11 segment.duration = 11
end end
return functions
""" """
And the node map And the node map
@ -254,16 +263,25 @@ Feature: Basic Distance Matrix
Scenario: Testbot - Travel time matrix for alternative loop paths Scenario: Testbot - Travel time matrix for alternative loop paths
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_precision = 3 functions.setup = function()
function segment_function (segment) 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.weight = 777
segment.duration = 3 segment.duration = 3
end end
return functions
""" """
And the node map And the node map
""" """

View File

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

View File

@ -39,13 +39,17 @@ Feature: Projection to nearest point on road
Scenario: Projection results negative duration Scenario: Projection results negative duration
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 functions = require('testbot')
function segment_function (segment)
function segment_function(profile, segment)
segment.weight = 5.5 segment.weight = 5.5
segment.duration = 2.8 segment.duration = 2.8
end end
functions.process_segment = segment_function
return functions
""" """
Given the node locations 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 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 profile.properties.traffic_signal_penalty = 0
properties.traffic_signal_penalty = 0 profile.properties.u_turn_penalty = 0
properties.u_turn_penalty = 0 profile.properties.weight_precision = 2
properties.weight_precision = 2
""" """
And the contract extra arguments "--segment-speed-file {speeds_file}" And the contract extra arguments "--segment-speed-file {speeds_file}"
And the customize 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 And the ways
| nodes | highway | | nodes | highway |
| ab | primary | | ab | primary |
And the profile file "testbot" extended with And the profile file "testbot" initialized with
""" """
api_version = 1 profile.properties.traffic_signal_penalty = 0
properties.traffic_signal_penalty = 0 profile.properties.u_turn_penalty = 0
properties.u_turn_penalty = 0 profile.properties.weight_precision = 2
properties.weight_precision = 2
""" """
And the contract extra arguments "--segment-speed-file {speeds_file}" And the contract extra arguments "--segment-speed-file {speeds_file}"
And the customize 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 @routing @testbot @turn_penalty
Feature: Turn Penalties Feature: Turn Penalties
Background: Scenario: Turns should incur a delay that depend on the angle
Given the profile "turnbot" Given the profile "turnbot"
Given a grid size of 200 meters Given a grid size of 200 meters
Scenario: Turns should incur a delay that depend on the angle
Given the node map Given the node map
""" """
c d e 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 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 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.forward_speed = 42 result.forward_speed = 42
result.backward_speed = 42 result.backward_speed = 42
end end
return functions
""" """
And the node map And the node map
""" """
@ -87,18 +96,27 @@ Feature: Weight tests
And it should exit with an error And it should exit with an error
Scenario: Step weights -- way_function: second way wins Scenario: Step weights -- way_function: second way wins
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.duration = 42 result.duration = 42
result.weight = 35 result.weight = 35
end end
return functions
""" """
Given the node map Given the node map
@ -119,19 +137,28 @@ Feature: Weight tests
| h,a | , | 140m +-1 | 35,0 | 42s,0s | | h,a | , | 140m +-1 | 35,0 | 42s,0s |
Scenario: Step weights -- way_function: higher weight_per_meter is preferred 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 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.duration = 42 result.duration = 42
result.forward_rate = 1 result.forward_rate = 1
result.backward_rate = 0.5 result.backward_rate = 0.5
end end
return functions
""" """
Given the node map Given the node map
@ -155,22 +182,32 @@ Feature: Weight tests
| h,f | , | 40m | 80,0 | 12s,0s | | h,f | , | 40m | 80,0 | 12s,0s |
Scenario: Step weights -- segment_function Scenario: Step weights -- segment_function
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.weight = 42 result.weight = 42
result.duration = 3 result.duration = 3
end end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 1 segment.weight = 1
segment.duration = 11 segment.duration = 11
end end
return functions
""" """
Given the node map Given the node map
@ -195,28 +232,39 @@ Feature: Weight tests
Scenario: Step weights -- segment_function and turn_function with weight precision 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 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
properties.weight_precision = 3 local profile = functions.setup_testbot()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.weight = 42 result.weight = 42
result.duration = 3 result.duration = 3
end end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 1.11 segment.weight = 1.11
segment.duration = 100 segment.duration = 100
end end
function turn_function (turn)
functions.process_turn = function(profile, turn)
print (turn.angle) print (turn.angle)
turn.weight = 2 + turn.angle / 100 turn.weight = 2 + turn.angle / 100
turn.duration = turn.angle turn.duration = turn.angle
end end
return functions
""" """
Given the node map Given the node map
@ -241,22 +289,32 @@ Feature: Weight tests
@traffic @speed @traffic @speed
Scenario: Step weights -- segment_function with speed and turn updates Scenario: Step weights -- segment_function with speed and turn updates
Given the profile file "testbot" extended with Given the profile file
""" """
api_version = 1 local functions = require('testbot')
properties.traffic_signal_penalty = 0 functions.setup_testbot = functions.setup
properties.u_turn_penalty = 0
properties.weight_name = 'steps' functions.setup = function()
function way_function(way, result) 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.forward_mode = mode.driving
result.backward_mode = mode.driving result.backward_mode = mode.driving
result.weight = 42 result.weight = 42
result.duration = 3 result.duration = 3
end end
function segment_function (segment)
functions.process_segment = function(profile, segment)
segment.weight = 10 segment.weight = 10
segment.duration = 11 segment.duration = 11
end end
return functions
""" """
And the node map And the node map
@ -289,10 +347,9 @@ Feature: Weight tests
@traffic @speed @traffic @speed
Scenario: Step weights -- segment_function with speed and turn updates with fallback to durations 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 profile.properties.weight_precision = 3
properties.weight_precision = 3
""" """
And the node map And the node map

View File

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

View File

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

View File

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

View File

@ -1,199 +1,196 @@
api_version = 1
-- Bicycle profile -- 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 api_version = 2
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'
-- Set to true if you need to call the node_function for every node. Set = require('lib/set')
-- Generally can be left as false to avoid unnecessary Lua calls Sequence = require('lib/sequence')
-- (which slow down pre-processing). Handlers = require("lib/way_handlers")
properties.call_tagless_node_function = false 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 return {
local walking_speed = 6 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_mode = mode.cycling, default_speed = default_speed,
default_speed = 15, walking_speed = walking_speed,
oneway_handling = true, oneway_handling = true,
traffic_light_penalty = 2, turn_penalty = 6,
u_turn_penalty = 20, turn_bias = 1.4,
turn_penalty = 6, use_public_transport = true,
turn_bias = 1.4,
use_public_transport = true,
allowed_start_modes = Set { allowed_start_modes = Set {
mode.cycling, mode.cycling,
mode.pushing_bike mode.pushing_bike
}, },
barrier_whitelist = Set { barrier_whitelist = Set {
'sump_buster', 'sump_buster',
'bus_trap', 'bus_trap',
'cycle_barrier', 'cycle_barrier',
'bollard', 'bollard',
'entrance', 'entrance',
'cattle_grid', 'cattle_grid',
'border_control', 'border_control',
'toll_booth', 'toll_booth',
'sally_port', 'sally_port',
'gate', 'gate',
'no', 'no',
'block' 'block'
}, },
access_tag_whitelist = Set { access_tag_whitelist = Set {
'yes', 'yes',
'permissive', 'permissive',
'designated' 'designated'
}, },
access_tag_blacklist = Set { access_tag_blacklist = Set {
'no', 'no',
'private', 'private',
'agricultural', 'agricultural',
'forestry', 'forestry',
'delivery' 'delivery'
}, },
restricted_access_tag_list = Set { }, restricted_access_tag_list = Set { },
restricted_highway_whitelist = Set { }, restricted_highway_whitelist = Set { },
access_tags_hierarchy = Sequence { access_tags_hierarchy = Sequence {
'bicycle', 'bicycle',
'vehicle', 'vehicle',
'access' 'access'
}, },
restrictions = Set { restrictions = Set {
'bicycle' 'bicycle'
}, },
cycleway_tags = Set { cycleway_tags = Set {
'track', 'track',
'lane', 'lane',
'opposite', 'opposite',
'opposite_lane', 'opposite_lane',
'opposite_track', 'opposite_track',
'share_busway', 'share_busway',
'sharrow', 'sharrow',
'shared', 'shared',
'shared_lane' 'shared_lane'
}, },
-- reduce the driving speed by 30% for unsafe roads -- reduce the driving speed by 30% for unsafe roads
-- only used for cyclability metric -- only used for cyclability metric
unsafe_highway_list = { unsafe_highway_list = {
primary = 0.7, primary = 0.7,
secondary = 0.75, secondary = 0.75,
tertiary = 0.8, tertiary = 0.8,
primary_link = 0.7, primary_link = 0.7,
secondary_link = 0.75, secondary_link = 0.75,
tertiary_link = 0.8, tertiary_link = 0.8,
}, },
service_penalties = { service_penalties = {
alley = 0.5, alley = 0.5,
}, },
bicycle_speeds = { bicycle_speeds = {
cycleway = default_speed, cycleway = default_speed,
primary = default_speed, primary = default_speed,
primary_link = default_speed, primary_link = default_speed,
secondary = default_speed, secondary = default_speed,
secondary_link = default_speed, secondary_link = default_speed,
tertiary = default_speed, tertiary = default_speed,
tertiary_link = default_speed, tertiary_link = default_speed,
residential = default_speed, residential = default_speed,
unclassified = default_speed, unclassified = default_speed,
living_street = default_speed, living_street = default_speed,
road = default_speed, road = default_speed,
service = default_speed, service = default_speed,
track = 12, track = 12,
path = 12 path = 12
}, },
pedestrian_speeds = { pedestrian_speeds = {
footway = walking_speed, footway = walking_speed,
pedestrian = walking_speed, pedestrian = walking_speed,
steps = 2 steps = 2
}, },
railway_speeds = { railway_speeds = {
train = 10, train = 10,
railway = 10, railway = 10,
subway = 10, subway = 10,
light_rail = 10, light_rail = 10,
monorail = 10, monorail = 10,
tram = 10 tram = 10
}, },
platform_speeds = { platform_speeds = {
platform = walking_speed platform = walking_speed
}, },
amenity_speeds = { amenity_speeds = {
parking = 10, parking = 10,
parking_entrance = 10 parking_entrance = 10
}, },
man_made_speeds = { man_made_speeds = {
pier = walking_speed pier = walking_speed
}, },
route_speeds = { route_speeds = {
ferry = 5 ferry = 5
}, },
bridge_speeds = { bridge_speeds = {
movable = 5 movable = 5
}, },
surface_speeds = { surface_speeds = {
asphalt = default_speed, asphalt = default_speed,
["cobblestone:flattened"] = 10, ["cobblestone:flattened"] = 10,
paving_stones = 10, paving_stones = 10,
compacted = 10, compacted = 10,
cobblestone = 6, cobblestone = 6,
unpaved = 6, unpaved = 6,
fine_gravel = 6, fine_gravel = 6,
gravel = 6, gravel = 6,
pebblestone = 6, pebblestone = 6,
ground = 6, ground = 6,
dirt = 6, dirt = 6,
earth = 6, earth = 6,
grass = 6, grass = 6,
mud = 3, mud = 3,
sand = 3, sand = 3,
sett = 10 sett = 10
}, },
tracktype_speeds = { tracktype_speeds = {
}, },
smoothness_speeds = { smoothness_speeds = {
}, },
avoid = Set { avoid = Set {
'impassable', 'impassable',
'construction', 'construction'
'proposed' }
} }
} end
local function parse_maxspeed(source) local function parse_maxspeed(source)
if not source then if not source then
@ -209,13 +206,7 @@ local function parse_maxspeed(source)
return n return n
end end
function get_restrictions(vector) function process_node (profile, node, result)
for i,v in ipairs(profile.restrictions) do
vector:Add(v)
end
end
function node_function (node, result)
-- parse access and barrier tags -- parse access and barrier tags
local highway = node:get_value_by_key("highway") local highway = node:get_value_by_key("highway")
local is_crossing = highway and highway == "crossing" local is_crossing = highway and highway == "crossing"
@ -243,41 +234,8 @@ function node_function (node, result)
end end
end end
function way_function (way, result) function handle_bicycle_tags(profile,way,result,data)
-- the intial filtering of ways based on presence of tags -- initial routability check, filters out buildings, boundaries, etc
-- 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
local route = way:get_value_by_key("route") local route = way:get_value_by_key("route")
local man_made = way:get_value_by_key("man_made") local man_made = way:get_value_by_key("man_made")
local railway = way:get_value_by_key("railway") 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 public_transport or public_transport=='') and
(not bridge or bridge=='') (not bridge or bridge=='')
then then
return return false
end end
-- access -- access
local access = find_access_tag(way, profile.access_tags_hierarchy) local access = find_access_tag(way, profile.access_tags_hierarchy)
if access and profile.access_tag_blacklist[access] then if access and profile.access_tag_blacklist[access] then
return return false
end end
-- other tags -- other tags
@ -371,8 +329,8 @@ function way_function (way, result)
way_type_allows_pushing = true way_type_allows_pushing = true
elseif access and profile.access_tag_whitelist[access] then elseif access and profile.access_tag_whitelist[access] then
-- unknown way, but valid access tag -- unknown way, but valid access tag
result.forward_speed = default_speed result.forward_speed = profile.default_speed
result.backward_speed = default_speed result.backward_speed = profile.default_speed
way_type_allows_pushing = true way_type_allows_pushing = true
end end
@ -447,18 +405,18 @@ function way_function (way, result)
push_backward_speed = profile.man_made_speeds[man_made] push_backward_speed = profile.man_made_speeds[man_made]
else else
if foot == 'yes' then if foot == 'yes' then
push_forward_speed = walking_speed push_forward_speed = profile.walking_speed
if not implied_oneway then if not implied_oneway then
push_backward_speed = walking_speed push_backward_speed = profile.walking_speed
end end
elseif foot_forward == 'yes' then elseif foot_forward == 'yes' then
push_forward_speed = walking_speed push_forward_speed = profile.walking_speed
elseif foot_backward == 'yes' then elseif foot_backward == 'yes' then
push_backward_speed = walking_speed push_backward_speed = profile.walking_speed
elseif way_type_allows_pushing then elseif way_type_allows_pushing then
push_forward_speed = walking_speed push_forward_speed = profile.walking_speed
if not implied_oneway then if not implied_oneway then
push_backward_speed = walking_speed push_backward_speed = profile.walking_speed
end end
end end
end end
@ -481,8 +439,8 @@ function way_function (way, result)
if bicycle == "dismount" then if bicycle == "dismount" then
result.forward_mode = mode.pushing_bike result.forward_mode = mode.pushing_bike
result.backward_mode = mode.pushing_bike result.backward_mode = mode.pushing_bike
result.forward_speed = walking_speed result.forward_speed = profile.walking_speed
result.backward_speed = walking_speed result.backward_speed = profile.walking_speed
end end
@ -502,7 +460,7 @@ function way_function (way, result)
-- convert duration into cyclability -- 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 safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1 local is_unsafe = safety_penalty < 1
local forward_is_unsafe = is_unsafe and not has_cycleway_right 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 result.weight = result.duration / forward_penalty
end end
end end
end
function process_way (profile, way, result)
-- the initial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
-- to increase performance, prefetching and initial tag check
-- is done directly instead of via a handler.
local handlers = Sequence { -- in general we should try to abort as soon as
-- compute speed taking into account way type, maxspeed tags, etc. -- possible if the way is not routable, to avoid doing
'handle_surface', -- 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 -- data table for storing intermediate values during processing
'handle_classification',
-- handle various other flags local data = {
'handle_roundabouts', -- prefetch tags
highway = way:get_value_by_key('highway'),
-- handle allowed start/end modes
'handle_startpoint',
-- set name, ref and pronunciation
'handle_names'
} }
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 end
function turn_function(turn) function process_turn(profile, turn)
-- compute turn penalty as angle^2, with a left/right bias -- compute turn penalty as angle^2, with a left/right bias
local normalized_angle = turn.angle / 90.0 local normalized_angle = turn.angle / 90.0
if normalized_angle >= 0.0 then if normalized_angle >= 0.0 then
@ -566,17 +554,20 @@ function turn_function(turn)
end end
if turn.direction_modifier == direction_modifier.uturn then 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 end
if turn.has_traffic_light then if turn.has_traffic_light then
turn.duration = turn.duration + profile.traffic_light_penalty turn.duration = turn.duration + profile.properties.traffic_light_penalty
end end
if properties.weight_name == 'cyclability' then if profile.properties.weight_name == 'cyclability' then
turn.weight = turn.duration 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
end end
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 -- Car profile
api_version = 1 api_version = 2
local find_access_tag = require("lib/access").find_access_tag Set = require('lib/set')
local Set = require('lib/set') Sequence = require('lib/sequence')
local Sequence = require('lib/sequence') Handlers = require("lib/way_handlers")
local Handlers = require("lib/handlers") find_access_tag = require("lib/access").find_access_tag
local next = next -- bind to local for speed limit = require("lib/maxspeed").limit
-- set profile properties function setup()
properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s local use_left_hand_driving = false
properties.use_turn_restrictions = true return {
properties.continue_straight_at_waypoint = true properties = {
properties.left_hand_driving = false max_speed_for_map_matching = 180/3.6, -- 180kmph -> m/s
-- For routing based on duration, but weighted for preferring certain roads left_hand_driving = use_left_hand_driving,
properties.weight_name = 'routability' -- For routing based on duration, but weighted for preferring certain roads
-- For shortest duration without penalties for accessibility weight_name = 'routability',
--properties.weight_name = 'duration' -- For shortest duration without penalties for accessibility
-- For shortest distance without penalties for accessibility -- weight_name = 'duration',
--properties.weight_name = 'distance' -- 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,
},
default_mode = mode.driving,
default_speed = 10,
oneway_handling = true,
side_road_multiplier = 0.8,
turn_penalty = 7.5,
speed_reduction = 0.8,
-- Set to true if you need to call the node_function for every node. -- Note: this biases right-side driving.
-- Generally can be left as false to avoid unnecessary Lua calls -- Should be inverted for left-driving countries.
-- (which slow down pre-processing). turn_bias = use_left_hand_driving and 1/1.075 or 1.075,
properties.call_tagless_node_function = false
-- a list of suffixes to suppress in name change instructions
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
local profile = { barrier_whitelist = Set {
default_mode = mode.driving, 'cattle_grid',
default_speed = 10, 'border_control',
oneway_handling = true, 'checkpoint',
'toll_booth',
'sally_port',
'gate',
'lift_gate',
'no',
'entrance'
},
side_road_multiplier = 0.8, access_tag_whitelist = Set {
turn_penalty = 7.5, 'yes',
speed_reduction = 0.8, 'motorcar',
traffic_light_penalty = 2, 'motor_vehicle',
u_turn_penalty = 20, 'vehicle',
'permissive',
'designated',
'hov'
},
-- Note: this biases right-side driving. access_tag_blacklist = Set {
-- Should be inverted for left-driving countries. 'no',
turn_bias = properties.left_hand_driving and 1/1.075 or 1.075, 'agricultural',
'forestry',
'emergency',
'psv',
'customers',
'private',
'delivery',
'destination'
},
-- a list of suffixes to suppress in name change instructions restricted_access_tag_list = Set {
suffix_list = { 'private',
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East' 'delivery',
}, 'destination',
'customers',
},
barrier_whitelist = Set { access_tags_hierarchy = Sequence {
'cattle_grid', 'motorcar',
'border_control', 'motor_vehicle',
'checkpoint', 'vehicle',
'toll_booth', 'access'
'sally_port', },
'gate',
'lift_gate',
'no',
'entrance'
},
access_tag_whitelist = Set { service_tag_forbidden = Set {
'yes', 'emergency_access'
'motorcar', },
'motor_vehicle',
'vehicle',
'permissive',
'designated',
'hov'
},
access_tag_blacklist = Set { restrictions = Sequence {
'no', 'motorcar',
'agricultural', 'motor_vehicle',
'forestry', 'vehicle'
'emergency', },
'psv',
'customers',
'private',
'delivery',
'destination'
},
restricted_access_tag_list = Set { avoid = Set {
'private', 'area',
'delivery', -- 'toll', -- uncomment this to avoid tolls
'destination', 'reversible',
'customers', 'impassable',
}, 'hov_lanes',
'steps',
'construction',
'proposed'
},
access_tags_hierarchy = Sequence { speeds = Sequence {
'motorcar', highway = {
'motor_vehicle', motorway = 90,
'vehicle', motorway_link = 45,
'access' 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
}
},
service_tag_forbidden = Set { service_penalties = {
'emergency_access' alley = 0.5,
}, parking = 0.5,
parking_aisle = 0.5,
driveway = 0.5,
["drive-through"] = 0.5,
["drive-thru"] = 0.5
},
restrictions = Sequence { restricted_highway_whitelist = Set {
'motorcar', 'motorway',
'motor_vehicle', 'motorway_link',
'vehicle' 'trunk',
}, 'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'residential',
'living_street',
},
avoid = Set { route_speeds = {
'area', ferry = 5,
-- 'toll', -- uncomment this to avoid tolls shuttle_train = 10
'reversible', },
'impassable',
'hov_lanes',
'steps',
'construction',
'proposed'
},
speeds = Sequence { bridge_speeds = {
highway = { movable = 5
motorway = 90, },
motorway_link = 45,
trunk = 85, -- surface/trackype/smoothness
trunk_link = 40, -- values were estimated from looking at the photos at the relevant wiki pages
primary = 65,
primary_link = 30, -- max speed for surfaces
secondary = 55, surface_speeds = {
secondary_link = 25, asphalt = nil, -- nil mean no limit. removing the line has the same effect
tertiary = 40, concrete = nil,
tertiary_link = 20, ["concrete:plates"] = nil,
unclassified = 25, ["concrete:lanes"] = nil,
residential = 25, paved = nil,
living_street = 10,
service = 15 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 end
function get_restrictions(vector) function process_node (profile, node, result)
for i,v in ipairs(profile.restrictions) do
vector:Add(v)
end
end
function node_function (node, result)
-- parse access and barrier tags -- parse access and barrier tags
local access = find_access_tag(node, profile.access_tags_hierarchy) local access = find_access_tag(node, profile.access_tags_hierarchy)
if access then if access then
@ -303,7 +289,7 @@ function node_function (node, result)
end end
end end
function way_function(way, result) function process_way(profile, way, result)
-- the intial filtering of ways based on presence of tags -- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways -- affects processing times significantly, because all ways
-- have to be checked. -- have to be checked.
@ -335,61 +321,61 @@ function way_function(way, result)
handlers = Sequence { handlers = Sequence {
-- set the default mode for this profile. if can be changed later -- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry -- 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 -- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable, -- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible -- toll=yes and oneway=reversible
'handle_blocked_ways', WayHandlers.blocked_ways,
-- determine access status by checking our hierarchy of -- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle -- access tags, e.g: motorcar, motor_vehicle, vehicle
'handle_access', WayHandlers.access,
-- check whether forward/backward directions are routable -- check whether forward/backward directions are routable
'handle_oneway', WayHandlers.oneway,
-- check a road's destination -- check a road's destination
'handle_destinations', WayHandlers.destinations,
-- check whether we're using a special transport mode -- check whether we're using a special transport mode
'handle_ferries', WayHandlers.ferries,
'handle_movables', WayHandlers.movables,
-- handle service road restrictions -- handle service road restrictions
'handle_service', WayHandlers.service,
-- handle hov -- handle hov
'handle_hov', WayHandlers.hov,
-- compute speed taking into account way type, maxspeed tags, etc. -- compute speed taking into account way type, maxspeed tags, etc.
'handle_speed', WayHandlers.speed,
'handle_surface', WayHandlers.surface,
'handle_maxspeed', WayHandlers.maxspeed,
'handle_penalties', WayHandlers.penalties,
-- compute class labels -- compute class labels
'handle_classes', WayHandlers.classes,
-- handle turn lanes and road classification, used for guidance -- handle turn lanes and road classification, used for guidance
'handle_turn_lanes', WayHandlers.turn_lanes,
'handle_classification', WayHandlers.classification,
-- handle various other flags -- handle various other flags
'handle_roundabouts', WayHandlers.roundabouts,
'handle_startpoint', WayHandlers.startpoint,
-- set name, ref and pronunciation -- set name, ref and pronunciation
'handle_names', WayHandlers.names,
-- set weight properties of the way -- 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 end
function turn_function (turn) function process_turn (profile, turn)
-- Use a sigmoid function to return a penalty that maxes out at turn_penalty -- 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 -- over the space of 0-180 degrees. Values here were chosen by fitting
-- the function to some turn penalty samples from real driving. -- the function to some turn penalty samples from real driving.
@ -397,7 +383,7 @@ function turn_function (turn)
local turn_bias = profile.turn_bias local turn_bias = profile.turn_bias
if turn.has_traffic_light then if turn.has_traffic_light then
turn.duration = profile.traffic_light_penalty turn.duration = profile.properties.traffic_light_penalty
end end
if turn.turn_type ~= turn_type.no_turn then if turn.turn_type ~= turn_type.no_turn then
@ -408,21 +394,28 @@ function turn_function (turn)
end end
if turn.direction_modifier == direction_modifier.u_turn then 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
end end
-- for distance based routing we don't want to have penalties based on turn angle -- 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 turn.weight = 0
else else
turn.weight = turn.duration turn.weight = turn.duration
end 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 -- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then 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 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 -- Foot profile
api_version = 1 api_version = 2
local find_access_tag = require("lib/access").find_access_tag Set = require('lib/set')
local Set = require('lib/set') Sequence = require('lib/sequence')
local Sequence = require('lib/sequence') Handlers = require("lib/way_handlers")
local Handlers = require("lib/handlers") find_access_tag = require("lib/access").find_access_tag
local next = next -- bind to local for speed
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s function setup()
properties.use_turn_restrictions = false local walking_speed = 5
properties.continue_straight_at_waypoint = false return {
properties.weight_name = 'duration' properties = {
--properties.weight_name = 'routability' weight_name = 'duration',
max_speed_for_map_matching = 40/3.6, -- kmph -> m/s
-- Set to true if you need to call the node_function for every node. call_tagless_node_function = false,
-- Generally can be left as false to avoid unnecessary Lua calls traffic_light_penalty = 2,
-- (which slow down pre-processing). u_turn_penalty = 2,
properties.call_tagless_node_function = false continue_straight_at_waypoint = false,
use_turn_restrictions = 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,
}, },
railway = { default_mode = mode.walking,
platform = walking_speed 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 = { access_tag_whitelist = Set {
parking = walking_speed, 'yes',
parking_entrance= walking_speed 'foot',
'permissive',
'designated'
}, },
man_made = { access_tag_blacklist = Set {
pier = walking_speed 'no',
'agricultural',
'forestry',
'private',
'delivery',
}, },
leisure = { restricted_access_tag_list = Set { },
track = walking_speed
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 process_node (profile, node, result)
function node_function (node, result)
-- parse access and barrier tags -- parse access and barrier tags
local access = find_access_tag(node, profile.access_tags_hierarchy) local access = find_access_tag(node, profile.access_tags_hierarchy)
if access then if access then
@ -171,7 +165,7 @@ function node_function (node, result)
end end
-- main entry point for processsing a way -- 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 -- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways -- affects processing times significantly, because all ways
-- have to be checked. -- have to be checked.
@ -208,59 +202,66 @@ function way_function(way, result)
local handlers = Sequence { local handlers = Sequence {
-- set the default mode for this profile. if can be changed later -- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry -- 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 -- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable, -- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible -- toll=yes and oneway=reversible
'handle_blocked_ways', WayHandlers.blocked_ways,
-- determine access status by checking our hierarchy of -- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle -- access tags, e.g: motorcar, motor_vehicle, vehicle
'handle_access', WayHandlers.access,
-- check whether forward/backward directons are routable -- check whether forward/backward directons are routable
'handle_oneway', WayHandlers.oneway,
-- check whether forward/backward directons are routable -- check whether forward/backward directons are routable
'handle_destinations', WayHandlers.destinations,
-- check whether we're using a special transport mode -- check whether we're using a special transport mode
'handle_ferries', WayHandlers.ferries,
'handle_movables', WayHandlers.movables,
-- compute speed taking into account way type, maxspeed tags, etc. -- compute speed taking into account way type, maxspeed tags, etc.
'handle_speed', WayHandlers.speed,
'handle_surface', WayHandlers.surface,
-- handle turn lanes and road classification, used for guidance -- handle turn lanes and road classification, used for guidance
'handle_classification', WayHandlers.classification,
-- handle various other flags -- handle various other flags
'handle_roundabouts', WayHandlers.roundabouts,
'handle_startpoint', WayHandlers.startpoint,
-- set name, ref and pronunciation -- set name, ref and pronunciation
'handle_names' WayHandlers.names
} }
Handlers.run(handlers,way,result,data,profile) WayHandlers.run(profile,way,result,data,handlers)
end end
function turn_function (turn) function process_turn (profile, turn)
turn.duration = 0. turn.duration = 0.
if turn.direction_modifier == direction_modifier.u_turn then 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
if turn.has_traffic_light then if turn.has_traffic_light then
turn.duration = profile.traffic_light_penalty turn.duration = profile.properties.traffic_light_penalty
end 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 -- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then if not turn.source_restricted and turn.target_restricted then
turn.weight = turn.weight + 3000 turn.weight = turn.weight + 3000
end end
end 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 end
function Debug.load_profile(profile) function Debug.load_profile(profile)
require(profile) Debug.functions = require(profile)
Debug.profile = Debug.functions.setup()
end end
function Debug.reset_tag_fetch_counts() function Debug.reset_tag_fetch_counts()
@ -115,7 +116,7 @@ function Debug.register_tag_fetch(k)
end end
function Debug.way_function(way,result) function Debug.process_way(way,result)
-- setup result table -- setup result table
result.road_classification = {} result.road_classification = {}
@ -132,8 +133,8 @@ function Debug.way_function(way,result)
-- reset tag counts -- reset tag counts
Debug:reset_tag_fetch_counts() Debug:reset_tag_fetch_counts()
-- call the global method defined in the profile file -- call the way processsing function
way_function(way,result) Debug.functions.process_way(Debug.profile,way,result)
end end
return Debug return Debug

View File

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

View File

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

View File

@ -1,50 +1,10 @@
api_version = 1 -- Rasterbot with interpolation profile
-- Rasterbot profile
-- Set to true if you need to call the node_function for every node. functions = require('rasterbot')
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
-- Minimalist node_ and way_functions in order to test source_ and segment_functions functions.process_segment = function(profile, segment)
local sourceData = raster:interpolate(profile.raster_source, segment.source.lon, segment.source.lat)
function node_function (node, result) local targetData = raster:interpolate(profile.raster_source, segment.target.lon, segment.target.lat)
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)
io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n") io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n")
local invalid = sourceData.invalid_data() local invalid = sourceData.invalid_data()
local scaled_weight = segment.weight local scaled_weight = segment.weight
@ -64,3 +24,5 @@ function segment_function (segment)
segment.weight = scaled_weight segment.weight = scaled_weight
segment.duration = scaled_duration segment.duration = scaled_duration
end end
return functions

View File

@ -1,49 +1,35 @@
api_version = 1
-- Testbot profile -- Testbot profile
-- Moves at fixed, well-known speeds, practical for testing speed and travel times: -- Moves at fixed, well-known speeds, practical for testing speed and travel times:
-- Primary road: 36km/h = 36000m/3600s = 100m/10s -- Primary road: 36km/h = 36000m/3600s = 100m/10s
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s -- Secondary road: 18km/h = 18000m/3600s = 100m/20s
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s -- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
speed_profile = { api_version = 2
["primary"] = 36,
["secondary"] = 18,
["tertiary"] = 12,
["steps"] = 6,
["default"] = 24
}
-- 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 default_speed = 24,
properties.use_turn_restrictions = true speeds = {
properties.max_speed_for_map_matching = 30/3.6 --km -> m/s primary = 36,
properties.weight_name = 'duration' secondary = 18,
tertiary = 12,
-- Set to true if you need to call the node_function for every node. steps = 6,
-- 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
end end
function node_function (node, result) function process_node (profile, node, result)
local traffic_signal = node:get_value_by_key("highway") local traffic_signal = node:get_value_by_key("highway")
if traffic_signal and traffic_signal == "traffic_signals" then if traffic_signal and traffic_signal == "traffic_signals" then
@ -52,7 +38,7 @@ function node_function (node, result)
end end
end end
function way_function (way, result) function process_way (profile, way, result)
local highway = way:get_value_by_key("highway") local highway = way:get_value_by_key("highway")
local name = way:get_value_by_key("name") local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway") local oneway = way:get_value_by_key("oneway")
@ -75,7 +61,7 @@ function way_function (way, result)
result.forward_mode = mode.route result.forward_mode = mode.route
result.backward_mode = mode.route result.backward_mode = mode.route
else 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 local speed_back = speed_forw
if highway == "river" then if highway == "river" then
@ -122,12 +108,19 @@ function way_function (way, result)
end end
end end
function turn_function (turn) function process_turn (profile, turn)
if turn.direction_modifier == direction_modifier.uturn then if turn.direction_modifier == direction_modifier.uturn then
turn.duration = uturn_penalty turn.duration = profile.properties.uturn_penalty
turn.weight = uturn_penalty turn.weight = profile.properties.uturn_penalty
end end
if turn.has_traffic_light then if turn.has_traffic_light then
turn.duration = turn.duration + traffic_light_penalty turn.duration = turn.duration + profile.properties.traffic_light_penalty
end end
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 -- Testbot, with turn penalty
-- Used for testing turn penalties -- Used for testing turn penalties
require 'testbot' functions = require 'testbot'
function turn_function (turn) functions.process_turn = function(profile, turn)
turn.duration = 20 * math.abs(turn.angle) / 180 turn.duration = 20 * math.abs(turn.angle) / 180
end end
return functions

View File

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

View File

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

View File

@ -216,15 +216,13 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
"sharp_left", "sharp_left",
extractor::guidance::DirectionModifier::SharpLeft); extractor::guidance::DirectionModifier::SharpLeft);
context.state.new_usertype<SourceContainer>("sources", context.state.new_usertype<RasterContainer>("raster",
"load", "load",
&SourceContainer::LoadRasterSource, &RasterContainer::LoadRasterSource,
"query", "query",
&SourceContainer::GetRasterDataFromSource, &RasterContainer::GetRasterDataFromSource,
"interpolate", "interpolate",
&SourceContainer::GetRasterInterpolateFromSource); &RasterContainer::GetRasterInterpolateFromSource);
context.state.new_enum("constants", "precision", COORDINATE_PRECISION);
context.state.new_usertype<ProfileProperties>( context.state.new_usertype<ProfileProperties>(
"ProfileProperties", "ProfileProperties",
@ -421,8 +419,10 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
context.state.new_usertype<RasterDatum>( context.state.new_usertype<RasterDatum>(
"RasterDatum", "datum", &RasterDatum::datum, "invalid_data", &RasterDatum::get_invalid); "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["properties"] = &context.properties;
context.state["sources"] = &context.sources;
// //
// end of register block // end of register block
@ -430,24 +430,15 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
util::luaAddScriptFolderToLoadPath(context.state.lua_state(), file_name.c_str()); util::luaAddScriptFolderToLoadPath(context.state.lua_state(), file_name.c_str());
context.state.script_file(file_name); sol::optional<sol::table> function_table = 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();
// Check profile API version // Check profile API version
auto maybe_version = context.state.get<sol::optional<int>>("api_version"); auto maybe_version = context.state.get<sol::optional<int>>("api_version");
if (maybe_version) if (maybe_version)
{
context.api_version = *maybe_version; context.api_version = *maybe_version;
else
{
context.api_version = 0;
} }
if (context.api_version < SUPPORTED_MIN_API_VERSION || if (context.api_version < SUPPORTED_MIN_API_VERSION ||
@ -459,14 +450,140 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
" are supported." + SOURCE_REF); " 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) 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: 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.GetUturnPenalty() == 0);
BOOST_ASSERT(context.properties.GetTrafficSignalPenalty() == 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; break;
}
case 0: 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"); BOOST_ASSERT(context.properties.GetWeightName() == "duration");
break; 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(); auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr); BOOST_ASSERT(context.state.lua_state() != nullptr);
std::vector<std::string> suffixes_vector; std::vector<std::string> strings;
sol::function function = context.state[function_name];
sol::function get_name_suffix_list = context.state["get_name_suffix_list"]; if (function.valid())
if (get_name_suffix_list.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() std::vector<std::string> Sol2ScriptingEnvironment::GetRestrictions()
{ {
auto &context = GetSol2Context(); auto &context = GetSol2Context();
BOOST_ASSERT(context.state.lua_state() != nullptr); switch (context.api_version)
std::vector<std::string> restrictions;
sol::function get_restrictions = context.state["get_restrictions"];
if (get_restrictions.valid())
{ {
get_restrictions(restrictions); case 2:
} return Sol2ScriptingEnvironment::GetStringListFromTable("restrictions");
case 1:
return restrictions; return Sol2ScriptingEnvironment::GetStringListFromFunction("get_restrictions");
} default:
return {};
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();
} }
} }
@ -593,6 +724,21 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
switch (context.api_version) 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: case 1:
if (context.has_turn_penalty_function) if (context.has_turn_penalty_function)
{ {
@ -644,6 +790,9 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment)
{ {
switch (context.api_version) switch (context.api_version)
{ {
case 2:
context.segment_function(context.profile_table, segment);
break;
case 1: case 1:
context.segment_function(segment); context.segment_function(segment);
break; break;
@ -660,14 +809,32 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node, ExtractionNode &
{ {
BOOST_ASSERT(state.lua_state() != nullptr); 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) void LuaScriptingContext::ProcessWay(const osmium::Way &way, ExtractionWay &result)
{ {
BOOST_ASSERT(state.lua_state() != nullptr); 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) BOOST_AUTO_TEST_CASE(raster_test)
{ {
SourceContainer sources; RasterContainer sources;
int source_id = int source_id =
sources.LoadRasterSource(OSRM_FIXTURES_DIR "/raster_data.asc", 1, 1.09, 1, 1.09, 10, 10); sources.LoadRasterSource(OSRM_FIXTURES_DIR "/raster_data.asc", 1, 1.09, 1, 1.09, 10, 10);
BOOST_CHECK_EQUAL(source_id, 0); BOOST_CHECK_EQUAL(source_id, 0);