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:
Michael Bell
2022-08-22 12:58:16 +01:00
committed by GitHub
parent b4b1aea567
commit 3cfd0e8334
5 changed files with 184 additions and 31 deletions
+57 -27
View File
@@ -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
+5 -3
View File
@@ -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;