-
-
Save TimMcCauley/5bd61b968849ebca4974b636e16f4fa9 to your computer and use it in GitHub Desktop.
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.0, -- in meters, 2.0m is the height slightly above biggest SUVs | |
vehicle_width = 1.9, -- in meters, ways with narrow tag are considered narrower than 2.2m | |
-- Size of the vehicle, to be limited mostly by legal restriction of the way | |
vehicle_length = 4.8, -- in meters, 4.8m is the length of large or family car | |
vehicle_weight = 2000, -- in kilograms | |
-- 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', | |
'arch' | |
}, | |
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, | |
["be-vlg:rural"] = 70, | |
["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, | |
["fr: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 | |
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, | |
WayHandlers.handle_length, | |
WayHandlers.handle_weight, | |
-- 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) | |
-- customized speed manipulation | |
forward_sp = result.forward_speed | |
backward_sp = result.backward_speed | |
factor = 1.0 | |
if way:get_location_tag('speed_factor') then | |
factor = way:get_location_tag('speed_factor') | |
end | |
if forward_sp and forward_sp > 0 then | |
result.forward_speed = forward_sp * factor | |
end | |
if backward_sp and backward_sp > 0 then | |
result.backward_speed = backward_sp * factor | |
end | |
-- end customized speed manipulation | |
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 | |
} |
Thanks! In that case it wouldnt be that bad. I could just have two sequential multipolygon jsons, one with each new speeds according to the two different depths im considering.
I cant wait to dive in! Thank you for your help. I am 0 familiar with lua so if there is an editor or viewer I should seek, any link to a set up tutorial / osm lua files handling tutorial would be amazing. Will report back if I have results.
Reg. the lua files, there are many flying around as gists and there is quite some information in the repo itself, you will have to browse around a bit. Printing within the lua file is usually my best friend. If Multipolygon does not work, you would also "script together" the CLI command if there are hundreds of location data arguments.
I don't think so, possibly a MultiPolygon will work but that then would use the same settings for all polygons within. Just give it a try, you will find eventually out :-)