-- 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.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) 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 }