Complete support for no_entry and no_exit turn restrictions (#5988)
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.
This commit is contained in:
		
							parent
							
								
									b4b1aea567
								
							
						
					
					
						commit
						3cfd0e8334
					
				| @ -36,6 +36,7 @@ | ||||
|       - CHANGED: Docker build, enabled arm64 build layer [#6172](https://github.com/Project-OSRM/osrm-backend/pull/6172) | ||||
|       - CHANGED: Docker build, enabled apt-get update/install caching in separate layer for build phase [#6175](https://github.com/Project-OSRM/osrm-backend/pull/6175) | ||||
|     - Routing: | ||||
|       - FIXED: Completed support for no_entry and no_exit turn restrictions. [#5988](https://github.com/Project-OSRM/osrm-backend/pull/5988) | ||||
|       - ADDED: Add support for non-round-trips with a single fixed endpoint. [#6050](https://github.com/Project-OSRM/osrm-backend/pull/6050) | ||||
| 
 | ||||
| # 5.26.0 | ||||
|  | ||||
| @ -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  | | ||||
|  | ||||
| @ -44,7 +44,7 @@ class RestrictionParser | ||||
|     RestrictionParser(bool use_turn_restrictions, | ||||
|                       bool parse_conditionals, | ||||
|                       std::vector<std::string> &restrictions); | ||||
|     boost::optional<InputTurnRestriction> TryParse(const osmium::Relation &relation) const; | ||||
|     std::vector<InputTurnRestriction> TryParse(const osmium::Relation &relation) const; | ||||
| 
 | ||||
|   private: | ||||
|     bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; | ||||
|  | ||||
| @ -50,13 +50,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<InputTurnRestriction> | ||||
| std::vector<InputTurnRestriction> | ||||
| 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); | ||||
| @ -85,17 +85,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) | ||||
|     { | ||||
| @ -111,21 +113,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 (boost::algorithm::starts_with(value, "no_exit")) | ||||
|             { | ||||
|                 is_multi_to = true; | ||||
|             } | ||||
|             else if (boost::algorithm::starts_with(value, "no_entry")) | ||||
|             { | ||||
|                 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<std::uint64_t>::max(); | ||||
|     auto from = INVALID_OSM_ID; | ||||
|     std::vector<OSMWayID> from_ways; | ||||
|     auto via_node = INVALID_OSM_ID; | ||||
|     std::vector<OSMWayID> via_ways; | ||||
|     auto to = INVALID_OSM_ID; | ||||
|     std::vector<OSMWayID> to_ways; | ||||
|     bool is_node_restriction = true; | ||||
| 
 | ||||
|     for (const auto &member : relation.members()) | ||||
| @ -157,11 +164,11 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const | ||||
|                          0 == strcmp("via", role)); | ||||
|             if (0 == strcmp("from", role)) | ||||
|             { | ||||
|                 from = static_cast<std::uint64_t>(member.ref()); | ||||
|                 from_ways.push_back({static_cast<std::uint64_t>(member.ref())}); | ||||
|             } | ||||
|             else if (0 == strcmp("to", role)) | ||||
|             { | ||||
|                 to = static_cast<std::uint64_t>(member.ref()); | ||||
|                 to_ways.push_back({static_cast<std::uint64_t>(member.ref())}); | ||||
|             } | ||||
|             else if (0 == strcmp("via", role)) | ||||
|             { | ||||
| @ -178,6 +185,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::vector<util::OpeningHours> condition; | ||||
|     // parse conditional tags
 | ||||
|     if (parse_conditionals) | ||||
|     { | ||||
| @ -199,32 +207,54 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const | ||||
|                 std::vector<util::OpeningHours> 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<InputTurnRestriction> 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 | ||||
|  | ||||
| @ -909,13 +909,15 @@ void Sol2ScriptingEnvironment::ProcessElements( | ||||
|         case osmium::item_type::relation: | ||||
|         { | ||||
|             const auto &relation = static_cast<const osmium::Relation &>(*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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user