diff --git a/CHANGELOG.md b/CHANGELOG.md index 77bf8efc1..cc561bbb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - CHANGED: Lazily generate optional route path data [#6045](https://github.com/Project-OSRM/osrm-backend/pull/6045) - 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) + - FIXED: Improvements to maneuver override processing [#6125](https://github.com/Project-OSRM/osrm-backend/pull/6125) # 5.26.0 - Changes from 5.25.0 diff --git a/features/guidance/maneuver-tag.feature b/features/guidance/maneuver-tag.feature index 6a4fab9f7..243090579 100644 --- a/features/guidance/maneuver-tag.feature +++ b/features/guidance/maneuver-tag.feature @@ -230,3 +230,100 @@ Feature: Maneuver tag support | waypoints | route | turns | | z,t | NY Ave,395,395 | depart,on ramp left,arrive | | z,b | NY Ave,,4th St,4th St | depart,on ramp left,fork slight right,arrive | + + Scenario: Gracefully handles maneuvers that are redundant for the profile + Given the node map + """ + a--b---c---d----f + | + | + e + """ + And the ways + | nodes | name | oneway | highway | + | abc | A Street | no | primary | + | ce | B Street | no | construction | + | cdf | A Street | no | primary | + + And the relations + | type | way:from | node:via | way:to | maneuver | direction | + | maneuver | abc | c | cdf | turn | slight_left | + + When I route I should get + | waypoints | route | turns | + | a,f | A Street,A Street | depart,arrive | + + Scenario: Handles uncompressed nodes in maneuver path + Given the node map + """ + a--b---c---f + | | + | | + g d---h + | + | + i-------e-------j + """ + And the ways + | nodes | name | oneway | + | abc | A Street | no | + | cf | B Street | no | + | cde | C Street | no | + | bg | D Street | no | + | dh | E Street | no | + | ei | F Street | no | + | ej | G Street | no | + + And the relations + | type | way:from | node:via | way:via | way:to | maneuver | direction | + | maneuver | abc | e | cde | ei | turn | sharp_right | + + When I route I should get + | waypoints | route | turns | + | a,i | A Street,C Street,F Street,F Street | depart,turn right,turn sharp right,arrive | + + + Scenario: Can be used with turn restrictions + Given the node map + """ + a---b---c + | + | + d + | + e---f + | + | + h------g---i + """ + And the ways + | nodes | name | oneway | + | ab | A Street | no | + | bc | B Street | no | + | bde | C Street | no | + | ef | D Street | no | + | eg | E Street | no | + | hg | F Street | no | + | gi | G Street | no | + + And the relations + | type | way:from | node:via | way:to | maneuver | direction | # | + | maneuver | ab | b | bde | turn | sharp_right | ending on a turn restriction via way | + | maneuver | bde | e | ef | turn | sharp_left | starting on a turn restriction via way | + + And the relations + | type | way:from | node:via | way:via | way:to | maneuver | direction | # | + | maneuver | cb | g | bde,eg | gi | turn | slight_left | turn restricted | + | maneuver | cb | g | bde,eg | hg | turn | slight_right | not turn restricted | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bde,eg | hg | no_right_turn | + | restriction | bc | bde,eg | gi | no_left_turn | + + When I route I should get + | waypoints | route | turns | + | a,e | A Street,C Street,C Street | depart,turn sharp right,arrive | + | b,f | C Street,D Street,D Street | depart,turn sharp left,arrive | + | c,h | B Street,E Street,F Street,F Street | depart,turn left,turn slight right,arrive | + | c,i | B Street,A Street,E Street,G Street,G Street | depart,turn uturn,turn right,end of road left,arrive | diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp index 9a976e327..9e9ddd036 100644 --- a/include/extractor/graph_compressor.hpp +++ b/include/extractor/graph_compressor.hpp @@ -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 @@ -18,6 +17,7 @@ namespace extractor class CompressedEdgeContainer; struct TurnRestriction; +struct UnresolvedManeuverOverride; class GraphCompressor { diff --git a/include/extractor/maneuver_override.hpp b/include/extractor/maneuver_override.hpp index ab0d2837a..79b23d301 100644 --- a/include/extractor/maneuver_override.hpp +++ b/include/extractor/maneuver_override.hpp @@ -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 #include +#include namespace osrm { @@ -17,7 +21,7 @@ namespace extractor // Data that is loaded from the OSM datafile directly struct InputManeuverOverride { - std::vector 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 node_sequence; std::vector 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 - 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 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 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(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 diff --git a/include/extractor/restriction.hpp b/include/extractor/restriction.hpp index 7f17eaeae..4dd73f747 100644 --- a/include/extractor/restriction.hpp +++ b/include/extractor/restriction.hpp @@ -6,6 +6,7 @@ #include "util/typedefs.hpp" #include "mapbox/variant.hpp" +#include "turn_path.hpp" #include 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 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 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 condition; - - OSMWayID From() const - { - return node_or_way.which() == RestrictionType::NODE_RESTRICTION - ? mapbox::util::get(node_or_way).from - : mapbox::util::get(node_or_way).from; - } - - OSMWayID To() const - { - return node_or_way.which() == RestrictionType::NODE_RESTRICTION - ? mapbox::util::get(node_or_way).to - : mapbox::util::get(node_or_way).to; - } - - RestrictionType Type() const - { - BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES); - return static_cast(node_or_way.which()); - } - - InputWayRestriction &AsWayRestriction() - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - const InputWayRestriction &AsWayRestriction() const - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - InputNodeRestriction &AsNodeRestriction() - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - const InputNodeRestriction &AsNodeRestriction() const - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION); - return mapbox::util::get(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 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 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 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(node_or_way).to - : mapbox::util::get(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(node_or_way).from - : mapbox::util::get(node_or_way).from; - } - - NodeID FirstVia() const - { - if (node_or_way.which() == RestrictionType::NODE_RESTRICTION) - { - return mapbox::util::get(node_or_way).via; - } - else - { - BOOST_ASSERT(!mapbox::util::get(node_or_way).via.empty()); - return mapbox::util::get(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(node_or_way); - } - - const WayRestriction &AsWayRestriction() const - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - NodeRestriction &AsNodeRestriction() - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - const NodeRestriction &AsNodeRestriction() const - { - BOOST_ASSERT(node_or_way.which() == RestrictionType::NODE_RESTRICTION); - return mapbox::util::get(node_or_way); - } - - RestrictionType Type() const - { - BOOST_ASSERT(node_or_way.which() < RestrictionType::NUM_RESTRICTION_TYPES); - return static_cast(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 diff --git a/include/extractor/restriction_compressor.hpp b/include/extractor/restriction_compressor.hpp deleted file mode 100644 index 500ec18e3..000000000 --- a/include/extractor/restriction_compressor.hpp +++ /dev/null @@ -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 -#include - -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 &restrictions, - std::vector &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 starts; - boost::unordered_multimap vias; - boost::unordered_multimap ends; - - boost::unordered_multimap maneuver_starts; - boost::unordered_multimap maneuver_ends; -}; - -} // namespace extractor -} // namespace osrm - -#endif // OSRM_EXTRACTOR_RESTRICTION_COMPRESSOR_HPP_ diff --git a/include/extractor/restriction_filter.hpp b/include/extractor/restriction_filter.hpp deleted file mode 100644 index 2bdc8831e..000000000 --- a/include/extractor/restriction_filter.hpp +++ /dev/null @@ -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 - -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 removeInvalidRestrictions(std::vector, - const util::NodeBasedDynamicGraph &); -} // namespace extractor -} // namespace osrm - -#endif // OSRM_EXTRACTOR_RESTRICTION_FILTER_HPP_ diff --git a/include/extractor/restriction_graph.hpp b/include/extractor/restriction_graph.hpp index 7f880b1fc..9b7d4eefe 100644 --- a/include/extractor/restriction_graph.hpp +++ b/include/extractor/restriction_graph.hpp @@ -4,7 +4,6 @@ #include #include -#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; diff --git a/include/extractor/turn_path.hpp b/include/extractor/turn_path.hpp new file mode 100644 index 000000000..78363f8dd --- /dev/null +++ b/include/extractor/turn_path.hpp @@ -0,0 +1,269 @@ +#ifndef OSRM_TURN_PATH_HPP +#define OSRM_TURN_PATH_HPP + +#include "util/typedefs.hpp" + +#include +#include +#include + +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 via; + OSMWayID to; +}; + +struct InputTurnPath +{ + mapbox::util::variant node_or_way; + + TurnPathType Type() const + { + BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES); + return static_cast(node_or_way.which()); + } + + OSMWayID From() const + { + return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH + ? mapbox::util::get(node_or_way).from + : mapbox::util::get(node_or_way).from; + } + + OSMWayID To() const + { + return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH + ? mapbox::util::get(node_or_way).to + : mapbox::util::get(node_or_way).to; + } + + InputViaWayPath &AsViaWayPath() + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + const InputViaWayPath &AsViaWayPath() const + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + InputViaNodePath &AsViaNodePath() + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + const InputViaNodePath &AsViaNodePath() const + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); + return mapbox::util::get(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 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 node_or_way; + + NodeID To() const + { + return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH + ? mapbox::util::get(node_or_way).to + : mapbox::util::get(node_or_way).to; + } + + NodeID From() const + { + return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH + ? mapbox::util::get(node_or_way).from + : mapbox::util::get(node_or_way).from; + } + + NodeID FirstVia() const + { + if (node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH) + { + return mapbox::util::get(node_or_way).via; + } + else + { + BOOST_ASSERT(!mapbox::util::get(node_or_way).via.empty()); + return mapbox::util::get(node_or_way).via[0]; + } + } + + ViaWayPath &AsViaWayPath() + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + const ViaWayPath &AsViaWayPath() const + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + ViaNodePath &AsViaNodePath() + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + const ViaNodePath &AsViaNodePath() const + { + BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); + return mapbox::util::get(node_or_way); + } + + TurnPathType Type() const + { + BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES); + return static_cast(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 diff --git a/include/extractor/turn_path_compressor.hpp b/include/extractor/turn_path_compressor.hpp new file mode 100644 index 000000000..d99130235 --- /dev/null +++ b/include/extractor/turn_path_compressor.hpp @@ -0,0 +1,54 @@ +#ifndef OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_ +#define OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_ + +#include "util/typedefs.hpp" + +#include +#include + +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 &restrictions, + std::vector &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 starts; + boost::unordered_multimap vias; + boost::unordered_multimap ends; +}; + +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_TURN_PATH_COMPRESSOR_HPP_ diff --git a/include/extractor/turn_path_filter.hpp b/include/extractor/turn_path_filter.hpp new file mode 100644 index 000000000..48f32b599 --- /dev/null +++ b/include/extractor/turn_path_filter.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 + +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 +std::vector removeInvalidTurnPaths(std::vector, const util::NodeBasedDynamicGraph &); +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_TURN_PATH_FILTER_HPP_ diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index b1f94a7b6..a362af172 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -10,24 +10,19 @@ #include "storage/io.hpp" #include "util/assert.hpp" -#include "util/bearing.hpp" #include "util/connectivity_checksum.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" -#include "util/exception.hpp" #include "util/integer_range.hpp" #include "util/log.hpp" #include "util/percent.hpp" #include "util/timing_util.hpp" #include -#include #include #include #include -#include -#include #include #include #include @@ -451,6 +446,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( { util::Log() << "Generating edge-expanded edges "; + // Keep a set of all maneuver turns so we can identify them as + // we generate the edge-expansion. + std::unordered_set unresolved_turns; + for (const auto &manuevers : unresolved_maneuver_overrides) + { + for (const auto &turn : manuevers.Turns()) + { + unresolved_turns.insert(turn); + } + } + std::size_t node_based_edge_counter = 0; SuffixTable street_name_suffix_table(scripting_environment); @@ -514,7 +520,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::vector delayed_data; // may need this std::vector conditionals; - std::unordered_map> turn_to_ebn_map; + std::unordered_multimap> turn_to_ebn_map; util::ConnectivityChecksum checksum; }; @@ -522,7 +528,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( m_connectivity_checksum = 0; - std::unordered_map> global_turn_to_ebn_map; + std::unordered_multimap> global_turn_to_ebn_map; // going over all nodes (which form the center of an intersection), we compute all possible // turns along these intersections. @@ -894,24 +900,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const auto outgoing_edge_target = m_node_based_graph.GetTarget(outgoing_edge.edge); - // TODO: this loop is not optimized - once we have a few - // overrides available, we should index this for faster - // lookups - for (auto &override : unresolved_maneuver_overrides) + const auto turn_nodes = NodeBasedTurn{ + incoming_edge.node, intersection_node, outgoing_edge_target}; + const auto is_maneuver_turn = unresolved_turns.count(turn_nodes) > 0; + + if (is_maneuver_turn) { - for (auto &turn : override.turn_sequence) - { - if (turn.from == incoming_edge.node && - turn.via == intersection_node && - turn.to == outgoing_edge_target) - { - const auto &ebn_from = - nbe_to_ebn_mapping[incoming_edge.edge]; - const auto &ebn_to = target_id; - buffer->turn_to_ebn_map[turn] = - std::make_pair(ebn_from, ebn_to); - } - } + const auto &ebn_from = nbe_to_ebn_mapping[incoming_edge.edge]; + const auto &ebn_to = target_id; + buffer->turn_to_ebn_map.insert( + {turn_nodes, std::make_pair(ebn_from, ebn_to)}); } { // scope to forget edge_with_data after @@ -1025,6 +1023,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( m_coordinates[intersection_node], restriction->condition}}); } + + // We also need to track maneuvers that traverse duplicate + // edges + if (is_maneuver_turn) + { + const auto &ebn_from = from_id; + const auto &ebn_to = via_target_id; + buffer->turn_to_ebn_map.insert( + {turn_nodes, std::make_pair(ebn_from, ebn_to)}); + } } else { @@ -1053,6 +1061,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( edge_geometries); buffer->delayed_data.push_back(edge_with_data); + + // We also need to track maneuvers that traverse duplicate + // edges + if (is_maneuver_turn) + { + const auto &ebn_from = from_id; + const auto &ebn_to = via_target_id; + buffer->turn_to_ebn_map.insert( + {turn_nodes, std::make_pair(ebn_from, ebn_to)}); + } } } } @@ -1107,35 +1125,64 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // Now, replace node-based-node ID values in the `node_sequence` with // the edge-based-node values we found and stored in the `turn_to_ebn_map` - for (auto &unresolved_override : unresolved_maneuver_overrides) + for (const auto &unresolved_override : unresolved_maneuver_overrides) { - StorageManeuverOverride storage_override; - storage_override.instruction_node = unresolved_override.instruction_node; - storage_override.override_type = unresolved_override.override_type; - storage_override.direction = unresolved_override.direction; + // There can be multiple edge-based-node sequences for a node-based-turn sequence + // due to duplicate edges in the restriction graph. + std::vector> node_sequences; - std::vector node_sequence(unresolved_override.turn_sequence.size() + 1, - SPECIAL_NODEID); + const auto &turns = unresolved_override.Turns(); - for (std::int64_t i = unresolved_override.turn_sequence.size() - 1; i >= 0; --i) - { - const auto v = global_turn_to_ebn_map.find(unresolved_override.turn_sequence[i]); - if (v != global_turn_to_ebn_map.end()) + BOOST_ASSERT(!turns.empty()); + // Populate the node sequences with the first turn values. + const auto first_turn_edges = global_turn_to_ebn_map.equal_range(turns[0]); + std::transform( + first_turn_edges.first, + first_turn_edges.second, + std::back_inserter(node_sequences), + [](const auto turn_edges) { + return std::vector{turn_edges.second.first, turn_edges.second.second}; + }); + + std::for_each(std::next(turns.begin()), turns.end(), [&](const auto &turn) { + std::vector> next_node_sequences; + const auto next_turn_edges = global_turn_to_ebn_map.equal_range(turn); + for (auto &node_sequence : node_sequences) { - node_sequence[i] = v->second.first; - node_sequence[i + 1] = v->second.second; + const auto found_it = std::find_if( + next_turn_edges.first, next_turn_edges.second, [&](const auto &turn_edges) { + const auto pre_turn_edge = turn_edges.second.first; + return (node_sequence.back() == pre_turn_edge); + }); + + if (found_it != next_turn_edges.second) + { + const auto post_turn_edge = found_it->second.second; + node_sequence.push_back(post_turn_edge); + next_node_sequences.push_back(std::move(node_sequence)); + } } + node_sequences = std::move(next_node_sequences); + }); + + for (const auto &node_sequence : node_sequences) + { + StorageManeuverOverride storage_override; + storage_override.instruction_node = unresolved_override.instruction_node; + storage_override.override_type = unresolved_override.override_type; + storage_override.direction = unresolved_override.direction; + + storage_override.node_sequence_offset_begin = maneuver_override_sequences.size(); + storage_override.node_sequence_offset_end = + maneuver_override_sequences.size() + node_sequence.size(); + + storage_override.start_node = node_sequence.front(); + + maneuver_override_sequences.insert( + maneuver_override_sequences.end(), node_sequence.begin(), node_sequence.end()); + + storage_maneuver_overrides.push_back(storage_override); } - storage_override.node_sequence_offset_begin = maneuver_override_sequences.size(); - storage_override.node_sequence_offset_end = - maneuver_override_sequences.size() + node_sequence.size(); - - storage_override.start_node = node_sequence.front(); - - maneuver_override_sequences.insert( - maneuver_override_sequences.end(), node_sequence.begin(), node_sequence.end()); - - storage_maneuver_overrides.push_back(storage_override); } } { diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index e498e1001..9ec1b2887 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -152,6 +152,228 @@ std::tuple find_turn_nodes(const oe::NodesOfWay } return std::make_tuple(SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID); } + +// Via-node paths describe a relation between the two segments closest +// to the shared via-node on the from and to ways. +// from: [a, b, c, d, e] +// to: [f, g, h, i, j] +// +// The via node establishes the orientation of the from/to intersection when choosing the +// segments. +// via | node path +// a=f | b,a,g +// a=j | b,a,i +// e=f | d,e,g +// e=j | d,e,i +oe::ViaNodePath find_via_node_path(const std::string &turn_relation_type, + const oe::NodesOfWay &from_segment, + const oe::NodesOfWay &to_segment, + const OSMNodeID via_node, + const std::function &to_internal_node) +{ + + OSMNodeID from, via, to; + std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node); + if (via == SPECIAL_OSM_NODEID) + { + // unconnected + osrm::util::Log(logDEBUG) << turn_relation_type + << " references unconnected way: " << from_segment.way_id; + return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + } + return oe::ViaNodePath{to_internal_node(from), to_internal_node(via), to_internal_node(to)}; +} + +// Via way paths are comprised of: +// 1. The segment in the from way that intersects with the via ways +// 2. All segments that make up the via path +// 3. The segment in the to way that intersects with the via path. +// +// from: [a, b, c, d, e] +// via: [[f, g, h, i, j], [k, l], [m, n, o]] +// to: [p, q, r, s] +// +// First establish the orientation of the from/via intersection by finding which end +// nodes both ways share. From this we can select the from segment. +// +// intersect | from segment | next_connection +// a=f | b,a | f +// a=j | b,a | j +// e=f | e,d | f +// e=j | e,d | j +// +// Use the next connection to inform the orientation of the first via +// way and the intersection between first and second via ways. +// +// next_connection | intersect | via result | next_next_connection +// f | j=k | [f,g,h,i,j] | k +// f | j=l | [f,g,h,i,j] | l +// j | f=k | [j,i,h,g,f] | k +// j | f=l | [j,i,h,g,f] | l +// +// This is continued for the remaining via ways, appending to the via result +// +// The final via/to intersection also uses the next_connection information in a similar fashion. +// +// next_connection | intersect | to_segment +// m | o=p | p,q +// m | o=s | s,r +// o | m=p | p,q +// o | m=s | s,r +// +// The final result is a list of nodes that represent a valid from->via->to path through the +// ways. +// +// E.g. if intersection nodes are a=j, f=l, k=o, m=s +// the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r} +oe::ViaWayPath find_via_way_path(const std::string &turn_relation_type, + const oe::NodesOfWay &from_way, + const std::vector &via_ways, + const oe::NodesOfWay &to_way, + const std::function &to_internal_node) +{ + BOOST_ASSERT(!via_ways.empty()); + + oe::ViaWayPath way_path; + + // Find the orientation of the connected ways starting with the from-via intersection. + OSMNodeID from, via; + std::tie(from, via, std::ignore) = + find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID); + if (via == SPECIAL_OSM_NODEID) + { + osrm::util::Log(logDEBUG) << turn_relation_type + << " has unconnected from and via ways: " << from_way.way_id + << ", " << via_ways.front().way_id; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + way_path.from = to_internal_node(from); + way_path.via.push_back(to_internal_node(via)); + + // Use the connection node from the previous intersection to inform our conversion of + // via ways into internal nodes. + OSMNodeID next_connection = via; + for (const auto &via_way : via_ways) + { + if (next_connection == via_way.first_segment_source_id()) + { + std::transform(std::next(via_way.node_ids.begin()), + via_way.node_ids.end(), + std::back_inserter(way_path.via), + to_internal_node); + next_connection = via_way.last_segment_target_id(); + } + else if (next_connection == via_way.last_segment_target_id()) + { + std::transform(std::next(via_way.node_ids.rbegin()), + via_way.node_ids.rend(), + std::back_inserter(way_path.via), + to_internal_node); + next_connection = via_way.first_segment_source_id(); + } + else + { + osrm::util::Log(logDEBUG) + << turn_relation_type << " has unconnected via way: " << via_way.way_id + << " to node " << next_connection; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + } + + // Add the final to node after the via-to intersection. + if (next_connection == to_way.first_segment_source_id()) + { + way_path.to = to_internal_node(to_way.first_segment_target_id()); + } + else if (next_connection == to_way.last_segment_target_id()) + { + way_path.to = to_internal_node(to_way.last_segment_source_id()); + } + else + { + osrm::util::Log(logDEBUG) << turn_relation_type + << " has unconnected via and to ways: " << via_ways.back().way_id + << ", " << to_way.way_id; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + return way_path; +} + +// Check if we were able to resolve all the involved OSM elements before translating to an +// internal via-way turn path +oe::ViaWayPath +get_via_way_path_from_OSM_ids(const std::string &turn_relation_type, + const std::unordered_map &referenced_ways, + const OSMWayID from_id, + const OSMWayID to_id, + const std::vector &via_ids, + const std::function &to_internal_node) +{ + auto const from_way_itr = referenced_ways.find(from_id); + if (from_way_itr->second.way_id != from_id) + { + osrm::util::Log(logDEBUG) << turn_relation_type + << " references invalid from way: " << from_id; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + + std::vector via_ways; + for (const auto &via_id : via_ids) + { + auto const via_segment_itr = referenced_ways.find(via_id); + if (via_segment_itr->second.way_id != via_id) + { + osrm::util::Log(logDEBUG) + << turn_relation_type << " references invalid via way: " << via_id; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + via_ways.push_back(via_segment_itr->second); + } + + auto const to_way_itr = referenced_ways.find(to_id); + if (to_way_itr->second.way_id != to_id) + { + osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid to way: " << to_id; + return oe::ViaWayPath{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + + return find_via_way_path( + turn_relation_type, from_way_itr->second, via_ways, to_way_itr->second, to_internal_node); +} + +// Check if we were able to resolve all the involved OSM elements before translating to an +// internal via-node turn path +oe::ViaNodePath +get_via_node_path_from_OSM_ids(const std::string &turn_relation_type, + const std::unordered_map &referenced_ways, + const OSMWayID from_id, + const OSMWayID to_id, + const OSMNodeID via_node, + const std::function &to_internal_node) +{ + + auto const from_segment_itr = referenced_ways.find(from_id); + + if (from_segment_itr->second.way_id != from_id) + { + osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid way: " << from_id; + return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + } + + auto const to_segment_itr = referenced_ways.find(to_id); + if (to_segment_itr->second.way_id != to_id) + { + osrm::util::Log(logDEBUG) << turn_relation_type << " references invalid way: " << to_id; + return oe::ViaNodePath{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + } + + return find_via_node_path(turn_relation_type, + from_segment_itr->second, + to_segment_itr->second, + via_node, + to_internal_node); +} + } // namespace namespace osrm @@ -177,10 +399,10 @@ ExtractionContainers::ExtractionContainers() * Processes the collected data and serializes it. * At this point nodes are still referenced by their OSM id. * - * - map start-end nodes of ways to ways used in restrictions to compute compressed - * trippe representation - * - filter nodes list to nodes that are referenced by ways - * - merge edges with nodes to include location of start/end points and serialize + * - Identify nodes of ways used in restrictions and maneuver overrides + * - Filter nodes list to nodes that are referenced by ways + * - Prepare edges and compute routing properties + * - Prepare and validate restrictions and maneuver overrides * */ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environment, @@ -723,11 +945,17 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverr const auto mark_ids = [&](auto const &external_maneuver_override) { NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}}; - std::for_each(external_maneuver_override.via_ways.begin(), - external_maneuver_override.via_ways.end(), - [&maneuver_override_ways, dummy_segment](const auto &element) { - maneuver_override_ways[element] = dummy_segment; - }); + const auto &turn_path = external_maneuver_override.turn_path; + maneuver_override_ways[turn_path.From()] = dummy_segment; + maneuver_override_ways[turn_path.To()] = dummy_segment; + if (external_maneuver_override.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH) + { + const auto &way = turn_path.AsViaWayPath(); + for (const auto &via : way.via) + { + maneuver_override_ways[via] = dummy_segment; + } + } }; // First, make an empty hashtable keyed by the ways referenced @@ -767,36 +995,6 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv return internal; }; - auto const get_turn_from_way_pair = [&](const OSMWayID &from_id, const OSMWayID &to_id) { - auto const from_segment_itr = maneuver_override_ways.find(from_id); - if (from_segment_itr->second.way_id != from_id) - { - util::Log(logDEBUG) << "Override references invalid way: " << from_id; - return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - - auto const to_segment_itr = maneuver_override_ways.find(to_id); - if (to_segment_itr->second.way_id != to_id) - { - util::Log(logDEBUG) << "Override references invalid way: " << to_id; - return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - - OSMNodeID from, via, to; - std::tie(from, via, to) = - find_turn_nodes(from_segment_itr->second, to_segment_itr->second, SPECIAL_OSM_NODEID); - if (via == SPECIAL_OSM_NODEID) - { - // unconnected - util::Log(logDEBUG) << "Maneuver override ways " << from_segment_itr->second.way_id - << " and " << to_segment_itr->second.way_id << " are not connected"; - return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - return NodeBasedTurn{osm_node_to_internal_nbn(from), - osm_node_to_internal_nbn(via), - osm_node_to_internal_nbn(to)}; - }; - const auto strings_to_turn_type_and_direction = [](const std::string &turn_string, const std::string &direction_string) { auto result = std::make_pair(guidance::TurnType::MaxTurnType, @@ -862,44 +1060,36 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv // Returns true on successful transformation, false in case of invalid references. // Later, the UnresolvedManeuverOverride will be converted into a final ManeuverOverride // once the edge-based-node IDs are generated by the edge-based-graph-factory - const auto transform = [&](const auto &external, auto &internal) { - // Create a stub override - auto maneuver_override = - UnresolvedManeuverOverride{{}, - osm_node_to_internal_nbn(external.via_node), - guidance::TurnType::Invalid, - guidance::DirectionModifier::MaxDirectionModifier}; - - // Convert Way IDs into node-based-node IDs - // We iterate from back to front here because the first node in the node_sequence - // must eventually be a source node, but all the others must be targets. - // the get_internal_pairs_from_ways returns (source,target), so if we - // iterate backwards, we will end up with source,target,target,target,target - // in a sequence, which is what we want - for (auto i = 0ul; i < external.via_ways.size() - 1; ++i) + const auto transform = [&](const auto &external_type, auto &internal_type) { + if (external_type.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH) { - // returns the two far ends of the referenced ways - auto turn = get_turn_from_way_pair(external.via_ways[i], external.via_ways[i + 1]); - - maneuver_override.turn_sequence.push_back(turn); + auto const &external = external_type.turn_path.AsViaWayPath(); + internal_type.turn_path.node_or_way = + get_via_way_path_from_OSM_ids(internal_type.Name(), + maneuver_override_ways, + external.from, + external.to, + external.via, + osm_node_to_internal_nbn); + } + else + { + BOOST_ASSERT(external_type.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH); + auto const &external = external_type.turn_path.AsViaNodePath(); + internal_type.turn_path.node_or_way = + get_via_node_path_from_OSM_ids(internal_type.Name(), + maneuver_override_ways, + external.from, + external.to, + external.via, + osm_node_to_internal_nbn); } - // check if we were able to resolve all the involved ways - // auto maneuver_override = - // get_maneuver_override_from_OSM_ids(external.from, external.to, - // external.via_node); + internal_type.instruction_node = osm_node_to_internal_nbn(external_type.via_node), + std::tie(internal_type.override_type, internal_type.direction) = + strings_to_turn_type_and_direction(external_type.maneuver, external_type.direction); - std::tie(maneuver_override.override_type, maneuver_override.direction) = - strings_to_turn_type_and_direction(external.maneuver, external.direction); - - if (!maneuver_override.Valid()) - { - util::Log(logDEBUG) << "Override is invalid"; - return false; - } - - internal = std::move(maneuver_override); - return true; + return internal_type.Valid(); }; const auto transform_into_internal_types = @@ -925,7 +1115,7 @@ void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuv ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWays() { - // Contains the nodes of each way that is part of an restriction + // Contains the nodes of each way that is part of a restriction ReferencedWays restriction_ways; // Prepare for extracting nodes for all restrictions @@ -937,23 +1127,17 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWa // nodes of these ways. const auto mark_ids = [&](auto const &turn_restriction) { NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}}; - if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) + const auto &turn_path = turn_restriction.turn_path; + restriction_ways[turn_path.From()] = dummy_segment; + restriction_ways[turn_path.To()] = dummy_segment; + if (turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH) { - const auto &way = turn_restriction.AsWayRestriction(); - restriction_ways[way.from] = dummy_segment; - restriction_ways[way.to] = dummy_segment; - for (const auto &v : way.via) + const auto &way = turn_path.AsViaWayPath(); + for (const auto &via : way.via) { - restriction_ways[v] = dummy_segment; + restriction_ways[via] = dummy_segment; } } - else - { - BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION); - const auto &node = turn_restriction.AsNodeRestriction(); - restriction_ways[node.from] = dummy_segment; - restriction_ways[node.to] = dummy_segment; - } }; std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids); @@ -992,234 +1176,41 @@ void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction return internal; }; - // Way restrictions are comprised of: - // 1. The segment in the from way that intersects with the via path - // 2. All segments that make up the via path - // 3. The segment in the to way that intersects with the via path. - // - // from: [a, b, c, d, e] - // via: [[f, g, h, i, j], [k, l], [m, n, o]] - // to: [p, q, r, s] - // - // First establish the orientation of the from/via intersection by finding which end - // nodes both ways share. From this we can select the from segment. - // - // intersect | from segment | next_connection - // a=f | b,a | f - // a=j | b,a | j - // e=f | e,d | f - // e=j | e,d | j - // - // Use the next connection to inform the orientation of the first via - // way and the intersection between first and second via ways. - // - // next_connection | intersect | via result | next_next_connection - // f | j=k | [f,g,h,i,j] | k - // f | j=l | [f,g,h,i,j] | l - // j | f=k | [j,i,h,g,f] | k - // j | f=l | [j,i,h,g,f] | l - // - // This is continued for the remaining via ways, appending to the via result - // - // The final via/to intersection also uses the next_connection information in a similar fashion. - // - // next_connection | intersect | to_segment - // m | o=p | p,q - // m | o=s | s,r - // o | m=p | p,q - // o | m=s | s,r - // - // The final result is a list of nodes that represent a valid from->via->to path through the - // ways. - // - // E.g. if intersection nodes are a=j, f=l, k=o, m=s - // the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r} - auto const find_way_restriction = [&](const NodesOfWay &from_way, - const std::vector &via_ways, - const NodesOfWay &to_way) { - BOOST_ASSERT(!via_ways.empty()); - - WayRestriction restriction; - - // Find the orientation of the connected ways starting with the from-via intersection. - OSMNodeID from, via; - std::tie(from, via, std::ignore) = - find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID); - if (via == SPECIAL_OSM_NODEID) - { - util::Log(logDEBUG) << "Restriction has unconnected from and via ways: " - << from_way.way_id << ", " << via_ways.front().way_id; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - restriction.from = to_internal(from); - restriction.via.push_back(to_internal(via)); - - // Use the connection node from the previous intersection to inform our conversion of - // via ways into internal nodes. - OSMNodeID next_connection = via; - for (const auto &via_way : via_ways) - { - if (next_connection == via_way.first_segment_source_id()) - { - std::transform(std::next(via_way.node_ids.begin()), - via_way.node_ids.end(), - std::back_inserter(restriction.via), - to_internal); - next_connection = via_way.last_segment_target_id(); - } - else if (next_connection == via_way.last_segment_target_id()) - { - std::transform(std::next(via_way.node_ids.rbegin()), - via_way.node_ids.rend(), - std::back_inserter(restriction.via), - to_internal); - next_connection = via_way.first_segment_source_id(); - } - else - { - util::Log(logDEBUG) << "Restriction has unconnected via way: " << via_way.way_id - << " to node " << next_connection; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - } - - // Add the final to node after the via-to intersection. - if (next_connection == to_way.first_segment_source_id()) - { - restriction.to = to_internal(to_way.first_segment_target_id()); - } - else if (next_connection == to_way.last_segment_target_id()) - { - restriction.to = to_internal(to_way.last_segment_source_id()); - } - else - { - util::Log(logDEBUG) << "Restriction has unconnected via and to ways: " - << via_ways.back().way_id << ", " << to_way.way_id; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - return restriction; - }; - - // Check if we were able to resolve all the involved OSM elements before translating to an - // internal restriction - auto const get_way_restriction_from_OSM_ids = - [&](auto const from_id, auto const to_id, const std::vector &via_ids) { - auto const from_way_itr = restriction_ways.find(from_id); - if (from_way_itr->second.way_id != from_id) - { - util::Log(logDEBUG) << "Restriction references invalid from way: " << from_id; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - - std::vector via_ways; - for (const auto &via_id : via_ids) - { - auto const via_segment_itr = restriction_ways.find(via_id); - if (via_segment_itr->second.way_id != via_id) - { - util::Log(logDEBUG) << "Restriction references invalid via way: " << via_id; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - via_ways.push_back(via_segment_itr->second); - } - - auto const to_way_itr = restriction_ways.find(to_id); - if (to_way_itr->second.way_id != to_id) - { - util::Log(logDEBUG) << "Restriction references invalid to way: " << to_id; - return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; - } - - return find_way_restriction(from_way_itr->second, via_ways, to_way_itr->second); - }; - - // Node restrictions are described as a restriction between the two segments closest - // to the shared via-node on the from and to ways. - // from: [a, b, c, d, e] - // to: [f, g, h, i, j] - // - // The via node establishes the orientation of the from/to intersection when choosing the - // segments. - // via | node restriction - // a=f | b,a,g - // a=j | b,a,i - // e=f | d,e,g - // e=j | d,e,i - auto const find_node_restriction = - [&](auto const &from_segment, auto const &to_segment, auto const via_node) { - OSMNodeID from, via, to; - std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node); - if (via == SPECIAL_OSM_NODEID) - { - // unconnected - util::Log(logDEBUG) - << "Restriction references unconnected way: " << from_segment.way_id; - return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - return NodeRestriction{to_internal(from), to_internal(via), to_internal(to)}; - }; - - // Check if we were able to resolve all the involved OSM elements before translating to an - // internal restriction - auto const get_node_restriction_from_OSM_ids = [&](auto const from_id, - auto const to_id, - const OSMNodeID via_node) { - auto const from_segment_itr = restriction_ways.find(from_id); - - if (from_segment_itr->second.way_id != from_id) - { - util::Log(logDEBUG) << "Restriction references invalid way: " << from_id; - return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - - auto const to_segment_itr = restriction_ways.find(to_id); - if (to_segment_itr->second.way_id != to_id) - { - util::Log(logDEBUG) << "Restriction references invalid way: " << to_id; - return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - } - - return find_node_restriction(from_segment_itr->second, to_segment_itr->second, via_node); - }; - // Transform an OSMRestriction (based on WayIDs) into an OSRM restriction (base on NodeIDs). // Returns true on successful transformation, false in case of invalid references. const auto transform = [&](const auto &external_type, auto &internal_type) { - if (external_type.Type() == RestrictionType::WAY_RESTRICTION) + if (external_type.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH) { - auto const &external = external_type.AsWayRestriction(); - auto const restriction = - get_way_restriction_from_OSM_ids(external.from, external.to, external.via); - - if (!restriction.Valid()) - return false; - - internal_type.node_or_way = restriction; - return true; + auto const &external = external_type.turn_path.AsViaWayPath(); + internal_type.turn_path.node_or_way = + get_via_way_path_from_OSM_ids(internal_type.Name(), + restriction_ways, + external.from, + external.to, + external.via, + to_internal); } else { - BOOST_ASSERT(external_type.Type() == RestrictionType::NODE_RESTRICTION); - auto const &external = external_type.AsNodeRestriction(); - - auto restriction = - get_node_restriction_from_OSM_ids(external.from, external.to, external.via); - - if (!restriction.Valid()) - return false; - - internal_type.node_or_way = restriction; - return true; + BOOST_ASSERT(external_type.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH); + auto const &external = external_type.turn_path.AsViaNodePath(); + internal_type.turn_path.node_or_way = + get_via_node_path_from_OSM_ids(internal_type.Name(), + restriction_ways, + external.from, + external.to, + external.via, + to_internal); } + internal_type.is_only = external_type.is_only; + internal_type.condition = std::move(external_type.condition); + return internal_type.Valid(); }; const auto transform_into_internal_types = [&](InputTurnRestriction &external_restriction) { TurnRestriction restriction; if (transform(external_restriction, restriction)) { - restriction.is_only = external_restriction.is_only; - restriction.condition = std::move(external_restriction.condition); turn_restrictions.push_back(std::move(restriction)); } }; diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index d3fa345c7..06cfcf1aa 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -13,11 +13,11 @@ #include "extractor/name_table.hpp" #include "extractor/node_based_graph_factory.hpp" #include "extractor/node_restriction_map.hpp" -#include "extractor/restriction_filter.hpp" #include "extractor/restriction_graph.hpp" #include "extractor/restriction_parser.hpp" #include "extractor/scripting_environment.hpp" #include "extractor/tarjan_scc.hpp" +#include "extractor/turn_path_filter.hpp" #include "extractor/way_restriction_map.hpp" #include "guidance/files.hpp" @@ -279,7 +279,9 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) edge_based_nodes_container = EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData())); - turn_restrictions = removeInvalidRestrictions(std::move(turn_restrictions), node_based_graph); + turn_restrictions = removeInvalidTurnPaths(std::move(turn_restrictions), node_based_graph); + unresolved_maneuver_overrides = + removeInvalidTurnPaths(std::move(unresolved_maneuver_overrides), node_based_graph); auto restriction_graph = constructRestrictionGraph(turn_restrictions); const auto number_of_node_based_nodes = node_based_graph.GetNumberOfNodes(); diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index 30ef5d593..d38266bd0 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -3,8 +3,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/extraction_turn.hpp" #include "extractor/restriction.hpp" -#include "extractor/restriction_compressor.hpp" -#include "guidance/intersection.hpp" +#include "extractor/turn_path_compressor.hpp" #include "util/dynamic_graph.hpp" #include "util/node_based_graph.hpp" @@ -32,28 +31,34 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); - RestrictionCompressor restriction_compressor(turn_restrictions, maneuver_overrides); + TurnPathCompressor turn_path_compressor(turn_restrictions, maneuver_overrides); // Some degree two nodes are not compressed if they act as entry/exit points into a // restriction path. - std::unordered_set restriction_via_nodes; + std::unordered_set incompressible_via_nodes; const auto remember_via_nodes = [&](const auto &restriction) { - if (restriction.Type() == RestrictionType::NODE_RESTRICTION) + if (restriction.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH) { - restriction_via_nodes.insert(restriction.AsNodeRestriction().via); + incompressible_via_nodes.insert(restriction.turn_path.AsViaNodePath().via); } else { - BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION); - const auto &way_restriction = restriction.AsWayRestriction(); + BOOST_ASSERT(restriction.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH); + const auto &way_restriction = restriction.turn_path.AsViaWayPath(); // We do not compress the first and last via nodes so that we know how to enter/exit // a restriction path and apply the restrictions correctly. - restriction_via_nodes.insert(way_restriction.via.front()); - restriction_via_nodes.insert(way_restriction.via.back()); + incompressible_via_nodes.insert(way_restriction.via.front()); + incompressible_via_nodes.insert(way_restriction.via.back()); } }; std::for_each(turn_restrictions.begin(), turn_restrictions.end(), remember_via_nodes); + for (const auto &maneuver : maneuver_overrides) + { + // Only incompressible is where the instruction occurs. + incompressible_via_nodes.insert(maneuver.instruction_node); + } + { const auto weight_multiplier = scripting_environment.GetProfileProperties().GetWeightMultiplier(); @@ -77,7 +82,7 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, } // check if v is an entry/exit via node for a turn restriction - if (restriction_via_nodes.count(node_v) > 0) + if (incompressible_via_nodes.count(node_v) > 0) { continue; } @@ -303,8 +308,8 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, graph.DeleteEdge(node_v, forward_e2); graph.DeleteEdge(node_v, reverse_e2); - // update any involved turn restrictions - restriction_compressor.Compress(node_u, node_v, node_w); + // update any involved turn relations + turn_path_compressor.Compress(node_u, node_v, node_w); // store compressed geometry in container geometry_compressor.CompressEdge(forward_e1, diff --git a/src/extractor/maneuver_override_relation_parser.cpp b/src/extractor/maneuver_override_relation_parser.cpp index 5a48adfb4..a9c697698 100644 --- a/src/extractor/maneuver_override_relation_parser.cpp +++ b/src/extractor/maneuver_override_relation_parser.cpp @@ -119,9 +119,15 @@ ManeuverOverrideRelationParser::TryParse(const osmium::Relation &relation) const if (valid_relation) { - maneuver_override.via_ways.push_back(from); - std::copy(via_ways.begin(), via_ways.end(), std::back_inserter(maneuver_override.via_ways)); - maneuver_override.via_ways.push_back(to); + if (via_ways.empty()) + { + maneuver_override.turn_path.node_or_way = InputViaNodePath{{from}, {via_node}, {to}}; + } + else + { + maneuver_override.turn_path.node_or_way = + InputViaWayPath{{from}, std::move(via_ways), {to}}; + } maneuver_override.via_node = via_node; } else diff --git a/src/extractor/restriction_compressor.cpp b/src/extractor/restriction_compressor.cpp deleted file mode 100644 index 21fdd7f86..000000000 --- a/src/extractor/restriction_compressor.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "extractor/restriction_compressor.hpp" -#include "extractor/restriction.hpp" - -#include -#include - -namespace osrm -{ -namespace extractor -{ - -RestrictionCompressor::RestrictionCompressor( - std::vector &restrictions, - std::vector &maneuver_overrides) -{ - // add a turn restriction ptr to the starts/ends maps, needs to be a reference! - auto index = [&](auto &element) { - starts.insert({element.From(), &element}); - ends.insert({element.To(), &element}); - if (element.Type() == RestrictionType::WAY_RESTRICTION) - { - const auto &way_via = element.AsWayRestriction().via; - BOOST_ASSERT(way_via.size() >= 2); - // No need to track the first and last via nodes as they will not be compressed. - for (const auto &via_node : - boost::make_iterator_range(way_via.begin() + 1, way_via.end() - 1)) - { - vias.insert({via_node, &element}); - } - } - }; - // !needs to be reference, so we can get the correct address - const auto index_starts_ends_vias = [&](auto &restriction) { index(restriction); }; - - // add all restrictions as their respective start/via/end pointers - std::for_each(restrictions.begin(), restrictions.end(), index_starts_ends_vias); - - auto index_maneuver = [&](auto &maneuver) { - for (auto &turn : maneuver.turn_sequence) - { - maneuver_starts.insert({turn.from, &turn}); - maneuver_ends.insert({turn.to, &turn}); - } - }; - // !needs to be reference, so we can get the correct address - std::for_each(maneuver_overrides.begin(), maneuver_overrides.end(), [&](auto &maneuver) { - index_maneuver(maneuver); - }); -} - -void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const NodeID to) -{ - // handle turn restrictions - // extract all start ptrs and move them from via to from. - auto all_starts_range = starts.equal_range(via); - std::vector start_ptrs; - std::transform(all_starts_range.first, - all_starts_range.second, - std::back_inserter(start_ptrs), - [](const auto pair) { return pair.second; }); - - const auto update_start = [&](auto ptr) { - if (ptr->Type() == RestrictionType::NODE_RESTRICTION) - { - - // ____ | from - p.from | via - p.via | to - p.to | ____ - auto &node_ptr = ptr->AsNodeRestriction(); - BOOST_ASSERT(node_ptr.from == via); - if (node_ptr.via == to) - { - node_ptr.from = from; - } - // ____ | to - p.from | via - p.via | from - p.to | ____ - else - { - BOOST_ASSERT(node_ptr.via == from); - node_ptr.from = to; - } - } - else - { - BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION); - auto &way_ptr = ptr->AsWayRestriction(); - // ____ | from - p.from | via - p.via[0] | to - p[1..],p.to | ____ - BOOST_ASSERT(way_ptr.from == via); - if (way_ptr.via.front() == to) - { - way_ptr.from = from; - } - // ____ | to - p.from | via - p.via[0] | from - p[1,..],p.to | ____ - else - { - BOOST_ASSERT(way_ptr.via.front() == from); - way_ptr.from = to; - } - } - }; - - std::for_each(start_ptrs.begin(), start_ptrs.end(), update_start); - - // update the ptrs in our mapping - starts.erase(via); - - const auto reinsert_start = [&](auto ptr) { starts.insert({ptr->From(), ptr}); }; - std::for_each(start_ptrs.begin(), start_ptrs.end(), reinsert_start); - - // extract all end ptrs and move them from via to to - auto all_ends_range = ends.equal_range(via); - std::vector end_ptrs; - std::transform(all_ends_range.first, - all_ends_range.second, - std::back_inserter(end_ptrs), - [](const auto pair) { return pair.second; }); - - const auto update_end = [&](auto ptr) { - if (ptr->Type() == RestrictionType::NODE_RESTRICTION) - { - auto &node_ptr = ptr->AsNodeRestriction(); - - BOOST_ASSERT(node_ptr.to == via); - // p.from | ____ - p.via | from - p.to | via - ____ | to - if (node_ptr.via == from) - { - node_ptr.to = to; - } - // p.from | ____ - p.via | to - p.to | via - ____ | from - else - { - BOOST_ASSERT(node_ptr.via == to); - node_ptr.to = from; - } - } - else - { - BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION); - auto &way_ptr = ptr->AsWayRestriction(); - - BOOST_ASSERT(way_ptr.to == via); - // p.from,p.via[..,n-1] | ____ - p.via[n] | from - p.to | via - ____ | to - if (way_ptr.via.back() == from) - { - way_ptr.to = to; - } - // p.from,p.via[..,n-1] | ____ - p.via[n] | to - p.to | via - ____ | from - else - { - BOOST_ASSERT(way_ptr.via.back() == to); - way_ptr.to = from; - } - } - }; - std::for_each(end_ptrs.begin(), end_ptrs.end(), update_end); - - // update end ptrs in mapping - ends.erase(via); - - const auto reinsert_end = [&](auto ptr) { ends.insert({ptr->To(), ptr}); }; - std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end); - - // remove compressed node from all via paths - auto all_vias_range = vias.equal_range(via); - - const auto update_via = [&](auto restriction_pair) { - BOOST_ASSERT(restriction_pair.second->Type() == RestrictionType::WAY_RESTRICTION); - auto &way_ptr = restriction_pair.second->AsWayRestriction(); - BOOST_ASSERT(std::find(way_ptr.via.begin(), way_ptr.via.end(), via) != way_ptr.via.end()); - way_ptr.via.erase(std::remove(way_ptr.via.begin(), way_ptr.via.end(), via), - way_ptr.via.end()); - }; - std::for_each(all_vias_range.first, all_vias_range.second, update_via); - - // update via ptrs in mapping - vias.erase(via); - - /**********************************************************************************************/ - - // handle maneuver overrides from nodes - // extract all startptrs - auto maneuver_starts_range = maneuver_starts.equal_range(via); - std::vector mnv_start_ptrs; - std::transform(maneuver_starts_range.first, - maneuver_starts_range.second, - std::back_inserter(mnv_start_ptrs), - [](const auto pair) { return pair.second; }); - - // update from nodes of maneuver overrides - const auto update_start_mnv = [&](auto ptr) { - // ____ | from - p.from | via - p.via | to - p.to | ____ - BOOST_ASSERT(ptr->from == via); - if (ptr->via == to) - { - ptr->from = from; - } - // ____ | to - p.from | via - p.via | from - p.to | ____ - else - { - BOOST_ASSERT(ptr->via == from); - ptr->from = to; - } - }; - std::for_each(mnv_start_ptrs.begin(), mnv_start_ptrs.end(), update_start_mnv); - - // update the ptrs in our mapping - maneuver_starts.erase(via); - const auto reinsert_start_mnv = [&](auto ptr) { maneuver_starts.insert({ptr->from, ptr}); }; - std::for_each(mnv_start_ptrs.begin(), mnv_start_ptrs.end(), reinsert_start_mnv); - - /**********************************************************************************************/ - // handle maneuver override to nodes - // extract all end ptrs and move them from via to to - auto maneuver_ends_range = maneuver_ends.equal_range(via); - std::vector mnv_end_ptrs; - std::transform(maneuver_ends_range.first, - maneuver_ends_range.second, - std::back_inserter(mnv_end_ptrs), - [](const auto pair) { return pair.second; }); - - const auto update_end_mnv = [&](auto ptr) { - BOOST_ASSERT(ptr->to == via); - // p.from | ____ - p.via | from - p.to | via - ____ | to - if (ptr->via == from) - { - ptr->to = to; - } - // p.from | ____ - p.via | to - p.to | via - ____ | from - else - { - BOOST_ASSERT(ptr->via == to); - ptr->to = from; - } - }; - std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), update_end_mnv); - - // update end ptrs in mapping - maneuver_ends.erase(via); - - const auto reinsert_end_mnvs = [&](auto ptr) { maneuver_ends.insert({ptr->to, ptr}); }; - std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), reinsert_end_mnvs); -} - -} // namespace extractor -} // namespace osrm diff --git a/src/extractor/restriction_filter.cpp b/src/extractor/restriction_filter.cpp deleted file mode 100644 index cc5484e06..000000000 --- a/src/extractor/restriction_filter.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "extractor/restriction_filter.hpp" -#include "util/node_based_graph.hpp" -#include "util/timing_util.hpp" -#include "util/typedefs.hpp" - -#include -#include - -namespace osrm -{ -namespace extractor -{ - -std::vector -removeInvalidRestrictions(std::vector restrictions, - const util::NodeBasedDynamicGraph &node_based_graph) -{ - util::UnbufferedLog log; - log << "Removing invalid restrictions..."; - TIMER_START(remove_invalid_restrictions); - - const auto is_valid_edge = [&node_based_graph](const auto from, const auto to) { - const auto eid = node_based_graph.FindEdge(from, to); - if (eid == SPECIAL_EDGEID) - { - util::Log(logDEBUG) << "Restriction has invalid edge: " << from << ", " << to; - return false; - } - - const auto &edge_data = node_based_graph.GetEdgeData(eid); - if (edge_data.reversed) - { - util::Log(logDEBUG) << "Restriction has non-traversable edge: " << from << ", " << to; - return false; - } - return true; - }; - - const auto is_valid_node = [is_valid_edge](const auto &node_restriction) { - return is_valid_edge(node_restriction.from, node_restriction.via) && - is_valid_edge(node_restriction.via, node_restriction.to); - }; - - const auto is_valid_way = [is_valid_edge](const auto &way_restriction) { - if (!is_valid_edge(way_restriction.from, way_restriction.via.front())) - return false; - - const auto invalid_it = std::adjacent_find( - way_restriction.via.begin(), - way_restriction.via.end(), - [&](auto via_from, auto via_to) { return !is_valid_edge(via_from, via_to); }); - if (invalid_it != way_restriction.via.end()) - return false; - - return is_valid_edge(way_restriction.via.back(), way_restriction.to); - }; - - const auto is_invalid = [is_valid_way, is_valid_node](const auto &restriction) { - if (restriction.Type() == RestrictionType::NODE_RESTRICTION) - { - return !is_valid_node(restriction.AsNodeRestriction()); - } - else - { - BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION); - return !is_valid_way(restriction.AsWayRestriction()); - } - }; - - const auto end_valid_restrictions = - std::remove_if(restrictions.begin(), restrictions.end(), is_invalid); - const auto num_removed = std::distance(end_valid_restrictions, restrictions.end()); - restrictions.erase(end_valid_restrictions, restrictions.end()); - - TIMER_STOP(remove_invalid_restrictions); - log << "removed " << num_removed << " invalid restrictions, after " - << TIMER_SEC(remove_invalid_restrictions) << "s"; - - return restrictions; -} - -} // namespace extractor -} // namespace osrm diff --git a/src/extractor/restriction_graph.cpp b/src/extractor/restriction_graph.cpp index 376b56b0e..aafcbcf6f 100644 --- a/src/extractor/restriction_graph.cpp +++ b/src/extractor/restriction_graph.cpp @@ -1,9 +1,10 @@ #include "extractor/restriction_graph.hpp" +#include "extractor/restriction.hpp" +#include "extractor/turn_path.hpp" #include "util/node_based_graph.hpp" #include "util/timing_util.hpp" #include -#include #include namespace osrm @@ -211,11 +212,11 @@ void buildGraph(RestrictionGraph &rg, const std::vector &restri const auto run_builder = [&](const auto &restriction) { builder_type builder(rg); - builder.start(restriction.From(), restriction.FirstVia()); - if (restriction.Type() == RestrictionType::WAY_RESTRICTION) + builder.start(restriction.turn_path.From(), restriction.turn_path.FirstVia()); + if (restriction.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH) { - const auto &way_restriction = restriction.AsWayRestriction(); - util::for_each_pair(way_restriction.via, + const auto &via_way_path = restriction.turn_path.AsViaWayPath(); + util::for_each_pair(via_way_path.via, [&](NodeID from, NodeID to) { builder.next(from, to); }); } builder.end(restriction); diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp index a135214f5..2e4328acf 100644 --- a/src/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -232,7 +232,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const 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. + // 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) @@ -243,12 +243,12 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const if (is_node_restriction) { // template struct requires bracket for ID initialisation :( - restriction.node_or_way = InputNodeRestriction{{from}, {via_node}, {to}}; + restriction.turn_path.node_or_way = InputViaNodePath{{from}, {via_node}, {to}}; } else { // template struct requires bracket for ID initialisation :( - restriction.node_or_way = InputWayRestriction{{from}, via_ways, {to}}; + restriction.turn_path.node_or_way = InputViaWayPath{{from}, via_ways, {to}}; } restriction_containers.push_back(std::move(restriction)); } diff --git a/src/extractor/turn_path_compressor.cpp b/src/extractor/turn_path_compressor.cpp new file mode 100644 index 000000000..039b273ea --- /dev/null +++ b/src/extractor/turn_path_compressor.cpp @@ -0,0 +1,164 @@ +#include "extractor/turn_path_compressor.hpp" +#include "extractor/maneuver_override.hpp" +#include "extractor/restriction.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +TurnPathCompressor::TurnPathCompressor(std::vector &restrictions, + std::vector &maneuver_overrides) +{ + // Track all turn paths by their respective start/via/end nodes. + auto index = [&](auto &element) { + starts.insert({element.From(), &element}); + ends.insert({element.To(), &element}); + if (element.Type() == TurnPathType::VIA_WAY_TURN_PATH) + { + // Some of the via nodes can not be compressed so don't need tracking + // (e.g. first and last via node of a restriction, instruction node of maneuver). + // However, for the sake of simplicity we'll track them all. + for (const auto &via_node : element.AsViaWayPath().via) + { + vias.insert({via_node, &element}); + } + } + }; + // We need to pass a reference to the index as we will mutate the turn path during compression. + const auto index_starts_ends_vias = [&](auto &relation) { index(relation.turn_path); }; + + std::for_each(restrictions.begin(), restrictions.end(), index_starts_ends_vias); + std::for_each(maneuver_overrides.begin(), maneuver_overrides.end(), index_starts_ends_vias); +} + +void TurnPathCompressor::Compress(const NodeID from, const NodeID via, const NodeID to) +{ + // handle turn restrictions + // extract all start ptrs and move them from via to from. + auto all_starts_range = starts.equal_range(via); + std::vector start_ptrs; + std::transform(all_starts_range.first, + all_starts_range.second, + std::back_inserter(start_ptrs), + [](const auto pair) { return pair.second; }); + + const auto update_start = [&](auto ptr) { + if (ptr->Type() == TurnPathType::VIA_NODE_TURN_PATH) + { + + // ____ | from - p.from | via - p.via | to - p.to | ____ + auto &node_ptr = ptr->AsViaNodePath(); + BOOST_ASSERT(node_ptr.from == via); + if (node_ptr.via == to) + { + node_ptr.from = from; + } + // ____ | to - p.from | via - p.via | from - p.to | ____ + else + { + BOOST_ASSERT(node_ptr.via == from); + node_ptr.from = to; + } + } + else + { + BOOST_ASSERT(ptr->Type() == TurnPathType::VIA_WAY_TURN_PATH); + auto &way_ptr = ptr->AsViaWayPath(); + // ____ | from - p.from | via - p.via[0] | to - p[1..],p.to | ____ + BOOST_ASSERT(way_ptr.from == via); + if (way_ptr.via.front() == to) + { + way_ptr.from = from; + } + // ____ | to - p.from | via - p.via[0] | from - p[1,..],p.to | ____ + else + { + BOOST_ASSERT(way_ptr.via.front() == from); + way_ptr.from = to; + } + } + }; + + std::for_each(start_ptrs.begin(), start_ptrs.end(), update_start); + + // update the ptrs in our mapping + starts.erase(via); + + const auto reinsert_start = [&](auto ptr) { starts.insert({ptr->From(), ptr}); }; + std::for_each(start_ptrs.begin(), start_ptrs.end(), reinsert_start); + + // extract all end ptrs and move them from via to to + auto all_ends_range = ends.equal_range(via); + std::vector end_ptrs; + std::transform(all_ends_range.first, + all_ends_range.second, + std::back_inserter(end_ptrs), + [](const auto pair) { return pair.second; }); + + const auto update_end = [&](auto ptr) { + if (ptr->Type() == TurnPathType::VIA_NODE_TURN_PATH) + { + auto &node_ptr = ptr->AsViaNodePath(); + + BOOST_ASSERT(node_ptr.to == via); + // p.from | ____ - p.via | from - p.to | via - ____ | to + if (node_ptr.via == from) + { + node_ptr.to = to; + } + // p.from | ____ - p.via | to - p.to | via - ____ | from + else + { + BOOST_ASSERT(node_ptr.via == to); + node_ptr.to = from; + } + } + else + { + BOOST_ASSERT(ptr->Type() == TurnPathType::VIA_WAY_TURN_PATH); + auto &way_ptr = ptr->AsViaWayPath(); + + BOOST_ASSERT(way_ptr.to == via); + // p.from,p.via[..,n-1] | ____ - p.via[n] | from - p.to | via - ____ | to + if (way_ptr.via.back() == from) + { + way_ptr.to = to; + } + // p.from,p.via[..,n-1] | ____ - p.via[n] | to - p.to | via - ____ | from + else + { + BOOST_ASSERT(way_ptr.via.back() == to); + way_ptr.to = from; + } + } + }; + std::for_each(end_ptrs.begin(), end_ptrs.end(), update_end); + + // update end ptrs in mapping + ends.erase(via); + + const auto reinsert_end = [&](auto ptr) { ends.insert({ptr->To(), ptr}); }; + std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end); + + // remove compressed node from all via paths + auto all_vias_range = vias.equal_range(via); + + const auto update_via = [&](auto restriction_pair) { + BOOST_ASSERT(restriction_pair.second->Type() == TurnPathType::VIA_WAY_TURN_PATH); + auto &way_ptr = restriction_pair.second->AsViaWayPath(); + BOOST_ASSERT(std::find(way_ptr.via.begin(), way_ptr.via.end(), via) != way_ptr.via.end()); + way_ptr.via.erase(std::remove(way_ptr.via.begin(), way_ptr.via.end(), via), + way_ptr.via.end()); + }; + std::for_each(all_vias_range.first, all_vias_range.second, update_via); + + // update via ptrs in mapping + vias.erase(via); +} + +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/turn_path_filter.cpp b/src/extractor/turn_path_filter.cpp new file mode 100644 index 000000000..b26245792 --- /dev/null +++ b/src/extractor/turn_path_filter.cpp @@ -0,0 +1,88 @@ +#include "extractor/turn_path_filter.hpp" +#include "extractor/maneuver_override.hpp" +#include "util/node_based_graph.hpp" +#include "util/timing_util.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +template +std::vector removeInvalidTurnPaths(std::vector turn_relations, + const util::NodeBasedDynamicGraph &node_based_graph) +{ + util::UnbufferedLog log; + log << "Removing invalid " << T::Name() << "s..."; + TIMER_START(remove_invalid_turn_paths); + + const auto is_valid_edge = [&node_based_graph](const auto from, const auto to) { + const auto eid = node_based_graph.FindEdge(from, to); + if (eid == SPECIAL_EDGEID) + { + util::Log(logDEBUG) << T::Name() << " has invalid edge: " << from << ", " << to; + return false; + } + + const auto &edge_data = node_based_graph.GetEdgeData(eid); + if (edge_data.reversed) + { + util::Log(logDEBUG) << T::Name() << " has non-traversable edge: " << from << ", " << to; + return false; + } + return true; + }; + + const auto is_valid_node = [is_valid_edge](const auto &via_node_path) { + return is_valid_edge(via_node_path.from, via_node_path.via) && + is_valid_edge(via_node_path.via, via_node_path.to); + }; + + const auto is_valid_way = [is_valid_edge](const auto &via_way_path) { + if (!is_valid_edge(via_way_path.from, via_way_path.via.front())) + return false; + + const auto invalid_it = std::adjacent_find( + via_way_path.via.begin(), via_way_path.via.end(), [&](auto via_from, auto via_to) { + return !is_valid_edge(via_from, via_to); + }); + if (invalid_it != via_way_path.via.end()) + return false; + + return is_valid_edge(via_way_path.via.back(), via_way_path.to); + }; + + const auto is_invalid = [is_valid_way, is_valid_node](const auto &turn_relation) { + if (turn_relation.turn_path.Type() == TurnPathType::VIA_NODE_TURN_PATH) + { + return !is_valid_node(turn_relation.turn_path.AsViaNodePath()); + } + + BOOST_ASSERT(turn_relation.turn_path.Type() == TurnPathType::VIA_WAY_TURN_PATH); + return !is_valid_way(turn_relation.turn_path.AsViaWayPath()); + }; + + const auto end_valid_relations = + std::remove_if(turn_relations.begin(), turn_relations.end(), is_invalid); + const auto num_removed = std::distance(end_valid_relations, turn_relations.end()); + turn_relations.erase(end_valid_relations, turn_relations.end()); + + TIMER_STOP(remove_invalid_turn_paths); + log << "removed " << num_removed << " invalid " << T::Name() << "s, after " + << TIMER_SEC(remove_invalid_turn_paths) << "s"; + + return turn_relations; +} + +template std::vector removeInvalidTurnPaths<>(std::vector, + const util::NodeBasedDynamicGraph &); +template std::vector +removeInvalidTurnPaths<>(std::vector, + const util::NodeBasedDynamicGraph &); + +} // namespace extractor +} // namespace osrm diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp index b7ec2e707..979f60ddd 100644 --- a/unit_tests/extractor/intersection_analysis_tests.cpp +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -23,7 +23,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) std::vector annotations{ {EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}, {EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}}; - std::vector restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}}; + std::vector restrictions{TurnRestriction{{ViaNodePath{0, 2, 1}}, false}}; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; diff --git a/unit_tests/extractor/restriction_graph.cpp b/unit_tests/extractor/restriction_graph.cpp index f44a25dd1..e0fac7bcc 100644 --- a/unit_tests/extractor/restriction_graph.cpp +++ b/unit_tests/extractor/restriction_graph.cpp @@ -15,14 +15,14 @@ using namespace osrm::extractor; TurnRestriction makeWayRestriction(NodeID from, std::vector via, NodeID to, bool is_only) { - WayRestriction wr{from, std::move(via), to}; - return TurnRestriction(wr, is_only); + ViaWayPath vwp{from, std::move(via), to}; + return TurnRestriction({vwp}, is_only); } TurnRestriction makeNodeRestriction(NodeID from, NodeID via, NodeID to, bool is_only) { - NodeRestriction nr{from, via, to}; - return TurnRestriction(nr, is_only); + ViaNodePath vnp{from, via, to}; + return TurnRestriction({vnp}, is_only); } struct instruction @@ -52,7 +52,7 @@ void checkInstructions(RestrictionGraph::RestrictionRange restrictions, restrictions.end(), std::back_inserter(actual_instructions), [](const auto &restriction) { - return instruction{restriction->To(), bool(restriction->is_only)}; + return instruction{restriction->turn_path.To(), bool(restriction->is_only)}; }); std::sort(actual_instructions.begin(), actual_instructions.end(),