From 680120f32dcadfcfca2365d1d4197c4759d0a8e7 Mon Sep 17 00:00:00 2001 From: tombay Date: Tue, 6 Aug 2024 11:12:35 +1000 Subject: [PATCH] Add files via upload Profiles to support country specific highway access. Country geometries provided by use of --location-dependent-data option of osrm-extract. --- profiles/countrybicycle.lua | 706 ++++++++++++++++++++++++++++++++++++ profiles/countrycar.lua | 527 +++++++++++++++++++++++++++ profiles/countryfoot.lua | 283 +++++++++++++++ 3 files changed, 1516 insertions(+) create mode 100644 profiles/countrybicycle.lua create mode 100644 profiles/countrycar.lua create mode 100644 profiles/countryfoot.lua diff --git a/profiles/countrybicycle.lua b/profiles/countrybicycle.lua new file mode 100644 index 000000000..766654f37 --- /dev/null +++ b/profiles/countrybicycle.lua @@ -0,0 +1,706 @@ +-- Bicycle profile + +api_version = 4 + +Set = require('lib/set') +Sequence = require('lib/sequence') +Handlers = require("lib/way_handlers") +TrafficSignal = require("lib/traffic_signal") +find_access_tag = require("lib/access").find_access_tag +country_speeds = require("lib/country_speeds") +limit = require("lib/maxspeed").limit +Measure = require("lib/measure") +inspect = require("lib/inspect") + +function setup() + local default_speed = 15 + local walking_speed = 4 + + return { + properties = { + u_turn_penalty = 20, + traffic_light_penalty = 2, + --weight_name = 'cyclability', + weight_name = 'duration', + process_call_tagless_node = false, + max_speed_for_map_matching = 110/3.6, -- kmph -> m/s + use_turn_restrictions = false, + continue_straight_at_waypoint = false, + mode_change_penalty = 30, + }, + + profile = 'bicycle', + default_mode = mode.cycling, + default_speed = default_speed, + walking_speed = walking_speed, + oneway_handling = true, + turn_penalty = 6, + turn_bias = 1.4, + use_public_transport = true, + + allowed_start_modes = Set { + mode.cycling, + mode.pushing_bike + }, + + barrier_blacklist = Set { + 'yes', + 'wall', + 'fence' + }, + + access_tag_whitelist = Set { + 'yes', + 'permissive', + 'designated' + }, + + access_tag_blacklist = Set { + 'no', + 'private', + 'agricultural', + 'forestry', + 'delivery', + -- When a way is tagged with `use_sidepath` a parallel way suitable for + -- cyclists is mapped and must be used instead (by law). This tag is + -- used on ways that normally may be used by cyclists, but not when + -- a signposted parallel cycleway is available. For purposes of routing + -- cyclists, this value should be treated as 'no access for bicycles'. + 'use_sidepath' + }, + + restricted_access_tag_list = Set { }, + + restricted_highway_whitelist = Set { }, + + -- tags disallow access to in combination with highway=service + service_access_tag_blacklist = Set { }, + + construction_whitelist = Set { + 'no', + 'widening', + 'minor', + }, + + access_tags_hierarchy = Sequence { + 'bicycle', + 'vehicle', + 'access' + }, + + restrictions = Set { + 'bicycle' + }, + + cycleway_tags = Set { + 'track', + 'lane', + 'share_busway', + 'sharrow', + 'shared', + 'shared_lane' + }, + + opposite_cycleway_tags = Set { + 'opposite', + 'opposite_lane', + 'opposite_track', + }, + + -- reduce the driving speed by 30% for unsafe roads + -- only used for cyclability metric + unsafe_highway_list = { + primary = 0.5, + secondary = 0.65, + tertiary = 0.8, + primary_link = 0.5, + secondary_link = 0.65, + tertiary_link = 0.8, + }, + + service_penalties = { + alley = 0.5, + }, + + bicycle_speeds = { + cycleway = default_speed, + primary = default_speed, + primary_link = default_speed, + secondary = default_speed, + secondary_link = default_speed, + tertiary = default_speed, + tertiary_link = default_speed, + residential = default_speed, + unclassified = default_speed, + living_street = default_speed, + road = default_speed, + service = default_speed, + track = 12, + path = 13 + }, + + pedestrian_speeds = { + footway = walking_speed, + pedestrian = walking_speed, + steps = 2 + }, + + railway_speeds = { + train = 10, + railway = 10, + subway = 10, + light_rail = 10, + monorail = 10, + tram = 10 + }, + + platform_speeds = { + platform = walking_speed + }, + + amenity_speeds = { + parking = 10, + parking_entrance = 10 + }, + + man_made_speeds = { + pier = walking_speed + }, + + route_speeds = { + ferry = 5 + }, + + bridge_speeds = { + movable = 5 + }, + + surface_speeds = { + asphalt = default_speed, + chipseal = default_speed, + concrete = default_speed, + concrete_lanes = default_speed, + wood = 10, + metal = 10, + ["cobblestone:flattened"] = 10, + paving_stones = 10, + compacted = 10, + cobblestone = 7, + unpaved = 6, + fine_gravel = 10, + gravel = 6, + pebblestone = 6, + grass_paver = 6, + ground = 10, + dirt = 8, + earth = 6, + grass = 6, + mud = 3, + sand = 3, + woodchips = 3, + sett = 9 + }, + + classes = Sequence { + 'ferry', 'tunnel' + }, + + -- Which classes should be excludable + -- This increases memory usage so its disabled by default. + excludable = Sequence { +-- Set {'ferry'} + }, + + tracktype_speeds = { + }, + + smoothness_speeds = { + }, + + avoid = Set { + 'impassable', + 'construction', + 'proposed' + }, + + uselocationtags = Set + { + 'countryspeeds', + } + } +end + +function process_node(profile, node, result) + -- parse access and barrier tags + local highway = node:get_value_by_key("highway") + local is_crossing = highway and highway == "crossing" + + local access = find_access_tag(node, profile.access_tags_hierarchy) + if access and access ~= "" then + -- access restrictions on crossing nodes are not relevant for + -- the traffic on the road + if profile.access_tag_blacklist[access] and not is_crossing then + result.barrier = true + end + else + local barrier = node:get_value_by_key("barrier") + if barrier and "" ~= barrier then + if profile.barrier_blacklist[barrier] then + result.barrier = true + end + end + end + + -- check if node is a traffic light + result.traffic_lights = TrafficSignal.get_value(node) +end + +function handle_bicycle_tags(profile,way,result,data) + -- initial routability check, filters out buildings, boundaries, etc + data.route = way:get_value_by_key("route") + data.man_made = way:get_value_by_key("man_made") + data.railway = way:get_value_by_key("railway") + data.amenity = way:get_value_by_key("amenity") + data.public_transport = way:get_value_by_key("public_transport") + data.bridge = way:get_value_by_key("bridge") + + if (not data.highway or data.highway == '') and + (not data.route or data.route == '') and + (not profile.use_public_transport or not data.railway or data.railway=='') and + (not data.amenity or data.amenity=='') and + (not data.man_made or data.man_made=='') and + (not data.public_transport or data.public_transport=='') and + (not data.bridge or data.bridge=='') + then + return false + end + + -- access + data.access = find_access_tag(way, profile.access_tags_hierarchy) + if data.access and profile.access_tag_blacklist[data.access] then + return false + end + + -- other tags + data.junction = way:get_value_by_key("junction") + data.maxspeed = Measure.get_max_speed(way:get_value_by_key ("maxspeed")) or 0 + data.maxspeed_forward = Measure.get_max_speed(way:get_value_by_key("maxspeed:forward")) or 0 + data.maxspeed_backward = Measure.get_max_speed(way:get_value_by_key("maxspeed:backward")) or 0 + data.barrier = way:get_value_by_key("barrier") + data.oneway = way:get_value_by_key("oneway") + data.oneway_bicycle = way:get_value_by_key("oneway:bicycle") + data.cycleway = way:get_value_by_key("cycleway") + data.cycleway_left = way:get_value_by_key("cycleway:left") + data.cycleway_right = way:get_value_by_key("cycleway:right") + data.duration = way:get_value_by_key("duration") + data.service = way:get_value_by_key("service") + data.foot = way:get_value_by_key("foot") + data.foot_forward = way:get_value_by_key("foot:forward") + data.foot_backward = way:get_value_by_key("foot:backward") + data.bicycle = way:get_value_by_key("bicycle") + + speed_handler(profile,way,result,data) + + oneway_handler(profile,way,result,data) + + cycleway_handler(profile,way,result,data) + + bike_push_handler(profile,way,result,data) + + + -- maxspeed + limit( result, data.maxspeed, data.maxspeed_forward, data.maxspeed_backward ) + + -- not routable if no speed assigned + -- this avoid assertions in debug builds + if result.forward_speed <= 0 and result.duration <= 0 then + result.forward_mode = mode.inaccessible + end + if result.backward_speed <= 0 and result.duration <= 0 then + result.backward_mode = mode.inaccessible + end + + safety_handler(profile,way,result,data) +end + + + +function speed_handler(profile,way,result,data) + + data.way_type_allows_pushing = false + + local extra_speeds + local extraspeed + + if profile.uselocationtags and profile.uselocationtags.countryspeeds then + extra_speeds = country_speeds.getAccessProfile(data, profile.profile) + if extra_speeds then + if extra_speeds.highway[data.highway] then + extraspeed = extra_speeds.highway[data.highway] + end + end + end + + -- speed + local bridge_speed = profile.bridge_speeds[data.bridge] + if (bridge_speed and bridge_speed > 0) then + data.highway = data.bridge + if data.duration and durationIsValid(data.duration) then + result.duration = math.max( parseDuration(data.duration), 1 ) + end + result.forward_speed = bridge_speed + result.backward_speed = bridge_speed + data.way_type_allows_pushing = true + elseif profile.route_speeds[data.route] then + -- ferries (doesn't cover routes tagged using relations) + result.forward_mode = mode.ferry + result.backward_mode = mode.ferry + if data.duration and durationIsValid(data.duration) then + result.duration = math.max( 1, parseDuration(data.duration) ) + else + result.forward_speed = profile.route_speeds[data.route] + result.backward_speed = profile.route_speeds[data.route] + end + -- railway platforms (old tagging scheme) + elseif data.railway and profile.platform_speeds[data.railway] then + result.forward_speed = profile.platform_speeds[data.railway] + result.backward_speed = profile.platform_speeds[data.railway] + data.way_type_allows_pushing = true + -- public_transport platforms (new tagging platform) + elseif data.public_transport and profile.platform_speeds[data.public_transport] then + result.forward_speed = profile.platform_speeds[data.public_transport] + result.backward_speed = profile.platform_speeds[data.public_transport] + data.way_type_allows_pushing = true + -- railways + elseif profile.use_public_transport and data.railway and profile.railway_speeds[data.railway] and profile.access_tag_whitelist[data.access] then + result.forward_mode = mode.train + result.backward_mode = mode.train + result.forward_speed = profile.railway_speeds[data.railway] + result.backward_speed = profile.railway_speeds[data.railway] + elseif data.amenity and profile.amenity_speeds[data.amenity] then + -- parking areas + result.forward_speed = profile.amenity_speeds[data.amenity] + result.backward_speed = profile.amenity_speeds[data.amenity] + data.way_type_allows_pushing = true + elseif extraspeed then + if extraspeed ~= -1 then + result.forward_speed = extraspeed + result.backward_speed = extraspeed + data.way_type_allows_pushing = true + end + elseif profile.bicycle_speeds[data.highway] then + -- regular ways + result.forward_speed = profile.bicycle_speeds[data.highway] + result.backward_speed = profile.bicycle_speeds[data.highway] + data.way_type_allows_pushing = true + elseif data.access and profile.access_tag_whitelist[data.access] then + -- unknown way, but valid access tag + result.forward_speed = profile.default_speed + result.backward_speed = profile.default_speed + data.way_type_allows_pushing = true + end +end + +function oneway_handler(profile,way,result,data) + -- oneway + data.implied_oneway = data.junction == "roundabout" or data.junction == "circular" or data.highway == "motorway" + data.reverse = false + + if data.oneway_bicycle == "yes" or data.oneway_bicycle == "1" or data.oneway_bicycle == "true" then + result.backward_mode = mode.inaccessible + elseif data.oneway_bicycle == "no" or data.oneway_bicycle == "0" or data.oneway_bicycle == "false" then + -- prevent other cases + elseif data.oneway_bicycle == "-1" then + result.forward_mode = mode.inaccessible + data.reverse = true + elseif data.oneway == "yes" or data.oneway == "1" or data.oneway == "true" then + result.backward_mode = mode.inaccessible + elseif data.oneway == "no" or data.oneway == "0" or data.oneway == "false" then + -- prevent other cases + elseif data.oneway == "-1" then + result.forward_mode = mode.inaccessible + data.reverse = true + elseif data.implied_oneway then + result.backward_mode = mode.inaccessible + end +end + +function cycleway_handler(profile,way,result,data) + -- cycleway + data.has_cycleway_forward = false + data.has_cycleway_backward = false + data.is_twoway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not data.implied_oneway + + -- cycleways on normal roads + if data.is_twoway then + if data.cycleway and profile.cycleway_tags[data.cycleway] then + data.has_cycleway_backward = true + data.has_cycleway_forward = true + end + if (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) then + data.has_cycleway_forward = true + end + if (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) then + data.has_cycleway_backward = true + end + else + local has_twoway_cycleway = (data.cycleway and profile.opposite_cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) + local has_opposite_cycleway = (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) + local has_oneway_cycleway = (data.cycleway and profile.cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) + + -- set cycleway even though it is an one-way if opposite is tagged + if has_twoway_cycleway then + data.has_cycleway_backward = true + data.has_cycleway_forward = true + elseif has_opposite_cycleway then + if not data.reverse then + data.has_cycleway_backward = true + else + data.has_cycleway_forward = true + end + elseif has_oneway_cycleway then + if not data.reverse then + data.has_cycleway_forward = true + else + data.has_cycleway_backward = true + end + + end + end + + if data.has_cycleway_backward then + result.backward_mode = mode.cycling + result.backward_speed = profile.bicycle_speeds["cycleway"] + end + + if data.has_cycleway_forward then + result.forward_mode = mode.cycling + result.forward_speed = profile.bicycle_speeds["cycleway"] + end +end + +function bike_push_handler(profile,way,result,data) + -- pushing bikes - if no other mode found + if result.forward_mode == mode.inaccessible or result.backward_mode == mode.inaccessible or + result.forward_speed == -1 or result.backward_speed == -1 then + if data.foot ~= 'no' then + local push_forward_speed = nil + local push_backward_speed = nil + + if profile.pedestrian_speeds[data.highway] then + push_forward_speed = profile.pedestrian_speeds[data.highway] + push_backward_speed = profile.pedestrian_speeds[data.highway] + elseif data.man_made and profile.man_made_speeds[data.man_made] then + push_forward_speed = profile.man_made_speeds[data.man_made] + push_backward_speed = profile.man_made_speeds[data.man_made] + else + if data.foot == 'yes' then + push_forward_speed = profile.walking_speed + if not data.implied_oneway then + push_backward_speed = profile.walking_speed + end + elseif data.foot_forward == 'yes' then + push_forward_speed = profile.walking_speed + elseif data.foot_backward == 'yes' then + push_backward_speed = profile.walking_speed + elseif data.way_type_allows_pushing then + push_forward_speed = profile.walking_speed + if not data.implied_oneway then + push_backward_speed = profile.walking_speed + end + end + end + + if push_forward_speed and (result.forward_mode == mode.inaccessible or result.forward_speed == -1) then + result.forward_mode = mode.pushing_bike + result.forward_speed = push_forward_speed + end + if push_backward_speed and (result.backward_mode == mode.inaccessible or result.backward_speed == -1)then + result.backward_mode = mode.pushing_bike + result.backward_speed = push_backward_speed + end + + end + + end + + -- dismount + if data.bicycle == "dismount" then + result.forward_mode = mode.pushing_bike + result.backward_mode = mode.pushing_bike + result.forward_speed = profile.walking_speed + result.backward_speed = profile.walking_speed + end +end + +function safety_handler(profile,way,result,data) + -- convert duration into cyclability + if profile.properties.weight_name == 'cyclability' then + local safety_penalty = profile.unsafe_highway_list[data.highway] or 1. + local is_unsafe = safety_penalty < 1 + + -- primaries that are one ways are probably huge primaries where the lanes need to be separated + if is_unsafe and data.highway == 'primary' and not data.is_twoway then + safety_penalty = safety_penalty * 0.5 + end + if is_unsafe and data.highway == 'secondary' and not data.is_twoway then + safety_penalty = safety_penalty * 0.6 + end + + local forward_is_unsafe = is_unsafe and not data.has_cycleway_forward + local backward_is_unsafe = is_unsafe and not data.has_cycleway_backward + local is_undesireable = data.highway == "service" and profile.service_penalties[data.service] + local forward_penalty = 1. + local backward_penalty = 1. + if forward_is_unsafe then + forward_penalty = math.min(forward_penalty, safety_penalty) + end + if backward_is_unsafe then + backward_penalty = math.min(backward_penalty, safety_penalty) + end + + if is_undesireable then + forward_penalty = math.min(forward_penalty, profile.service_penalties[data.service]) + backward_penalty = math.min(backward_penalty, profile.service_penalties[data.service]) + end + + if result.forward_speed > 0 then + -- convert from km/h to m/s + result.forward_rate = result.forward_speed / 3.6 * forward_penalty + end + if result.backward_speed > 0 then + -- convert from km/h to m/s + result.backward_rate = result.backward_speed / 3.6 * backward_penalty + end + if result.duration > 0 then + result.weight = result.duration / forward_penalty + end + end +end + + + +function process_way(profile, way, result) + -- the initial filtering of ways based on presence of tags + -- affects processing times significantly, because all ways + -- have to be checked. + -- to increase performance, prefetching and initial tag check + -- is done 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'), + + route = nil, + man_made = nil, + railway = nil, + amenity = nil, + public_transport = nil, + bridge = nil, + + access = nil, + + junction = nil, + maxspeed = nil, + maxspeed_forward = nil, + maxspeed_backward = nil, + barrier = nil, + oneway = nil, + oneway_bicycle = nil, + cycleway = nil, + cycleway_left = nil, + cycleway_right = nil, + duration = nil, + service = nil, + foot = nil, + foot_forward = nil, + foot_backward = nil, + bicycle = nil, + + way_type_allows_pushing = false, + has_cycleway_forward = false, + has_cycleway_backward = false, + is_twoway = true, + reverse = false, + implied_oneway = false + } + + if profile.uselocationtags and profile.uselocationtags.countryspeeds then + data.location = country_speeds.getcountrytag(way) + end + + local 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, + + -- our main handler + handle_bicycle_tags, + + -- compute speed taking into account way type, maxspeed tags, etc. + WayHandlers.surface, + + -- handle turn lanes and road classification, used for guidance + WayHandlers.classification, + + -- handle allowed start/end modes + WayHandlers.startpoint, + + -- handle roundabouts + WayHandlers.roundabouts, + + -- set name, ref and pronunciation + WayHandlers.names, + + -- set classes + WayHandlers.classes, + + -- set weight properties of the way + WayHandlers.weights + } + + WayHandlers.run(profile, way, result, data, handlers) +end + +function process_turn(profile, turn) + -- compute turn penalty as angle^2, with a left/right bias + local normalized_angle = turn.angle / 90.0 + if normalized_angle >= 0.0 then + turn.duration = normalized_angle * normalized_angle * profile.turn_penalty / profile.turn_bias + else + turn.duration = normalized_angle * normalized_angle * profile.turn_penalty * profile.turn_bias + end + + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end + if profile.properties.weight_name == 'cyclability' then + turn.weight = turn.duration + end + if turn.source_mode == mode.cycling and turn.target_mode ~= mode.cycling then + turn.weight = turn.weight + profile.properties.mode_change_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +} diff --git a/profiles/countrycar.lua b/profiles/countrycar.lua new file mode 100644 index 000000000..cb7598b65 --- /dev/null +++ b/profiles/countrycar.lua @@ -0,0 +1,527 @@ +-- Car profile + +api_version = 4 + +Set = require('lib/set') +Sequence = require('lib/sequence') +Handlers = require("lib/way_handlers") +Relations = require("lib/relations") +TrafficSignal = require("lib/traffic_signal") +find_access_tag = require("lib/access").find_access_tag +country_speeds = require("lib/country_speeds") +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, + }, + + profile = 'vehicle', + 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' + }, + + hwyspeeds = 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-bru:rural"] = 70, + ["be-bru:urban"] = 30, + ["be-vlg:rural"] = 70, + ["bg:motorway"] = 140, + ["by:urban"] = 60, + ["by:motorway"] = 110, + ["ca-on:rural"] = 80, + ["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, + ["es:trunk"] = 90, + ["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, + ['ph:urban'] = 40, + ['ph:rural'] = 80, + ['ph:motorway'] = 100, + ['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 = { + }, + + uselocationtags = Set + { + 'countryspeeds', + } + + } +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 + + -- make an exception for lowered/flat barrier=kerb + -- and incorrect tagging of highway crossing kerb as highway barrier + local kerb = node:get_value_by_key("kerb") + local highway = node:get_value_by_key("highway") + local flat_kerb = kerb and ("lowered" == kerb or "flush" == kerb) + local highway_crossing_kerb = barrier == "kerb" and highway and highway == "crossing" + + if not profile.barrier_whitelist[barrier] + and not rising_bollard + and not flat_kerb + and not highway_crossing_kerb + or restricted_by_height then + result.barrier = true + end + end + end + + -- check if node is a traffic light + result.traffic_lights = TrafficSignal.get_value(node) +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 + + if profile.uselocationtags and profile.uselocationtags.countryspeeds then + data.location = country_speeds.getcountrytag(way) + 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. + country_speeds.wayspeed, + WayHandlers.maxspeed, + WayHandlers.surface, + 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 +} diff --git a/profiles/countryfoot.lua b/profiles/countryfoot.lua new file mode 100644 index 000000000..eb0552d15 --- /dev/null +++ b/profiles/countryfoot.lua @@ -0,0 +1,283 @@ +-- Foot profile + +api_version = 2 + +Set = require('lib/set') +Sequence = require('lib/sequence') +Handlers = require("lib/way_handlers") +find_access_tag = require("lib/access").find_access_tag +country_speeds = require("lib/country_speeds") +inspect = require("/lib/inspect") + +function setup() + local walking_speed = 5 + return { + properties = { + weight_name = 'duration', + max_speed_for_map_matching = 40/3.6, -- kmph -> m/s + call_tagless_node_function = false, + traffic_light_penalty = 2, + u_turn_penalty = 2, + continue_straight_at_waypoint = false, + use_turn_restrictions = false, + }, + + profile = 'foot', + default_mode = mode.walking, + default_speed = walking_speed, + oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway' + + barrier_blacklist = Set { + 'yes', + 'wall', + 'fence' + }, + + access_tag_whitelist = Set { + 'yes', + 'foot', + 'permissive', + 'designated' + }, + + access_tag_blacklist = Set { + 'no', + 'agricultural', + 'forestry', + 'private', + 'delivery', + }, + + restricted_access_tag_list = Set { }, + + restricted_highway_whitelist = Set { }, + + construction_whitelist = Set {}, + + access_tags_hierarchy = Sequence { + 'foot', + 'access' + }, + + -- tags disallow access to in combination with highway=service + service_access_tag_blacklist = Set { }, + + restrictions = Sequence { + 'foot' + }, + + -- 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', + 'proposed', + 'motorroad' + }, + + hwyspeeds = 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, + }, + }, + + otherspeeds = Sequence { + railway = { + platform = walking_speed + }, + + amenity = { + parking = walking_speed, + parking_entrance= walking_speed + }, + + man_made = { + pier = walking_speed + }, + + leisure = { + track = walking_speed + } + }, + + route_speeds = { + ferry = 5 + }, + + bridge_speeds = { + }, + + 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 = { + }, + + uselocationtags = Set + { + 'countryspeeds', + } + } +end + +function process_node(profile, node, result) + -- parse access and barrier tags + local access = find_access_tag(node, profile.access_tags_hierarchy) + if access then + if profile.access_tag_blacklist[access] then + result.barrier = true + end + else + local barrier = node:get_value_by_key("barrier") + if barrier then + -- make an exception for rising bollard barriers + local bollard = node:get_value_by_key("bollard") + local rising_bollard = bollard and "rising" == bollard + + if profile.barrier_blacklist[barrier] and not rising_bollard 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 + -- Direction should only apply to vehicles + result.traffic_lights = true + end +end + +-- main entry point for processsing a way +function process_way(profile, way, result) + -- 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'), + 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') + } + + -- perform an quick initial check and abort if the way is + -- obviously not routable. here we require at least one + -- 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 + if profile.uselocationtags and profile.uselocationtags.countryspeeds then + data.location = country_speeds.getcountrytag(way) + end + + local 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, + + -- determine access status by checking our hierarchy of + -- access tags, e.g: motorcar, motor_vehicle, vehicle + WayHandlers.access, + + -- check whether forward/backward directons are routable + WayHandlers.oneway, + + -- check whether forward/backward directons are routable + WayHandlers.destinations, + + -- check whether we're using a special transport mode + WayHandlers.ferries, + WayHandlers.movables, + + -- compute speed taking into account way type, maxspeed tags, etc. + country_speeds.wayspeed, + WayHandlers.surface, + + -- handle turn lanes and road classification, used for guidance + WayHandlers.classification, + + -- handle various other flags + WayHandlers.roundabouts, + WayHandlers.startpoint, + + -- set name, ref and pronunciation + WayHandlers.names, + + -- set weight properties of the way + WayHandlers.weights + } + + WayHandlers.run(profile, way, result, data, handlers) +end + +function process_turn (profile, turn) + turn.duration = 0. + + if turn.direction_modifier == direction_modifier.u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + end + + if turn.has_traffic_light then + turn.duration = profile.properties.traffic_light_penalty + 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 = turn.weight + 3000 + end + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +}