factor out common lua code to helper files

This commit is contained in:
Emil Tin 2017-01-23 21:23:33 +01:00 committed by Michael Krasnyk
parent b3ef27d104
commit 97c0a74c04
7 changed files with 956 additions and 1115 deletions

View File

@ -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.

View File

@ -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
""" """

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +1,131 @@
-- 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
barrier_whitelist = Set {
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'block'
}
access_tag_whitelist = Set {
'yes',
'foot',
'permissive',
'designated'
}
access_tag_blacklist = Set {
'no',
'private',
'agricultural',
'forestry',
'delivery'
}
access_tags_hierarchy = Sequence {
'foot',
'access'
}
restrictions = Sequence { 'foot' }
-- A list of suffixes to suppress in name change instructions
-- Note: a Set does not work here because it's read from C++
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
}
walking_speed = 5
speed_profile = {
primary = walking_speed,
primary_link = walking_speed,
secondary = walking_speed,
secondary_link = walking_speed,
tertiary = walking_speed,
tertiary_link = walking_speed,
unclassified = walking_speed,
residential = walking_speed,
road = walking_speed,
living_street = walking_speed,
service = walking_speed,
track = walking_speed,
path = walking_speed,
steps = walking_speed,
pedestrian = walking_speed,
footway = walking_speed,
pier = walking_speed,
default = walking_speed
}
route_speeds = {
ferry = 5
}
platform_speeds = {
platform = walking_speed
}
amenity_speeds = {
parking = walking_speed,
parking_entrance = walking_speed
}
man_made_speeds = {
pier = walking_speed
}
surface_speeds = {
fine_gravel = walking_speed*0.75,
gravel = walking_speed*0.75,
pebblestone = walking_speed*0.75,
mud = walking_speed*0.5,
sand = walking_speed*0.5
}
tracktype_speeds = {}
smoothness_speeds = {}
leisure_speeds = {
track = walking_speed
}
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s
properties.use_turn_restrictions = false properties.use_turn_restrictions = false
properties.continue_straight_at_waypoint = false properties.continue_straight_at_waypoint = false
properties.weight_name = 'duration' properties.weight_name = 'duration'
local traffic_light_penalty = 2 local walking_speed = 5
local u_turn_penalty = 2
local profile = {
default_mode = mode.walking,
default_speed = walking_speed,
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
traffic_light_penalty = 2,
u_turn_penalty = 2,
barrier_whitelist = Set {
'cycle_barrier',
'bollard',
'entrance',
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'no',
'block'
},
access_tag_whitelist = Set {
'yes',
'foot',
'permissive',
'designated'
},
access_tag_blacklist = Set {
'no',
'private',
'agricultural',
'forestry',
'delivery'
},
access_tags_hierarchy = Sequence {
'foot',
'access'
},
-- setting oneway_handling to 'specific' means that the plain 'oneway' tag is ignored, restrictions = Sequence {
-- but oneway:foot (or or more specific modes) is respected. 'foot'
},
local oneway_handling = 'specific' -- list of suffixes to suppress in name change instructions
suffix_list = Set {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East'
},
avoid = Set {
'impassable'
},
speeds = Sequence {
highway = {
primary = walking_speed,
primary_link = walking_speed,
secondary = walking_speed,
secondary_link = walking_speed,
tertiary = walking_speed,
tertiary_link = walking_speed,
unclassified = walking_speed,
residential = walking_speed,
road = walking_speed,
living_street = walking_speed,
service = walking_speed,
track = walking_speed,
path = walking_speed,
steps = walking_speed,
pedestrian = walking_speed,
footway = walking_speed,
pier = walking_speed,
},
local ignore_areas = false railway = {
platform = walking_speed
},
amenity = {
parking = walking_speed,
parking_entrance= walking_speed
},
function get_name_suffix_list(vector) man_made = {
for index,suffix in ipairs(suffix_list) do pier = walking_speed
vector:Add(suffix) },
end
end leisure = {
track = walking_speed
}
},
route_speeds = {
ferry = 5
},
surface_speeds = {
fine_gravel = walking_speed*0.75,
gravel = walking_speed*0.75,
pebblestone = walking_speed*0.75,
mud = walking_speed*0.5,
sand = walking_speed*0.5
}
}
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')
}
-- set the default mode for this profile. if can be changed later -- perform an quick initial check and abort if the way is
-- in case it turns we're e.g. on a ferry -- obviously not routable. here we require at least one
if set_default_mode(way,result) == false then return end -- 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
-- check various tags that could indicate that the way is not local handlers = Sequence {
-- routable. this includes things like status=impassable, -- set the default mode for this profile. if can be changed later
-- toll=yes and oneway=reversible -- in case it turns we're e.g. on a ferry
if is_way_blocked(way,result) == false then return end 'handle_default_mode',
-- determine access status by checking our hierarchy of -- check various tags that could indicate that the way is not
-- access tags, e.g: motorcar, motor_vehicle, vehicle -- routable. this includes things like status=impassable,
if handle_access(way,result,data) == false then return end -- toll=yes and oneway=reversible
'handle_blocked_ways',
-- check whether forward/backward directons are routable -- determine access status by checking our hierarchy of
if handle_oneway(way,result,data) == false then return end -- access tags, e.g: motorcar, motor_vehicle, vehicle
'handle_access',
-- 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_oneway',
-- check whether we're using a special transport mode -- check whether forward/backward directons are routable
if handle_ferries(way,result) == false then return end 'handle_destinations',
if handle_movables(way,result) == false then return end
-- compute speed taking into account way type, maxspeed tags, etc. -- check whether we're using a special transport mode
if handle_speed(way,result,data) == false then return end 'handle_ferries',
'handle_movables',
-- handle turn lanes and road classification, used for guidance -- compute speed taking into account way type, maxspeed tags, etc.
if handle_classification(way,result,data) == false then return end 'handle_speed',
'handle_surface',
-- handle various other flags -- handle turn lanes and road classification, used for guidance
if handle_roundabouts(way,result) == false then return end 'handle_classification',
if handle_startpoint(way,result) == false then return end
-- set name, ref and pronunciation -- handle various other flags
if handle_names(way,result) == false then return end 'handle_roundabouts',
'handle_startpoint',
-- set name, ref and pronunciation
'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

View File

@ -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
View 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

View File

@ -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