From 76f13f34c64e2cba5f717926bc27d978347945e4 Mon Sep 17 00:00:00 2001 From: Michael Bell Date: Fri, 19 Mar 2021 20:37:21 +0000 Subject: [PATCH] Complete support for no_entry and no_exit turn restrictions The internal representation of turn restrictions expects only one `from` way and only one `to` way. `no_entry` and `no_exit` turn restrictions can have multiple `from` and `to` ways respectively. This means they are not fully supported by OSRM's restriction parser. We complete support for these turn restriction types by parsing all ways and converting a valid restriction with multiple `from`/`to` members into multiple internal restrictions. --- CHANGELOG.md | 2 + features/car/restrictions.feature | 120 ++++++++++++++++++++ include/extractor/restriction_parser.hpp | 2 +- src/extractor/restriction_parser.cpp | 84 +++++++++----- src/extractor/scripting_environment_lua.cpp | 8 +- 5 files changed, 185 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f9007696a3..94c8c245a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - FIXED: `valid` type in documentation examples [#5990](https://github.com/Project-OSRM/osrm-backend/issues/5990) - Profile: - FIXED: Add kerb barrier exception to default car profile. [#5999](https://github.com/Project-OSRM/osrm-backend/pull/5999) + - Features + - FIXED: Completed support for no_entry and no_exit turn restrictions. [#5988](https://github.com/Project-OSRM/osrm-backend/pull/5988) # 5.24.0 - Changes from 5.23.0 diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index da76f4dedc0..b9c878b0025 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -1008,3 +1008,123 @@ Feature: Car - Turn restrictions | from | to | route | | d | x | bd,abc,xa,xa | | d | z | bd,abc,cz,cz | + + + Scenario: Multiple restricted entrances + Given the node map + """ + b + | + a----e----c + | + d + """ + + And the ways + | nodes | + | ae | + | be | + | ce | + | de | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ae,be | ed | e | no_entry | + + When I route I should get + | from | to | route | + | a | d | ae,ce,ce,de,de | + | b | d | be,ce,ce,de,de | + | c | d | ce,de,de | + + + Scenario: Multiple restricted exits + Given the node map + """ + b + | + a----e----c + | + d + """ + + And the ways + | nodes | + | ae | + | be | + | ce | + | de | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ae | ce,de | e | no_exit | + + When I route I should get + | from | to | route | + | a | b | ae,be,be | + | a | c | ae,be,be,ce,ce | + | a | d | ae,be,be,de,de | + + + Scenario: Invalid restricted entrances/exits + Given the node map + """ + b + | + a----e----c + | + d + """ + + And the ways + | nodes | + | ae | + | be | + | ce | + | de | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ae | ce,de | e | no_entry | + | restriction | ae,be | ed | e | no_exit | + + When I route I should get + | from | to | route | + | a | b | ae,be,be | + | a | c | ae,ce,ce | + | a | d | ae,de,de | + | b | d | be,de,de | + | c | d | ce,de,de | + + + Scenario: Invalid multi from/to restrictions + Given the node map + """ + b + | + a----e----c + | + d + """ + + And the ways + | nodes | + | ae | + | be | + | ce | + | de | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ae,de | ce,de | e | no_right_turn | + | restriction | ae,be | ce,de | e | no_straight_on | + | restriction | ae,be | be,ce | e | only_left_turn | + | restriction | ae,be | ce,de | e | only_straight_on | + + When I route I should get + | from | to | route | + | a | b | ae,be,be | + | a | c | ae,ce,ce | + | a | d | ae,de,de | + | b | d | be,de,de | + | c | d | ce,de,de | diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp index bf3f264554b..8e0cb0601ca 100644 --- a/include/extractor/restriction_parser.hpp +++ b/include/extractor/restriction_parser.hpp @@ -44,7 +44,7 @@ class RestrictionParser RestrictionParser(bool use_turn_restrictions, bool parse_conditionals, std::vector &restrictions); - boost::optional TryParse(const osmium::Relation &relation) const; + std::vector TryParse(const osmium::Relation &relation) const; private: bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp index 28ff24a57db..86bd3d7d1b1 100644 --- a/src/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -52,13 +52,13 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_, * in the corresponding profile. We use it for both namespacing restrictions, as in * restriction:motorcar as well as whitelisting if its in except:motorcar. */ -boost::optional +std::vector RestrictionParser::TryParse(const osmium::Relation &relation) const { // return if turn restrictions should be ignored if (!use_turn_restrictions) { - return boost::none; + return {}; } osmium::tags::KeyFilter filter(false); @@ -87,17 +87,19 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const // if it's not a restriction, continue; if (std::distance(fi_begin, fi_end) == 0) { - return boost::none; + return {}; } // check if the restriction should be ignored const char *except = relation.get_value_by_key("except"); if (except != nullptr && ShouldIgnoreRestriction(except)) { - return boost::none; + return {}; } bool is_only_restriction = false; + bool is_multi_from = false; + bool is_multi_to = false; for (; fi_begin != fi_end; ++fi_begin) { @@ -113,21 +115,26 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const else if (value.find("no_") == 0 && !boost::algorithm::ends_with(value, "_on_red")) { is_only_restriction = false; + if (value.find("no_exit") == 0) + { + is_multi_to = true; + } + else if (value.find("no_entry") == 0) + { + is_multi_from = true; + } } else // unrecognized value type { - return boost::none; + return {}; } } - InputTurnRestriction restriction_container; - restriction_container.is_only = is_only_restriction; - constexpr auto INVALID_OSM_ID = std::numeric_limits::max(); - auto from = INVALID_OSM_ID; + std::vector from_ways; auto via_node = INVALID_OSM_ID; std::vector via_ways; - auto to = INVALID_OSM_ID; + std::vector to_ways; bool is_node_restriction = true; for (const auto &member : relation.members()) @@ -159,11 +166,11 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const 0 == strcmp("via", role)); if (0 == strcmp("from", role)) { - from = static_cast(member.ref()); + from_ways.push_back({static_cast(member.ref())}); } else if (0 == strcmp("to", role)) { - to = static_cast(member.ref()); + to_ways.push_back({static_cast(member.ref())}); } else if (0 == strcmp("via", role)) { @@ -180,6 +187,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const } } + std::vector condition; // parse conditional tags if (parse_conditionals) { @@ -201,32 +209,54 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const std::vector hours = util::ParseOpeningHours(p.condition); // found unrecognized condition, continue if (hours.empty()) - return boost::none; + return {}; - restriction_container.condition = std::move(hours); + condition = std::move(hours); } } } - if (from != INVALID_OSM_ID && (via_node != INVALID_OSM_ID || !via_ways.empty()) && - to != INVALID_OSM_ID) + std::vector restriction_containers; + if (!from_ways.empty() && (via_node != INVALID_OSM_ID || !via_ways.empty()) && !to_ways.empty()) { - if (is_node_restriction) + if (from_ways.size() > 1 && !is_multi_from) { - // template struct requires bracket for ID initialisation :( - restriction_container.node_or_way = InputNodeRestriction{{from}, {via_node}, {to}}; + util::Log(logDEBUG) << "Parsed restriction " << relation.id() + << " unexpectedly contains " << from_ways.size() + << " from ways, skipping..."; + return {}; } - else + if (to_ways.size() > 1 && !is_multi_to) { - // template struct requires bracket for ID initialisation :( - restriction_container.node_or_way = InputWayRestriction{{from}, via_ways, {to}}; + util::Log(logDEBUG) << "Parsed restriction " << relation.id() + << " unexpectedly contains " << to_ways.size() + << " to ways, skipping..."; + return {}; + } + // Internally restrictions are represented with one 'from' and one 'to' way. + // Therefore we need to convert a multi from/to restriction into multiple restrictions. + for (const auto &from : from_ways) + { + for (const auto &to : to_ways) + { + InputTurnRestriction restriction; + restriction.is_only = is_only_restriction; + restriction.condition = condition; + if (is_node_restriction) + { + // template struct requires bracket for ID initialisation :( + restriction.node_or_way = InputNodeRestriction{{from}, {via_node}, {to}}; + } + else + { + // template struct requires bracket for ID initialisation :( + restriction.node_or_way = InputWayRestriction{{from}, via_ways, {to}}; + } + restriction_containers.push_back(std::move(restriction)); + } } - return restriction_container; - } - else - { - return boost::none; } + return restriction_containers; } bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index 69f1cb0950b..dd636997fbd 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -907,13 +907,15 @@ void Sol2ScriptingEnvironment::ProcessElements( case osmium::item_type::relation: { const auto &relation = static_cast(*entity); - if (auto result_res = restriction_parser.TryParse(relation)) + auto results = restriction_parser.TryParse(relation); + if (!results.empty()) { - resulting_restrictions.push_back(*result_res); + std::move( + results.begin(), results.end(), std::back_inserter(resulting_restrictions)); } else if (auto result_res = maneuver_override_parser.TryParse(relation)) { - resulting_maneuver_overrides.push_back(*result_res); + resulting_maneuver_overrides.push_back(std::move(*result_res)); } } break;