factor out common lua code to helper files
This commit is contained in:
parent
b3ef27d104
commit
97c0a74c04
@ -20,6 +20,8 @@
|
|||||||
- `properties.uturn_penalty` is deprecated. Set it in the `turn_function`. The turn type is exposed as `ExtractionTurn::direction_modifier`.
|
- `properties.uturn_penalty` is deprecated. Set it in the `turn_function`. The turn type is exposed as `ExtractionTurn::direction_modifier`.
|
||||||
- `properties.traffic_light_penalty` is deprecated. Traffic light penalties now need to be set over in the turn function.
|
- `properties.traffic_light_penalty` is deprecated. Traffic light penalties now need to be set over in the turn function.
|
||||||
Each turn with a traffic light is marked with `ExtractionTurn::has_traffic_light = true`.
|
Each turn with a traffic light is marked with `ExtractionTurn::has_traffic_light = true`.
|
||||||
|
- Renamed the helper file `profiles/lib/directional.lua` to `profiles/lib/tags.lua` since it now provides more general tags parsing utility functions.
|
||||||
|
- The car and foot profiles now depend on the helper file `profiles/lib/handlers.lua`.
|
||||||
- Infrastructure
|
- Infrastructure
|
||||||
- Disabled link-time optimized (LTO) builds by default. Enable by passing `-DENABLE_LTO=ON` to `cmake` if you need the performance and know what you are doing.
|
- Disabled link-time optimized (LTO) builds by default. Enable by passing `-DENABLE_LTO=ON` to `cmake` if you need the performance and know what you are doing.
|
||||||
- Datafile versioning is now based on OSRM semver values, rather than source code checksums.
|
- Datafile versioning is now based on OSRM semver values, rather than source code checksums.
|
||||||
|
@ -12,7 +12,7 @@ Feature: Testbot - side bias
|
|||||||
Given the profile file "car" extended with
|
Given the profile file "car" extended with
|
||||||
"""
|
"""
|
||||||
properties.left_hand_driving = true
|
properties.left_hand_driving = true
|
||||||
turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
profile.turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
||||||
"""
|
"""
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -35,7 +35,7 @@ Feature: Testbot - side bias
|
|||||||
Given the profile file "car" extended with
|
Given the profile file "car" extended with
|
||||||
"""
|
"""
|
||||||
properties.left_hand_driving = false
|
properties.left_hand_driving = false
|
||||||
turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
profile.turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
||||||
"""
|
"""
|
||||||
And the node map
|
And the node map
|
||||||
"""
|
"""
|
||||||
|
627
profiles/car.lua
627
profiles/car.lua
@ -1,15 +1,42 @@
|
|||||||
|
-- Car profile
|
||||||
|
|
||||||
api_version = 1
|
api_version = 1
|
||||||
|
|
||||||
-- Car profile
|
|
||||||
local find_access_tag = require("lib/access").find_access_tag
|
local find_access_tag = require("lib/access").find_access_tag
|
||||||
local get_destination = require("lib/destination").get_destination
|
|
||||||
local set_classification = require("lib/guidance").set_classification
|
|
||||||
local get_turn_lanes = require("lib/guidance").get_turn_lanes
|
|
||||||
local Set = require('lib/set')
|
local Set = require('lib/set')
|
||||||
local Sequence = require('lib/sequence')
|
local Sequence = require('lib/sequence')
|
||||||
local Directional = require('lib/directional')
|
local Handlers = require("lib/handlers")
|
||||||
|
local next = next -- bind to local for speed
|
||||||
|
|
||||||
|
-- set profile properties
|
||||||
|
properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s
|
||||||
|
properties.use_turn_restrictions = true
|
||||||
|
properties.continue_straight_at_waypoint = true
|
||||||
|
properties.left_hand_driving = false
|
||||||
|
-- this will use the duration and {forward/backward}_speed values as weight
|
||||||
|
properties.weight_name = 'duration'
|
||||||
|
--properties.weight_name = 'distance'
|
||||||
|
|
||||||
|
local profile = {
|
||||||
|
default_mode = mode.driving,
|
||||||
|
default_speed = 10,
|
||||||
|
oneway_handling = true,
|
||||||
|
|
||||||
|
side_road_speed_multiplier = 0.8,
|
||||||
|
turn_penalty = 7.5,
|
||||||
|
speed_reduction = 0.8,
|
||||||
|
traffic_light_penalty = 2,
|
||||||
|
u_turn_penalty = 20,
|
||||||
|
|
||||||
|
-- Note: this biases right-side driving.
|
||||||
|
-- Should be inverted for left-driving countries.
|
||||||
|
turn_bias = properties.left_hand_driving and 1/1.075 or 1.075,
|
||||||
|
|
||||||
|
-- a list of suffixes to suppress in name change instructions
|
||||||
|
suffix_list = {
|
||||||
|
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
|
||||||
|
},
|
||||||
|
|
||||||
-- Begin of globals
|
|
||||||
barrier_whitelist = Set {
|
barrier_whitelist = Set {
|
||||||
'cattle_grid',
|
'cattle_grid',
|
||||||
'border_control',
|
'border_control',
|
||||||
@ -20,7 +47,7 @@ barrier_whitelist = Set {
|
|||||||
'lift_gate',
|
'lift_gate',
|
||||||
'no',
|
'no',
|
||||||
'entrance'
|
'entrance'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tag_whitelist = Set {
|
access_tag_whitelist = Set {
|
||||||
'yes',
|
'yes',
|
||||||
@ -30,7 +57,7 @@ access_tag_whitelist = Set {
|
|||||||
'permissive',
|
'permissive',
|
||||||
'designated',
|
'designated',
|
||||||
'destination'
|
'destination'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tag_blacklist = Set {
|
access_tag_blacklist = Set {
|
||||||
'no',
|
'no',
|
||||||
@ -40,29 +67,35 @@ access_tag_blacklist = Set {
|
|||||||
'emergency',
|
'emergency',
|
||||||
'psv',
|
'psv',
|
||||||
'delivery'
|
'delivery'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tags_hierarchy = Sequence {
|
access_tags_hierarchy = Sequence {
|
||||||
'motorcar',
|
'motorcar',
|
||||||
'motor_vehicle',
|
'motor_vehicle',
|
||||||
'vehicle',
|
'vehicle',
|
||||||
'access'
|
'access'
|
||||||
}
|
},
|
||||||
|
|
||||||
service_tag_forbidden = Set {
|
service_tag_forbidden = Set {
|
||||||
'emergency_access'
|
'emergency_access'
|
||||||
}
|
},
|
||||||
|
|
||||||
restrictions = Sequence {
|
restrictions = Sequence {
|
||||||
'motorcar', "motor_vehicle", "vehicle" }
|
'motorcar',
|
||||||
|
'motor_vehicle',
|
||||||
|
'vehicle'
|
||||||
|
},
|
||||||
|
|
||||||
-- A list of suffixes to suppress in name change instructions
|
avoid = Set {
|
||||||
-- Note: a Set does not work here because it's read from C++
|
'area',
|
||||||
suffix_list = {
|
'toll',
|
||||||
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
|
'reversible',
|
||||||
}
|
'impassable',
|
||||||
|
'hov_lanes'
|
||||||
|
},
|
||||||
|
|
||||||
speed_profile = {
|
speeds = Sequence {
|
||||||
|
highway = {
|
||||||
motorway = 90,
|
motorway = 90,
|
||||||
motorway_link = 45,
|
motorway_link = 45,
|
||||||
trunk = 85,
|
trunk = 85,
|
||||||
@ -77,21 +110,25 @@ speed_profile = {
|
|||||||
residential = 25,
|
residential = 25,
|
||||||
living_street = 10,
|
living_street = 10,
|
||||||
service = 15,
|
service = 15,
|
||||||
--track = 5,
|
|
||||||
ferry = 5,
|
|
||||||
movable = 5,
|
|
||||||
shuttle_train = 10,
|
|
||||||
default = 10
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
-- service speeds
|
|
||||||
service_speeds = {
|
service_speeds = {
|
||||||
alley = 5,
|
alley = 5,
|
||||||
parking = 5,
|
parking = 5,
|
||||||
parking_aisle = 5,
|
parking_aisle = 5,
|
||||||
driveway = 5,
|
driveway = 5,
|
||||||
["drive-through"] = 5
|
["drive-through"] = 5
|
||||||
}
|
},
|
||||||
|
|
||||||
|
route_speeds = {
|
||||||
|
ferry = 5,
|
||||||
|
shuttle_train = 10
|
||||||
|
},
|
||||||
|
|
||||||
|
bridge_speeds = {
|
||||||
|
movable = 5
|
||||||
|
},
|
||||||
|
|
||||||
-- surface/trackype/smoothness
|
-- surface/trackype/smoothness
|
||||||
-- values were estimated from looking at the photos at the relevant wiki pages
|
-- values were estimated from looking at the photos at the relevant wiki pages
|
||||||
@ -132,7 +169,7 @@ surface_speeds = {
|
|||||||
sand = 20,
|
sand = 20,
|
||||||
|
|
||||||
mud = 10
|
mud = 10
|
||||||
}
|
},
|
||||||
|
|
||||||
-- max speed for tracktypes
|
-- max speed for tracktypes
|
||||||
tracktype_speeds = {
|
tracktype_speeds = {
|
||||||
@ -141,7 +178,7 @@ tracktype_speeds = {
|
|||||||
grade3 = 30,
|
grade3 = 30,
|
||||||
grade4 = 25,
|
grade4 = 25,
|
||||||
grade5 = 20
|
grade5 = 20
|
||||||
}
|
},
|
||||||
|
|
||||||
-- max speed for smoothnesses
|
-- max speed for smoothnesses
|
||||||
smoothness_speeds = {
|
smoothness_speeds = {
|
||||||
@ -151,7 +188,7 @@ smoothness_speeds = {
|
|||||||
horrible = 10,
|
horrible = 10,
|
||||||
very_horrible = 5,
|
very_horrible = 5,
|
||||||
impassable = 0
|
impassable = 0
|
||||||
}
|
},
|
||||||
|
|
||||||
-- http://wiki.openstreetmap.org/wiki/Speed_limits
|
-- http://wiki.openstreetmap.org/wiki/Speed_limits
|
||||||
maxspeed_table_default = {
|
maxspeed_table_default = {
|
||||||
@ -159,7 +196,7 @@ maxspeed_table_default = {
|
|||||||
rural = 90,
|
rural = 90,
|
||||||
trunk = 110,
|
trunk = 110,
|
||||||
motorway = 130
|
motorway = 130
|
||||||
}
|
},
|
||||||
|
|
||||||
-- List only exceptions
|
-- List only exceptions
|
||||||
maxspeed_table = {
|
maxspeed_table = {
|
||||||
@ -188,77 +225,25 @@ maxspeed_table = {
|
|||||||
["nl:trunk"] = 100,
|
["nl:trunk"] = 100,
|
||||||
["none"] = 140
|
["none"] = 140
|
||||||
}
|
}
|
||||||
|
}
|
||||||
-- set profile properties
|
|
||||||
properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s
|
|
||||||
properties.use_turn_restrictions = true
|
|
||||||
properties.continue_straight_at_waypoint = true
|
|
||||||
properties.left_hand_driving = false
|
|
||||||
-- this will use the duration and {forward/backward}_speed values as weight
|
|
||||||
properties.weight_name = 'duration'
|
|
||||||
--properties.weight_name = 'distance'
|
|
||||||
|
|
||||||
local side_road_speed_multiplier = 0.8
|
|
||||||
local turn_penalty = 7.5
|
|
||||||
local traffic_light_penalty = 2
|
|
||||||
local u_turn_penalty = 20
|
|
||||||
|
|
||||||
-- Note: this biases right-side driving. Should be
|
|
||||||
-- inverted for left-driving countries.
|
|
||||||
local turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
|
||||||
|
|
||||||
local obey_oneway = true
|
|
||||||
local ignore_areas = true
|
|
||||||
local ignore_hov_ways = true
|
|
||||||
local ignore_toll_ways = false
|
|
||||||
|
|
||||||
local abs = math.abs
|
|
||||||
local min = math.min
|
|
||||||
local max = math.max
|
|
||||||
|
|
||||||
local speed_reduction = 0.8
|
|
||||||
|
|
||||||
function get_name_suffix_list(vector)
|
function get_name_suffix_list(vector)
|
||||||
for index,suffix in ipairs(suffix_list) do
|
for index,suffix in ipairs(profile.suffix_list) do
|
||||||
vector:Add(suffix)
|
vector:Add(suffix)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_restrictions(vector)
|
function get_restrictions(vector)
|
||||||
for i,v in ipairs(restrictions) do
|
for i,v in ipairs(profile.restrictions) do
|
||||||
vector:Add(v)
|
vector:Add(v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parse_maxspeed(source)
|
|
||||||
if not source then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
local n = tonumber(source:match("%d*"))
|
|
||||||
if n then
|
|
||||||
if string.match(source, "mph") or string.match(source, "mp/h") then
|
|
||||||
n = (n*1609)/1000
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- parse maxspeed like FR:urban
|
|
||||||
source = string.lower(source)
|
|
||||||
n = maxspeed_table[source]
|
|
||||||
if not n then
|
|
||||||
local highway_type = string.match(source, "%a%a:(%a+)")
|
|
||||||
n = maxspeed_table_default[highway_type]
|
|
||||||
if not n then
|
|
||||||
n = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return n
|
|
||||||
end
|
|
||||||
|
|
||||||
function node_function (node, result)
|
function node_function (node, result)
|
||||||
-- parse access and barrier tags
|
-- parse access and barrier tags
|
||||||
local access = find_access_tag(node, access_tags_hierarchy)
|
local access = find_access_tag(node, profile.access_tags_hierarchy)
|
||||||
if access then
|
if access then
|
||||||
if access_tag_blacklist[access] then
|
if profile.access_tag_blacklist[access] then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -268,7 +253,7 @@ function node_function (node, result)
|
|||||||
local bollard = node:get_value_by_key("bollard")
|
local bollard = node:get_value_by_key("bollard")
|
||||||
local rising_bollard = bollard and "rising" == bollard
|
local rising_bollard = bollard and "rising" == bollard
|
||||||
|
|
||||||
if not barrier_whitelist[barrier] and not rising_bollard then
|
if not profile.barrier_whitelist[barrier] and not rising_bollard then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -281,459 +266,95 @@ function node_function (node, result)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- abort early if this way is obviouslt not routable
|
|
||||||
function initial_routability_check(way,result,data)
|
|
||||||
data.highway = way:get_value_by_key('highway')
|
|
||||||
|
|
||||||
return data.highway ~= nil or
|
function way_function(way, result)
|
||||||
way:get_value_by_key('route') ~= nil or
|
-- the intial filtering of ways based on presence of tags
|
||||||
way:get_value_by_key('bridge') ~= nil
|
-- affects processing times significantly, because all ways
|
||||||
end
|
-- have to be checked.
|
||||||
|
-- to increase performance, prefetching and intial tag check
|
||||||
|
-- is done in directly instead of via a handler.
|
||||||
|
|
||||||
-- all lanes restricted to hov vehicles?
|
-- in general we should try to abort as soon as
|
||||||
local function has_all_designated_hov_lanes(lanes)
|
-- possible if the way is not routable, to avoid doing
|
||||||
if not lanes then
|
-- unnecessary work. this implies we should check things that
|
||||||
return false
|
-- commonly forbids access early, and handle edge cases later.
|
||||||
end
|
|
||||||
-- This gmatch call effectively splits the string on | chars.
|
|
||||||
-- we append an extra | to the end so that we can match the final part
|
|
||||||
for lane in (lanes .. '|'):gmatch("([^|]*)|") do
|
|
||||||
if lane and lane ~= "designated" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle high occupancy vehicle tags
|
-- data table for storing intermediate values during processing
|
||||||
function handle_hov(way,result,data)
|
local data = {
|
||||||
-- respect user-preference for HOV
|
-- prefetch tags
|
||||||
if not ignore_hov_ways then
|
highway = way:get_value_by_key('highway'),
|
||||||
|
bridge = way:get_value_by_key('bridge'),
|
||||||
|
route = way:get_value_by_key('route')
|
||||||
|
}
|
||||||
|
|
||||||
|
-- perform an quick initial check and abort if the way is
|
||||||
|
-- obviously not routable. here we require at least one
|
||||||
|
-- of the prefetched tags to be present, ie. the data table
|
||||||
|
-- cannot be empty
|
||||||
|
if next(data) == nil then -- is the data table empty?
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if way is hov only
|
handlers = Sequence {
|
||||||
local hov = way:get_value_by_key("hov")
|
|
||||||
if "designated" == hov then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if all lanes are hov only
|
|
||||||
local hov_lanes_forward, hov_lanes_backward = Directional.get_values_by_key(way,data,'hov:lanes')
|
|
||||||
local inaccessible_forward = has_all_designated_hov_lanes(hov_lanes_forward)
|
|
||||||
local inaccessible_backward = has_all_designated_hov_lanes(hov_lanes_backward)
|
|
||||||
|
|
||||||
if inaccessible_forward then
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
if inaccessible_backward then
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle various that can block access
|
|
||||||
function is_way_blocked(way,result)
|
|
||||||
-- we dont route over areas
|
|
||||||
local area = way:get_value_by_key("area")
|
|
||||||
if ignore_areas and "yes" == area then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- respect user-preference for toll=yes ways
|
|
||||||
local toll = way:get_value_by_key("toll")
|
|
||||||
if ignore_toll_ways and "yes" == toll then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Reversible oneways change direction with low frequency (think twice a day):
|
|
||||||
-- do not route over these at all at the moment because of time dependence.
|
|
||||||
-- Note: alternating (high frequency) oneways are handled below with penalty.
|
|
||||||
local oneway = way:get_value_by_key("oneway")
|
|
||||||
if "reversible" == oneway then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local impassable = way:get_value_by_key("impassable")
|
|
||||||
if "yes" == impassable then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local status = way:get_value_by_key("status")
|
|
||||||
if "impassable" == status then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set default mode
|
|
||||||
function set_default_mode(way,result)
|
|
||||||
result.forward_mode = mode.driving
|
|
||||||
result.backward_mode = mode.driving
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check accessibility by traversing our acces tag hierarchy
|
|
||||||
function handle_access(way,result,data)
|
|
||||||
data.forward_access, data.backward_access =
|
|
||||||
Directional.get_values_by_set(way,data,access_tags_hierarchy)
|
|
||||||
|
|
||||||
if access_tag_blacklist[data.forward_access] then
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
|
|
||||||
if access_tag_blacklist[data.backward_access] then
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
|
|
||||||
if result.forward_mode == mode.inaccessible and result.backward_mode == mode.inaccessible then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handling ferries and piers
|
|
||||||
function handle_ferries(way,result)
|
|
||||||
local route = way:get_value_by_key("route")
|
|
||||||
if route then
|
|
||||||
local route_speed = speed_profile[route]
|
|
||||||
if route_speed and route_speed > 0 then
|
|
||||||
local duration = way:get_value_by_key("duration")
|
|
||||||
if duration and durationIsValid(duration) then
|
|
||||||
result.duration = max( parseDuration(duration), 1 )
|
|
||||||
end
|
|
||||||
result.forward_mode = mode.ferry
|
|
||||||
result.backward_mode = mode.ferry
|
|
||||||
result.forward_speed = route_speed
|
|
||||||
result.backward_speed = route_speed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handling movable bridges
|
|
||||||
function handle_movables(way,result)
|
|
||||||
local bridge = way:get_value_by_key("bridge")
|
|
||||||
if bridge then
|
|
||||||
local bridge_speed = speed_profile[bridge]
|
|
||||||
if bridge_speed and bridge_speed > 0 then
|
|
||||||
local capacity_car = way:get_value_by_key("capacity:car")
|
|
||||||
if capacity_car ~= 0 then
|
|
||||||
local duration = way:get_value_by_key("duration")
|
|
||||||
if duration and durationIsValid(duration) then
|
|
||||||
result.duration = max( parseDuration(duration), 1 )
|
|
||||||
end
|
|
||||||
result.forward_speed = bridge_speed
|
|
||||||
result.backward_speed = bridge_speed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle speed (excluding maxspeed)
|
|
||||||
function handle_speed(way,result,data)
|
|
||||||
if result.forward_speed == -1 then
|
|
||||||
local highway_speed = speed_profile[data.highway]
|
|
||||||
-- Set the avg speed on the way if it is accessible by road class
|
|
||||||
if highway_speed then
|
|
||||||
result.forward_speed = highway_speed
|
|
||||||
result.backward_speed = highway_speed
|
|
||||||
else
|
|
||||||
-- Set the avg speed on ways that are marked accessible
|
|
||||||
if access_tag_whitelist[data.forward_access] then
|
|
||||||
result.forward_speed = speed_profile["default"]
|
|
||||||
end
|
|
||||||
|
|
||||||
if access_tag_whitelist[data.backward_access] then
|
|
||||||
result.backward_speed = speed_profile["default"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if -1 == result.forward_speed and -1 == result.backward_speed then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if handle_side_roads(way,result) == false then return false end
|
|
||||||
if handle_surface(way,result) == false then return false end
|
|
||||||
if handle_maxspeed(way,data,result) == false then return false end
|
|
||||||
if handle_speed_scaling(way,result) == false then return false end
|
|
||||||
if handle_alternating_speed(way,result) == false then return false end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- reduce speed on special side roads
|
|
||||||
function handle_side_roads(way,result)
|
|
||||||
local sideway = way:get_value_by_key("side_road")
|
|
||||||
if "yes" == sideway or "rotary" == sideway then
|
|
||||||
result.forward_speed = result.forward_speed * side_road_speed_multiplier
|
|
||||||
result.backward_speed = result.backward_speed * side_road_speed_multiplier
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- reduce speed on bad surfaces
|
|
||||||
function handle_surface(way,result)
|
|
||||||
local surface = way:get_value_by_key("surface")
|
|
||||||
local tracktype = way:get_value_by_key("tracktype")
|
|
||||||
local smoothness = way:get_value_by_key("smoothness")
|
|
||||||
|
|
||||||
if surface and surface_speeds[surface] then
|
|
||||||
result.forward_speed = math.min(surface_speeds[surface], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(surface_speeds[surface], result.backward_speed)
|
|
||||||
end
|
|
||||||
if tracktype and tracktype_speeds[tracktype] then
|
|
||||||
result.forward_speed = math.min(tracktype_speeds[tracktype], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(tracktype_speeds[tracktype], result.backward_speed)
|
|
||||||
end
|
|
||||||
if smoothness and smoothness_speeds[smoothness] then
|
|
||||||
result.forward_speed = math.min(smoothness_speeds[smoothness], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(smoothness_speeds[smoothness], result.backward_speed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handles name, including ref and pronunciation
|
|
||||||
function handle_names(way,result)
|
|
||||||
-- parse the remaining tags
|
|
||||||
local name = way:get_value_by_key("name")
|
|
||||||
local pronunciation = way:get_value_by_key("name:pronunciation")
|
|
||||||
local ref = way:get_value_by_key("ref")
|
|
||||||
|
|
||||||
-- Set the name that will be used for instructions
|
|
||||||
if name then
|
|
||||||
result.name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
if ref then
|
|
||||||
result.ref = canonicalizeStringList(ref, ";")
|
|
||||||
end
|
|
||||||
|
|
||||||
if pronunciation then
|
|
||||||
result.pronunciation = pronunciation
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle turn lanes
|
|
||||||
function handle_turn_lanes(way,result,data)
|
|
||||||
local forward, backward = get_turn_lanes(way,data)
|
|
||||||
|
|
||||||
if forward then
|
|
||||||
result.turn_lanes_forward = forward
|
|
||||||
end
|
|
||||||
|
|
||||||
if backward then
|
|
||||||
result.turn_lanes_backward = backward
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- junctions
|
|
||||||
function handle_roundabouts(way,result)
|
|
||||||
local junction = way:get_value_by_key("junction");
|
|
||||||
|
|
||||||
if junction == "roundabout" then
|
|
||||||
result.roundabout = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- See Issue 3361: roundabout-shaped not following roundabout rules.
|
|
||||||
-- This will get us "At Strausberger Platz do Maneuver X" instead of multiple quick turns.
|
|
||||||
-- In a new API version we can think of having a separate type passing it through to the user.
|
|
||||||
if junction == "circular" then
|
|
||||||
result.circular = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- service roads
|
|
||||||
function handle_service(way,result)
|
|
||||||
local service = way:get_value_by_key("service")
|
|
||||||
if service then
|
|
||||||
-- Set don't allow access to certain service roads
|
|
||||||
if service_tag_forbidden[service] then
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- scale speeds to get better average driving times
|
|
||||||
function handle_speed_scaling(way,result)
|
|
||||||
local width = math.huge
|
|
||||||
local lanes = math.huge
|
|
||||||
if result.forward_speed > 0 or result.backward_speed > 0 then
|
|
||||||
local width_string = way:get_value_by_key("width")
|
|
||||||
if width_string and tonumber(width_string:match("%d*")) then
|
|
||||||
width = tonumber(width_string:match("%d*"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local lanes_string = way:get_value_by_key("lanes")
|
|
||||||
if lanes_string and tonumber(lanes_string:match("%d*")) then
|
|
||||||
lanes = tonumber(lanes_string:match("%d*"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_bidirectional = result.forward_mode ~= mode.inaccessible and
|
|
||||||
result.backward_mode ~= mode.inaccessible
|
|
||||||
|
|
||||||
local service = way:get_value_by_key("service")
|
|
||||||
if result.forward_speed > 0 then
|
|
||||||
local scaled_speed = result.forward_speed * speed_reduction
|
|
||||||
local penalized_speed = math.huge
|
|
||||||
if service and service_speeds[service] then
|
|
||||||
penalized_speed = service_speeds[service]
|
|
||||||
elseif width <= 3 or (lanes <= 1 and is_bidirectional) then
|
|
||||||
penalized_speed = result.forward_speed / 2
|
|
||||||
end
|
|
||||||
result.forward_speed = math.min(penalized_speed, scaled_speed)
|
|
||||||
end
|
|
||||||
|
|
||||||
if result.backward_speed > 0 then
|
|
||||||
local scaled_speed = result.backward_speed * speed_reduction
|
|
||||||
local penalized_speed = math.huge
|
|
||||||
if service and service_speeds[service]then
|
|
||||||
penalized_speed = service_speeds[service]
|
|
||||||
elseif width <= 3 or (lanes <= 1 and is_bidirectional) then
|
|
||||||
penalized_speed = result.backward_speed / 2
|
|
||||||
end
|
|
||||||
result.backward_speed = math.min(penalized_speed, scaled_speed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle oneways tags
|
|
||||||
function handle_oneway(way,result,data)
|
|
||||||
local oneway = way:get_value_by_key("oneway")
|
|
||||||
data.oneway = oneway
|
|
||||||
if obey_oneway then
|
|
||||||
if oneway == "-1" then
|
|
||||||
data.is_reverse_oneway = true
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
elseif oneway == "yes" or
|
|
||||||
oneway == "1" or
|
|
||||||
oneway == "true" then
|
|
||||||
data.is_forward_oneway = true
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
else
|
|
||||||
local junction = way:get_value_by_key("junction")
|
|
||||||
if data.highway == "motorway" or
|
|
||||||
junction == "roundabout" or
|
|
||||||
junction == "circular" then
|
|
||||||
if oneway ~= "no" then
|
|
||||||
-- implied oneway
|
|
||||||
data.is_forward_oneway = true
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle destination tags
|
|
||||||
function handle_destinations(way,result,data)
|
|
||||||
if data.is_forward_oneway or data.is_reverse_oneway then
|
|
||||||
local destination = get_destination(way, data.is_forward_oneway)
|
|
||||||
result.destinations = canonicalizeStringList(destination, ",")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- maxspeed and advisory maxspeed
|
|
||||||
function handle_maxspeed(way,data,result)
|
|
||||||
local keys = Sequence { 'maxspeed:advisory', 'maxspeed' }
|
|
||||||
local forward, backward = Directional.get_values_by_set(way,data,keys)
|
|
||||||
forward = parse_maxspeed(forward)
|
|
||||||
backward = parse_maxspeed(backward)
|
|
||||||
|
|
||||||
if forward and forward > 0 then
|
|
||||||
result.forward_speed = forward
|
|
||||||
end
|
|
||||||
|
|
||||||
if backward and backward > 0 then
|
|
||||||
result.backward_speed = backward
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Handle high frequency reversible oneways (think traffic signal controlled, changing direction every 15 minutes).
|
|
||||||
-- Scaling speed to take average waiting time into account plus some more for start / stop.
|
|
||||||
function handle_alternating_speed(way,result)
|
|
||||||
if "alternating" == way:get_value_by_key('oneway') then
|
|
||||||
local scaling_factor = 0.4
|
|
||||||
if result.forward_speed ~= math.huge then
|
|
||||||
result.forward_speed = result.forward_speed * scaling_factor
|
|
||||||
end
|
|
||||||
if result.backward_speed ~= math.huge then
|
|
||||||
result.backward_speed = result.backward_speed * scaling_factor
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- determine if this way can be used as a start/end point for routing
|
|
||||||
function handle_startpoint(way,result)
|
|
||||||
-- only allow this road as start point if it not a ferry
|
|
||||||
result.is_startpoint = result.forward_mode == mode.driving or
|
|
||||||
result.backward_mode == mode.driving
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set the road classification based on guidance globals configuration
|
|
||||||
function handle_classification(way,result,data)
|
|
||||||
set_classification(data.highway,result,way)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- main entry point for processsing a way
|
|
||||||
function way_function(way, result)
|
|
||||||
-- intermediate values used during processing
|
|
||||||
local data = {}
|
|
||||||
|
|
||||||
-- to optimize processing, 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 complicated edge
|
|
||||||
-- cases later.
|
|
||||||
|
|
||||||
-- perform an quick initial check and abort if way is obviously
|
|
||||||
-- not routable, e.g. because it does not have any of the key
|
|
||||||
-- tags indicating routability
|
|
||||||
if initial_routability_check(way,result,data) == false then return end
|
|
||||||
|
|
||||||
-- 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
|
||||||
if set_default_mode(way,result) == false then return end
|
'handle_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
|
||||||
if is_way_blocked(way,result) == false then return end
|
'handle_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
|
||||||
if handle_access(way,result,data) == false then return end
|
'handle_access',
|
||||||
|
|
||||||
-- check whether forward/backward directons are routable
|
-- check whether forward/backward directons are routable
|
||||||
if handle_oneway(way,result,data) == false then return end
|
'handle_oneway',
|
||||||
|
|
||||||
-- check whether forward/backward directons are routable
|
-- check whether forward/backward directons are routable
|
||||||
if handle_destinations(way,result,data) == false then return end
|
'handle_destinations',
|
||||||
|
|
||||||
-- check whether we're using a special transport mode
|
-- check whether we're using a special transport mode
|
||||||
if handle_ferries(way,result) == false then return end
|
'handle_ferries',
|
||||||
if handle_movables(way,result) == false then return end
|
'handle_movables',
|
||||||
|
|
||||||
-- handle service road restrictions
|
-- handle service road restrictions
|
||||||
if handle_service(way,result) == false then return end
|
'handle_service',
|
||||||
|
|
||||||
-- check high occupancy vehicle restrictions
|
-- check high occupancy vehicle restrictions
|
||||||
if handle_hov(way,result,data) == false then return end
|
'handle_hov',
|
||||||
|
|
||||||
-- compute speed taking into account way type, maxspeed tags, etc.
|
-- compute speed taking into account way type, maxspeed tags, etc.
|
||||||
if handle_speed(way,result,data) == false then return end
|
'handle_speed',
|
||||||
|
'handle_side_roads',
|
||||||
|
'handle_surface',
|
||||||
|
'handle_maxspeed',
|
||||||
|
'handle_speed_scaling',
|
||||||
|
'handle_alternating_speed',
|
||||||
|
|
||||||
-- handle turn lanes and road classification, used for guidance
|
-- handle turn lanes and road classification, used for guidance
|
||||||
if handle_turn_lanes(way,result,data) == false then return end
|
'handle_turn_lanes',
|
||||||
if handle_classification(way,result,data) == false then return end
|
'handle_classification',
|
||||||
|
|
||||||
-- handle various other flags
|
-- handle various other flags
|
||||||
if handle_roundabouts(way,result) == false then return end
|
'handle_roundabouts',
|
||||||
if handle_startpoint(way,result) == false then return end
|
'handle_startpoint',
|
||||||
|
|
||||||
-- set name, ref and pronunciation
|
-- set name, ref and pronunciation
|
||||||
if handle_names(way,result) == false then return end
|
'handle_names'
|
||||||
|
}
|
||||||
|
|
||||||
|
Handlers.run(handlers,way,result,data,profile)
|
||||||
end
|
end
|
||||||
|
|
||||||
function turn_function (turn)
|
function turn_function (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.
|
||||||
|
local turn_penalty = profile.turn_penalty
|
||||||
|
local turn_bias = profile.turn_bias
|
||||||
|
|
||||||
if turn.turn_type ~= turn_type.no_turn then
|
if turn.turn_type ~= turn_type.no_turn then
|
||||||
if turn.angle >= 0 then
|
if turn.angle >= 0 then
|
||||||
turn.duration = turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias)))
|
turn.duration = turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias)))
|
||||||
@ -742,11 +363,11 @@ 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 + u_turn_penalty
|
turn.duration = turn.duration + profile.u_turn_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.traffic_light_penalty
|
||||||
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
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
|
-- Foot profile
|
||||||
|
|
||||||
api_version = 1
|
api_version = 1
|
||||||
|
|
||||||
-- Foot profile
|
|
||||||
local find_access_tag = require("lib/access").find_access_tag
|
local find_access_tag = require("lib/access").find_access_tag
|
||||||
local get_destination = require("lib/destination").get_destination
|
|
||||||
local set_classification = require("lib/guidance").set_classification
|
|
||||||
local Set = require('lib/set')
|
local Set = require('lib/set')
|
||||||
local Sequence = require('lib/sequence')
|
local Sequence = require('lib/sequence')
|
||||||
local Directional = require('lib/directional')
|
local Handlers = require("lib/handlers")
|
||||||
|
local next = next -- bind to local for speed
|
||||||
|
|
||||||
|
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s
|
||||||
|
properties.use_turn_restrictions = false
|
||||||
|
properties.continue_straight_at_waypoint = false
|
||||||
|
properties.weight_name = 'duration'
|
||||||
|
|
||||||
|
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 {
|
barrier_whitelist = Set {
|
||||||
'cycle_barrier',
|
'cycle_barrier',
|
||||||
@ -19,37 +33,43 @@ barrier_whitelist = Set {
|
|||||||
'gate',
|
'gate',
|
||||||
'no',
|
'no',
|
||||||
'block'
|
'block'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tag_whitelist = Set {
|
access_tag_whitelist = Set {
|
||||||
'yes',
|
'yes',
|
||||||
'foot',
|
'foot',
|
||||||
'permissive',
|
'permissive',
|
||||||
'designated'
|
'designated'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tag_blacklist = Set {
|
access_tag_blacklist = Set {
|
||||||
'no',
|
'no',
|
||||||
'private',
|
'private',
|
||||||
'agricultural',
|
'agricultural',
|
||||||
'forestry',
|
'forestry',
|
||||||
'delivery'
|
'delivery'
|
||||||
}
|
},
|
||||||
|
|
||||||
access_tags_hierarchy = Sequence {
|
access_tags_hierarchy = Sequence {
|
||||||
'foot',
|
'foot',
|
||||||
'access'
|
'access'
|
||||||
}
|
},
|
||||||
|
|
||||||
restrictions = Sequence { 'foot' }
|
restrictions = Sequence {
|
||||||
|
'foot'
|
||||||
|
},
|
||||||
|
|
||||||
-- A list of suffixes to suppress in name change instructions
|
-- list of suffixes to suppress in name change instructions
|
||||||
-- Note: a Set does not work here because it's read from C++
|
suffix_list = Set {
|
||||||
suffix_list = {
|
|
||||||
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
|
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
|
||||||
}
|
},
|
||||||
|
|
||||||
walking_speed = 5
|
avoid = Set {
|
||||||
|
'impassable'
|
||||||
|
},
|
||||||
|
|
||||||
speed_profile = {
|
speeds = Sequence {
|
||||||
|
highway = {
|
||||||
primary = walking_speed,
|
primary = walking_speed,
|
||||||
primary_link = walking_speed,
|
primary_link = walking_speed,
|
||||||
secondary = walking_speed,
|
secondary = walking_speed,
|
||||||
@ -67,25 +87,29 @@ speed_profile = {
|
|||||||
pedestrian = walking_speed,
|
pedestrian = walking_speed,
|
||||||
footway = walking_speed,
|
footway = walking_speed,
|
||||||
pier = walking_speed,
|
pier = walking_speed,
|
||||||
default = 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 = {
|
route_speeds = {
|
||||||
ferry = 5
|
ferry = 5
|
||||||
}
|
},
|
||||||
|
|
||||||
platform_speeds = {
|
|
||||||
platform = walking_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
amenity_speeds = {
|
|
||||||
parking = walking_speed,
|
|
||||||
parking_entrance = walking_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
man_made_speeds = {
|
|
||||||
pier = walking_speed
|
|
||||||
}
|
|
||||||
|
|
||||||
surface_speeds = {
|
surface_speeds = {
|
||||||
fine_gravel = walking_speed*0.75,
|
fine_gravel = walking_speed*0.75,
|
||||||
@ -94,74 +118,14 @@ surface_speeds = {
|
|||||||
mud = walking_speed*0.5,
|
mud = walking_speed*0.5,
|
||||||
sand = walking_speed*0.5
|
sand = walking_speed*0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
tracktype_speeds = {}
|
|
||||||
|
|
||||||
smoothness_speeds = {}
|
|
||||||
|
|
||||||
leisure_speeds = {
|
|
||||||
track = walking_speed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s
|
|
||||||
properties.use_turn_restrictions = false
|
|
||||||
properties.continue_straight_at_waypoint = false
|
|
||||||
properties.weight_name = 'duration'
|
|
||||||
|
|
||||||
local traffic_light_penalty = 2
|
|
||||||
local u_turn_penalty = 2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- setting oneway_handling to 'specific' means that the plain 'oneway' tag is ignored,
|
|
||||||
-- but oneway:foot (or or more specific modes) is respected.
|
|
||||||
|
|
||||||
local oneway_handling = 'specific'
|
|
||||||
|
|
||||||
local ignore_areas = false
|
|
||||||
|
|
||||||
|
|
||||||
function get_name_suffix_list(vector)
|
|
||||||
for index,suffix in ipairs(suffix_list) do
|
|
||||||
vector:Add(suffix)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_restrictions(vector)
|
|
||||||
for i,v in ipairs(restrictions) do
|
|
||||||
vector:Add(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parse_maxspeed(source)
|
|
||||||
if not source then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
local n = tonumber(source:match("%d*"))
|
|
||||||
if n then
|
|
||||||
if string.match(source, "mph") or string.match(source, "mp/h") then
|
|
||||||
n = (n*1609)/1000
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- parse maxspeed like FR:urban
|
|
||||||
source = string.lower(source)
|
|
||||||
n = maxspeed_table[source]
|
|
||||||
if not n then
|
|
||||||
local highway_type = string.match(source, "%a%a:(%a+)")
|
|
||||||
n = maxspeed_table_default[highway_type]
|
|
||||||
if not n then
|
|
||||||
n = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return n
|
|
||||||
end
|
|
||||||
|
|
||||||
function node_function (node, result)
|
function node_function (node, result)
|
||||||
-- parse access and barrier tags
|
-- parse access and barrier tags
|
||||||
local access = find_access_tag(node, access_tags_hierarchy)
|
local access = find_access_tag(node, profile.access_tags_hierarchy)
|
||||||
if access then
|
if access then
|
||||||
if access_tag_blacklist[access] then
|
if profile.access_tag_blacklist[access] then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -171,7 +135,7 @@ function node_function (node, result)
|
|||||||
local bollard = node:get_value_by_key("bollard")
|
local bollard = node:get_value_by_key("bollard")
|
||||||
local rising_bollard = bollard and "rising" == bollard
|
local rising_bollard = bollard and "rising" == bollard
|
||||||
|
|
||||||
if not barrier_whitelist[barrier] and not rising_bollard then
|
if not profile.barrier_whitelist[barrier] and not rising_bollard then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -184,330 +148,91 @@ function node_function (node, result)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- abort early if this way is obviouslt not routable
|
|
||||||
function initial_routability_check(way,result,data)
|
|
||||||
data.highway = way:get_value_by_key('highway')
|
|
||||||
data.leisure = way:get_value_by_key("leisure")
|
|
||||||
data.route = way:get_value_by_key("route")
|
|
||||||
data.bridge = way:get_value_by_key("bridge")
|
|
||||||
data.man_made = way:get_value_by_key("man_made")
|
|
||||||
data.railway = way:get_value_by_key("railway")
|
|
||||||
data.platform = way:get_value_by_key("platform")
|
|
||||||
data.amenity = way:get_value_by_key("amenity")
|
|
||||||
data.public_transport = way:get_value_by_key("public_transport")
|
|
||||||
|
|
||||||
return data.highway ~= nil or
|
|
||||||
data.leisure ~= nil or
|
|
||||||
data.route ~= nil or
|
|
||||||
data.bridge ~= nil or
|
|
||||||
data.railway ~= nil or
|
|
||||||
data.platform ~= nil or
|
|
||||||
data.amenity ~= nil or
|
|
||||||
data.man_made ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle various that can block access
|
|
||||||
function is_way_blocked(way,result)
|
|
||||||
-- we dont route over areas
|
|
||||||
local area = way:get_value_by_key("area")
|
|
||||||
if ignore_areas and "yes" == area then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local impassable = way:get_value_by_key("impassable")
|
|
||||||
if "yes" == impassable then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local status = way:get_value_by_key("status")
|
|
||||||
if "impassable" == status then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set default mode
|
|
||||||
function set_default_mode(way,result)
|
|
||||||
result.forward_mode = mode.walking
|
|
||||||
result.backward_mode = mode.walking
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check accessibility by traversing our acces tag hierarchy
|
|
||||||
function handle_access(way,result,data)
|
|
||||||
data.forward_access, data.backward_access =
|
|
||||||
Directional.get_values_by_set(way,data,access_tags_hierarchy)
|
|
||||||
|
|
||||||
if access_tag_blacklist[data.forward_access] then
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
|
|
||||||
if access_tag_blacklist[data.backward_access] then
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
|
|
||||||
if result.forward_mode == mode.inaccessible and result.backward_mode == mode.inaccessible then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handling ferries and piers
|
|
||||||
function handle_ferries(way,result)
|
|
||||||
local route = way:get_value_by_key("route")
|
|
||||||
if route then
|
|
||||||
local route_speed = route_speeds[route]
|
|
||||||
if route_speed and route_speed > 0 then
|
|
||||||
local duration = way:get_value_by_key("duration")
|
|
||||||
if duration and durationIsValid(duration) then
|
|
||||||
result.duration = math.max( parseDuration(duration), 1 )
|
|
||||||
end
|
|
||||||
result.forward_mode = mode.ferry
|
|
||||||
result.backward_mode = mode.ferry
|
|
||||||
result.forward_speed = route_speed
|
|
||||||
result.backward_speed = route_speed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handling movable bridges
|
|
||||||
function handle_movables(way,result)
|
|
||||||
local bridge = way:get_value_by_key("bridge")
|
|
||||||
if bridge then
|
|
||||||
local bridge_speed = speed_profile[bridge]
|
|
||||||
if bridge_speed and bridge_speed > 0 then
|
|
||||||
local capacity_car = way:get_value_by_key("capacity:car")
|
|
||||||
if capacity_car ~= 0 then
|
|
||||||
local duration = way:get_value_by_key("duration")
|
|
||||||
if duration and durationIsValid(duration) then
|
|
||||||
result.duration = max( parseDuration(duration), 1 )
|
|
||||||
end
|
|
||||||
result.forward_speed = bridge_speed
|
|
||||||
result.backward_speed = bridge_speed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle speed (excluding maxspeed)
|
|
||||||
function handle_speed(way,result,data)
|
|
||||||
if result.forward_speed == -1 then
|
|
||||||
local speed = speed_profile[data.highway] or
|
|
||||||
platform_speeds[data.railway] or -- old tagging scheme
|
|
||||||
platform_speeds[data.platform] or
|
|
||||||
amenity_speeds[data.amenity] or
|
|
||||||
man_made_speeds[data.man_made] or
|
|
||||||
leisure_speeds[data.leisure]
|
|
||||||
|
|
||||||
if speed then
|
|
||||||
-- set speed by way type
|
|
||||||
result.forward_speed = highway_speed
|
|
||||||
result.backward_speed = highway_speed
|
|
||||||
result.forward_speed = speed
|
|
||||||
result.backward_speed = speed
|
|
||||||
else
|
|
||||||
-- Set the avg speed on ways that are marked accessible
|
|
||||||
if access_tag_whitelist[data.forward_access] then
|
|
||||||
result.forward_speed = speed_profile["default"]
|
|
||||||
end
|
|
||||||
|
|
||||||
if access_tag_whitelist[data.backward_access] then
|
|
||||||
result.backward_speed = speed_profile["default"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if -1 == result.forward_speed and -1 == result.backward_speed then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if handle_surface(way,result) == false then return false end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- reduce speed on bad surfaces
|
|
||||||
function handle_surface(way,result)
|
|
||||||
local surface = way:get_value_by_key("surface")
|
|
||||||
local tracktype = way:get_value_by_key("tracktype")
|
|
||||||
local smoothness = way:get_value_by_key("smoothness")
|
|
||||||
|
|
||||||
if surface and surface_speeds[surface] then
|
|
||||||
result.forward_speed = math.min(surface_speeds[surface], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(surface_speeds[surface], result.backward_speed)
|
|
||||||
end
|
|
||||||
if tracktype and tracktype_speeds[tracktype] then
|
|
||||||
result.forward_speed = math.min(tracktype_speeds[tracktype], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(tracktype_speeds[tracktype], result.backward_speed)
|
|
||||||
end
|
|
||||||
if smoothness and smoothness_speeds[smoothness] then
|
|
||||||
result.forward_speed = math.min(smoothness_speeds[smoothness], result.forward_speed)
|
|
||||||
result.backward_speed = math.min(smoothness_speeds[smoothness], result.backward_speed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handles name, including ref and pronunciation
|
|
||||||
function handle_names(way,result)
|
|
||||||
-- parse the remaining tags
|
|
||||||
local name = way:get_value_by_key("name")
|
|
||||||
local pronunciation = way:get_value_by_key("name:pronunciation")
|
|
||||||
local ref = way:get_value_by_key("ref")
|
|
||||||
|
|
||||||
-- Set the name that will be used for instructions
|
|
||||||
if name then
|
|
||||||
result.name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
if ref then
|
|
||||||
result.ref = canonicalizeStringList(ref, ";")
|
|
||||||
end
|
|
||||||
|
|
||||||
if pronunciation then
|
|
||||||
result.pronunciation = pronunciation
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle turn lanes
|
|
||||||
function handle_turn_lanes(way,result,data)
|
|
||||||
local forward, backward = get_turn_lanes(way,data)
|
|
||||||
|
|
||||||
if forward then
|
|
||||||
result.turn_lanes_forward = forward
|
|
||||||
end
|
|
||||||
|
|
||||||
if backward then
|
|
||||||
result.turn_lanes_backward = backward
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- junctions
|
|
||||||
function handle_roundabouts(way,result)
|
|
||||||
local junction = way:get_value_by_key("junction");
|
|
||||||
|
|
||||||
if junction == "roundabout" then
|
|
||||||
result.roundabout = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- See Issue 3361: roundabout-shaped not following roundabout rules.
|
|
||||||
-- This will get us "At Strausberger Platz do Maneuver X" instead of multiple quick turns.
|
|
||||||
-- In a new API version we can think of having a separate type passing it through to the user.
|
|
||||||
if junction == "circular" then
|
|
||||||
result.circular = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle oneways tags
|
|
||||||
function handle_oneway(way,result,data)
|
|
||||||
local oneway
|
|
||||||
if oneway_handling == true then
|
|
||||||
oneway = Directional.get_value_by_prefixed_sequence(way,restrictions,'oneway') or way:get_value_by_key('oneway')
|
|
||||||
elseif oneway_handling == 'specific' then
|
|
||||||
oneway = Directional.get_value_by_prefixed_sequence(way,restrictions,'oneway')
|
|
||||||
end
|
|
||||||
|
|
||||||
data.oneway = oneway
|
|
||||||
|
|
||||||
if oneway then
|
|
||||||
if oneway == "-1" then
|
|
||||||
data.is_reverse_oneway = true
|
|
||||||
result.forward_mode = mode.inaccessible
|
|
||||||
elseif oneway == "yes" or
|
|
||||||
oneway == "1" or
|
|
||||||
oneway == "true" then
|
|
||||||
data.is_forward_oneway = true
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
else
|
|
||||||
local junction = way:get_value_by_key("junction")
|
|
||||||
if data.highway == "motorway" or
|
|
||||||
junction == "roundabout" or
|
|
||||||
junction == "circular" then
|
|
||||||
if oneway ~= "no" then
|
|
||||||
-- implied oneway
|
|
||||||
data.is_forward_oneway = true
|
|
||||||
result.backward_mode = mode.inaccessible
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle destination tags
|
|
||||||
function handle_destinations(way,result,data)
|
|
||||||
if data.is_forward_oneway or data.is_reverse_oneway then
|
|
||||||
local destination = get_destination(way, data.is_forward_oneway)
|
|
||||||
result.destinations = canonicalizeStringList(destination, ",")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- determine if this way can be used as a start/end point for routing
|
|
||||||
function handle_startpoint(way,result)
|
|
||||||
-- only allow this road as start point if it not a ferry
|
|
||||||
result.is_startpoint = result.forward_mode == mode.walking or
|
|
||||||
result.backward_mode == mode.walking
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set the road classification based on guidance globals configuration
|
|
||||||
function handle_classification(way,result,data)
|
|
||||||
set_classification(data.highway,result,way)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- main entry point for processsing a way
|
-- main entry point for processsing a way
|
||||||
function way_function(way, result)
|
function way_function(way, result)
|
||||||
-- intermediate values used during processing
|
-- the intial filtering of ways based on presence of tags
|
||||||
local data = {}
|
-- 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.
|
||||||
|
|
||||||
-- to optimize processing, we should try to abort as soon as
|
-- in general we should try to abort as soon as
|
||||||
-- possible if the way is not routable, to avoid doing
|
-- possible if the way is not routable, to avoid doing
|
||||||
-- unnecessary work. this implies we should check things that
|
-- unnecessary work. this implies we should check things that
|
||||||
-- commonly forbids access early, and handle complicated edge
|
-- commonly forbids access early, and handle edge cases later.
|
||||||
-- cases later.
|
|
||||||
|
|
||||||
-- perform an quick initial check and abort if way is obviously
|
-- data table for storing intermediate values during processing
|
||||||
-- not routable, e.g. because it does not have any of the key
|
local data = {
|
||||||
-- tags indicating routability
|
-- prefetch tags
|
||||||
if initial_routability_check(way,result,data) == false then return end
|
highway = way:get_value_by_key('highway'),
|
||||||
|
bridge = way:get_value_by_key('bridge'),
|
||||||
|
route = way:get_value_by_key('route'),
|
||||||
|
leisure = way:get_value_by_key('leisure'),
|
||||||
|
man_made = way:get_value_by_key('man_made'),
|
||||||
|
railway = way:get_value_by_key('railway'),
|
||||||
|
platform = way:get_value_by_key('platform'),
|
||||||
|
amenity = way:get_value_by_key('amenity'),
|
||||||
|
public_transport = way:get_value_by_key('public_transport')
|
||||||
|
}
|
||||||
|
|
||||||
|
-- perform an quick initial check and abort if the way is
|
||||||
|
-- obviously not routable. here we require at least one
|
||||||
|
-- of the prefetched tags to be present, ie. the data table
|
||||||
|
-- cannot be empty
|
||||||
|
if next(data) == nil then -- is the data table empty?
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
if set_default_mode(way,result) == false then return end
|
'handle_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
|
||||||
if is_way_blocked(way,result) == false then return end
|
'handle_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
|
||||||
if handle_access(way,result,data) == false then return end
|
'handle_access',
|
||||||
|
|
||||||
-- check whether forward/backward directons are routable
|
-- check whether forward/backward directons are routable
|
||||||
if handle_oneway(way,result,data) == false then return end
|
'handle_oneway',
|
||||||
|
|
||||||
-- check whether forward/backward directons are routable
|
-- check whether forward/backward directons are routable
|
||||||
if handle_destinations(way,result,data) == false then return end
|
'handle_destinations',
|
||||||
|
|
||||||
-- check whether we're using a special transport mode
|
-- check whether we're using a special transport mode
|
||||||
if handle_ferries(way,result) == false then return end
|
'handle_ferries',
|
||||||
if handle_movables(way,result) == false then return end
|
'handle_movables',
|
||||||
|
|
||||||
-- compute speed taking into account way type, maxspeed tags, etc.
|
-- compute speed taking into account way type, maxspeed tags, etc.
|
||||||
if handle_speed(way,result,data) == false then return end
|
'handle_speed',
|
||||||
|
'handle_surface',
|
||||||
|
|
||||||
-- handle turn lanes and road classification, used for guidance
|
-- handle turn lanes and road classification, used for guidance
|
||||||
if handle_classification(way,result,data) == false then return end
|
'handle_classification',
|
||||||
|
|
||||||
-- handle various other flags
|
-- handle various other flags
|
||||||
if handle_roundabouts(way,result) == false then return end
|
'handle_roundabouts',
|
||||||
if handle_startpoint(way,result) == false then return end
|
'handle_startpoint',
|
||||||
|
|
||||||
-- set name, ref and pronunciation
|
-- set name, ref and pronunciation
|
||||||
if handle_names(way,result) == false then return end
|
'handle_names'
|
||||||
|
}
|
||||||
|
|
||||||
|
Handlers.run(handlers,way,result,data,profile)
|
||||||
end
|
end
|
||||||
|
|
||||||
function turn_function (turn)
|
function turn_function (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 + u_turn_penalty
|
turn.duration = turn.duration + profile.u_turn_penalty
|
||||||
end
|
end
|
||||||
|
|
||||||
if turn.has_traffic_light then
|
if turn.has_traffic_light then
|
||||||
turn.duration = traffic_light_penalty
|
turn.duration = profile.traffic_light_penalty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
local Directional = require('lib/directional')
|
local Tags = require('lib/tags')
|
||||||
local Set = require('lib/set')
|
local Set = require('lib/set')
|
||||||
|
|
||||||
local Guidance = {}
|
local Guidance = {}
|
||||||
@ -108,7 +108,7 @@ end
|
|||||||
|
|
||||||
-- returns forward,backward psv lane count
|
-- returns forward,backward psv lane count
|
||||||
local function get_psv_counts(way,data)
|
local function get_psv_counts(way,data)
|
||||||
local psv_forward, psv_backward = Directional.get_values_by_key(way,data,'lanes:psv')
|
local psv_forward, psv_backward = Tags.get_forward_backward_by_key(way,data,'lanes:psv')
|
||||||
if psv_forward then
|
if psv_forward then
|
||||||
psv_forward = tonumber(psv_forward)
|
psv_forward = tonumber(psv_forward)
|
||||||
end
|
end
|
||||||
@ -135,8 +135,8 @@ end
|
|||||||
-- this is broken for left-sided driving. It needs to switch left and right in case of left-sided driving
|
-- this is broken for left-sided driving. It needs to switch left and right in case of left-sided driving
|
||||||
function Guidance.get_turn_lanes(way,data)
|
function Guidance.get_turn_lanes(way,data)
|
||||||
local psv_fw, psv_bw = get_psv_counts(way,data)
|
local psv_fw, psv_bw = get_psv_counts(way,data)
|
||||||
local turn_lanes_fw, turn_lanes_bw = Directional.get_values_by_key(way,data,'turn:lanes')
|
local turn_lanes_fw, turn_lanes_bw = Tags.get_forward_backward_by_key(way,data,'turn:lanes')
|
||||||
local vehicle_lanes_fw, vehicle_lanes_bw = Directional.get_values_by_key(way,data,'vehicle:lanes')
|
local vehicle_lanes_fw, vehicle_lanes_bw = Tags.get_forward_backward_by_key(way,data,'vehicle:lanes')
|
||||||
|
|
||||||
--note: backward lanes swap psv_bw and psv_fw
|
--note: backward lanes swap psv_bw and psv_fw
|
||||||
return process_lanes(turn_lanes_fw,vehicle_lanes_fw,psv_bw,psv_fw) or turn_lanes,
|
return process_lanes(turn_lanes_fw,vehicle_lanes_fw,psv_bw,psv_fw) or turn_lanes,
|
||||||
|
461
profiles/lib/handlers.lua
Normal file
461
profiles/lib/handlers.lua
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
-- Profile handlers dealing with various aspects of tag parsing
|
||||||
|
--
|
||||||
|
-- You can run a selection you find useful in your profile,
|
||||||
|
-- or do you own processing if/when required.
|
||||||
|
|
||||||
|
|
||||||
|
local get_turn_lanes = require("lib/guidance").get_turn_lanes
|
||||||
|
local set_classification = require("lib/guidance").set_classification
|
||||||
|
local get_destination = require("lib/destination").get_destination
|
||||||
|
local Tags = require('lib/tags')
|
||||||
|
|
||||||
|
Handlers = {}
|
||||||
|
|
||||||
|
-- check that way has at least one tag that could imply routability-
|
||||||
|
-- we store the checked tags in data, to avoid fetching again later
|
||||||
|
function Handlers.handle_tag_prefetch(way,result,data,profile)
|
||||||
|
for key,v in pairs(profile.prefetch) do
|
||||||
|
data[key] = way:get_value_by_key( key )
|
||||||
|
end
|
||||||
|
return next(data) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set default mode
|
||||||
|
function Handlers.handle_default_mode(way,result,data,profile)
|
||||||
|
result.forward_mode = profile.default_mode
|
||||||
|
result.backward_mode = profile.default_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handles name, including ref and pronunciation
|
||||||
|
function Handlers.handle_names(way,result,data,profile)
|
||||||
|
-- parse the remaining tags
|
||||||
|
local name = way:get_value_by_key("name")
|
||||||
|
local pronunciation = way:get_value_by_key("name:pronunciation")
|
||||||
|
local ref = way:get_value_by_key("ref")
|
||||||
|
|
||||||
|
-- Set the name that will be used for instructions
|
||||||
|
if name then
|
||||||
|
result.name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
if ref then
|
||||||
|
result.ref = canonicalizeStringList(ref, ";")
|
||||||
|
end
|
||||||
|
|
||||||
|
if pronunciation then
|
||||||
|
result.pronunciation = pronunciation
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- junctions
|
||||||
|
function Handlers.handle_roundabouts(way,result,data,profile)
|
||||||
|
local junction = way:get_value_by_key("junction");
|
||||||
|
|
||||||
|
if junction == "roundabout" then
|
||||||
|
result.roundabout = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- See Issue 3361: roundabout-shaped not following roundabout rules.
|
||||||
|
-- This will get us "At Strausberger Platz do Maneuver X" instead of multiple quick turns.
|
||||||
|
-- In a new API version we can think of having a separate type passing it through to the user.
|
||||||
|
if junction == "circular" then
|
||||||
|
result.circular = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- determine if this way can be used as a start/end point for routing
|
||||||
|
function Handlers.handle_startpoint(way,result,data,profile)
|
||||||
|
-- only allow this road as start point if it not a ferry
|
||||||
|
result.is_startpoint = result.forward_mode == profile.default_mode or
|
||||||
|
result.backward_mode == profile.default_mode
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle turn lanes
|
||||||
|
function Handlers.handle_turn_lanes(way,result,data,profile)
|
||||||
|
local forward, backward = get_turn_lanes(way,data)
|
||||||
|
|
||||||
|
if forward then
|
||||||
|
result.turn_lanes_forward = forward
|
||||||
|
end
|
||||||
|
|
||||||
|
if backward then
|
||||||
|
result.turn_lanes_backward = backward
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set the road classification based on guidance globals configuration
|
||||||
|
function Handlers.handle_classification(way,result,data,profile)
|
||||||
|
set_classification(data.highway,result,way)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle destination tags
|
||||||
|
function Handlers.handle_destinations(way,result,data,profile)
|
||||||
|
if data.is_forward_oneway or data.is_reverse_oneway then
|
||||||
|
local destination = get_destination(way, data.is_forward_oneway)
|
||||||
|
result.destinations = canonicalizeStringList(destination, ",")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handling ferries and piers
|
||||||
|
function Handlers.handle_ferries(way,result,data,profile)
|
||||||
|
local route = data.route
|
||||||
|
if route then
|
||||||
|
local route_speed = profile.route_speeds[route]
|
||||||
|
if route_speed and route_speed > 0 then
|
||||||
|
local duration = way:get_value_by_key("duration")
|
||||||
|
if duration and durationIsValid(duration) then
|
||||||
|
result.duration = math.max( parseDuration(duration), 1 )
|
||||||
|
end
|
||||||
|
result.forward_mode = mode.ferry
|
||||||
|
result.backward_mode = mode.ferry
|
||||||
|
result.forward_speed = route_speed
|
||||||
|
result.backward_speed = route_speed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handling movable bridges
|
||||||
|
function Handlers.handle_movables(way,result,data,profile)
|
||||||
|
local bridge = data.bridge
|
||||||
|
if bridge then
|
||||||
|
local bridge_speed = profile.bridge_speeds[bridge]
|
||||||
|
if bridge_speed and bridge_speed > 0 then
|
||||||
|
local capacity_car = way:get_value_by_key("capacity:car")
|
||||||
|
if capacity_car ~= 0 then
|
||||||
|
local duration = way:get_value_by_key("duration")
|
||||||
|
if duration and durationIsValid(duration) then
|
||||||
|
result.duration = math.max( parseDuration(duration), 1 )
|
||||||
|
end
|
||||||
|
result.forward_speed = bridge_speed
|
||||||
|
result.backward_speed = bridge_speed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- service roads
|
||||||
|
function Handlers.handle_service(way,result,data,profile)
|
||||||
|
local service = way:get_value_by_key("service")
|
||||||
|
if service then
|
||||||
|
-- Set don't allow access to certain service roads
|
||||||
|
if profile.service_tag_forbidden[service] then
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- all lanes restricted to hov vehicles?
|
||||||
|
function Handlers.has_all_designated_hov_lanes(lanes)
|
||||||
|
if not lanes then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
-- This gmatch call effectively splits the string on | chars.
|
||||||
|
-- we append an extra | to the end so that we can match the final part
|
||||||
|
for lane in (lanes .. '|'):gmatch("([^|]*)|") do
|
||||||
|
if lane and lane ~= "designated" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle high occupancy vehicle tags
|
||||||
|
function Handlers.handle_hov(way,result,data,profile)
|
||||||
|
-- respect user-preference for HOV
|
||||||
|
if not profile.avoid.hov_lanes then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if way is hov only
|
||||||
|
local hov = way:get_value_by_key("hov")
|
||||||
|
if "designated" == hov then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if all lanes are hov only
|
||||||
|
local hov_lanes_forward, hov_lanes_backward = Tags.get_forward_backward_by_key(way,data,'hov:lanes')
|
||||||
|
local inaccessible_forward = Handlers.has_all_designated_hov_lanes(hov_lanes_forward)
|
||||||
|
local inaccessible_backward = Handlers.has_all_designated_hov_lanes(hov_lanes_backward)
|
||||||
|
|
||||||
|
if inaccessible_forward then
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
if inaccessible_backward then
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check accessibility by traversing our acces tag hierarchy
|
||||||
|
function Handlers.handle_access(way,result,data,profile)
|
||||||
|
data.forward_access, data.backward_access =
|
||||||
|
Tags.get_forward_backward_by_set(way,data,profile.access_tags_hierarchy)
|
||||||
|
|
||||||
|
if profile.access_tag_blacklist[data.forward_access] then
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
|
||||||
|
if profile.access_tag_blacklist[data.backward_access] then
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
|
||||||
|
if result.forward_mode == mode.inaccessible and result.backward_mode == mode.inaccessible then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle speed (excluding maxspeed)
|
||||||
|
function Handlers.handle_speed(way,result,data,profile)
|
||||||
|
if result.forward_speed ~= -1 then
|
||||||
|
return -- abort if already set, eg. by a route
|
||||||
|
end
|
||||||
|
|
||||||
|
local key,value,speed = Tags.get_constant_by_key_value(way,profile.speeds)
|
||||||
|
|
||||||
|
if speed then
|
||||||
|
-- set speed by way type
|
||||||
|
result.forward_speed = highway_speed
|
||||||
|
result.backward_speed = highway_speed
|
||||||
|
result.forward_speed = speed
|
||||||
|
result.backward_speed = speed
|
||||||
|
else
|
||||||
|
-- Set the avg speed on ways that are marked accessible
|
||||||
|
if profile.access_tag_whitelist[data.forward_access] then
|
||||||
|
result.forward_speed = profile.default_speed
|
||||||
|
end
|
||||||
|
|
||||||
|
if profile.access_tag_whitelist[data.backward_access] then
|
||||||
|
result.backward_speed = profile.default_speed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if result.forward_speed == -1 and result.backward_speed == -1 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reduce speed on special side roads
|
||||||
|
function Handlers.handle_side_roads(way,result,data,profile)
|
||||||
|
local sideway = way:get_value_by_key("side_road")
|
||||||
|
if "yes" == sideway or
|
||||||
|
"rotary" == sideway then
|
||||||
|
result.forward_speed = result.forward_speed * side_road_speed_multiplier
|
||||||
|
result.backward_speed = result.backward_speed * side_road_speed_multiplier
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reduce speed on bad surfaces
|
||||||
|
function Handlers.handle_surface(way,result,data,profile)
|
||||||
|
local surface = way:get_value_by_key("surface")
|
||||||
|
local tracktype = way:get_value_by_key("tracktype")
|
||||||
|
local smoothness = way:get_value_by_key("smoothness")
|
||||||
|
|
||||||
|
if surface and profile.surface_speeds[surface] then
|
||||||
|
result.forward_speed = math.min(profile.surface_speeds[surface], result.forward_speed)
|
||||||
|
result.backward_speed = math.min(profile.surface_speeds[surface], result.backward_speed)
|
||||||
|
end
|
||||||
|
if tracktype and profile.tracktype_speeds[tracktype] then
|
||||||
|
result.forward_speed = math.min(profile.tracktype_speeds[tracktype], result.forward_speed)
|
||||||
|
result.backward_speed = math.min(profile.tracktype_speeds[tracktype], result.backward_speed)
|
||||||
|
end
|
||||||
|
if smoothness and profile.smoothness_speeds[smoothness] then
|
||||||
|
result.forward_speed = math.min(profile.smoothness_speeds[smoothness], result.forward_speed)
|
||||||
|
result.backward_speed = math.min(profile.smoothness_speeds[smoothness], result.backward_speed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- scale speeds to get better average driving times
|
||||||
|
function Handlers.handle_speed_scaling(way,result,data,profile)
|
||||||
|
local width = math.huge
|
||||||
|
local lanes = math.huge
|
||||||
|
if result.forward_speed > 0 or result.backward_speed > 0 then
|
||||||
|
local width_string = way:get_value_by_key("width")
|
||||||
|
if width_string and tonumber(width_string:match("%d*")) then
|
||||||
|
width = tonumber(width_string:match("%d*"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local lanes_string = way:get_value_by_key("lanes")
|
||||||
|
if lanes_string and tonumber(lanes_string:match("%d*")) then
|
||||||
|
lanes = tonumber(lanes_string:match("%d*"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_bidirectional = result.forward_mode ~= mode.inaccessible and
|
||||||
|
result.backward_mode ~= mode.inaccessible
|
||||||
|
|
||||||
|
local service = way:get_value_by_key("service")
|
||||||
|
if result.forward_speed > 0 then
|
||||||
|
local scaled_speed = result.forward_speed * profile.speed_reduction
|
||||||
|
local penalized_speed = math.huge
|
||||||
|
if service and profile.service_speeds[service] then
|
||||||
|
penalized_speed = profile.service_speeds[service]
|
||||||
|
elseif width <= 3 or (lanes <= 1 and is_bidirectional) then
|
||||||
|
penalized_speed = result.forward_speed / 2
|
||||||
|
end
|
||||||
|
result.forward_speed = math.min(penalized_speed, scaled_speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
if result.backward_speed > 0 then
|
||||||
|
local scaled_speed = result.backward_speed * profile.speed_reduction
|
||||||
|
local penalized_speed = math.huge
|
||||||
|
if service and profile.service_speeds[service]then
|
||||||
|
penalized_speed = profile.service_speeds[service]
|
||||||
|
elseif width <= 3 or (lanes <= 1 and is_bidirectional) then
|
||||||
|
penalized_speed = result.backward_speed / 2
|
||||||
|
end
|
||||||
|
result.backward_speed = math.min(penalized_speed, scaled_speed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- maxspeed and advisory maxspeed
|
||||||
|
function Handlers.handle_maxspeed(way,result,data,profile)
|
||||||
|
local keys = Sequence { 'maxspeed:advisory', 'maxspeed' }
|
||||||
|
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
|
||||||
|
forward = Handlers.parse_maxspeed(forward,profile)
|
||||||
|
backward = Handlers.parse_maxspeed(backward,profile)
|
||||||
|
|
||||||
|
if forward and forward > 0 then
|
||||||
|
result.forward_speed = forward
|
||||||
|
end
|
||||||
|
|
||||||
|
if backward and backward > 0 then
|
||||||
|
result.backward_speed = backward
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle high frequency reversible oneways (think traffic signal controlled, changing direction every 15 minutes).
|
||||||
|
-- Scaling speed to take average waiting time into account plus some more for start / stop.
|
||||||
|
function Handlers.handle_alternating_speed(way,result,data,profile)
|
||||||
|
if "alternating" == data.oneway then
|
||||||
|
local scaling_factor = 0.4
|
||||||
|
if result.forward_speed ~= math.huge then
|
||||||
|
result.forward_speed = result.forward_speed * scaling_factor
|
||||||
|
end
|
||||||
|
if result.backward_speed ~= math.huge then
|
||||||
|
result.backward_speed = result.backward_speed * scaling_factor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Handlers.parse_maxspeed(source,profile)
|
||||||
|
if not source then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
local n = tonumber(source:match("%d*"))
|
||||||
|
if n then
|
||||||
|
if string.match(source, "mph") or string.match(source, "mp/h") then
|
||||||
|
n = (n*1609)/1000
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- parse maxspeed like FR:urban
|
||||||
|
source = string.lower(source)
|
||||||
|
n = profile.maxspeed_table[source]
|
||||||
|
if not n then
|
||||||
|
local highway_type = string.match(source, "%a%a:(%a+)")
|
||||||
|
n = profile.maxspeed_table_default[highway_type]
|
||||||
|
if not n then
|
||||||
|
n = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle oneways tags
|
||||||
|
function Handlers.handle_oneway(way,result,data,profile)
|
||||||
|
if not profile.oneway_handling then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local oneway
|
||||||
|
if profile.oneway_handling == true then
|
||||||
|
oneway = Tags.get_value_by_prefixed_sequence(way,profile.restrictions,'oneway') or way:get_value_by_key("oneway")
|
||||||
|
elseif profile.oneway_handling == 'specific' then
|
||||||
|
oneway = Tags.get_value_by_prefixed_sequence(way,profile.restrictions,'oneway')
|
||||||
|
end
|
||||||
|
|
||||||
|
data.oneway = oneway
|
||||||
|
|
||||||
|
if oneway == "-1" then
|
||||||
|
data.is_reverse_oneway = true
|
||||||
|
result.forward_mode = mode.inaccessible
|
||||||
|
elseif oneway == "yes" or
|
||||||
|
oneway == "1" or
|
||||||
|
oneway == "true" then
|
||||||
|
data.is_forward_oneway = true
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
elseif profile.oneway_handling == true then
|
||||||
|
local junction = way:get_value_by_key("junction")
|
||||||
|
if data.highway == "motorway" or
|
||||||
|
junction == "roundabout" or
|
||||||
|
junction == "circular" then
|
||||||
|
if oneway ~= "no" then
|
||||||
|
-- implied oneway
|
||||||
|
data.is_forward_oneway = true
|
||||||
|
result.backward_mode = mode.inaccessible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- handle various that can block access
|
||||||
|
function Handlers.handle_blocked_ways(way,result,data,profile)
|
||||||
|
|
||||||
|
-- areas
|
||||||
|
if profile.avoid.area and way:get_value_by_key("area") == "yes" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- toll roads
|
||||||
|
if profile.avoid.toll and way:get_value_by_key("toll") == "yes" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reversible oneways change direction with low frequency (think twice a day):
|
||||||
|
-- do not route over these at all at the moment because of time dependence.
|
||||||
|
-- Note: alternating (high frequency) oneways are handled below with penalty.
|
||||||
|
if profile.avoid.reversible and way:get_value_by_key("oneway") == "reversible" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- impassables
|
||||||
|
if profile.avoid.impassable then
|
||||||
|
if way:get_value_by_key("impassable") == "yes" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if way:get_value_by_key("status") == "impassable" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call a sequence of handlers, aborting in case a handler returns false. Example:
|
||||||
|
--
|
||||||
|
-- handlers = Sequence {
|
||||||
|
-- 'handle_tag_prefetch',
|
||||||
|
-- 'handle_default_mode',
|
||||||
|
-- 'handle_blocked_ways',
|
||||||
|
-- 'handle_access',
|
||||||
|
-- 'handle_speed',
|
||||||
|
-- 'handle_names'
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- Handlers.run(handlers,way,result,data,profile)
|
||||||
|
--
|
||||||
|
-- Each method in the list will be called on the Handlers object.
|
||||||
|
-- All handlers must accept the parameteres (way,result,data,profile) and return false
|
||||||
|
-- if the handler chain should be aborted.
|
||||||
|
-- To ensure the correct order of method calls, use a Sequence of handler names.
|
||||||
|
|
||||||
|
function Handlers.run(handlers,way,result,data,profile)
|
||||||
|
for i,handler in ipairs(handlers) do
|
||||||
|
if Handlers[handler](way,result,data,profile) == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Handlers
|
@ -1,4 +1,6 @@
|
|||||||
local Directional = {}
|
-- Helpers for searching and parsing tags
|
||||||
|
|
||||||
|
local Tags = {}
|
||||||
|
|
||||||
-- return [forward,backward] values for a specific tag.
|
-- return [forward,backward] values for a specific tag.
|
||||||
-- e.g. for maxspeed search forward:
|
-- e.g. for maxspeed search forward:
|
||||||
@ -8,7 +10,7 @@ local Directional = {}
|
|||||||
-- maxspeed:backward
|
-- maxspeed:backward
|
||||||
-- maxspeed
|
-- maxspeed
|
||||||
|
|
||||||
function Directional.get_values_by_key(way,data,key)
|
function Tags.get_forward_backward_by_key(way,data,key)
|
||||||
local forward = way:get_value_by_key(key .. ':forward')
|
local forward = way:get_value_by_key(key .. ':forward')
|
||||||
local backward = way:get_value_by_key(key .. ':backward')
|
local backward = way:get_value_by_key(key .. ':backward')
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ end
|
|||||||
-- advisory:backward
|
-- advisory:backward
|
||||||
-- advisory
|
-- advisory
|
||||||
|
|
||||||
function Directional.get_values_by_set(way,data,keys)
|
function Tags.get_forward_backward_by_set(way,data,keys)
|
||||||
local forward, backward
|
local forward, backward
|
||||||
for i,key in ipairs(keys) do
|
for i,key in ipairs(keys) do
|
||||||
if not forward then
|
if not forward then
|
||||||
@ -62,7 +64,7 @@ end
|
|||||||
-- oneway:motor_vehicle
|
-- oneway:motor_vehicle
|
||||||
-- oneway:vehicle
|
-- oneway:vehicle
|
||||||
|
|
||||||
function Directional.get_value_by_prefixed_sequence(way,seq,prefix)
|
function Tags.get_value_by_prefixed_sequence(way,seq,prefix)
|
||||||
local v
|
local v
|
||||||
for i,key in ipairs(seq) do
|
for i,key in ipairs(seq) do
|
||||||
v = way:get_value_by_key(prefix .. ':' .. key)
|
v = way:get_value_by_key(prefix .. ':' .. key)
|
||||||
@ -78,7 +80,7 @@ end
|
|||||||
-- motor_vehicle:oneway
|
-- motor_vehicle:oneway
|
||||||
-- vehicle:oneway
|
-- vehicle:oneway
|
||||||
|
|
||||||
function Directional.get_value_by_postfixed_sequence(way,seq,postfix)
|
function Tags.get_value_by_postfixed_sequence(way,seq,postfix)
|
||||||
local v
|
local v
|
||||||
for i,key in ipairs(seq) do
|
for i,key in ipairs(seq) do
|
||||||
v = way:get_value_by_key(key .. ':' .. postfix)
|
v = way:get_value_by_key(key .. ':' .. postfix)
|
||||||
@ -88,5 +90,35 @@ function Directional.get_value_by_postfixed_sequence(way,seq,postfix)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check if key-value pairs are set in a way and return a
|
||||||
|
-- corresponding constant if it is. e.g. for this input:
|
||||||
|
--
|
||||||
|
-- local speeds = {
|
||||||
|
-- highway = {
|
||||||
|
-- residential = 20,
|
||||||
|
-- primary = 40
|
||||||
|
-- },
|
||||||
|
-- amenity = {
|
||||||
|
-- parking = 10
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
-- we would check whether the following key-value combinations
|
||||||
|
-- are set, and return the corresponding constant:
|
||||||
|
--
|
||||||
|
-- highway = residential => 20
|
||||||
|
-- highway = primary => 40
|
||||||
|
-- amenity = parking => 10
|
||||||
|
|
||||||
return Directional
|
function Tags.get_constant_by_key_value(way,lookup)
|
||||||
|
for key,set in pairs(lookup) do
|
||||||
|
local way_value = way:get_value_by_key(key)
|
||||||
|
for value,t in pairs(set) do
|
||||||
|
if way_value == value then
|
||||||
|
return key,value,t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Tags
|
Loading…
Reference in New Issue
Block a user