Skip to content

Instantly share code, notes, and snippets.

@ZsoltMedgyesi-Itineris
Created November 21, 2018 08:30
Show Gist options
  • Save ZsoltMedgyesi-Itineris/a50efd2a65456a6ec5ae72fd0a35d76d to your computer and use it in GitHub Desktop.
Save ZsoltMedgyesi-Itineris/a50efd2a65456a6ec5ae72fd0a35d76d to your computer and use it in GitHub Desktop.
OSRM profile which gives wayIDs back as road names.
-- Car profile
api_version = 4
Set = require('lib/set')
Sequence = require('lib/sequence')
Handlers = require("lib/way_handlers")
Relations = require("lib/relations")
find_access_tag = require("lib/access").find_access_tag
limit = require("lib/maxspeed").limit
Utils = require("lib/utils")
Measure = require("lib/measure")
function setup()
return {
properties = {
max_speed_for_map_matching = 180/3.6, -- 180kmph -> m/s
-- For routing based on duration, but weighted for preferring certain roads
weight_name = 'routability',
-- For shortest duration without penalties for accessibility
-- weight_name = 'duration',
-- For shortest distance without penalties for accessibility
-- weight_name = 'distance',
process_call_tagless_node = false,
u_turn_penalty = 20,
continue_straight_at_waypoint = true,
use_turn_restrictions = true,
left_hand_driving = false,
traffic_light_penalty = 2,
},
default_mode = mode.driving,
default_speed = 10,
oneway_handling = true,
side_road_multiplier = 0.8,
turn_penalty = 7.5,
speed_reduction = 0.8,
turn_bias = 1.075,
cardinal_directions = false,
-- Size of the vehicle, to be limited by physical restriction of the way
vehicle_height = 2.5, -- in meters, 2.5m is the height of van
vehicle_width = 1.9, -- in meters, ways with narrow tag are considered narrower than 2.2m
-- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
},
barrier_whitelist = Set {
'cattle_grid',
'border_control',
'toll_booth',
'sally_port',
'gate',
'lift_gate',
'no',
'entrance',
'height_restrictor'
},
access_tag_whitelist = Set {
'yes',
'motorcar',
'motor_vehicle',
'vehicle',
'permissive',
'designated',
'hov'
},
access_tag_blacklist = Set {
'no',
'agricultural',
'forestry',
'emergency',
'psv',
'customers',
'private',
'delivery',
'destination'
},
-- tags disallow access to in combination with highway=service
service_access_tag_blacklist = Set {
'private'
},
restricted_access_tag_list = Set {
'private',
'delivery',
'destination',
'customers',
},
access_tags_hierarchy = Sequence {
'motorcar',
'motor_vehicle',
'vehicle',
'access'
},
service_tag_forbidden = Set {
'emergency_access'
},
restrictions = Sequence {
'motorcar',
'motor_vehicle',
'vehicle'
},
classes = Sequence {
'toll', 'motorway', 'ferry', 'restricted', 'tunnel'
},
-- classes to support for exclude flags
excludable = Sequence {
Set {'toll'},
Set {'motorway'},
Set {'ferry'}
},
avoid = Set {
'area',
-- 'toll', -- uncomment this to avoid tolls
'reversible',
'impassable',
'hov_lanes',
'steps',
'construction',
'proposed'
},
speeds = Sequence {
highway = {
motorway = 90,
motorway_link = 45,
trunk = 85,
trunk_link = 40,
primary = 65,
primary_link = 30,
secondary = 55,
secondary_link = 25,
tertiary = 40,
tertiary_link = 20,
unclassified = 25,
residential = 25,
living_street = 10,
service = 15
}
},
service_penalties = {
alley = 0.5,
parking = 0.5,
parking_aisle = 0.5,
driveway = 0.5,
["drive-through"] = 0.5,
["drive-thru"] = 0.5
},
restricted_highway_whitelist = Set {
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'residential',
'living_street',
'unclassified',
'service'
},
construction_whitelist = Set {
'no',
'widening',
'minor',
},
route_speeds = {
ferry = 5,
shuttle_train = 10
},
bridge_speeds = {
movable = 5
},
-- surface/trackype/smoothness
-- values were estimated from looking at the photos at the relevant wiki pages
-- max speed for surfaces
surface_speeds = {
asphalt = nil, -- nil mean no limit. removing the line has the same effect
concrete = nil,
["concrete:plates"] = nil,
["concrete:lanes"] = nil,
paved = nil,
cement = 80,
compacted = 80,
fine_gravel = 80,
paving_stones = 60,
metal = 60,
bricks = 60,
grass = 40,
wood = 40,
sett = 40,
grass_paver = 40,
gravel = 40,
unpaved = 40,
ground = 40,
dirt = 40,
pebblestone = 40,
tartan = 40,
cobblestone = 30,
clay = 30,
earth = 20,
stone = 20,
rocky = 20,
sand = 20,
mud = 10
},
-- max speed for tracktypes
tracktype_speeds = {
grade1 = 60,
grade2 = 40,
grade3 = 30,
grade4 = 25,
grade5 = 20
},
-- max speed for smoothnesses
smoothness_speeds = {
intermediate = 80,
bad = 40,
very_bad = 20,
horrible = 10,
very_horrible = 5,
impassable = 0
},
-- http://wiki.openstreetmap.org/wiki/Speed_limits
maxspeed_table_default = {
urban = 50,
rural = 90,
trunk = 110,
motorway = 130
},
-- List only exceptions
maxspeed_table = {
["at:rural"] = 100,
["at:trunk"] = 100,
["be:motorway"] = 120,
["by:urban"] = 60,
["by:motorway"] = 110,
["ch:rural"] = 80,
["ch:trunk"] = 100,
["ch:motorway"] = 120,
["cz:trunk"] = 0,
["cz:motorway"] = 0,
["de:living_street"] = 7,
["de:rural"] = 100,
["de:motorway"] = 0,
["dk:rural"] = 80,
["gb:nsl_single"] = (60*1609)/1000,
["gb:nsl_dual"] = (70*1609)/1000,
["gb:motorway"] = (70*1609)/1000,
["nl:rural"] = 80,
["nl:trunk"] = 100,
['no:rural'] = 80,
['no:motorway'] = 110,
['pl:rural'] = 100,
['pl:trunk'] = 120,
['pl:motorway'] = 140,
["ro:trunk"] = 100,
["ru:living_street"] = 20,
["ru:urban"] = 60,
["ru:motorway"] = 110,
["uk:nsl_single"] = (60*1609)/1000,
["uk:nsl_dual"] = (70*1609)/1000,
["uk:motorway"] = (70*1609)/1000,
['za:urban'] = 60,
['za:rural'] = 100,
["none"] = 140
},
relation_types = Sequence {
"route"
},
-- classify highway tags when necessary for turn weights
highway_turn_classification = {
},
-- classify access tags when necessary for turn weights
access_turn_classification = {
}
}
end
function process_node(profile, node, result, relations)
-- parse access and barrier tags
local access = find_access_tag(node, profile.access_tags_hierarchy)
if access then
if profile.access_tag_blacklist[access] and not profile.restricted_access_tag_list[access] then
result.barrier = true
end
else
local barrier = node:get_value_by_key("barrier")
if barrier then
-- check height restriction barriers
local restricted_by_height = false
if barrier == 'height_restrictor' then
local maxheight = Measure.get_max_height(node:get_value_by_key("maxheight"), node)
restricted_by_height = maxheight and maxheight < profile.vehicle_height
end
-- make an exception for rising bollard barriers
local bollard = node:get_value_by_key("bollard")
local rising_bollard = bollard and "rising" == bollard
if not profile.barrier_whitelist[barrier] and not rising_bollard or restricted_by_height then
result.barrier = true
end
end
end
-- check if node is a traffic light
local tag = node:get_value_by_key("highway")
if "traffic_signals" == tag then
result.traffic_lights = true
end
end
function process_way(profile, way, result, relations)
-- the intial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
-- have to be checked.
-- to increase performance, prefetching and intial tag check
-- is done in directly instead of via a handler.
-- in general we should try to abort as soon as
-- possible if the way is not routable, to avoid doing
-- unnecessary work. this implies we should check things that
-- commonly forbids access early, and handle edge cases later.
-- data table for storing intermediate values during processing
local data = {
-- prefetch tags
highway = way:get_value_by_key('highway'),
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.
-- highway or route tags must be in data table, bridge is optional
if (not data.highway or data.highway == '') and
(not data.route or data.route == '')
then
return
end
result.name = way:id()
handlers = Sequence {
-- set the default mode for this profile. if can be changed later
-- in case it turns we're e.g. on a ferry
WayHandlers.default_mode,
-- check various tags that could indicate that the way is not
-- routable. this includes things like status=impassable,
-- toll=yes and oneway=reversible
WayHandlers.blocked_ways,
WayHandlers.avoid_ways,
WayHandlers.handle_height,
WayHandlers.handle_width,
-- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle
WayHandlers.access,
-- check whether forward/backward directions are routable
WayHandlers.oneway,
-- check a road's destination
WayHandlers.destinations,
-- check whether we're using a special transport mode
WayHandlers.ferries,
WayHandlers.movables,
-- handle service road restrictions
WayHandlers.service,
-- handle hov
WayHandlers.hov,
-- compute speed taking into account way type, maxspeed tags, etc.
WayHandlers.speed,
WayHandlers.surface,
WayHandlers.maxspeed,
WayHandlers.penalties,
-- compute class labels
WayHandlers.classes,
-- handle turn lanes and road classification, used for guidance
WayHandlers.turn_lanes,
WayHandlers.classification,
-- handle various other flags
WayHandlers.roundabouts,
WayHandlers.startpoint,
WayHandlers.driving_side,
-- set name, ref and pronunciation
--WayHandlers.names,
-- set weight properties of the way
WayHandlers.weights,
-- set classification of ways relevant for turns
WayHandlers.way_classification_for_turn
}
WayHandlers.run(profile, way, result, data, handlers, relations)
if profile.cardinal_directions then
Relations.process_way_refs(way, relations, result)
end
end
function process_turn(profile, turn)
-- Use a sigmoid function to return a penalty that maxes out at turn_penalty
-- over the space of 0-180 degrees. Values here were chosen by fitting
-- the function to some turn penalty samples from real driving.
local turn_penalty = profile.turn_penalty
local turn_bias = turn.is_left_hand_driving and 1. / profile.turn_bias or profile.turn_bias
if turn.has_traffic_light then
turn.duration = profile.properties.traffic_light_penalty
end
if turn.number_of_roads > 2 or turn.source_mode ~= turn.target_mode or turn.is_u_turn then
if turn.angle >= 0 then
turn.duration = turn.duration + turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias)))
else
turn.duration = turn.duration + turn_penalty / (1 + math.exp( -((13 * turn_bias) * -turn.angle/180 - 6.5/turn_bias)))
end
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
end
end
-- for distance based routing we don't want to have penalties based on turn angle
if profile.properties.weight_name == 'distance' then
turn.weight = 0
else
turn.weight = turn.duration
end
if profile.properties.weight_name == 'routability' then
-- penalize turns from non-local access only segments onto local access only tags
if not turn.source_restricted and turn.target_restricted then
turn.weight = constants.max_turn_weight
end
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment