profiles: Directional helper, some car refactoring

This commit is contained in:
Emil Tin 2016-11-18 20:49:51 +01:00 committed by Daniel J. H
parent 0fbd18b0dd
commit 532cbfce13
6 changed files with 334 additions and 274 deletions

View File

@ -149,6 +149,7 @@ Feature: Car - Restricted access
| runway | | | | yes | |
| primary | | | | no | x |
@hov
Scenario: Car - only designated HOV ways are ignored by default
Then routability should be
| highway | hov | bothw |
@ -156,6 +157,7 @@ Feature: Car - Restricted access
| primary | yes | x |
| primary | no | x |
@hov
Scenario: Car - a way with all lanes HOV-designated is inaccessible by default (similar to hov=designated)
Then routability should be
| highway | hov:lanes:forward | hov:lanes:backward | hov:lanes | oneway | forw | backw |
@ -166,8 +168,8 @@ Feature: Car - Restricted access
| primary | designated\|no | designated\|no | | | x | x |
| primary | yes\|no | yes\|no | | | x | x |
| primary | | | | | x | x |
| primary | designated | | | -1 | | |
| primary | | designated | | -1 | | x |
| primary | designated | | | -1 | | x |
| primary | | designated | | -1 | | |
| primary | | | designated | yes | | |
| primary | | | designated | -1 | | |
| primary | | | designated\| | yes | x | |

View File

@ -123,3 +123,14 @@ OSRM will use 4/5 of the projected free-flow speed.
| primary | 30 | 1 | -1 | | 23 km/h |
| primary | 30 | 1 | | 15 km/h | 15 km/h |
| primary | 30 | 2 | | 23 km/h | 23 km/h |
Scenario: Car - Forwward/backward maxspeed on reverse oneways
Then routability should be
| highway | maxspeed | maxspeed:forward | maxspeed:backward | oneway | forw | backw |
| primary | | | | -1 | | 52 km/h |
| primary | 30 | | | -1 | | 23 km/h |
| primary | | 30 | | -1 | | 52 km/h |
| primary | | | 30 | -1 | | 23 km/h |
| primary | 20 | 30 | | -1 | | 16 km/h |
| primary | 20 | | 30 | -1 | | 23 km/h |

View File

@ -5,6 +5,7 @@ local set_classification = require("lib/guidance").set_classification
local get_turn_lanes = require("lib/guidance").get_turn_lanes
local Set = require('lib/set')
local Sequence = require('lib/sequence')
local Directional = require('lib/directional')
-- Begin of globals
barrier_whitelist = Set {
@ -287,72 +288,54 @@ end
-- abort early if this way is obviouslt not routable
function initial_routability_check(way,result,data)
data.speed_type = way:get_value_by_key('highway')
data.highway = way:get_value_by_key('highway')
return data.speed_type ~= nil or
return data.highway ~= nil or
way:get_value_by_key('route') ~= nil or
way:get_value_by_key('bridge') ~= nil
end
-- all lanes restricted to hov vehicles?
local function has_all_designated_hov_lanes(lanes)
if not lanes then
return false
end
-- This gmatch call effectively splits the string on | chars.
-- we append an extra | to the end so that we can match the final part
for lane in (lanes .. '|'):gmatch("([^|]*)|") do
if lane and lane ~= "designated" then
return false
end
end
return true
end
-- handle high occupancy vehicle tags
function handle_hov(way,result)
-- respect user-preference for HOV-only ways
if ignore_hov_ways then
function handle_hov(way,result,data)
-- respect user-preference for HOV
if not ignore_hov_ways then
return
end
-- check if way is hov only
local hov = way:get_value_by_key("hov")
if "designated" == hov then
return false
end
-- also respect user-preference for HOV-only ways when all lanes are HOV-designated
local function has_all_designated_hov_lanes(lanes)
local all = true
-- This gmatch call effectively splits the string on | chars.
-- we append an extra | to the end so that we can match the final part
for lane in (lanes .. '|'):gmatch("([^|]*)|") do
if lane and lane ~= "designated" then
all = false
break
end
end
return all
end
-- check if all lanes are hov only
local hov_lanes_forward, hov_lanes_backward = Directional.get_values_by_key(way,data,'hov:lanes')
local inaccessible_forward = has_all_designated_hov_lanes(hov_lanes_forward)
local inaccessible_backward = has_all_designated_hov_lanes(hov_lanes_backward)
local hov_lanes = way:get_value_by_key("hov:lanes")
local hov_lanes_forward = way:get_value_by_key("hov:lanes:forward")
local hov_lanes_backward = way:get_value_by_key("hov:lanes:backward")
local hov_all_designated = hov_lanes and
has_all_designated_hov_lanes(hov_lanes)
local hov_all_designated_forward = hov_lanes_forward and
has_all_designated_hov_lanes(hov_lanes_forward)
local hov_all_designated_backward = hov_lanes_backward and
has_all_designated_hov_lanes(hov_lanes_backward)
-- forward/backward lane depend on a way's direction
local oneway = way:get_value_by_key("oneway")
local reverse = oneway == "-1"
if hov_all_designated or hov_all_designated_forward then
if reverse then
result.backward_mode = mode.inaccessible
else
if inaccessible_forward then
result.forward_mode = mode.inaccessible
end
end
if hov_all_designated_backward then
if reverse then
result.forward_mode = mode.inaccessible
else
if inaccessible_backward then
result.backward_mode = mode.inaccessible
end
end
end
end
-- handle various that can block access
function is_way_blocked(way,result)
-- we dont route over areas
@ -440,18 +423,11 @@ end
-- handle speed (excluding maxspeed)
function handle_speed(way,result,data)
if result.forward_speed == -1 then
local highway_speed = speed_profile[way:get_value_by_key("highway")]
local max_speed = parse_maxspeed( way:get_value_by_key("maxspeed") )
local highway_speed = speed_profile[data.highway]
-- 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[data.access] then
@ -459,11 +435,6 @@ function handle_speed(way,result,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)
end
if -1 == result.forward_speed and -1 == result.backward_speed then
@ -472,7 +443,7 @@ function handle_speed(way,result,data)
if handle_side_roads(way,result) == false then return false end
if handle_surface(way,result) == false then return false end
if handle_maxspeed(way,result) == false then return false end
if handle_maxspeed(way,data,result) == false then return false end
if handle_speed_scaling(way,result) == false then return false end
if handle_alternating_speed(way,result) == false then return false end
end
@ -529,23 +500,15 @@ function handle_names(way,result)
end
-- handle turn lanes
function handle_turn_lanes(way,result)
local turn_lanes = ''
local turn_lanes_forward = ''
local turn_lanes_backward = ''
function handle_turn_lanes(way,result,data)
local forward, backward = get_turn_lanes(way,data)
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;
if forward then
result.turn_lanes_forward = forward
end
if turn_lanes_backward and turn_lanes_backward ~= '' then
result.turn_lanes_backward = turn_lanes_backward;
end
if backward then
result.turn_lanes_backward = backward
end
end
@ -633,69 +596,55 @@ function handle_speed_scaling(way,result)
end
end
-- oneways
function handle_oneway(way,result)
-- handle oneways tags
function handle_oneway(way,result,data)
local oneway = way:get_value_by_key("oneway")
data.oneway = oneway
if obey_oneway then
if oneway == "-1" then
data.is_reverse_oneway = true
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
way:get_value_by_key("junction") == "roundabout" or
way:get_value_by_key("junction") == "circular" or
(way:get_value_by_key("highway") == "motorway" and oneway ~= "no") then
oneway == "true" then
data.is_forward_oneway = true
result.backward_mode = mode.inaccessible
else
local junction = way:get_value_by_key("junction")
if data.highway == "motorway" or
junction == "roundabout" or
junction == "circular" then
if oneway ~= "no" then
-- implied oneway
data.is_forward_oneway = true
result.backward_mode = mode.inaccessible
local is_forward = true
local destination = get_destination(way, is_forward)
result.destinations = canonicalizeStringList(destination, ",")
end
end
end
end
end
-- handle destination tags
function handle_destinations(way,result,data)
if data.is_forward_oneway or data.is_reverse_oneway then
local destination = get_destination(way, data.is_forward_oneway)
result.destinations = canonicalizeStringList(destination, ",")
end
end
-- maxspeed and advisory maxspeed
function handle_maxspeed(way,result)
-- 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
function handle_maxspeed(way,data,result)
local keys = Sequence { 'maxspeed:advisory', 'maxspeed' }
local forward, backward = Directional.get_values_by_set(way,data,keys)
forward = parse_maxspeed(forward)
backward = parse_maxspeed(backward)
if forward and forward > 0 then
result.forward_speed = forward
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
if backward and backward > 0 then
result.backward_speed = backward
end
end
@ -713,6 +662,7 @@ function handle_alternating_speed(way,result)
end
end
-- determine if this way can be used as a start/end point for routing
function handle_startpoint(way,result)
-- only allow this road as start point if it not a ferry
@ -721,8 +671,8 @@ function handle_startpoint(way,result)
end
-- set the road classification based on guidance globals configuration
function handle_classification(way,result)
set_classification(way:get_value_by_key("highway"),result,way)
function handle_classification(way,result,data)
set_classification(data.highway,result,way)
end
-- main entry point for processsing a way
@ -754,8 +704,11 @@ function way_function(way, result)
-- access tags, e.g: motorcar, motor_vehicle, vehicle
if handle_access(way,result,data) == false then return end
-- check high occupancy vehicle restrictions
if handle_hov(way,result) == false then return end
-- check whether forward/backward directons are routable
if handle_oneway(way,result,data) == false then return end
-- check whether forward/backward directons are routable
if handle_destinations(way,result,data) == false then return end
-- check whether we're using a special transport mode
if handle_ferries(way,result) == false then return end
@ -764,24 +717,21 @@ function way_function(way, result)
-- handle service road restrictions
if handle_service(way,result) == false then return end
-- check whether forward/backward directons are routable
if handle_oneway(way,result) == false then return end
-- check high occupancy vehicle restrictions
if handle_hov(way,result,data) == false then return end
-- compute speed taking into account way type, maxspeed tags, etc.
if handle_speed(way,result,data) == false then return end
-- handle turn lanes and road classification, used for guidance
if handle_turn_lanes(way,result) == false then return end
if handle_classification(way,result) == false then return end
if handle_turn_lanes(way,result,data) == false then return end
if handle_classification(way,result,data) == false then return end
-- handle various other flags
if handle_roundabouts(way,result) == false then return end
if handle_startpoint(way,result) == false then return end
if handle_restricted(way,result,data) == false then return end
-- handle roundabout flags
if handle_roundabouts(way,result) == false then return end
-- check if this way can be routed through, but not used as
-- origin or destination
if handle_restricted(way,result,data) == false then return end

View File

@ -56,13 +56,32 @@ local way = {
--route = 'ferry',
--duration = '00:01:00',
--hov = 'designated',
["hov:lanes:forward"] = 'designated',
--access = 'no'
--destination = 'Berlin',
["destination:ref"] = 'Nuremberg',
--["destination:ref"] = 'Nuremberg',
--["destination:ref:forward"] = 'Hamburg;Dresden',
}
-- tag function normally provided via C++
function way:get_value_by_key(k)
if not self['_debug'] then
self['_debug'] = {
_counts = {}
}
end
if self['_debug']['_total'] then
self['_debug']['_total'] = self['_debug']['_total'] + 1
else
self['_debug']['_total'] = 1
end
if self['_debug']['_counts'][k] then
self['_debug']['_counts'][k] = self['_debug']['_counts'][k] + 1
else
self['_debug']['_counts'][k] = 1
end
return self[k]
end
@ -83,6 +102,27 @@ function canonicalizeStringList(str)
return str
end
-- helpers for sorting associative array
function get_keys_sorted_by_value(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return sortFunction(tbl[a], tbl[b])
end)
return keys
end
-- helper for printing sorted array
function print_sorted(sorted,associative)
for _, key in ipairs(sorted) do
print(associative[key], key)
end
end
-- start state of result table, normally set form C++
local result = {
road_classification = {},
@ -100,3 +140,9 @@ way_function(way,result)
pprint(way)
print("=>")
pprint(result)
print("\n")
print("Tag fetches:")
sorted_counts = get_keys_sorted_by_value(way._debug._counts, function(a, b) return a > b end)
print_sorted(sorted_counts, way._debug._counts)
print(way._debug._total, 'total')

View File

@ -0,0 +1,59 @@
local Directional = {}
-- return [forward,backward] values for a specific tag.
-- e.g. for maxspeed search forward:
-- maxspeed:forward
-- maxspeed
-- and backward:
-- maxspeed:backward
-- maxspeed
function Directional.get_values_by_key(way,data,key)
local forward = way:get_value_by_key(key .. ':forward')
local backward = way:get_value_by_key(key .. ':backward')
if forward and backward then
return forward, backward
end
local common = way:get_value_by_key(key)
return forward or common,
backward or common
end
-- return [forward,backward] values, searching a
-- prioritized sequence of tags
-- e.g. for the sequence [maxspeed,advisory] search forward:
-- maxspeed:forward
-- maxspeed
-- advisory:forward
-- advisory
-- and for backward:
-- maxspeed:backward
-- maxspeed
-- advisory:backward
-- advisory
function Directional.get_values_by_set(way,data,keys)
local forward, backward
for i,key in ipairs(keys) do
if not forward then
forward = way:get_value_by_key(key .. ':forward')
end
if not backward then
backward = way:get_value_by_key(key .. ':backward')
end
if not forward or not backward then
local common = way:get_value_by_key(key)
forward = forward or common
backward = backward or common
end
if forward and backward then
break
end
end
return forward, backward
end
return Directional

View File

@ -1,46 +1,64 @@
local Directional = require('lib/directional')
local Set = require('lib/set')
local Guidance = {}
-- Guidance: Default Mapping from roads to types/priorities
highway_classes = { ["motorway"] = road_priority_class.motorway,
["motorway_link"] = road_priority_class.link_road,
["trunk"] = road_priority_class.trunk,
["trunk_link"] = road_priority_class.link_road,
["primary"] = road_priority_class.primary,
["primary_link"] = road_priority_class.link_road,
["secondary"] = road_priority_class.secondary,
["secondary_link"] = road_priority_class.link_road,
["tertiary"] = road_priority_class.tertiary,
["tertiary_link"] = road_priority_class.link_road,
["unclassified"] = road_priority_class.side_residential,
["residential"] = road_priority_class.side_residential,
["service"] = road_priority_class.connectivity,
["living_street"] = road_priority_class.main_residential,
["track"] = road_priority_class.bike_path,
["path"] = road_priority_class.bike_path,
["footway"] = road_priority_class.foot_path,
["pedestrian"] = road_priority_class.foot_path,
["steps"] = road_priority_class.foot_path}
highway_classes = {
motorway = road_priority_class.motorway,
motorway_link = road_priority_class.link_road,
trunk = road_priority_class.trunk,
trunk_link = road_priority_class.link_road,
primary = road_priority_class.primary,
primary_link = road_priority_class.link_road,
secondary = road_priority_class.secondary,
secondary_link = road_priority_class.link_road,
tertiary = road_priority_class.tertiary,
tertiary_link = road_priority_class.link_road,
unclassified = road_priority_class.side_residential,
residential = road_priority_class.side_residential,
service = road_priority_class.connectivity,
living_street = road_priority_class.main_residential,
track = road_priority_class.bike_path,
path = road_priority_class.bike_path,
footway = road_priority_class.foot_path,
pedestrian = road_priority_class.foot_path,
steps = road_priority_class.foot_path
}
default_highway_class = road_priority_class.connectivity;
motorway_types = { ["motorway"] = true, ["motorway_link"] = true, ["trunk"] = true, ["trunk_link"] = true }
motorway_types = Set {
'motorway',
'motorway_link',
'trunk',
'trunk_link'
}
-- these road types are set with a car in mind. For bicycle/walk we probably need different ones
road_types = { ["motorway"] = true,
["motorway_link"] = true,
["trunk"] = true,
["trunk_link"] = true,
["primary"] = true,
["primary_link"] = true,
["secondary"] = true,
["secondary_link"] = true,
["tertiary"] = true,
["tertiary_link"] = true,
["unclassified"] = true,
["residential"] = true,
["living_street"] = true }
road_types = Set {
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'secondary_link',
'tertiary',
'tertiary_link',
'unclassified',
'residential',
'living_street'
}
link_types = { ["motorway_link"] = true, ["trunk_link"] = true, ["primary_link"] = true, ["secondary_link"] = true, ["tertiary_link"] = true }
link_types = Set {
'motorway_link',
'trunk_link',
'primary_link',
'secondary_link',
'tertiary_link'
}
function Guidance.set_classification (highway, result, input_way)
if motorway_types[highway] then
@ -89,66 +107,40 @@ function Guidance.set_classification (highway, result, input_way)
end
-- returns forward,backward psv lane count
local function get_psv_counts(way)
local psv = way:get_value_by_key("lanes:psv")
local psv_forward = way:get_value_by_key("lanes:psv:forward");
local psv_backward = way:get_value_by_key("lanes:psv:backward");
local fw = 0;
local bw = 0;
if psv then
fw = tonumber(psv)
if( fw == nil ) then
fw = 0
end
end
local function get_psv_counts(way,data)
local psv_forward, psv_backward = Directional.get_values_by_key(way,data,'lanes:psv')
if psv_forward then
fw = tonumber(psv_forward)
if( fw == nil ) then
fw = 0
end
psv_forward = tonumber(psv_forward)
end
if psv_backward then
bw = tonumber(psv_backward);
if( bw == nil ) then
bw = 0
psv_backward = tonumber(psv_backward)
end
end
return fw, bw
return psv_forward or 0,
psv_backward or 0
end
-- trims lane string with regard to supported lanes
local function process_lanes(turn_lane,vehicle_lane,first_count,second_count)
if turn_lane then
if vehicle_lane then
turn_lane = applyAccessTokens(turn_lane,vehicle_lane)
local function process_lanes(turn_lanes,vehicle_lanes,first_count,second_count)
if turn_lanes then
if vehicle_lanes then
return applyAccessTokens(turn_lanes,vehicle_lanes)
elseif first_count ~= 0 or second_count ~= 0 then
turn_lane = trimLaneString(turn_lane, first_count, second_count)
return trimLaneString(turn_lanes, first_count, second_count)
else
return turn_lanes
end
end
return turn_lane;
end
-- this is broken for left-sided driving. It needs to switch left and right in case of left-sided driving
function Guidance.get_turn_lanes(way)
local fw_psv = 0
local bw_psv = 0
fw_psv, bw_psv = get_psv_counts(way)
function Guidance.get_turn_lanes(way,data)
local psv_fw, psv_bw = get_psv_counts(way,data)
local turn_lanes_fw, turn_lanes_bw = Directional.get_values_by_key(way,data,'turn:lanes')
local vehicle_lanes_fw, vehicle_lanes_bw = Directional.get_values_by_key(way,data,'vehicle:lanes')
local turn_lanes = way:get_value_by_key("turn:lanes")
local turn_lanes_fw = way:get_value_by_key("turn:lanes:forward")
local turn_lanes_bw = way:get_value_by_key("turn:lanes:backward")
local vehicle_lanes = way:get_value_by_key("vehicle:lanes");
local vehicle_lanes_fw = way:get_value_by_key("vehicle:lanes:forward");
local vehicle_lanes_bw = way:get_value_by_key("vehicle:lanes:backward");
turn_lanes = process_lanes(turn_lanes,vehicle_lanes,bw_psv,fw_psv)
turn_lanes_fw = process_lanes(turn_lanes_fw,vehicle_lanes_fw,bw_psv,fw_psv)
--backwards turn lanes need to treat bw_psv as fw_psv and vice versa
turn_lanes_bw = process_lanes(turn_lanes_bw,vehicle_lanes_bw,fw_psv,bw_psv)
return turn_lanes, turn_lanes_fw, turn_lanes_bw
--note: backward lanes swap psv_bw and psv_fw
return process_lanes(turn_lanes_fw,vehicle_lanes_fw,psv_bw,psv_fw) or turn_lanes,
process_lanes(turn_lanes_bw,vehicle_lanes_bw,psv_fw,psv_bw) or turn_lanes
end
return Guidance