Skip to content

Spitting out all the details of a routing profile #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 161 additions & 20 deletions docs/itinero/basic-concepts/profiles/lua.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,43 @@ The default way to define a vehicle profile is by using [Lua](https://en.wikiped
A vehicle can be defined by one lua script. This vehicle definition gets embedded in a @routerdb when created. This is the minimum code needed to define a profile:

```lua

-- every vehicle type has a name:
name = "car"
-- whitelists for profile and meta

-- Enable extra optimizations; stronlgy urged to set to 'true'. No functional impact towards route planning
normalize = true

-- Turn restrictions applying to these vehicles will be applied:
vehicle_types = {"vehicle", "car"}

-- which tags can be used by the profile to calculate weights - see explanation below
profile_whitelist = {
"highway"
}

-- which tags are kept in the output profile - see explanation below
meta_whitelist = {
"name"
}
-- profile definitions linking a function to a profile


--[[
Profiles is used by itinero to know what behaviours of the vehicle are known; in this case these are 'car', 'car.shortest' and 'car.specificProfile'.

Each profile has the following attributes:

- name: the name of the subprofile
- function_name: a function that calculates the behaviour of this profile, for each segment:
- the speed for this segment in km/h that segment
- the factor (per meter) for that segment
- metric: what is the metric that we will optimize for. Can be one of the following:
- distance: only keep distance into account, neither speed nor factor will be used. Think speed=1 and factor=1
- time: for every segment, the factor used is 1/speed (aka: time needed for this segment); the routeplanner will optimize for that
- custom: the weight (per meter) for each segment will be used. This custom factor will be minimized. This implies that the metric 'speed' will not be used anymore for determining the route


--]]
profiles = {
{
name = "",
Expand All @@ -30,17 +58,77 @@ profiles = {
name = "shortest",
function_name = "factor_and_speed",
metric = "distance",
}
},
{
name = "specificProfile",
function_name = "some_factor_and_speed",
metric = "custom"
}
}
-- the main function turning attributes into a factor_and_speed and a tag whitelist

--[[

A "factor and speed function" converts a set of attributes into a speed and/or factor.
The input 'attributes' are the tags of the segment, as saved in OSM.
The input 'result' is pointer to a table where some values have to be specified by the end of the procedure call - see in this procedure for docs


Usually the default _factor_and_speed_ will define a speed for each possible set of profile attributes. Based on this Itinero can define a _fastest_ and a _shortest_ profile with metrics being _time_ and _distance_ respectively.

A _custom_ profile uses a _factor_ to define weights of edges. This way, the factor can be used to guess a feeling of comfort, safety, preferences for certain roads, ...

- With metric _distance_: Itinero will use the function to get the speed and set factor to a constant, usually 1.
- With metric _time_: Itinero will use the function to get the speed and set factor to 1/speed.
- With metric _custom_: Itinero will use the function to get the speed and the factor.

NOTE: the name of this function can be chosen, it only has to match the name given above.

]]--
function factor_and_speed (attributes, result)

result.speed = 0
result.direction = 0
result.canstop = true
result.attributes_to_keep = {}
--[[
Can this edge be accessed?
0: no
1: yes
]]--
result.access = 0


--[[
Do we have to drive in a certain direction?
0: twoway
1: oneway with the drawing direction of the highway
2: oneway against the drawing direction of the highway
]]
result.direction = 0

--[[
Can a vehicle stop halfway the edge?
(e.g. can we drop of a package in the middle of the motorway or do we have to drive around to the nearby residential?)
]]
result.canstop = true

--[[
On average, how fast would we be driving here (in km/h)?
This is used exclusively if 'time' is the used metric
Note: this should never exceed the legal max speed
]]
result.speed = 0

-- get default speed profiles
--[[
What is the weight (per kilometer) of this edge?
This is used exclusively if 'custom' is the used metric and will be minimized.
Note that setting `result.factor = 1 / speed` is equivalent to using the `time`-metric
]]
result.factor = 0

--[[
Which attributes (aka. key/value-pairs aka. tags) of this highway did we use to determine speed and factor ?
]]
result.attributes_to_keep = {}


-- An example is:
local highway = attributes.highway
if highway == "motorway" or
highway == "motorway_link" then
Expand All @@ -60,23 +148,76 @@ The main _factor_and_speed_ is function is the most important active part of the
- _profiles_: Defines the profiles for this vehicle, by default at minimum _fastest_ (with no name) and _shortest_ have to be defined.
- _factor_and_speed_: The core of the vehicle definition, converts edge attributes into a factor and speed.

#### Profiles

A profile describes **the behaviour of a vehicle** and is defined by a _metric_, a function to calculate speed/factor and a name:
#### The profile and meta whitelists

- _metric_: This can be _time_, _distance_ or completely _custom_.
- _function_name_: The name of the function to calculate factor and/or speed.
- _name_: The name of the profile (an empty string automatically means 'fastest', the default).
_summary_:
- Tags of which the key is in `profile_whitelist` are used to determine speed and factor and visible in the called procedure. They must be copied if the tag is used to determine a factor.
- Tags of which the key is in `meta_whitelist` are copied from OSM and only visible in the constructed route.
- There are a few subtle pitfalls, read them below.

#### Factor and speed functions
When building a route planner, the routeplanner cares about certain information at certain times. The first phase consists of knowing how much time every street takes, the second phase is to find a sequence of streets so that the resulting sequence has a minimal time. At last, this sequence has to be translated into a useable format which has extra information such as streetnames.

A factor and speed function converts a set of attributes into a speed and/or factor. Usually the default _factor_and_speed_ will define a speed for each possible set of profile attributes. Based on this Itinero can define a _fastest_ and a _shortest_ profile with metrics being _time_ and _distance_ respectively. A _custom_ profile uses a _factor_ to define weights of edges. For shortest this factor is constant, usually equal to 1.
Ofcourse, the streetname has no effect on the speed or comfort of a street. In other words, it is only necessary to load the streetnames during the last phase, the translation.

To summarize there are three options when using a _factor_and_speed_ function:
To support this, two lists are defined: `profile_whitelist` and `meta_whitelist`.

- With metric _distance_: Itinero will use the function to get the speed and set factor to a constant, usually 1.
- With metric _time_: Itinero will use the function to get the speed and set factor to 1/speed.
- With metric _custom_: Itinero will use the function to get the speed and the factor.
The *profile_whitelist* contains the keys that can be used during the weight calculation. Typical examples are `highway` (in order to read the road classification), `access` (to know if a vehicle is allowed on the path), `maxspeed`, `surface` (to calculate comfort or even incorportate that a bad surface will go slower).

The function called by the profile (in the example `factor_and_speed`) gets a table called `attributes`. This table only contains tags where the key is within `profile_whitelist`.

Note that, whenever an attribute is used to determine speed and/or factor, the value must ALWAYS be added to `result.attributes_to_keep`. Itinero uses further optimizations based on the attribute table. Using a tag and not adding it will result in bugs.


The *meta_whitelist* contains values that are needed only in the output route. The prime example here is `name` and `ref`, the streetname of the road. Other examples are `bridge` and `tunnel` - they do not have an impact on speed or comfort, but they can be important clues for navigation. Another example is `colour` can contain colour of (cycle)networks, which might be needed as well.

Having a value in the `meta_whitelist` means that the OSM tag is immediatly copied to the output.

### Handling relations

If you need information from relations to do route planning (e.g. prefer cycling networks), add a function called `relation_tag_processor`.
This function is called for every relation. The tags that should be kept must be copied into `result.attributes_to_keep`.

All these attributes are then added onto the attributes of every element which is part of the relation. If a key already exists in the tagcollection of that way, the new value will be appended with `,` between them.

Important: the keys mentioned in `attributes_to_keep` have to be in the `profile_whitelist`, the keys needed by this function must be in `meta_whitelist`.

```Lua

-- Processes the relation. All tags which are added to result.attributes_to_keep will be copied to 'attributes' of each individual way
function relation_tag_processor (attributes, result)
result.attributes_to_keep = {}
if attributes.network == "lcn" then
result.attributes_to_keep.lcn = "yes"
end

if attributes.colour ~= nil and
(result.attributes_to_keep.brussels == "yes" or result.attributes_to_keep.genk == "yes")
then
result.attributes_to_keep.cyclecolour = attributes.colour
end
end
```

### Handling nodes

At the moment, giving delays for e.g. traffic lights is not supported.

[https://github.com/itinero/routing/issues/285]

`node_restrictions` can calculate that a road is not accessible, see an example here: [https://github.com/itinero/routing/blob/develop/src/Itinero/Osm/Vehicles/car.lua#L93]

### Logging and debugging

In case of debugging, the following code can be used to debug a table (e.g. attributes):


for k, v in pairs( attributes ) do
itinero.log(tostring(k) .. "-->" .. tostring(v))
end
itinero.log("\n------------------\n")

This kills performance, don't use in production

## Default profiles

Expand Down