From 0f94fb9d6dfa8a5f136a79e41efd08627b7d896c Mon Sep 17 00:00:00 2001 From: Emil Tin Date: Sun, 8 Sep 2013 22:51:44 +0200 Subject: [PATCH] update foot profile, add tests for access, barriers, ferries, names, surface, more --- features/foot/access.feature | 95 +++++++ features/foot/access_node.feature | 50 ++++ features/foot/area.feature | 103 ++++++++ features/foot/barrier.feature | 39 +++ features/foot/names.feature | 33 +++ features/foot/ref.feature | 41 ++++ features/foot/roundabout.feature | 34 +++ features/foot/surface.feature | 15 ++ features/step_definitions/routability.rb | 6 +- profiles/foot.lua | 299 ++++++++++++----------- 10 files changed, 567 insertions(+), 148 deletions(-) create mode 100644 features/foot/access.feature create mode 100644 features/foot/access_node.feature create mode 100644 features/foot/area.feature create mode 100644 features/foot/barrier.feature create mode 100644 features/foot/names.feature create mode 100644 features/foot/ref.feature create mode 100644 features/foot/roundabout.feature create mode 100644 features/foot/surface.feature diff --git a/features/foot/access.feature b/features/foot/access.feature new file mode 100644 index 000000000..b870ae392 --- /dev/null +++ b/features/foot/access.feature @@ -0,0 +1,95 @@ +@routing @foot @access +Feature: Foot - Access tags on ways +Reference: http://wiki.openstreetmap.org/wiki/Key:access + + Background: + Given the profile "foot" + + Scenario: Foot - Access tag hierachy on ways + Then routability should be + | highway | access | foot | bothw | + | footway | | | x | + | footway | | yes | x | + | footway | | no | | + | footway | yes | | x | + | footway | yes | yes | x | + | footway | yes | no | | + | footway | no | | | + | footway | no | yes | x | + | footway | no | no | | + | motorway | | | | + | motorway | | yes | x | + | motorway | | no | | + | motorway | yes | | x | + | motorway | yes | yes | x | + | motorway | yes | no | | + | motorway | no | | | + | motorway | no | yes | x | + | motorway | no | no | | + + + Scenario: Foot - Overwriting implied acccess on ways + Then routability should be + | highway | access | foot | bothw | + | footway | | | x | + | motorway | | | | + | footway | no | | | + | footway | | | x | + | footway | | no | | + | motorway | yes | | x | + | motorway | | | | + | motorway | | yes | x | + + Scenario: Foot - Access tags on ways + Then routability should be + | access | foot | bothw | + | | | x | + | yes | | x | + | permissive | | x | + | designated | | x | + | some_tag | | x | + | no | | | + | private | | | + | agricultural | | | + | forestery | | | + | | yes | x | + | | permissive | x | + | | designated | x | + | | some_tag | x | + | | no | | + | | private | | + | | agricultural | | + | | forestery | | + + Scenario: Foot - Access tags on both node and way + Then routability should be + | access | node/access | bothw | + | yes | yes | x | + | yes | no | | + | yes | some_tag | x | + | no | yes | | + | no | no | | + | no | some_tag | | + | some_tag | yes | x | + | some_tag | no | | + | some_tag | some_tag | x | + + Scenario: Foot - Access combinations + Then routability should be + | highway | access | foot | bothw | + | motorway | private | yes | x | + | footway | | permissive | x | + | track | forestry | permissive | x | + | footway | yes | no | | + | primary | | private | | + | residential | permissive | no | | + + Scenario: Foot - Ignore access tags for other modes + Then routability should be + | highway | boat | motor_vehicle | moped | bothw | + | river | yes | | | | + | footway | no | | | x | + | motorway | | yes | | | + | footway | | no | | x | + | motorway | | | yes | | + | footway | | | no | x | diff --git a/features/foot/access_node.feature b/features/foot/access_node.feature new file mode 100644 index 000000000..477f8b4a8 --- /dev/null +++ b/features/foot/access_node.feature @@ -0,0 +1,50 @@ +@routing @foot @access +Feature: Foot - Access tags on nodes +Reference: http://wiki.openstreetmap.org/wiki/Key:access + + Background: + Given the profile "foot" + + Scenario: Foot - Access tag hierachy on nodes + Then routability should be + | node/access | node/foot | bothw | + | | | x | + | | yes | x | + | | no | | + | yes | | x | + | yes | yes | x | + | yes | no | | + | no | | | + | no | yes | x | + | no | no | | + + Scenario: Foot - Overwriting implied acccess on nodes doesn't overwrite way + Then routability should be + | highway | node/access | node/foot | bothw | + | footway | | | x | + | footway | no | | | + | footway | | no | | + | motorway | | | | + | motorway | yes | | | + | motorway | | yes | | + + Scenario: Foot - Access tags on nodes + Then routability should be + | node/access | node/foot | bothw | + | | | x | + | yes | | x | + | permissive | | x | + | designated | | x | + | some_tag | | x | + | no | | | + | private | | | + | agricultural | | | + | forestery | | | + | no | yes | x | + | no | permissive | x | + | no | designated | x | + | no | some_tag | x | + | yes | no | | + | yes | private | | + | yes | agricultural | | + | yes | forestery | | diff --git a/features/foot/area.feature b/features/foot/area.feature new file mode 100644 index 000000000..6edd585d6 --- /dev/null +++ b/features/foot/area.feature @@ -0,0 +1,103 @@ +@routing @foot @area +Feature: Foot - Squares and other areas + + Background: + Given the profile "foot" + + @square + Scenario: Foot - Route along edge of a squares + Given the node map + | x | | + | a | b | + | d | c | + + And the ways + | nodes | area | highway | + | xa | | primary | + | abcda | yes | residential | + + When I route I should get + | from | to | route | + | a | b | abcda | + | a | d | abcda | + | b | c | abcda | + | c | b | abcda | + | c | d | abcda | + | d | c | abcda | + | d | a | abcda | + | a | d | abcda | + + @building + Scenario: Foot - Don't route on buildings + Given the node map + | x | | + | a | b | + | d | c | + + And the ways + | nodes | highway | area | building | access | + | xa | primary | | | | + | abcda | (nil) | yes | yes | yes | + + When I route I should get + | from | to | route | + | a | b | xa | + | a | d | xa | + | b | c | xa | + | c | b | xa | + | c | d | xa | + | d | c | xa | + | d | a | xa | + | a | d | xa | + + @parking + Scenario: Foot - parking areas + Given the node map + | e | | | f | + | x | a | b | y | + | | d | c | | + + And the ways + | nodes | highway | amenity | + | xa | primary | | + | by | primary | | + | xefy | primary | | + | abcda | (nil) | parking | + + When I route I should get + | from | to | route | + | x | y | xa,abcda,by | + | y | x | by,abcda,xa | + | a | b | abcda | + | a | d | abcda | + | b | c | abcda | + | c | b | abcda | + | c | d | abcda | + | d | c | abcda | + | d | a | abcda | + | a | d | abcda | + + @train @platform + Scenario: Foot - railway platforms + Given the node map + | x | a | b | y | + | | d | c | | + + And the ways + | nodes | highway | railway | + | xa | primary | | + | by | primary | | + | abcda | (nil) | platform | + + When I route I should get + | from | to | route | + | x | y | xa,abcda,by | + | y | x | by,abcda,xa | + | a | b | abcda | + | a | d | abcda | + | b | c | abcda | + | c | b | abcda | + | c | d | abcda | + | d | c | abcda | + | d | a | abcda | + | a | d | abcda | diff --git a/features/foot/barrier.feature b/features/foot/barrier.feature new file mode 100644 index 000000000..18fa4deac --- /dev/null +++ b/features/foot/barrier.feature @@ -0,0 +1,39 @@ +@routing @foot @barrier +Feature: Barriers + + Background: + Given the profile "foot" + + Scenario: Foot - Barriers + Then routability should be + | node/barrier | bothw | + | | x | + | bollard | x | + | gate | x | + | cycle_barrier | x | + | cattle_grid | x | + | border_control | x | + | toll_booth | x | + | sally_port | x | + | entrance | x | + | wall | | + | fence | | + | some_tag | | + + Scenario: Foot - Access tag trumphs barriers + Then routability should be + | node/barrier | node/access | bothw | + | bollard | | x | + | bollard | yes | x | + | bollard | permissive | x | + | bollard | designated | x | + | bollard | no | | + | bollard | private | | + | bollard | agricultural | | + | wall | | | + | wall | yes | x | + | wall | permissive | x | + | wall | designated | x | + | gate | no | | + | gate | private | | + | gate | agricultural | | diff --git a/features/foot/names.feature b/features/foot/names.feature new file mode 100644 index 000000000..d5fdcc7e4 --- /dev/null +++ b/features/foot/names.feature @@ -0,0 +1,33 @@ +@routing @foot @names +Feature: Foot - Street names in instructions + + Background: + Given the profile "foot" + + Scenario: Foot - A named street + Given the node map + | a | b | + | | c | + + And the ways + | nodes | name | + | ab | My Way | + | bc | Your Way | + + When I route I should get + | from | to | route | + | a | c | My Way,Your Way | + + @unnamed + Scenario: Foot - Use way type to describe unnamed ways + Given the node map + | a | b | c | d | + + And the ways + | nodes | highway | name | + | ab | footway | | + | bcd | track | | + + When I route I should get + | from | to | route | + | a | d | {highway:footway},{highway:track} | diff --git a/features/foot/ref.feature b/features/foot/ref.feature new file mode 100644 index 000000000..bc0c77e14 --- /dev/null +++ b/features/foot/ref.feature @@ -0,0 +1,41 @@ +@routing @foot @ref @name +Feature: Foot - Way ref + + Background: + Given the profile "foot" + + Scenario: Foot - Way with both name and ref + Given the node map + | a | b | + + And the ways + | nodes | name | ref | + | ab | Utopia Drive | E7 | + + When I route I should get + | from | to | route | + | a | b | Utopia Drive / E7 | + + Scenario: Foot - Way with only ref + Given the node map + | a | b | + + And the ways + | nodes | name | ref | + | ab | | E7 | + + When I route I should get + | from | to | route | + | a | b | E7 | + + Scenario: Foot - Way with only name + Given the node map + | a | b | + + And the ways + | nodes | name | + | ab | Utopia Drive | + + When I route I should get + | from | to | route | + | a | b | Utopia Drive | diff --git a/features/foot/roundabout.feature b/features/foot/roundabout.feature new file mode 100644 index 000000000..cfe65d476 --- /dev/null +++ b/features/foot/roundabout.feature @@ -0,0 +1,34 @@ +@routing @foot @roundabout @instruction +Feature: Roundabout Instructions + + Background: + Given the profile "foot" + + @todo + Scenario: Foot - Roundabout instructions + You can walk in both directions on a roundabout, bu the normal roundabout instructions don't + make sense when you're going the opposite way around the roundabout. + + Given the node map + | | | v | | | + | | | d | | | + | s | a | | c | u | + | | | b | | | + | | | t | | | + + And the ways + | nodes | junction | + | sa | | + | tb | | + | uc | | + | vd | | + | abcda | roundabout | + + When I route I should get + | from | to | route | turns | + | s | t | sa,tb | head,enter_roundabout-1,destination | + | s | u | sa,uc | head,enter_roundabout-2,destination | + | s | v | sa,vd | head,enter_roundabout-3,destination | + | u | v | uc,vd | head,enter_roundabout-1,destination | + | u | s | uc,sa | head,enter_roundabout-2,destination | + | u | t | uc,tb | head,enter_roundabout-3,destination | diff --git a/features/foot/surface.feature b/features/foot/surface.feature new file mode 100644 index 000000000..556c3d893 --- /dev/null +++ b/features/foot/surface.feature @@ -0,0 +1,15 @@ +@routing @foot @surface +Feature: Foot - Surfaces + + Background: + Given the profile "foot" + + Scenario: Foot - Slow surfaces + Then routability should be + | highway | surface | bothw | + | footway | | 145s ~10% | + | footway | fine_gravel | 193s ~10% | + | footway | gravel | 193s ~10% | + | footway | pebbelstone | 193s ~10% | + | footway | mud | 289s ~10% | + | footway | sand | 289s ~10% | diff --git a/features/step_definitions/routability.rb b/features/step_definitions/routability.rb index ed83c4a06..d01d2234f 100644 --- a/features/step_definitions/routability.rb +++ b/features/step_definitions/routability.rb @@ -26,11 +26,7 @@ Then /^routability should be$/ do |table| if got[direction].empty? == false route = way_list json['route_instructions'] if route != "w#{i}" - if row[direction].empty? == true - got[direction] = want - else - got[direction] = "testing w#{i}, but got #{route}!?" - end + got[direction] = '' elsif want =~ /^\d+s/ time = json['route_summary']['total_time'] got[direction] = "#{time}s" diff --git a/profiles/foot.lua b/profiles/foot.lua index 6b785c3b6..724519046 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -1,48 +1,67 @@ -- Foot profile --- Begin of globals +require("lib/access") -bollards_whitelist = { [""] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true} +barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true} access_tag_whitelist = { ["yes"] = true, ["foot"] = true, ["permissive"] = true, ["designated"] = true } access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestery"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } -access_tags = { "foot" } +access_tags_hierachy = { "foot", "access" } service_tag_restricted = { ["parking_aisle"] = true } ignore_in_grid = { ["ferry"] = true } restriction_exception_tags = { "foot" } -speed_profile = { - ["primary"] = 5, - ["primary_link"] = 5, - ["secondary"] = 5, - ["secondary_link"] = 5, - ["tertiary"] = 5, - ["tertiary_link"] = 5, - ["unclassified"] = 5, - ["residential"] = 5, - ["road"] = 5, - ["living_street"] = 5, - ["service"] = 5, - ["track"] = 5, - ["path"] = 5, - ["steps"] = 5, - ["ferry"] = 5, - ["pedestrian"] = 5, - ["footway"] = 5, - ["pier"] = 5, - ["default"] = 5 +walking_speed = 5 + +speeds = { + ["primary"] = walking_speed, + ["primary_link"] = walking_speed, + ["secondary"] = walking_speed, + ["secondary_link"] = walking_speed, + ["tertiary"] = walking_speed, + ["tertiary_link"] = walking_speed, + ["unclassified"] = walking_speed, + ["residential"] = walking_speed, + ["road"] = walking_speed, + ["living_street"] = walking_speed, + ["service"] = walking_speed, + ["track"] = walking_speed, + ["path"] = walking_speed, + ["steps"] = walking_speed, + ["pedestrian"] = walking_speed, + ["footway"] = walking_speed, + ["pier"] = walking_speed, + ["default"] = walking_speed } +route_speeds = { + ["ferry"] = 5 +} + +platform_speeds = { + ["platform"] = walking_speed +} + +amenity_speeds = { + ["parking"] = walking_speed, + ["parking_entrance"] = walking_speed +} + +man_made_speeds = { + ["pier"] = walking_speed +} + +surface_speeds = { + ["fine_gravel"] = walking_speed*0.75, + ["gravel"] = walking_speed*0.75, + ["pebbelstone"] = walking_speed*0.75, + ["mud"] = walking_speed*0.5, + ["sand"] = walking_speed*0.5 +} -take_minimum_of_speeds = true -obey_oneway = true -obey_bollards = false -use_restrictions = false -ignore_areas = true -- future feature traffic_signal_penalty = 2 u_turn_penalty = 2 use_turn_restrictions = false --- End of globals function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do @@ -51,142 +70,136 @@ function get_exceptions(vector) end function node_function (node) - local barrier = node.tags:Find ("barrier") - local access = node.tags:Find ("access") - local traffic_signal = node.tags:Find("highway") + local barrier = node.tags:Find ("barrier") + local access = Access.find_access_tag(node, access_tags_hierachy) + local traffic_signal = node.tags:Find("highway") - --flag node if it carries a traffic light + -- flag node if it carries a traffic light + if traffic_signal == "traffic_signals" then + node.traffic_light = true + end - if traffic_signal == "traffic_signals" then - node.traffic_light = true; - end + -- parse access and barrier tags + if access and access ~= "" then + if access_tag_blacklist[access] then + node.bollard = true + else + node.bollard = false + end + elseif barrier and barrier ~= "" then + if barrier_whitelist[barrier] then + node.bollard = false + else + node.bollard = true + end + end - if obey_bollards then - --flag node as unpassable if it black listed as unpassable - if access_tag_blacklist[barrier] then - node.bollard = true; - end - - --reverse the previous flag if there is an access tag specifying entrance - if node.bollard and not bollards_whitelist[barrier] and not access_tag_whitelist[barrier] then - node.bollard = false; - end - end - return 1 + return 1 end function way_function (way) + -- initial routability check, filters out buildings, boundaries, etc + local highway = way.tags:Find("highway") + local route = way.tags:Find("route") + local man_made = way.tags:Find("man_made") + local railway = way.tags:Find("railway") + local amenity = way.tags:Find("amenity") + local public_transport = way.tags:Find("public_transport") + if (not highway or highway == '') and + (not route or route == '') and + (not railway or railway=='') and + (not amenity or amenity=='') and + (not man_made or man_made=='') and + (not public_transport or public_transport=='') + then + return 0 + end + + -- don't route on ways that are still under construction + if highway=='construction' then + return 0 + end - -- First, get the properties of each way that we come across - local highway = way.tags:Find("highway") - local name = way.tags:Find("name") - local ref = way.tags:Find("ref") - local junction = way.tags:Find("junction") - local route = way.tags:Find("route") - local maxspeed = parseMaxspeed(way.tags:Find ( "maxspeed") ) - local man_made = way.tags:Find("man_made") - local barrier = way.tags:Find("barrier") - local oneway = way.tags:Find("oneway") - local onewayClass = way.tags:Find("oneway:foot") - local duration = way.tags:Find("duration") - local service = way.tags:Find("service") - local area = way.tags:Find("area") - local access = way.tags:Find("access") - - -- Second parse the way according to these properties - - if ignore_areas and ("yes" == area) then + -- access + local access = Access.find_access_tag(way, access_tags_hierachy) + if access_tag_blacklist[access] then return 0 - end - - -- Check if we are allowed to access the way - if access_tag_blacklist[access] ~=nil and access_tag_blacklist[access] then - return 0; end + + local name = way.tags:Find("name") + local ref = way.tags:Find("ref") + local junction = way.tags:Find("junction") + local onewayClass = way.tags:Find("oneway:foot") + local duration = way.tags:Find("duration") + local service = way.tags:Find("service") + local area = way.tags:Find("area") + local foot = way.tags:Find("foot") + local surface = way.tags:Find("surface") - -- Check if our vehicle types are forbidden - for i,v in ipairs(access_tags) do - local mode_value = way.tags:Find(v) - if nil ~= mode_value and "no" == mode_value then - return 0; - end - end - - - -- Set the name that will be used for instructions - if "" ~= ref then - way.name = ref + -- name + if "" ~= ref and "" ~= name then + way.name = name .. ' / ' .. ref + elseif "" ~= ref then + way.name = ref elseif "" ~= name then - way.name = name + way.name = name + else + way.name = "{highway:"..highway.."}" -- if no name exists, use way type + -- this encoding scheme is excepted to be a temporary solution end + -- roundabouts if "roundabout" == junction then way.roundabout = true; end - -- Handling ferries and piers - - if (speed_profile[route] ~= nil and speed_profile[route] > 0) or - (speed_profile[man_made] ~= nil and speed_profile[man_made] > 0) - then - if durationIsValid(duration) then - way.duration = math.max( parseDuration(duration), 1 ); - end - way.direction = Way.bidirectional; - if speed_profile[route] ~= nil then - highway = route; - elseif speed_profile[man_made] ~= nil then - highway = man_made; - end - if not way.is_duration_set then - way.speed = speed_profile[highway] - end - - end - - -- Set the avg speed on the way if it is accessible by road class - if (speed_profile[highway] ~= nil and way.speed == -1 ) then - way.speed = speed_profile[highway] - end - - -- Set the avg speed on ways that are marked accessible - if access_tag_whitelist[access] and way.speed == -1 then - if (0 < maxspeed and not take_minimum_of_speeds) or maxspeed == 0 then - maxspeed = math.huge - end - way.speed = math.min(speed_profile["default"], maxspeed) - end - - -- Set access restriction flag if access is allowed under certain restrictions only - if access ~= "" and access_tag_restricted[access] then - way.is_access_restricted = true - end - - -- Set access restriction flag if service is allowed under certain restrictions only - if service ~= "" and service_tag_restricted[service] then - way.is_access_restricted = true - end - - -- Set direction according to tags on way - if obey_oneway then - if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then - way.direction = Way.oneway - elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then - way.direction = Way.bidirectional - elseif onewayClass == "-1" then - way.direction = Way.opposite + -- speed + if route_speeds[route] then + -- ferries (doesn't cover routes tagged using relations) + way.direction = Way.bidirectional + way.ignore_in_grid = true + if durationIsValid(duration) then + way.duration = math.max( 1, parseDuration(duration) ) else - way.direction = Way.bidirectional + way.speed = route_speeds[route] end - else + elseif railway and platform_speeds[railway] then + -- railway platforms (old tagging scheme) + way.speed = platform_speeds[railway] + elseif platform_speeds[public_transport] then + -- public_transport platforms (new tagging platform) + way.speed = platform_speeds[public_transport] + elseif amenity and amenity_speeds[amenity] then + -- parking areas + way.speed = amenity_speeds[amenity] + elseif speeds[highway] then + -- regular ways + way.speed = speeds[highway] + elseif access and access_tag_whitelist[access] then + -- unknown way, but valid access tag + way.speed = walking_speed + end + + -- oneway + if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then + way.direction = Way.oneway + elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then + way.direction = Way.bidirectional + elseif onewayClass == "-1" then + way.direction = Way.opposite + else way.direction = Way.bidirectional end - -- Override general direction settings of there is a specific one for our mode of travel + -- surfaces + if surface then + surface_speed = surface_speeds[surface] + if surface_speed then + way.speed = math.min(way.speed, surface_speed) + way.backward_speed = math.min(way.backward_speed, surface_speed) + end + end - if ignore_in_grid[highway] ~= nil and ignore_in_grid[highway] then - way.ignore_in_grid = true - end way.type = 1 - return 1 + return 1 end