osrm-backend/include/extractor/turn_path.hpp
Michael Bell a98074a051
Improvements to maneuver override processing (#6215)
This change unblocks the osrm-extract debug build, which is
currently failing on a maneuver override assertion.

The processing of maneuver overrides currently has three issues
- It assumes the via node(s) can't be compressed (the failing assertion)
- It can't handle via-paths containing incompressible nodes
- It doesn't interop with turn restriction on the same path

Turn restrictions and maneuver overrides both use the same
from-via-to path representation.
Therefore, we can fix these issues by consolidating their
structures and reusing the path representation for
turn restrictions, which already is robust to the above
issues.

This also simplifies some of the codebase by removing maneuver
override specific path processing.

There are ~100 maneuver overrides in the OSM database, so the
impact on processing and routing will be minimal.
2022-08-24 16:19:24 +01:00

270 lines
7.3 KiB
C++

#ifndef OSRM_TURN_PATH_HPP
#define OSRM_TURN_PATH_HPP
#include "util/typedefs.hpp"
#include <algorithm>
#include <mapbox/variant.hpp>
#include <vector>
namespace osrm
{
namespace extractor
{
// Outside view of the variant, these are equal to the `which()` results
enum TurnPathType
{
VIA_NODE_TURN_PATH = 0,
VIA_WAY_TURN_PATH = 1,
NUM_TURN_PATH_TYPES = 2
};
// OSM turn restrictions and maneuver overrides are relations that use the same path
// representation. Therefore, we can represent these paths by a shared, common structure.
//
// from: the way from which the turn sequence begins
// via: a node or list of ways, representing the intermediate path taken in the turn sequence
// to: the final way in the turn sequence
//
// We will have two representations of the paths, via-node and via-way, to represent the two options
// for the intermediate path. We parse both into the same input container
//
// A path turning at a single node. This is the most common type of relation:
//
// a - b - c
// |
// d
//
// ab via b to bd
struct InputViaNodePath
{
OSMWayID from;
OSMNodeID via;
OSMWayID to;
};
// A turn path that uses one or more via-way in between
//
// e - f - g
// |
// d
// |
// a - b - c
//
// ab via bd,df to fe
struct InputViaWayPath
{
OSMWayID from;
std::vector<OSMWayID> via;
OSMWayID to;
};
struct InputTurnPath
{
mapbox::util::variant<InputViaNodePath, InputViaWayPath> node_or_way;
TurnPathType Type() const
{
BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES);
return static_cast<TurnPathType>(node_or_way.which());
}
OSMWayID From() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<InputViaNodePath>(node_or_way).from
: mapbox::util::get<InputViaWayPath>(node_or_way).from;
}
OSMWayID To() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<InputViaNodePath>(node_or_way).to
: mapbox::util::get<InputViaWayPath>(node_or_way).to;
}
InputViaWayPath &AsViaWayPath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<InputViaWayPath>(node_or_way);
}
const InputViaWayPath &AsViaWayPath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<InputViaWayPath>(node_or_way);
}
InputViaNodePath &AsViaNodePath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<InputViaNodePath>(node_or_way);
}
const InputViaNodePath &AsViaNodePath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<InputViaNodePath>(node_or_way);
}
};
// Internally, we convert the turn paths into a node-based-node representation.
// This allows us to correctly track the edges as they processed, such as during graph compression.
// Having access to the nodes directly allows look-up of the edges in the processed structures,
// and can be utilised during edge-based-graph generation.
//
// Once again, we keep two representations of the paths, via-node and via-way, for more efficient
// representation of the more common via-node path.
//
// a - b - c
// |
// d
//
// a via b to d
struct ViaNodePath
{
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 ViaNodePath &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
//
//
// e - f - g
// |
// d
// |
// a - b - c
//
// a via bdf to e
// (after compression) a via bf to e
struct ViaWayPath
{
// A way path 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 path 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 ViaWayPath &other) const
{
return std::tie(from, via, to) == std::tie(other.from, other.via, other.to);
}
};
// Wrapper for turn paths that gives more information on its type / handles the switch
// between node/way paths
struct TurnPath
{
mapbox::util::variant<ViaNodePath, ViaWayPath> node_or_way;
NodeID To() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<ViaNodePath>(node_or_way).to
: mapbox::util::get<ViaWayPath>(node_or_way).to;
}
NodeID From() const
{
return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH
? mapbox::util::get<ViaNodePath>(node_or_way).from
: mapbox::util::get<ViaWayPath>(node_or_way).from;
}
NodeID FirstVia() const
{
if (node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH)
{
return mapbox::util::get<ViaNodePath>(node_or_way).via;
}
else
{
BOOST_ASSERT(!mapbox::util::get<ViaWayPath>(node_or_way).via.empty());
return mapbox::util::get<ViaWayPath>(node_or_way).via[0];
}
}
ViaWayPath &AsViaWayPath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<ViaWayPath>(node_or_way);
}
const ViaWayPath &AsViaWayPath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH);
return mapbox::util::get<ViaWayPath>(node_or_way);
}
ViaNodePath &AsViaNodePath()
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<ViaNodePath>(node_or_way);
}
const ViaNodePath &AsViaNodePath() const
{
BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH);
return mapbox::util::get<ViaNodePath>(node_or_way);
}
TurnPathType Type() const
{
BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES);
return static_cast<TurnPathType>(node_or_way.which());
}
bool operator==(const TurnPath &other) const
{
if (Type() != other.Type())
return false;
if (Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
return AsViaWayPath() == other.AsViaWayPath();
}
else
{
return AsViaNodePath() == other.AsViaNodePath();
}
}
bool Valid() const
{
if (Type() == TurnPathType::VIA_WAY_TURN_PATH)
{
return AsViaWayPath().Valid();
}
else
{
return AsViaNodePath().Valid();
}
};
};
} // namespace extractor
} // namespace osrm
#endif // OSRM_TURN_PATH_HPP