Currently we don't route over the Golden Gate bridge by default. This sets the value to false by default. To test the behavior for tolls when ignored, we would need issue #2781 implemented.
534 lines
16 KiB
Lua
534 lines
16 KiB
Lua
-- Car profile
|
|
local find_access_tag = require("lib/access").find_access_tag
|
|
local get_destination = require("lib/destination").get_destination
|
|
local set_classification = require("lib/guidance").set_classification
|
|
local get_turn_lanes = require("lib/guidance").get_turn_lanes
|
|
|
|
-- 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 = {
|
|
["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
|
|
properties.left_hand_driving = false
|
|
|
|
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 = properties.left_hand_driving and 1/1.2 or 1.2
|
|
|
|
local obey_oneway = true
|
|
local ignore_areas = true
|
|
local ignore_hov_ways = true
|
|
local ignore_toll_ways = false
|
|
|
|
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
|
|
|
|
-- respect user-preference for HOV-only ways
|
|
local hov = way:get_value_by_key("hov")
|
|
if ignore_hov_ways and hov and "designated" == hov then
|
|
return
|
|
end
|
|
|
|
-- respect user-preference for toll=yes ways
|
|
local toll = way:get_value_by_key("toll")
|
|
if ignore_toll_ways and toll and "yes" == toll 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 if 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
|
|
|
|
-- set the road classification based on guidance globals configuration
|
|
set_classification(highway,result)
|
|
|
|
-- parse the remaining tags
|
|
local name = way:get_value_by_key("name")
|
|
local pronunciation = way:get_value_by_key("name:pronunciation")
|
|
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
|
|
local has_pronunciation = pronunciation and "" ~= pronunciation
|
|
|
|
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
|
|
end
|
|
|
|
if has_pronunciation then
|
|
result.pronunciation = pronunciation
|
|
end
|
|
|
|
local turn_lanes = ""
|
|
local turn_lanes_forward = ""
|
|
local turn_lanes_backward = ""
|
|
|
|
turn_lanes, turn_lanes_forward, turn_lanes_backward = get_turn_lanes(way)
|
|
if turn_lanes and turn_lanes ~= "" then
|
|
result.turn_lanes_forward = turn_lanes;
|
|
result.turn_lanes_backward = turn_lanes;
|
|
else
|
|
if turn_lanes_forward and turn_lanes_forward ~= "" then
|
|
result.turn_lanes_forward = turn_lanes_forward;
|
|
end
|
|
|
|
if turn_lanes_backward and turn_lanes_backward ~= "" then
|
|
result.turn_lanes_backward = turn_lanes_backward;
|
|
end
|
|
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" and oneway ~= "no") then
|
|
result.backward_mode = mode.inaccessible
|
|
|
|
-- If we're on a oneway and there is no ref tag, re-use destination tag as ref.
|
|
local destination = get_destination(way)
|
|
local has_destination = destination and "" ~= destination
|
|
|
|
if has_destination and has_name and not has_ref then
|
|
result.name = name .. " (" .. destination .. ")"
|
|
end
|
|
|
|
result.destinations = destination
|
|
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
|