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.
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/maneuver_override.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <memory>
|
||||
@@ -18,6 +17,7 @@ namespace extractor
|
||||
|
||||
class CompressedEdgeContainer;
|
||||
struct TurnRestriction;
|
||||
struct UnresolvedManeuverOverride;
|
||||
|
||||
class GraphCompressor
|
||||
{
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
#include "turn_path.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/vector_view.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <mapbox/variant.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -17,7 +21,7 @@ namespace extractor
|
||||
// Data that is loaded from the OSM datafile directly
|
||||
struct InputManeuverOverride
|
||||
{
|
||||
std::vector<OSMWayID> via_ways;
|
||||
InputTurnPath turn_path;
|
||||
OSMNodeID via_node;
|
||||
std::string maneuver;
|
||||
std::string direction;
|
||||
@@ -26,9 +30,7 @@ struct InputManeuverOverride
|
||||
// Object returned by the datafacade
|
||||
struct ManeuverOverride
|
||||
{
|
||||
// util::ViewOrVector<NodeID, storage::Ownership::View> node_sequence;
|
||||
std::vector<NodeID> node_sequence;
|
||||
// before the turn, then later, the edge_based_node_id of the turn
|
||||
NodeID instruction_node; // node-based node ID
|
||||
guidance::TurnType::Enum override_type;
|
||||
guidance::DirectionModifier::Enum direction;
|
||||
@@ -40,12 +42,12 @@ struct StorageManeuverOverride
|
||||
std::uint32_t node_sequence_offset_begin;
|
||||
std::uint32_t node_sequence_offset_end;
|
||||
NodeID start_node;
|
||||
// before the turn, then later, the edge_based_node_id of the turn
|
||||
NodeID instruction_node; // node-based node ID
|
||||
guidance::TurnType::Enum override_type;
|
||||
guidance::DirectionModifier::Enum direction;
|
||||
};
|
||||
|
||||
// Used to identify maneuver turns whilst generating edge-based graph
|
||||
struct NodeBasedTurn
|
||||
{
|
||||
NodeID from;
|
||||
@@ -58,29 +60,88 @@ struct NodeBasedTurn
|
||||
}
|
||||
};
|
||||
|
||||
// Internal representation of maneuvers during graph extraction phase
|
||||
struct UnresolvedManeuverOverride
|
||||
{
|
||||
|
||||
std::vector<NodeBasedTurn>
|
||||
turn_sequence; // initially the internal node-based-node ID of the node
|
||||
// before the turn, then later, the edge_based_node_id of the turn
|
||||
// The turn sequence that the maneuver override applies to.
|
||||
TurnPath turn_path;
|
||||
NodeID instruction_node; // node-based node ID
|
||||
guidance::TurnType::Enum override_type;
|
||||
guidance::DirectionModifier::Enum direction;
|
||||
|
||||
UnresolvedManeuverOverride()
|
||||
{
|
||||
turn_path = {ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}};
|
||||
instruction_node = SPECIAL_NODEID;
|
||||
override_type = guidance::TurnType::Invalid;
|
||||
direction = guidance::DirectionModifier::MaxDirectionModifier;
|
||||
}
|
||||
|
||||
// check if all parts of the restriction reference an actual node
|
||||
bool Valid() const
|
||||
{
|
||||
return !turn_sequence.empty() &&
|
||||
std::none_of(turn_sequence.begin(),
|
||||
turn_sequence.end(),
|
||||
[](const auto &n) {
|
||||
return n.from == SPECIAL_NODEID || n.via == SPECIAL_NODEID ||
|
||||
n.to == SPECIAL_NODEID;
|
||||
}) &&
|
||||
(direction != guidance::DirectionModifier::MaxDirectionModifier ||
|
||||
override_type != guidance::TurnType::Invalid);
|
||||
if ((direction == guidance::DirectionModifier::MaxDirectionModifier &&
|
||||
override_type == guidance::TurnType::Invalid) ||
|
||||
instruction_node == SPECIAL_NODEID || !turn_path.Valid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
|
||||
{
|
||||
const auto node_path = turn_path.AsViaNodePath();
|
||||
if (node_path.via != instruction_node)
|
||||
{
|
||||
util::Log(logDEBUG) << "Maneuver via-node " << node_path.via
|
||||
<< " does not match instruction node " << instruction_node;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
|
||||
const auto way_path = turn_path.AsViaWayPath();
|
||||
|
||||
if (std::find(way_path.via.begin(), way_path.via.end(), instruction_node) ==
|
||||
way_path.via.end())
|
||||
{
|
||||
util::Log(logDEBUG) << "Maneuver via-way path does not contain instruction node "
|
||||
<< instruction_node;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate sequence of node-based-node turns. Used to identify the maneuver's edge-based-node
|
||||
// turns during graph expansion.
|
||||
std::vector<NodeBasedTurn> Turns() const
|
||||
{
|
||||
if (turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH)
|
||||
{
|
||||
const auto node_maneuver = turn_path.AsViaNodePath();
|
||||
return {{node_maneuver.from, node_maneuver.via, node_maneuver.to}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH);
|
||||
std::vector<NodeBasedTurn> result;
|
||||
const auto way_maneuver = turn_path.AsViaWayPath();
|
||||
BOOST_ASSERT(way_maneuver.via.size() >= 2);
|
||||
result.push_back({way_maneuver.from, way_maneuver.via[0], way_maneuver.via[1]});
|
||||
|
||||
for (auto i : util::irange<size_t>(0, way_maneuver.via.size() - 2))
|
||||
{
|
||||
result.push_back(
|
||||
{way_maneuver.via[i], way_maneuver.via[i + 1], way_maneuver.via[i + 2]});
|
||||
}
|
||||
result.push_back({way_maneuver.via[way_maneuver.via.size() - 2],
|
||||
way_maneuver.via.back(),
|
||||
way_maneuver.to});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string Name() { return "maneuver override"; };
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "mapbox/variant.hpp"
|
||||
#include "turn_path.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace osrm
|
||||
@@ -13,282 +14,54 @@ 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
|
||||
};
|
||||
|
||||
// External (OSM) representation of restriction
|
||||
struct InputTurnRestriction
|
||||
{
|
||||
// keep in the same order as the turn restrictions below
|
||||
mapbox::util::variant<InputNodeRestriction, InputWayRestriction> node_or_way;
|
||||
InputTurnPath turn_path;
|
||||
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
|
||||
// Internal (OSRM) representation of restriction
|
||||
struct TurnRestriction
|
||||
{
|
||||
// keep in the same order as the turn restrictions above
|
||||
mapbox::util::variant<NodeRestriction, WayRestriction> node_or_way;
|
||||
// The turn sequence that the restriction applies to.
|
||||
TurnPath turn_path;
|
||||
// Indicates if the restriction turn *must* or *must not* be taken.
|
||||
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(const WayRestriction &way_restriction, bool is_only = false)
|
||||
: node_or_way(way_restriction), is_only(is_only)
|
||||
explicit TurnRestriction(const TurnPath &turn_path, bool is_only = false)
|
||||
: turn_path(turn_path), is_only(is_only)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TurnRestriction()
|
||||
{
|
||||
node_or_way = NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID};
|
||||
turn_path = {ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}};
|
||||
}
|
||||
|
||||
NodeID To() const
|
||||
bool IsTurnRestricted(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;
|
||||
return is_only ? turn_path.To() != to : turn_path.To() == 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 Valid() const { return turn_path.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();
|
||||
}
|
||||
return turn_path == other.turn_path;
|
||||
}
|
||||
|
||||
static std::string Name() { return "turn restriction"; };
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#ifndef OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
|
||||
#define OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
|
||||
|
||||
#include "extractor/maneuver_override.hpp"
|
||||
#include "extractor/restriction.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct NodeRestriction;
|
||||
struct TurnRestriction;
|
||||
|
||||
// OSRM stores restrictions as node -> [node] -> node instead of way -> node -> way (or
|
||||
// way->[way]->way) as it is done in OSM. These restrictions need to match the state of graph
|
||||
// compression which we perform in the graph compressor that removes certain degree two nodes from
|
||||
// the graph (all but the ones with penalties/barriers, as of the state of writing).
|
||||
// Since this graph compression ins performed after creating the restrictions in the extraction
|
||||
// phase, we need to update the involved nodes whenever one of the nodes is compressed.
|
||||
//
|
||||
//
|
||||
// !!!! Will bind to the restrictions vector and modify it in-place !!!!
|
||||
class RestrictionCompressor
|
||||
{
|
||||
public:
|
||||
RestrictionCompressor(std::vector<TurnRestriction> &restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
|
||||
|
||||
// account for the compression of `from-via-to` into `from-to`
|
||||
void Compress(const NodeID from, const NodeID via, const NodeID to);
|
||||
|
||||
private:
|
||||
// A turn restriction is given as `from star via node to end`. Edges ending at `head` being
|
||||
// contracted move the head pointer to their respective head. Edges starting at tail move the
|
||||
// tail values to their respective tails.
|
||||
// Via nodes that are compressed are removed from the restriction representation.
|
||||
// We do not compress the first and last via nodes of a restriction as they act as
|
||||
// entrance/exit points into the restriction graph. For a node restriction, the first and last
|
||||
// via nodes are the same.
|
||||
boost::unordered_multimap<NodeID, TurnRestriction *> starts;
|
||||
boost::unordered_multimap<NodeID, TurnRestriction *> vias;
|
||||
boost::unordered_multimap<NodeID, TurnRestriction *> ends;
|
||||
|
||||
boost::unordered_multimap<NodeID, NodeBasedTurn *> maneuver_starts;
|
||||
boost::unordered_multimap<NodeID, NodeBasedTurn *> maneuver_ends;
|
||||
};
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_
|
||||
#define OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_
|
||||
|
||||
#include "extractor/restriction.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
// To avoid handling invalid restrictions / creating unnecessary duplicate nodes for via-ways, we do
|
||||
// a pre-flight check for restrictions and remove all invalid restrictions from the data. Use as
|
||||
// `restrictions = removeInvalidRestrictions(std::move(restrictions))`
|
||||
std::vector<TurnRestriction> removeInvalidRestrictions(std::vector<TurnRestriction>,
|
||||
const util::NodeBasedDynamicGraph &);
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include "extractor/restriction_filter.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
@@ -13,6 +12,8 @@ namespace osrm
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct TurnRestriction;
|
||||
|
||||
namespace restriction_graph_details
|
||||
{
|
||||
struct transferBuilder;
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
#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
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_
|
||||
#define OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct TurnPath;
|
||||
struct TurnRestriction;
|
||||
struct UnresolvedManeuverOverride;
|
||||
|
||||
// OSRM stores turn paths as node -> [node] -> node instead of way -> node -> way (or
|
||||
// way->[way]->way) as it is done in OSM. These paths need to match the state of graph
|
||||
// compression which we perform in the graph compressor that removes certain degree two nodes from
|
||||
// the graph (all but the ones with penalties/barriers, as of the state of writing).
|
||||
// Since this graph compression is performed after creating the turn paths in the extraction
|
||||
// phase, we need to update the involved nodes whenever one of the nodes is compressed.
|
||||
//
|
||||
//
|
||||
// !!!! Will bind to the restriction/maneuver vectors and modify it in-place !!!!
|
||||
class TurnPathCompressor
|
||||
{
|
||||
public:
|
||||
TurnPathCompressor(std::vector<TurnRestriction> &restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
|
||||
|
||||
// account for the compression of `from-via-to` into `from-to`
|
||||
void Compress(const NodeID from, const NodeID via, const NodeID to);
|
||||
|
||||
private:
|
||||
// A turn path is given as `from start via node(s) to end`. Edges ending at `head` being
|
||||
// contracted move the head pointer to their respective head. Edges starting at tail move the
|
||||
// tail values to their respective tails.
|
||||
// Via nodes that are compressed are removed from the restriction representation.
|
||||
// We do not compress the first and last via nodes of a restriction as they act as
|
||||
// entrance/exit points into the restriction graph. For a node restriction, the first and last
|
||||
// via nodes are the same.
|
||||
// Similarly, we do not compress the instruction via node in a maneuver override, as we need
|
||||
// this to identify the location of the maneuver during routing path-processing.
|
||||
boost::unordered_multimap<NodeID, TurnPath *> starts;
|
||||
boost::unordered_multimap<NodeID, TurnPath *> vias;
|
||||
boost::unordered_multimap<NodeID, TurnPath *> ends;
|
||||
};
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_
|
||||
#define OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_
|
||||
|
||||
#include "extractor/restriction.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
// To avoid handling invalid turn paths / creating unnecessary duplicate nodes for via-ways, we do
|
||||
// a pre-flight check for paths and remove all invalid turn relations from the data. Use as
|
||||
// `restrictions = removeInvalidRestrictions(std::move(restrictions))`
|
||||
template <typename T>
|
||||
std::vector<T> removeInvalidTurnPaths(std::vector<T>, const util::NodeBasedDynamicGraph &);
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_
|
||||
Reference in New Issue
Block a user