diff --git a/profiles/car.lua b/profiles/car.lua index 738c523b8..6b2e46353 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -235,40 +235,20 @@ function node_function (node, result) end end +-- abort early if this way is obviouslt not routable function should_abort_early(data) return TagCache.get(data,'highway') == nil and TagCache.get(data,'route') == nil and TagCache.get(data,'bridge') == nil end -function way_function(way, result) - local data = { - way = way, - cache = {}, - } - - local route = TagCache.get(data,"route") - local bridge = TagCache.get(data,"bridge") - - if should_abort_early(data) then - return - end - - -- default to driving mode, may get overwritten below - result.forward_mode = mode.driving - result.backward_mode = mode.driving - - -- we dont route over areas - local area = TagCache.get(data,"area") - if ignore_areas and area and "yes" == area then - return - end - +-- handle high occupancy vehicle tags +function handle_hov(data) -- respect user-preference for HOV-only ways if ignore_hov_ways then local hov = TagCache.get(data,"hov") if hov and "designated" == hov then - return + return false end -- also respect user-preference for HOV-only ways when all lanes are HOV-designated @@ -304,26 +284,53 @@ function way_function(way, result) if hov_all_designated or hov_all_designated_forward then if reverse then - result.backward_mode = mode.inaccessible + data.result.backward_mode = mode.inaccessible else - result.forward_mode = mode.inaccessible + data.result.forward_mode = mode.inaccessible end end if hov_all_designated_backward then if reverse then - result.forward_mode = mode.inaccessible + data.result.forward_mode = mode.inaccessible else - result.backward_mode = mode.inaccessible + data.result.backward_mode = mode.inaccessible end end - end -- hov handling + end +end +-- handle squares and other areas +function handle_area(data) + -- we dont route over areas + local area = TagCache.get(data,"area") + if ignore_areas and area and "yes" == area then + return false + end +end + +-- handle toll roads +function handle_toll(data) -- respect user-preference for toll=yes ways local toll = TagCache.get(data,"toll") if ignore_toll_ways and toll and "yes" == toll then - return + return false + end +end + +-- handle various that can block access +function handle_blocking(data) + if handle_area(data) == false then + return false + end + + if handle_hov(data) == false then + return false + end + + if handle_toll(data) == false then + return false end -- Reversible oneways change direction with low frequency (think twice a day): @@ -331,246 +338,218 @@ function way_function(way, result) -- Note: alternating (high frequency) oneways are handled below with penalty. local oneway = TagCache.get(data,"oneway") if oneway and "reversible" == oneway then - return + return false end local impassable = TagCache.get(data,"impassable") if impassable and "yes" == impassable then - return + return false end local status = TagCache.get(data,"status") if status and "impassable" == status then - return + return false end +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 +-- set default mode +function handle_default_mode(data) + data.result.forward_mode = mode.driving + data.result.backward_mode = mode.driving +end + +-- check accessibility by traversing our acces tag hierarchy +function handle_access(data) + data.access = find_access_tag(data.way, access_tags_hierarchy) + if access_tag_blacklist[data.access] then + return false end +end - -- handling ferries and piers +-- handling ferries and piers +function handle_ferries(data) + local route = TagCache.get(data,"route") local route_speed = speed_profile[route] if (route_speed and route_speed > 0) then - TagCache.set(data,"highway",route) - local duration = TagCache.get(data,"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 + TagCache.set(data,"highway",route) + local duration = TagCache.get(data,"duration") + if duration and durationIsValid(duration) then + data.result.duration = max( parseDuration(duration), 1 ) + end + data.result.forward_mode = mode.ferry + data.result.backward_mode = mode.ferry + data.result.forward_speed = route_speed + data.result.backward_speed = route_speed end +end - -- handling movable bridges +-- handling movable bridges +function handle_movables(data) + local bridge = TagCache.get(data,"bridge") local bridge_speed = speed_profile[bridge] local capacity_car = TagCache.get(data,"capacity:car") if (bridge_speed and bridge_speed > 0) and (capacity_car ~= 0) then TagCache.set(data,"highway",bridge) local duration = TagCache.get(data,"duration") if duration and durationIsValid(duration) then - result.duration = max( parseDuration(duration), 1 ) + data.result.duration = max( parseDuration(duration), 1 ) end - result.forward_speed = bridge_speed - result.backward_speed = bridge_speed + data.result.forward_speed = bridge_speed + data.result.backward_speed = bridge_speed end +end - -- leave early if this way is not accessible - if "" == TagCache.get(data,"highway") then - return - end - - if result.forward_speed == -1 then +-- handle speed (excluding maxspeed) +function handle_speed(data) + if data.result.forward_speed == -1 then local highway_speed = speed_profile[TagCache.get(data,"highway")] local max_speed = parse_maxspeed( TagCache.get(data,"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 + data.result.forward_speed = max_speed + data.result.backward_speed = max_speed -- max_speed = math.huge else - result.forward_speed = highway_speed - result.backward_speed = highway_speed + data.result.forward_speed = highway_speed + data.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"] + if access_tag_whitelist[data.access] then + data.result.forward_speed = speed_profile["default"] + data.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) + data.result.forward_speed = min(data.result.forward_speed, max_speed) + data.result.backward_speed = min(data.result.backward_speed, max_speed) end - if -1 == result.forward_speed and -1 == result.backward_speed then - return + if -1 == data.result.forward_speed and -1 == data.result.backward_speed then + return false + end + + if handle_side_roads(data) == false then + return false end - -- reduce speed on special side roads + if handle_surface(data) == false then + return false + end +end + +-- reduce speed on special side roads +function handle_side_roads(data) local sideway = TagCache.get(data,"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 + data.result.forward_speed = data.result.forward_speed * side_road_speed_multiplier + data.result.backward_speed = data.result.backward_speed * side_road_speed_multiplier end +end - -- reduce speed on bad surfaces +-- reduce speed on bad surfaces +function handle_surface(data) local surface = TagCache.get(data,"surface") local tracktype = TagCache.get(data,"tracktype") local smoothness = TagCache.get(data,"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) + data.result.forward_speed = math.min(surface_speeds[surface], data.result.forward_speed) + data.result.backward_speed = math.min(surface_speeds[surface], data.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) + data.result.forward_speed = math.min(tracktype_speeds[tracktype], data.result.forward_speed) + data.result.backward_speed = math.min(tracktype_speeds[tracktype], data.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) + data.result.forward_speed = math.min(smoothness_speeds[smoothness], data.result.forward_speed) + data.result.backward_speed = math.min(smoothness_speeds[smoothness], data.result.backward_speed) end +end - -- set the road classification based on guidance globals configuration - set_classification(TagCache.get(data,"highway"),result,way) - +-- handles name, including ref and pronunciation +function handle_names(data) -- parse the remaining tags local name = TagCache.get(data,"name") local pronunciation = TagCache.get(data,"name:pronunciation") local ref = TagCache.get(data,"ref") - local junction = TagCache.get(data,"junction") - -- local barrier = TagCache.get(data,"barrier", "") - -- local cycleway = TagCache.get(data,"cycleway", "") - local service = TagCache.get(data,"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 then - result.name = name + if name then + data.result.name = name end - if has_ref then - result.ref = canonicalizeStringList(ref, ";") + if ref then + data.result.ref = canonicalizeStringList(ref, ";") end - if has_pronunciation then - result.pronunciation = pronunciation + if pronunciation then + data.result.pronunciation = pronunciation end +end +-- handle turn lanes +function handle_turn_lanes(data) 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; + turn_lanes, turn_lanes_forward, turn_lanes_backward = get_turn_lanes(data.way) + if turn_lanes and turn_lanes ~= "" then + data.result.turn_lanes_forward = turn_lanes; + data.result.turn_lanes_backward = turn_lanes; else if turn_lanes_forward and turn_lanes_forward ~= "" then - result.turn_lanes_forward = turn_lanes_forward; + data.result.turn_lanes_forward = turn_lanes_forward; end if turn_lanes_backward and turn_lanes_backward ~= "" then - result.turn_lanes_backward = turn_lanes_backward; + data.result.turn_lanes_backward = turn_lanes_backward; end end +end - - if junction and "roundabout" == junction then - result.roundabout = true +-- junctions +function handle_junctions(data) + if TagCache.get(data,"junction") == "roundabout" then + data.result.roundabout = true end +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 +-- Set access restriction flag if access is allowed under certain restrictions only +function handle_restricted(data) + if data.access ~= "" and access_tag_restricted[data.access] then + data.result.is_access_restricted = true end +end - if service and service ~= "" then +-- service roads +function handle_service(data) + local service = TagCache.get(data,"service") + if service then -- Set access restriction flag if service is allowed under certain restrictions only if service_tag_restricted[service] then - result.is_access_restricted = true + data.result.is_access_restricted = true end -- Set don't allow access to certain service roads if service_tag_forbidden[service] then - result.forward_mode = mode.inaccessible - result.backward_mode = mode.inaccessible - return + data.result.forward_mode = mode.inaccessible + data.result.backward_mode = mode.inaccessible + return false end end +end - -- Set direction according to tags on way - if obey_oneway then - if oneway == "-1" then - result.forward_mode = mode.inaccessible - - local is_forward = false - local destination = get_destination(way, is_forward) - result.destinations = canonicalizeStringList(destination, ",") - elseif oneway == "yes" or - oneway == "1" or - oneway == "true" or - junction == "roundabout" or - (TagCache.get(data,"highway") == "motorway" and oneway ~= "no") then - - result.backward_mode = mode.inaccessible - - local is_forward = true - local destination = get_destination(way, is_forward) - result.destinations = canonicalizeStringList(destination, ",") - end - end - - -- Override speed settings if explicit forward/backward maxspeeds are given - local maxspeed_forward = parse_maxspeed(TagCache.get(data,"maxspeed:forward")) - local maxspeed_backward = parse_maxspeed(TagCache.get(data,"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(TagCache.get(data,"maxspeed:advisory")) - local advisory_forward = parse_maxspeed(TagCache.get(data,"maxspeed:advisory:forward")) - local advisory_backward = parse_maxspeed(TagCache.get(data,"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 - +-- scale speeds to get better average driving times +function handle_speed_scaling(data) local width = math.huge local lanes = math.huge - if result.forward_speed > 0 or result.backward_speed > 0 then + if data.result.forward_speed > 0 or data.result.backward_speed > 0 then local width_string = TagCache.get(data,"width") if width_string and tonumber(width_string:match("%d*")) then width = tonumber(width_string:match("%d*")) @@ -582,45 +561,166 @@ function way_function(way, result) end end - local is_bidirectional = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible + local is_bidirectional = data.result.forward_mode ~= mode.inaccessible and + data.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 + local service = TagCache.get(data,"service") + if data.result.forward_speed > 0 then + local scaled_speed = data.result.forward_speed * speed_reduction local penalized_speed = math.huge if service and service ~= "" and service_speeds[service] then penalized_speed = service_speeds[service] elseif width <= 3 or (lanes <= 1 and is_bidirectional) then - penalized_speed = result.forward_speed / 2 + penalized_speed = data.result.forward_speed / 2 end - result.forward_speed = math.min(penalized_speed, scaled_speed) + data.result.forward_speed = math.min(penalized_speed, scaled_speed) end - if result.backward_speed > 0 then - local scaled_speed = result.backward_speed * speed_reduction + if data.result.backward_speed > 0 then + local scaled_speed = data.result.backward_speed * speed_reduction local penalized_speed = math.huge if service and service ~= "" and service_speeds[service]then penalized_speed = service_speeds[service] elseif width <= 3 or (lanes <= 1 and is_bidirectional) then - penalized_speed = result.backward_speed / 2 + penalized_speed = data.result.backward_speed / 2 end - result.backward_speed = math.min(penalized_speed, scaled_speed) + data.result.backward_speed = math.min(penalized_speed, scaled_speed) + end +end + +-- oneways +function handle_oneway(data) + local oneway = TagCache.get(data,"oneway") + if obey_oneway then + if oneway == "-1" then + data.result.forward_mode = mode.inaccessible + + local is_forward = false + local destination = get_destination(data.way, is_forward) + data.result.destinations = canonicalizeStringList(destination, ",") + elseif oneway == "yes" or + oneway == "1" or + oneway == "true" or + TagCache.get(data,"junction") == "roundabout" or + (TagCache.get(data,"highway") == "motorway" and oneway ~= "no") then + + data.result.backward_mode = mode.inaccessible + + local is_forward = true + local destination = get_destination(data.way, is_forward) + data.result.destinations = canonicalizeStringList(destination, ",") + end + end +end + +-- maxspeed and advisory maxspeed +function handle_maxspeed(data) + -- Override speed settings if explicit forward/backward maxspeeds are given + local maxspeed_forward = parse_maxspeed(TagCache.get(data,"maxspeed:forward")) + local maxspeed_backward = parse_maxspeed(TagCache.get(data,"maxspeed:backward")) + if maxspeed_forward and maxspeed_forward > 0 then + if mode.inaccessible ~= data.result.forward_mode and + mode.inaccessible ~= data.result.backward_mode then + data.result.backward_speed = data.result.forward_speed + end + data.result.forward_speed = maxspeed_forward + end + if maxspeed_backward and maxspeed_backward > 0 then + data.result.backward_speed = maxspeed_backward end - -- Handle high frequency reversible oneways (think traffic signal controlled, changing direction every 15 minutes). - -- Scaling speed to take average waiting time into account plus some more for start / stop. - if oneway and "alternating" == oneway then + -- Override speed settings if advisory forward/backward maxspeeds are given + local advisory_speed = parse_maxspeed(TagCache.get(data,"maxspeed:advisory")) + local advisory_forward = parse_maxspeed(TagCache.get(data,"maxspeed:advisory:forward")) + local advisory_backward = parse_maxspeed(TagCache.get(data,"maxspeed:advisory:backward")) + -- apply bi-directional advisory speed first + if advisory_speed and advisory_speed > 0 then + if mode.inaccessible ~= data.result.forward_mode then + data.result.forward_speed = advisory_speed + end + if mode.inaccessible ~= data.result.backward_mode then + data.result.backward_speed = advisory_speed + end + end + if advisory_forward and advisory_forward > 0 then + if mode.inaccessible ~= data.result.forward_mode and mode.inaccessible ~= data.result.backward_mode then + data.result.backward_speed = data.result.forward_speed + end + data.result.forward_speed = advisory_forward + end + if advisory_backward and advisory_backward > 0 then + data.result.backward_speed = advisory_backward + end +end + +-- Handle high frequency reversible oneways (think traffic signal controlled, changing direction every 15 minutes). +-- Scaling speed to take average waiting time into account plus some more for start / stop. +function handle_alternating(data) + if "alternating" == TagCache.get(data,'oneway') then local scaling_factor = 0.4 - if result.forward_speed ~= math.huge then - result.forward_speed = result.forward_speed * scaling_factor + if data.result.forward_speed ~= math.huge then + data.result.forward_speed = data.result.forward_speed * scaling_factor end - if result.backward_speed ~= math.huge then - result.backward_speed = result.backward_speed * scaling_factor + if data.result.backward_speed ~= math.huge then + data.result.backward_speed = data.result.backward_speed * scaling_factor end end +end +-- determine if this way can be used as a start/end point for routing +function handle_startpoint(data) -- 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 + data.result.is_startpoint = data.result.forward_mode == mode.driving or + data.result.backward_mode == mode.driving +end + +-- main entry point for processsing a way +function way_function(way, result) + + -- define a table that we can pass around to helper functions + -- so they have access to the input/output objects. + + -- we also use a table as a local cache of tags, to avoid calling + -- into C++ more than once for each tag, without having to + -- pass lists of already fetches tags around. + + local data = { + way = way, + result = result, + cache = {}, + } + + -- perform each procesing step sequentially. + -- most steps can abort processing, meaning the way + -- is not routable + + if should_abort_early(data) then return end + handle_default_mode(data) + if handle_blocking(data) == false then return end + if handle_access(data) == false then return end + if handle_ferries(data) == false then return end + if handle_movables(data) == false then return end + + -- leave early if this way is not accessible + if "" == TagCache.get(data,"highway") then return end + + if handle_speed(data) == false then return end + + -- set the road classification based on guidance globals configuration + set_classification(TagCache.get(data,"highway"),result,way) + + handle_names(data) + handle_turn_lanes(data) + handle_junctions(data) + handle_restricted(data) + + if handle_service(data) == false then return end + if handle_oneway(data) == false then return end + if handle_maxspeed(data) == false then return end + + handle_speed_scaling(data) + handle_alternating(data) + handle_startpoint(data) end function turn_function (angle) diff --git a/profiles/debug.lua b/profiles/debug.lua index 6145b912f..b8e52646d 100644 --- a/profiles/debug.lua +++ b/profiles/debug.lua @@ -1,9 +1,6 @@ --- Enable calling our lua profile code directly, which makes it easier to debug. --- We simulate the normal C++ environment by defining some globals and functions. --- FIXME: --- There are a few C++ tag helper methods that LUA code can call. --- Debugging LUA code thay uses these will not work unless we reimplement the --- methods in LUA. +-- Enable calling our lua profile code directly from the lua command line, +-- which makes it easier to debug. +-- We simulate the normal C++ environment by defining the required globals and functions. -- Usage: -- > cd profiles @@ -13,7 +10,6 @@ -- for more convenient printing of tables local pprint = require('lib/pprint') - -- globals that are normally set from C++ -- profiles code modifies this table @@ -47,13 +43,43 @@ mode = { -- input tags, normally extracted from OSM data local way = { - highway = 'primary' + highway = 'primary', + name = 'Main Street', + --width = '3', + --maxspeed = '30', + --['maxspeed:advisory'] = '25', + --oneway = '-1', + --service = 'alley', + --['oneway:bicycle'] = 'yes', + --junction = 'roundabout', + --['name:pronunciation'] = 'fuerloong', + --route = 'ferry', + --duration = '00:01:00', + --hov = 'designated', + --access = 'no' } --- function normally provided via C++ +-- tag function normally provided via C++ function way:get_value_by_key(k) return self[k] end +-- Mock C++ helper functions which are called from LUA. +-- FIXME +-- Debugging LUA code that uses these will not work correctly +-- unless we reimplement themethods in LUA. + +function durationIsValid(str) + return true +end + +function parseDuration(str) + return 1 +end + +function canonicalizeStringList(str) + return str +end + -- start state of result table, normally set form C++ local result = { road_classification = {},