Skip to content

Instantly share code, notes, and snippets.

@vgeorge
Last active September 7, 2018 15:20
Show Gist options
  • Save vgeorge/3e229acdbc61dba39233d3086d1679a1 to your computer and use it in GitHub Desktop.
Save vgeorge/3e229acdbc61dba39233d3086d1679a1 to your computer and use it in GitHub Desktop.
OSRM profile generator prototype
const _ = require("lodash");
const luafmt = require("lua-fmt");
function toLua(element) {
let properties = [];
// Array
if (Array.isArray(element)) {
properties = element.map(value => `'${value}'`);
return `{\n${properties.join(",\n")}\n}`;
}
// Object
else if (typeof element === "object") {
Object.keys(element).forEach(key => {
properties.push(` ["${key}"] = ${toLua(element[key])}`);
});
return `{\n${properties.join(",\n")}\n}`;
}
// String
else if (typeof element === "string") {
return `"${element}"`;
}
// Other
else return element;
}
const profile = {
properties: {
max_speed_for_map_matching: 50,
weight_name: "routability",
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_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,
vehicle_height: 2.5,
vehicle_width: 1.9,
vehicle_length: 4.8,
vehicle_weight: 3500,
barrier_whitelist: [
"cattle_grid",
"border_control",
"toll_booth",
"sally_port",
"gate",
"lift_gate",
"no",
"entrance",
"height_restrictor"
],
access_tag_whitelist: [
"yes",
"motorcar",
"motor_vehicle",
"vehicle",
"permissive",
"designated",
"hov"
],
access_tag_blacklist: [
"no",
"agricultural",
"forestry",
"emergency",
"psv",
"customers",
"private",
"delivery",
"destination"
],
service_access_tag_blacklist: ["private"],
restricted_access_tag_blacklist: [
"private",
"delivery",
"destination",
"customers"
],
access_tags_hierarchy: ["motorcar", "motor_vehicle", "vehicle", "access"],
service_tag_forbidden: ["emergency_access"],
restrictions: ["motorcar", "motor_vehicle", "vehicle"],
classes: ["toll", "motorway", "ferry", "restricted", "tunnel"],
avoid: [
"area",
"reversible",
"impassable",
"hov_lanes",
"steps",
"construction",
"proposed"
],
speeds: {
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: [
"motorway",
"motorway_link",
"trunk",
"trunk_link",
"primary",
"primary_link",
"secondary",
"secondary_link",
"tertiary",
"tertiary_link",
"residential",
"living_street",
"unclassified",
"service"
],
construction_whitelist: ["no", "widening", "minor"],
route_speeds: {
ferry: 5,
shuttle_train: 10
},
bridge_speeds: {
movable: 5
},
surface_speeds: {
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
},
tracktype_speeds: {
grade1: 60,
grade2: 40,
grade3: 30,
grade4: 25,
grade5: 20
},
smoothness_speeds: {
intermediate: 80,
bad: 40,
very_bad: 20,
horrible: 10,
very_horrible: 5,
impassable: 0
},
maxspeed_table_default: {
urban: 50,
rural: 90,
trunk: 110,
motorway: 130
},
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,
["fr:rural"]: 80,
["gb:nsl_single"]: 96.54,
["gb:nsl_dual"]: 112.63,
["gb:motorway"]: 112.63,
["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"]: 96.54,
["uk:nsl_dual"]: 112.63,
["uk:motorway"]: 112.63,
["za:urban"]: 60,
["za:rural"]: 100,
["none"]: 140
},
relation_types: ["route"]
};
const carProfile = `-- 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 = ${toLua(profile.properties)},
default_mode = mode.driving,
default_speed = ${profile.default_speed},
oneway_handling = ${profile.oneway_handling},
side_road_multiplier = ${profile.side_road_multiplier},
turn_penalty = ${profile.turn_penalty},
speed_reduction = ${profile.speed_reduction},
turn_bias = ${profile.turn_bias},
cardinal_directions = ${profile.cardinal_directions},
-- Size of the vehicle, to be limited by physical restriction of the way
vehicle_height = ${profile.vehicle_height},
vehicle_width = ${profile.vehicle_width},
-- Size of the vehicle, to be limited mostly by legal restriction of the way
vehicle_length = ${profile.vehicle_length},
vehicle_weight = ${profile.vehicle_weight},
-- 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 ${toLua(profile.barrier_whitelist)},
access_tag_whitelist = Set ${toLua(profile.access_tag_whitelist)},
access_tag_blacklist = Set ${toLua(profile.access_tag_blacklist)},
-- tags disallow access to in combination with highway=service
service_access_tag_blacklist = Set ${toLua(
profile.service_access_tag_blacklist
)},
restricted_access_tag_list = Set ${toLua(
profile.restricted_access_tag_blacklist
)},
access_tags_hierarchy = Sequence ${toLua(profile.access_tags_hierarchy)},
service_tag_forbidden = Set ${toLua(profile.service_tag_forbidden)},
restrictions = Sequence ${toLua(profile.restrictions)},
classes = Sequence ${toLua(profile.classes)},
-- classes to support for exclude flags
excludable = Sequence {
Set {'toll'},
Set {'motorway'},
Set {'ferry'}
},
avoid = Set ${toLua(profile.avoid)},
speeds = Sequence ${toLua(profile.speeds)},
service_penalties = ${toLua(profile.service_penalties)},
restricted_highway_whitelist = Set ${toLua(
profile.restricted_highway_whitelist
)},
construction_whitelist = Set ${toLua(profile.construction_whitelist)},
route_speeds = ${toLua(profile.route_speeds)},
bridge_speeds = ${toLua(profile.bridge_speeds)},
-- surface/trackype/smoothness
-- values were estimated from looking at the photos at the relevant wiki pages
-- max speed for surfaces
surface_speeds = ${toLua(profile.surface_speeds)},
-- max speed for tracktypes
tracktype_speeds = ${toLua(profile.tracktype_speeds)},
-- max speed for smoothnesses
smoothness_speeds = ${toLua(profile.smoothness_speeds)},
-- http://wiki.openstreetmap.org/wiki/Speed_limits
maxspeed_table_default = ${toLua(profile.maxspeed_table_default)},
-- List only exceptions
maxspeed_table = ${toLua(profile.maxspeed_table)},
relation_types = Sequence ${toLua(profile.relation_types)},
-- 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)
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
}
`;
console.log(luafmt.formatText(carProfile));
Why this file was generated:
- https://github.com/WorldBank-Transport/ram-backend/issues/228
Why this file exists:
- https://stackoverflow.com/questions/19896900/how-to-change-the-name-of-a-gist-in-github#20949455
{
"name": "profile-editor",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"start": "node index.js > custom-profile.lua"
},
"dependencies": {
"lodash": "^4.17.10",
"lua-fmt": "^2.6.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment