-- Car profile local find_access_tag = require("lib/access").find_access_tag -- Begin of globals barrier_whitelist = { ["cattle_grid"] = true, ["border_control"] = true, ["checkpoint"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["lift_gate"] = true, ["no"] = true, ["entrance"] = true } access_tag_whitelist = { ["yes"] = true, ["motorcar"] = true, ["motor_vehicle"] = true, ["vehicle"] = true, ["permissive"] = true, ["designated"] = true, ["destination"] = true } access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true, ["delivery"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierarchy = { "motorcar", "motor_vehicle", "vehicle", "access" } service_tag_restricted = { ["parking_aisle"] = true } restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" } -- A list of suffixes to suppress in name change instructions suffix_list = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "North", "South", "West", "East" } speed_profile = { ["Expressway"] = 120, ["National"] = 80, ["Provincial"] = 60, ["Township"] = 20, ["County"] = 20, ["Rural"] = 20, ["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, -- ["track"] = 5, ["ferry"] = 5, ["movable"] = 5, ["shuttle_train"] = 10, ["default"] = 10 } -- 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 = { ["ch:rural"] = 80, ["ch:trunk"] = 100, ["ch:motorway"] = 120, ["de:living_street"] = 7, ["ru:living_street"] = 20, ["ru:urban"] = 60, ["ua:urban"] = 60, ["at:rural"] = 100, ["de:rural"] = 100, ["at:trunk"] = 100, ["cz:trunk"] = 0, ["ro:trunk"] = 100, ["cz:motorway"] = 0, ["de:motorway"] = 0, ["ru:motorway"] = 110, ["gb:nsl_single"] = (60*1609)/1000, ["gb:nsl_dual"] = (70*1609)/1000, ["gb:motorway"] = (70*1609)/1000, ["uk:nsl_single"] = (60*1609)/1000, ["uk:nsl_dual"] = (70*1609)/1000, ["uk:motorway"] = (70*1609)/1000, ["none"] = 140 } -- set profile properties properties.u_turn_penalty = 20 properties.traffic_signal_penalty = 2 properties.use_turn_restrictions = true properties.continue_straight_at_waypoint = true local side_road_speed_multiplier = 0.8 local turn_penalty = 10 -- Note: this biases right-side driving. Should be -- inverted for left-driving countries. local turn_bias = 1.2 local obey_oneway = true local ignore_areas = true local abs = math.abs local min = math.min local max = math.max local speed_reduction = 0.8 function get_name_suffix_list(vector) for index,suffix in ipairs(suffix_list) do vector:Add(suffix) end end function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do vector:Add(v) end end local function parse_maxspeed(source) if not source then return 0 end local n = tonumber(source:match("%d*")) if n then if string.match(source, "mph") or string.match(source, "mp/h") then n = (n*1609)/1000 end else -- parse maxspeed like FR:urban source = string.lower(source) n = maxspeed_table[source] if not n then local highway_type = string.match(source, "%a%a:(%a+)") n = maxspeed_table_default[highway_type] if not n then n = 0 end end end return n end function node_function (node, result) -- parse access and barrier tags local access = find_access_tag(node, access_tags_hierarchy) if access and access ~= "" then if access_tag_blacklist[access] then result.barrier = true end else local barrier = node:get_value_by_key("barrier") if barrier and "" ~= 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 not barrier_whitelist[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 tag and "traffic_signals" == tag then result.traffic_lights = true end end function way_function (way, result) local highway = way:get_value_by_key("highway") local route = way:get_value_by_key("route") local bridge = way:get_value_by_key("bridge") if not ((highway and highway ~= "") or (route and route ~= "") or (bridge and bridge ~= "")) then return end -- we dont route over areas local area = way:get_value_by_key("area") if ignore_areas and area and "yes" == area then return end -- check if oneway tag is unsupported local oneway = way:get_value_by_key("oneway") if oneway and "reversible" == oneway then return end local impassable = way:get_value_by_key("impassable") if impassable and "yes" == impassable then return end local status = way:get_value_by_key("status") if status and "impassable" == status then return end -- Check if we are allowed to access the way local access = find_access_tag(way, access_tags_hierarchy) if access_tag_blacklist[access] then return end result.forward_mode = mode.driving result.backward_mode = mode.driving -- handling ferries and piers local route_speed = speed_profile[route] if (route_speed and route_speed > 0) then highway = route local duration = way:get_value_by_key("duration") if duration and durationIsValid(duration) then result.duration = max( parseDuration(duration), 1 ) end result.forward_mode = mode.ferry result.backward_mode = mode.ferry result.forward_speed = route_speed result.backward_speed = route_speed end -- handling movable bridges local bridge_speed = speed_profile[bridge] local capacity_car = way:get_value_by_key("capacity:car") if (bridge_speed and bridge_speed > 0) and (capacity_car ~= 0) then highway = bridge local duration = way:get_value_by_key("duration") if duration and durationIsValid(duration) then result.duration = max( parseDuration(duration), 1 ) end result.forward_speed = bridge_speed result.backward_speed = bridge_speed end -- leave early of this way is not accessible if "" == highway then return end if result.forward_speed == -1 then local highway_speed = speed_profile[highway] local max_speed = parse_maxspeed( way:get_value_by_key("maxspeed") ) -- Set the avg speed on the way if it is accessible by road class if highway_speed then if max_speed and max_speed > highway_speed then result.forward_speed = max_speed result.backward_speed = max_speed -- max_speed = math.huge else result.forward_speed = highway_speed result.backward_speed = highway_speed end else -- Set the avg speed on ways that are marked accessible if access_tag_whitelist[access] then result.forward_speed = speed_profile["default"] result.backward_speed = speed_profile["default"] end end if 0 == max_speed then max_speed = math.huge end result.forward_speed = min(result.forward_speed, max_speed) result.backward_speed = min(result.backward_speed, max_speed) end if -1 == result.forward_speed and -1 == result.backward_speed then return end -- reduce speed on special side roads local sideway = way:get_value_by_key("side_road") if "yes" == sideway or "rotary" == sideway then result.forward_speed = result.forward_speed * side_road_speed_multiplier result.backward_speed = result.backward_speed * side_road_speed_multiplier end -- reduce speed on bad surfaces local surface = way:get_value_by_key("surface") local tracktype = way:get_value_by_key("tracktype") local smoothness = way:get_value_by_key("smoothness") if surface and surface_speeds[surface] then result.forward_speed = math.min(surface_speeds[surface], result.forward_speed) result.backward_speed = math.min(surface_speeds[surface], result.backward_speed) end if tracktype and tracktype_speeds[tracktype] then result.forward_speed = math.min(tracktype_speeds[tracktype], result.forward_speed) result.backward_speed = math.min(tracktype_speeds[tracktype], result.backward_speed) end if smoothness and smoothness_speeds[smoothness] then result.forward_speed = math.min(smoothness_speeds[smoothness], result.forward_speed) result.backward_speed = math.min(smoothness_speeds[smoothness], result.backward_speed) end -- parse the remaining tags local name = way:get_value_by_key("name") local ref = way:get_value_by_key("ref") local junction = way:get_value_by_key("junction") -- local barrier = way:get_value_by_key("barrier", "") -- local cycleway = way:get_value_by_key("cycleway", "") local service = way:get_value_by_key("service") -- Set the name that will be used for instructions local has_ref = ref and "" ~= ref local has_name = name and "" ~= name if has_name and has_ref then result.name = name .. " (" .. ref .. ")" elseif has_ref then result.name = ref elseif has_name then result.name = name -- else -- result.name = highway -- if no name exists, use way type end if junction and "roundabout" == junction then result.roundabout = true end -- Set access restriction flag if access is allowed under certain restrictions only -- if access ~= "" and access_tag_restricted[access] then -- result.is_access_restricted = true -- end -- Set access restriction flag if service is allowed under certain restrictions only -- if service and service ~= "" and service_tag_restricted[service] then -- result.is_access_restricted = true -- end -- Set direction according to tags on way if obey_oneway then if oneway == "-1" then result.forward_mode = mode.inaccessible elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" or (highway == "motorway_link" and oneway ~="no") or (highway == "motorway" and oneway ~= "no") then result.backward_mode = mode.inaccessible end end -- Override speed settings if explicit forward/backward maxspeeds are given local maxspeed_forward = parse_maxspeed(way:get_value_by_key("maxspeed:forward")) local maxspeed_backward = parse_maxspeed(way:get_value_by_key("maxspeed:backward")) if maxspeed_forward and maxspeed_forward > 0 then if mode.inaccessible ~= result.forward_mode and mode.inaccessible ~= result.backward_mode then result.backward_speed = result.forward_speed end result.forward_speed = maxspeed_forward end if maxspeed_backward and maxspeed_backward > 0 then result.backward_speed = maxspeed_backward end -- Override speed settings if advisory forward/backward maxspeeds are given local advisory_speed = parse_maxspeed(way:get_value_by_key("maxspeed:advisory")) local advisory_forward = parse_maxspeed(way:get_value_by_key("maxspeed:advisory:forward")) local advisory_backward = parse_maxspeed(way:get_value_by_key("maxspeed:advisory:backward")) -- apply bi-directional advisory speed first if advisory_speed and advisory_speed > 0 then if mode.inaccessible ~= result.forward_mode then result.forward_speed = advisory_speed end if mode.inaccessible ~= result.backward_mode then result.backward_speed = advisory_speed end end if advisory_forward and advisory_forward > 0 then if mode.inaccessible ~= result.forward_mode and mode.inaccessible ~= result.backward_mode then result.backward_speed = result.forward_speed end result.forward_speed = advisory_forward end if advisory_backward and advisory_backward > 0 then result.backward_speed = advisory_backward end local width = math.huge local lanes = math.huge if result.forward_speed > 0 or result.backward_speed > 0 then local width_string = way:get_value_by_key("width") if width_string and tonumber(width_string:match("%d*")) then width = tonumber(width_string:match("%d*")) end local lanes_string = way:get_value_by_key("lanes") if lanes_string and tonumber(lanes_string:match("%d*")) then lanes = tonumber(lanes_string:match("%d*")) end end local is_bidirectional = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible -- scale speeds to get better avg driving times if result.forward_speed > 0 then local scaled_speed = result.forward_speed*speed_reduction + 11 local penalized_speed = math.huge if width <= 3 or (lanes <= 1 and is_bidirectional) then penalized_speed = result.forward_speed / 2 end result.forward_speed = math.min(penalized_speed, scaled_speed) end if result.backward_speed > 0 then local scaled_speed = result.backward_speed*speed_reduction + 11 local penalized_speed = math.huge if width <= 3 or (lanes <= 1 and is_bidirectional) then penalized_speed = result.backward_speed / 2 end result.backward_speed = math.min(penalized_speed, scaled_speed) end -- only allow this road as start point if it not a ferry result.is_startpoint = result.forward_mode == mode.driving or result.backward_mode == mode.driving end function turn_function (angle) ---- compute turn penalty as angle^2, with a left/right bias k = turn_penalty/(90.0*90.0) if angle>=0 then return angle*angle*k/turn_bias else return angle*angle*k*turn_bias end end