Allow specifing a weight for routing that is independent of duration
This commit is contained in:
committed by
Patrick Niklaus
parent
e463733138
commit
279f8aabfb
+44
-16
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
|
||||
-- Bicycle profile
|
||||
|
||||
@@ -92,14 +92,16 @@ surface_speeds = {
|
||||
}
|
||||
|
||||
-- these need to be global because they are accesed externaly
|
||||
properties.traffic_signal_penalty = 2
|
||||
properties.u_turn_penalty = 20
|
||||
properties.max_speed_for_map_matching = 110/3.6 -- kmph -> m/s
|
||||
properties.use_turn_restrictions = false
|
||||
properties.continue_straight_at_waypoint = false
|
||||
properties.weight_name = 'duration'
|
||||
--properties.weight_name = 'cyclability'
|
||||
|
||||
local obey_oneway = true
|
||||
local ignore_areas = true
|
||||
local traffic_light_penalty = 2
|
||||
local u_turn_penalty = 20
|
||||
local turn_penalty = 6
|
||||
local turn_bias = 1.4
|
||||
-- reduce the driving speed by 30% for unsafe roads
|
||||
@@ -272,10 +274,6 @@ function way_function (way, result)
|
||||
-- regular ways
|
||||
result.forward_speed = bicycle_speeds[highway]
|
||||
result.backward_speed = bicycle_speeds[highway]
|
||||
if safety_penalty < 1 and unsafe_highway_list[highway] then
|
||||
result.forward_speed = result.forward_speed * safety_penalty
|
||||
result.backward_speed = result.backward_speed * safety_penalty
|
||||
end
|
||||
elseif access and access_tag_whitelist[access] then
|
||||
-- unknown way, but valid access tag
|
||||
result.forward_speed = default_speed
|
||||
@@ -399,15 +397,45 @@ function way_function (way, result)
|
||||
|
||||
-- maxspeed
|
||||
limit( result, maxspeed, maxspeed_forward, maxspeed_backward )
|
||||
end
|
||||
|
||||
function turn_function (angle)
|
||||
-- compute turn penalty as angle^2, with a left/right bias
|
||||
-- multiplying by 10 converts to deci-seconds see issue #1318
|
||||
k = 10*turn_penalty/(90.0*90.0)
|
||||
if angle>=0 then
|
||||
return angle*angle*k/turn_bias
|
||||
else
|
||||
return angle*angle*k*turn_bias
|
||||
-- convert duration into cyclability
|
||||
local is_unsafe = safety_penalty < 1 and unsafe_highway_list[highway]
|
||||
if result.forward_speed > 0 then
|
||||
-- convert from km/h to m/s
|
||||
result.forward_rate = result.forward_speed / 3.6;
|
||||
if is_unsafe then
|
||||
result.forward_rate = result.forward_rate * safety_penalty
|
||||
end
|
||||
end
|
||||
if result.backward_speed > 0 then
|
||||
-- convert from km/h to m/s
|
||||
result.backward_rate = result.backward_speed / 3.6;
|
||||
if is_unsafe then
|
||||
result.backward_rate = result.backward_rate * safety_penalty
|
||||
end
|
||||
end
|
||||
if result.duration > 0 then
|
||||
result.weight = result.duration;
|
||||
if is_unsafe then
|
||||
result.weight = result.weight * (1+safety_penalty)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function turn_function(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 * turn_penalty / turn_bias
|
||||
else
|
||||
turn.duration = normalized_angle * normalized_angle * turn_penalty * turn_bias
|
||||
end
|
||||
|
||||
if turn.direction_modifier == direction_modifier.uturn then
|
||||
turn.duration = turn.duration + u_turn_penalty
|
||||
end
|
||||
|
||||
if turn.has_traffic_light then
|
||||
turn.duration = turn.duration + traffic_light_penalty
|
||||
end
|
||||
end
|
||||
|
||||
+37
-19
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
|
||||
-- Car profile
|
||||
local find_access_tag = require("lib/access").find_access_tag
|
||||
@@ -190,16 +190,19 @@ maxspeed_table = {
|
||||
}
|
||||
|
||||
-- set profile properties
|
||||
properties.u_turn_penalty = 20
|
||||
properties.traffic_signal_penalty = 2
|
||||
properties.max_speed_for_map_matching = 180/3.6 -- 180kmph -> m/s
|
||||
properties.use_turn_restrictions = true
|
||||
properties.continue_straight_at_waypoint = true
|
||||
properties.left_hand_driving = false
|
||||
-- this will use the duration and {forward/backward}_speed values as weight
|
||||
properties.weight_name = 'duration'
|
||||
--properties.weight_name = 'distance'
|
||||
|
||||
local side_road_speed_multiplier = 0.8
|
||||
|
||||
local turn_penalty = 7.5
|
||||
local traffic_light_penalty = 2
|
||||
local u_turn_penalty = 20
|
||||
|
||||
-- Note: this biases right-side driving. Should be
|
||||
-- inverted for left-driving countries.
|
||||
local turn_bias = properties.left_hand_driving and 1/1.075 or 1.075
|
||||
@@ -335,7 +338,7 @@ function is_way_blocked(way,result)
|
||||
if ignore_areas and "yes" == area then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- respect user-preference for toll=yes ways
|
||||
local toll = way:get_value_by_key("toll")
|
||||
if ignore_toll_ways and "yes" == toll then
|
||||
@@ -445,7 +448,7 @@ function handle_speed(way,result,data)
|
||||
if -1 == result.forward_speed and -1 == result.backward_speed then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
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,data,result) == false then return false end
|
||||
@@ -454,10 +457,9 @@ function handle_speed(way,result,data)
|
||||
end
|
||||
|
||||
-- reduce speed on special side roads
|
||||
function handle_side_roads(way,result)
|
||||
function handle_side_roads(way,result)
|
||||
local sideway = way:get_value_by_key("side_road")
|
||||
if "yes" == sideway or
|
||||
"rotary" == sideway then
|
||||
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
|
||||
@@ -562,7 +564,7 @@ function handle_speed_scaling(way,result)
|
||||
end
|
||||
end
|
||||
|
||||
local is_bidirectional = result.forward_mode ~= mode.inaccessible and
|
||||
local is_bidirectional = result.forward_mode ~= mode.inaccessible and
|
||||
result.backward_mode ~= mode.inaccessible
|
||||
|
||||
local service = way:get_value_by_key("service")
|
||||
@@ -605,7 +607,7 @@ function handle_oneway(way,result,data)
|
||||
else
|
||||
local junction = way:get_value_by_key("junction")
|
||||
if data.highway == "motorway" or
|
||||
junction == "roundabout" or
|
||||
junction == "roundabout" or
|
||||
junction == "circular" then
|
||||
if oneway ~= "no" then
|
||||
-- implied oneway
|
||||
@@ -659,7 +661,7 @@ 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
|
||||
result.is_startpoint = result.forward_mode == mode.driving or
|
||||
result.is_startpoint = result.forward_mode == mode.driving or
|
||||
result.backward_mode == mode.driving
|
||||
end
|
||||
|
||||
@@ -678,7 +680,7 @@ function way_function(way, result)
|
||||
-- unnecessary work. this implies we should check things that
|
||||
-- commonly forbids access early, and handle complicated edge
|
||||
-- cases later.
|
||||
|
||||
|
||||
-- perform an quick initial check and abort if way is obviously
|
||||
-- not routable, e.g. because it does not have any of the key
|
||||
-- tags indicating routability
|
||||
@@ -728,14 +730,30 @@ function way_function(way, result)
|
||||
if handle_names(way,result) == false then return end
|
||||
end
|
||||
|
||||
function turn_function (angle)
|
||||
function turn_function (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.
|
||||
-- multiplying by 10 converts to deci-seconds see issue #1318
|
||||
if angle>=0 then
|
||||
return 10 * turn_penalty / (1 + 2.718 ^ - ((13 / turn_bias) * angle/180 - 6.5*turn_bias))
|
||||
else
|
||||
return 10 * turn_penalty / (1 + 2.718 ^ - ((13 * turn_bias) * - angle/180 - 6.5/turn_bias))
|
||||
if turn.turn_type ~= turn_type.no_turn then
|
||||
if turn.angle >= 0 then
|
||||
turn.duration = turn_penalty / (1 + math.exp( -((13 / turn_bias) * turn.angle/180 - 6.5*turn_bias)))
|
||||
else
|
||||
turn.duration = turn_penalty / (1 + math.exp( -((13 * turn_bias) * -turn.angle/180 - 6.5/turn_bias)))
|
||||
end
|
||||
|
||||
if turn.direction_modifier == direction_modifier.u_turn then
|
||||
turn.duration = turn.duration + u_turn_penalty
|
||||
end
|
||||
|
||||
if turn.has_traffic_light then
|
||||
turn.duration = turn.duration + traffic_light_penalty
|
||||
end
|
||||
|
||||
-- for distance based routing we don't want to have penalties based on turn angle
|
||||
if properties.weight_name == 'distance' then
|
||||
turn.weight = 0
|
||||
else
|
||||
turn.weight = turn.duration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,7 +36,6 @@ obey_oneway = true
|
||||
use_restrictions = true
|
||||
ignore_areas = true -- future feature
|
||||
traffic_signal_penalty = 7 -- seconds
|
||||
u_turn_penalty = 20
|
||||
|
||||
-- nodes processing, called from OSRM
|
||||
function node_function(node)
|
||||
|
||||
+34
-20
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
|
||||
-- Foot profile
|
||||
local find_access_tag = require("lib/access").find_access_tag
|
||||
@@ -21,16 +21,16 @@ barrier_whitelist = Set {
|
||||
'block'
|
||||
}
|
||||
access_tag_whitelist = Set {
|
||||
'yes',
|
||||
'foot',
|
||||
'permissive',
|
||||
'designated'
|
||||
'yes',
|
||||
'foot',
|
||||
'permissive',
|
||||
'designated'
|
||||
}
|
||||
access_tag_blacklist = Set {
|
||||
'no',
|
||||
'private',
|
||||
'agricultural',
|
||||
'forestry',
|
||||
'no',
|
||||
'private',
|
||||
'agricultural',
|
||||
'forestry',
|
||||
'delivery'
|
||||
}
|
||||
|
||||
@@ -103,11 +103,13 @@ leisure_speeds = {
|
||||
track = walking_speed
|
||||
}
|
||||
|
||||
properties.traffic_signal_penalty = 2
|
||||
properties.u_turn_penalty = 2
|
||||
properties.max_speed_for_map_matching = 40/3.6 -- kmph -> m/s
|
||||
properties.use_turn_restrictions = false
|
||||
properties.continue_straight_at_waypoint = false
|
||||
properties.weight_name = 'duration'
|
||||
|
||||
local traffic_light_penalty = 2
|
||||
local u_turn_penalty = 2
|
||||
|
||||
|
||||
|
||||
@@ -193,7 +195,7 @@ function initial_routability_check(way,result,data)
|
||||
data.platform = way:get_value_by_key("platform")
|
||||
data.amenity = way:get_value_by_key("amenity")
|
||||
data.public_transport = way:get_value_by_key("public_transport")
|
||||
|
||||
|
||||
return data.highway ~= nil or
|
||||
data.leisure ~= nil or
|
||||
data.route ~= nil or
|
||||
@@ -293,14 +295,14 @@ function handle_speed(way,result,data)
|
||||
amenity_speeds[data.amenity] or
|
||||
man_made_speeds[data.man_made] or
|
||||
leisure_speeds[data.leisure]
|
||||
|
||||
|
||||
if speed then
|
||||
-- set speed by way type
|
||||
result.forward_speed = highway_speed
|
||||
result.backward_speed = highway_speed
|
||||
result.forward_speed = speed
|
||||
result.backward_speed = speed
|
||||
else
|
||||
else
|
||||
-- Set the avg speed on ways that are marked accessible
|
||||
if access_tag_whitelist[data.forward_access] then
|
||||
result.forward_speed = speed_profile["default"]
|
||||
@@ -315,7 +317,7 @@ function handle_speed(way,result,data)
|
||||
if -1 == result.forward_speed and -1 == result.backward_speed then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
if handle_surface(way,result) == false then return false end
|
||||
end
|
||||
|
||||
@@ -398,9 +400,9 @@ function handle_oneway(way,result,data)
|
||||
elseif oneway_handling == 'specific' then
|
||||
oneway = Directional.get_value_by_prefixed_sequence(way,restrictions,'oneway')
|
||||
end
|
||||
|
||||
|
||||
data.oneway = oneway
|
||||
|
||||
|
||||
if oneway then
|
||||
if oneway == "-1" then
|
||||
data.is_reverse_oneway = true
|
||||
@@ -413,7 +415,7 @@ function handle_oneway(way,result,data)
|
||||
else
|
||||
local junction = way:get_value_by_key("junction")
|
||||
if data.highway == "motorway" or
|
||||
junction == "roundabout" or
|
||||
junction == "roundabout" or
|
||||
junction == "circular" then
|
||||
if oneway ~= "no" then
|
||||
-- implied oneway
|
||||
@@ -436,7 +438,7 @@ 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
|
||||
result.is_startpoint = result.forward_mode == mode.walking or
|
||||
result.is_startpoint = result.forward_mode == mode.walking or
|
||||
result.backward_mode == mode.walking
|
||||
end
|
||||
|
||||
@@ -455,7 +457,7 @@ function way_function(way, result)
|
||||
-- unnecessary work. this implies we should check things that
|
||||
-- commonly forbids access early, and handle complicated edge
|
||||
-- cases later.
|
||||
|
||||
|
||||
-- perform an quick initial check and abort if way is obviously
|
||||
-- not routable, e.g. because it does not have any of the key
|
||||
-- tags indicating routability
|
||||
@@ -497,3 +499,15 @@ function way_function(way, result)
|
||||
-- set name, ref and pronunciation
|
||||
if handle_names(way,result) == false then return end
|
||||
end
|
||||
|
||||
function turn_function (turn)
|
||||
turn.duration = 0.
|
||||
|
||||
if turn.direction_modifier == direction_modifier.u_turn then
|
||||
turn.duration = turn.duration + u_turn_penalty
|
||||
end
|
||||
|
||||
if turn.has_traffic_light then
|
||||
turn.duration = traffic_light_penalty
|
||||
end
|
||||
end
|
||||
|
||||
+16
-9
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
-- Rasterbot profile
|
||||
|
||||
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
|
||||
@@ -37,18 +37,25 @@ function source_function ()
|
||||
)
|
||||
end
|
||||
|
||||
function segment_function (source, target, distance, weight)
|
||||
local sourceData = sources:query(raster_source, source.lon, source.lat)
|
||||
local targetData = sources:query(raster_source, target.lon, target.lat)
|
||||
function segment_function (segment)
|
||||
local sourceData = sources:query(raster_source, segment.source.lon, segment.source.lat)
|
||||
local targetData = sources:query(raster_source, segment.target.lon, segment.target.lat)
|
||||
io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n")
|
||||
local invalid = sourceData.invalid_data()
|
||||
local scaled_weight = segment.weight
|
||||
local scaled_duration = segment.duration
|
||||
|
||||
if sourceData.datum ~= invalid and targetData.datum ~= invalid then
|
||||
local slope = math.abs(sourceData.datum - targetData.datum) / distance
|
||||
local slope = math.abs(sourceData.datum - targetData.datum) / segment.distance
|
||||
scaled_weight = scaled_weight / (1.0 - (slope * 5.0))
|
||||
scaled_duration = scaled_duration / (1.0 - (slope * 5.0))
|
||||
io.write(" slope: " .. slope .. "\n")
|
||||
io.write(" was speed: " .. weight.speed .. "\n")
|
||||
|
||||
weight.speed = weight.speed * (1 - (slope * 5))
|
||||
io.write(" new speed: " .. weight.speed .. "\n")
|
||||
io.write(" was weight: " .. segment.weight .. "\n")
|
||||
io.write(" new weight: " .. scaled_weight .. "\n")
|
||||
io.write(" was duration: " .. segment.duration .. "\n")
|
||||
io.write(" new duration: " .. scaled_duration .. "\n")
|
||||
end
|
||||
|
||||
segment.weight = scaled_weight
|
||||
segment.duration = scaled_duration
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
-- Rasterbot profile
|
||||
|
||||
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
|
||||
@@ -37,18 +37,25 @@ function source_function ()
|
||||
)
|
||||
end
|
||||
|
||||
function segment_function (source, target, distance, weight)
|
||||
local sourceData = sources:interpolate(raster_source, source.lon, source.lat)
|
||||
local targetData = sources:interpolate(raster_source, target.lon, target.lat)
|
||||
function segment_function (segment)
|
||||
local sourceData = sources:interpolate(raster_source, segment.source.lon, segment.source.lat)
|
||||
local targetData = sources:interpolate(raster_source, segment.target.lon, segment.target.lat)
|
||||
io.write("evaluating segment: " .. sourceData.datum .. " " .. targetData.datum .. "\n")
|
||||
local invalid = sourceData.invalid_data()
|
||||
local scaled_weight = segment.weight
|
||||
local scaled_duration = segment.duration
|
||||
|
||||
if sourceData.datum ~= invalid and targetData.datum ~= invalid then
|
||||
local slope = math.abs(sourceData.datum - targetData.datum) / distance
|
||||
local slope = math.abs(sourceData.datum - targetData.datum) / segment.distance
|
||||
io.write(" slope: " .. slope .. "\n")
|
||||
io.write(" was speed: " .. weight.speed .. "\n")
|
||||
io.write(" was weight: " .. segment.weight .. "\n")
|
||||
io.write(" was speed: " .. segment.duration .. "\n")
|
||||
|
||||
weight.speed = weight.speed * (1 - (slope * 5))
|
||||
io.write(" new speed: " .. weight.speed .. "\n")
|
||||
scaled_weight = scaled_weight / (1 - (slope * 5))
|
||||
io.write(" new weight: " .. scaled_weight .. "\n")
|
||||
scaled_duration = scaled_duration / (1 - (slope * 5))
|
||||
io.write(" new speed: " .. scaled_duration .. "\n")
|
||||
end
|
||||
segment.weight = scaled_weight
|
||||
segment.duration = scaled_duration
|
||||
end
|
||||
|
||||
+15
-3
@@ -1,4 +1,4 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
-- Testbot profile
|
||||
|
||||
-- Moves at fixed, well-known speeds, practical for testing speed and travel times:
|
||||
@@ -19,9 +19,11 @@ speed_profile = {
|
||||
|
||||
properties.continue_straight_at_waypoint = true
|
||||
properties.use_turn_restrictions = true
|
||||
properties.traffic_signal_penalty = 7 -- seconds
|
||||
properties.u_turn_penalty = 20
|
||||
properties.max_speed_for_map_matching = 30/3.6 --km -> m/s
|
||||
properties.weight_name = 'duration'
|
||||
|
||||
local uturn_penalty = 20
|
||||
local traffic_light_penalty = 7 -- seconds
|
||||
|
||||
function limit_speed(speed, limits)
|
||||
-- don't use ipairs(), since it stops at the first nil value
|
||||
@@ -114,3 +116,13 @@ function way_function (way, result)
|
||||
result.roundabout = true
|
||||
end
|
||||
end
|
||||
|
||||
function turn_function (turn)
|
||||
if turn.direction_modifier == direction_modifier.uturn then
|
||||
turn.duration = uturn_penalty
|
||||
turn.weight = uturn_penalty
|
||||
end
|
||||
if turn.has_traffic_light then
|
||||
turn.duration = turn.duration + traffic_light_penalty
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
api_version = 0
|
||||
api_version = 1
|
||||
|
||||
-- Testbot, with turn penalty
|
||||
-- Used for testing turn penalties
|
||||
|
||||
require 'testbot'
|
||||
|
||||
function turn_function (angle)
|
||||
-- multiplying by 10 converts to deci-seconds see issue #1318
|
||||
return 10*20*math.abs(angle)/180
|
||||
function turn_function (turn)
|
||||
turn.duration = 20 * math.abs(turn.angle) / 180
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user