osrm-backend/include/extractor/restriction.hpp
Michael Bell 5266ac1635
Add support for multiple via-way restrictions (#5907)
Currently OSRM only supports turn restrictions with a single via-node or one
via-way. OSM allows for multiple via-ways to represent longer and more
complex restrictions.

This PR extends the use of duplicate nodes for representng via-way turn
restrictions to also support multi via-way restrictions. Effectively, this
increases the edge-based graph size by the number of edges in multi via-way
restrictions. However, given the low number of these restrictions it
has little effect on total graph size.

In addition, we add a new step in the extraction phase that constructs
a restriction graph to support more complex relationships between restrictions,
such as nested restrictions and overlapping restrictions.
2020-12-20 13:59:57 -08:00

297 lines
8.7 KiB
C++

#ifndef RESTRICTION_HPP
#define RESTRICTION_HPP
#include "util/coordinate.hpp"
#include "util/opening_hours.hpp"
#include "util/typedefs.hpp"
#include "mapbox/variant.hpp"
#include <limits>
namespace osrm
{
namespace extractor
{
// OSM offers two types of restrictions, via node and via-way restrictions. We parse both into the
// same input container
//
// A restriction turning at a single node. This is the most common type of restriction:
//
// a - b - c
// |
// d
//
// ab via b to bd
struct InputNodeRestriction
{
OSMWayID from;
OSMNodeID via;
OSMWayID to;
};
// A restriction that uses one or more via-way in between
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe -- no u turn
struct InputWayRestriction
{
OSMWayID from;
std::vector<OSMWayID> via;
OSMWayID to;
};
// Outside view of the variant, these are equal to the `which()` results
enum RestrictionType
{
NODE_RESTRICTION = 0,
WAY_RESTRICTION = 1,
NUM_RESTRICTION_TYPES = 2
};
struct InputTurnRestriction
{
// keep in the same order as the turn restrictions below
mapbox::util::variant<InputNodeRestriction, InputWayRestriction> node_or_way;
bool is_only;
// We represent conditional and unconditional restrictions with the same structure.
// Unconditional restrictions will have empty conditions.
std::vector<util::OpeningHours> condition;
OSMWayID From() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<InputNodeRestriction>(node_or_way).from
: mapbox::util::get<InputWayRestriction>(node_or_way).from;
}
OSMWayID To() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<InputNodeRestriction>(node_or_way).to
: mapbox::util::get<InputWayRestriction>(node_or_way).to;
}
RestrictionType Type() const
{
BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES);
return static_cast<RestrictionType>(node_or_way.which());
}
InputWayRestriction &AsWayRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<InputWayRestriction>(node_or_way);
}
const InputWayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<InputWayRestriction>(node_or_way);
}
InputNodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<InputNodeRestriction>(node_or_way);
}
const InputNodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<InputNodeRestriction>(node_or_way);
}
};
// OSRM manages restrictions based on node IDs which refer to the last node along the edge. Note
// that this has the side-effect of not allowing parallel edges!
//
// a - b - c
// |
// d
//
// ab via b to bd
struct NodeRestriction
{
NodeID from;
NodeID via;
NodeID to;
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via != SPECIAL_NODEID;
};
bool operator==(const NodeRestriction &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// A way restriction in the context of OSRM requires translation into NodeIDs. This is due to the
// compression happening in the graph creation process which would make it difficult to track
// way-ids over a series of operations. Having access to the nodes directly allows look-up of the
// edges in the processed structures
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe -- no u turn
struct WayRestriction
{
// A way restriction in OSRM needs to track all nodes that make up the via ways. Whilst most
// of these nodes will be removed by compression, some nodes will contain features that need to
// be considered when routing (e.g. intersections, nested restrictions, etc).
NodeID from;
std::vector<NodeID> via;
NodeID to;
// check if all parts of the restriction reference an actual node
bool Valid() const
{
return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via.size() >= 2 &&
std::all_of(via.begin(), via.end(), [](NodeID i) { return i != SPECIAL_NODEID; });
};
bool operator==(const WayRestriction &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// Wrapper for turn restrictions that gives more information on its type / handles the switch
// between node/way restrictions
struct TurnRestriction
{
// keep in the same order as the turn restrictions above
mapbox::util::variant<NodeRestriction, WayRestriction> node_or_way;
bool is_only;
// We represent conditional and unconditional restrictions with the same structure.
// Unconditional restrictions will have empty conditions.
std::vector<util::OpeningHours> condition;
// construction for NodeRestrictions
explicit TurnRestriction(NodeRestriction node_restriction, bool is_only = false)
: node_or_way(node_restriction), is_only(is_only)
{
}
// construction for WayRestrictions
explicit TurnRestriction(WayRestriction way_restriction, bool is_only = false)
: node_or_way(way_restriction), is_only(is_only)
{
}
explicit TurnRestriction()
{
node_or_way = NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
}
NodeID To() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<NodeRestriction>(node_or_way).to
: mapbox::util::get<WayRestriction>(node_or_way).to;
}
NodeID From() const
{
return node_or_way.which() == RestrictionType::NODE_RESTRICTION
? mapbox::util::get<NodeRestriction>(node_or_way).from
: mapbox::util::get<WayRestriction>(node_or_way).from;
}
NodeID FirstVia() const
{
if (node_or_way.which() == RestrictionType::NODE_RESTRICTION)
{
return mapbox::util::get<NodeRestriction>(node_or_way).via;
}
else
{
BOOST_ASSERT(!mapbox::util::get<WayRestriction>(node_or_way).via.empty());
return mapbox::util::get<WayRestriction>(node_or_way).via[0];
}
}
bool IsTurnRestricted(NodeID to) const { return is_only ? To() != to : To() == to; }
bool IsUnconditional() const { return condition.empty(); }
WayRestriction &AsWayRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<WayRestriction>(node_or_way);
}
const WayRestriction &AsWayRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION);
return mapbox::util::get<WayRestriction>(node_or_way);
}
NodeRestriction &AsNodeRestriction()
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<NodeRestriction>(node_or_way);
}
const NodeRestriction &AsNodeRestriction() const
{
BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION);
return mapbox::util::get<NodeRestriction>(node_or_way);
}
RestrictionType Type() const
{
BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES);
return static_cast<RestrictionType>(node_or_way.which());
}
// check if all elements of the edge are considered valid
bool Valid() const
{
if (node_or_way.which() == RestrictionType::WAY_RESTRICTION)
{
auto const &restriction = AsWayRestriction();
return restriction.Valid();
}
else
{
auto const &restriction = AsNodeRestriction();
return restriction.Valid();
}
}
bool operator==(const TurnRestriction &other) const
{
if (is_only != other.is_only)
return false;
if (Type() != other.Type())
return false;
if (Type() == RestrictionType::WAY_RESTRICTION)
{
return AsWayRestriction() == other.AsWayRestriction();
}
else
{
return AsNodeRestriction() == other.AsNodeRestriction();
}
}
};
} // namespace extractor
} // namespace osrm
#endif // RESTRICTION_HPP