diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index ce835fb485c..dea84143de1 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -537,7 +537,7 @@ Feature: Car - Turn restrictions | from | to | route | | a | h | abc,cde,efc,cgh,cgh | - @restriction + @restriction-way Scenario: Car - prohibit turn Given the node map """ @@ -563,9 +563,11 @@ Feature: Car - Turn restrictions | restriction | ab | be | de | no_right_turn | When I route I should get - | from | to | route | - | a | d | ab,be,de,de | - | a | f | ab,be,ef,ef | + | from | to | route | turns | locations | + | a | d | ab,be,ef,ef,de,de | depart,turn right,turn left,continue uturn,new name straight,arrive | a,b,e,f,e,d | + | a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f | + | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | + | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | @restriction @overlap Scenario: Car - prohibit turn @@ -597,11 +599,11 @@ Feature: Car - Turn restrictions | restriction | bc | be | ef | no_left_turn | When I route I should get - | from | to | route | - | a | d | ab,be,de,de | - | a | f | ab,be,ef,ef | - | c | d | bc,be,de,de | - | c | f | bc,be,ef,ef | + | from | to | route | + | a | d | ab,be,ef,ef,de,de | + | a | f | ab,be,ef,ef | + | c | d | bc,be,de,de | + | c | f | bc,be,de,de,ef,ef | @restriction-way @overlap Scenario: Two times same way @@ -618,9 +620,25 @@ Feature: Car - Turn restrictions | | | | | | - a - b - c - f - | | \ | - i - d - e + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + a - b - c - - - - - - - - - - - - - - - - - - - f + | | \ / + i - d - e - - - - - - - - - - - - - - - - - """ And the ways @@ -641,7 +659,7 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | - | a | i | ab,bc,cd,fedib,fedib | + | a | i | ab,bc,cf,fedib,fedib | @restriction-way @overlap @@ -682,8 +700,8 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | - | a | j | left,third,right,right | - | f | e | right,first,left,left | + | a | j | left,first,right,right | + | f | e | right,third,left,left | @restriction Scenario: Car - allow only turn @@ -711,9 +729,11 @@ Feature: Car - Turn restrictions | restriction | ab | be | ef | only_left_on | When I route I should get - | from | to | route | - | a | d | ab,be,de,de | - + | from | to | route | turns | locations | + | a | d | ab,be,ef,ef,de,de | depart,turn right,turn left,continue uturn,new name straight,arrive | a,b,e,f,e,d | + | a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f | + | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | + | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | @restriction Scenario: Car - allow only turn @@ -810,14 +830,15 @@ Feature: Car - Turn restrictions | restriction | gf | fb,bc | cd | only_u_turn | When I route I should get - | from | to | route | - | a | d | abcd,abcd | - | a | e | abcd,ce,ce | - | a | f | abcd,hfb,hfb | - | g | e | gf,hfb,abcd,ce,ce | - | g | d | gf,hfb,abcd,abcd | - | h | e | hfb,abcd,ce,ce | - | h | d | hfb,abcd,abcd | + | from | to | route | turns | locations | + | a | d | abcd,ce,ce,abcd,abcd | depart,turn left,continue uturn,turn left,arrive | a,c,e,c,d | + | a | e | abcd,ce,ce | depart,turn left,arrive | a,c,e | + | a | f | abcd,hfb,hfb | depart,turn right,arrive | a,b,f | + | g | e | gf,hfb,abcd,ce,ce | depart,turn right,turn right,turn left,arrive | g,f,b,c,e | + | g | d | gf,hfb,abcd,abcd | depart,turn right,turn right,arrive | g,f,b,d | + | h | e | hfb,abcd,ce,ce | depart,end of road right,turn left,arrive | h,b,c,e | + | h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d | + @restriction Scenario: Car - prohibit turn, traffic lights @@ -855,7 +876,8 @@ Feature: Car - Turn restrictions When I route I should get - | from | to | route | - | h | j | ab,be,de,de | - | h | f | ab,be,ef,ef | - + | from | to | route | turns | locations | + | a | d | ab,be,ef,ef,de,de | depart,turn right,turn left,continue uturn,new name straight,arrive | a,b,e,f,e,d | + | a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f | + | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | + | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index e5a5d74713a..3edbd20ea60 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -17,6 +17,7 @@ #include "extractor/profile_properties.hpp" #include "extractor/query_node.hpp" #include "extractor/restriction_map.hpp" +#include "extractor/way_restriction_map.hpp" #include "util/concurrent_id_map.hpp" #include "util/deallocating_vector.hpp" @@ -78,7 +79,6 @@ class EdgeBasedGraphFactory CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, - std::shared_ptr restriction_map, const std::vector &coordinates, const extractor::PackedOSMIDs &osm_node_ids, ProfileProperties profile_properties, @@ -91,7 +91,9 @@ class EdgeBasedGraphFactory const std::string &turn_weight_penalties_filename, const std::string &turn_duration_penalties_filename, const std::string &turn_penalties_index_filename, - const std::string &cnbg_ebg_mapping_path); + const std::string &cnbg_ebg_mapping_path, + const RestrictionMap &restriction_map, + const WayRestrictionMap &way_restriction_map); // The following get access functions destroy the content in the factory void GetEdgeBasedEdges(util::DeallocatingVector &edges); @@ -106,7 +108,7 @@ class EdgeBasedGraphFactory std::vector GetBearingClasses() const; std::vector GetEntryClasses() const; - unsigned GetHighestEdgeID(); + std::uint64_t GetNumberOfEdgeBasedNodes() const; // Basic analysis of a turn (u --(e1)-- v --(e2)-- w) // with known angle. @@ -134,12 +136,15 @@ class EdgeBasedGraphFactory std::vector m_edge_based_node_segments; EdgeBasedNodeDataContainer m_edge_based_node_container; util::DeallocatingVector m_edge_based_edge_list; - EdgeID m_max_edge_id; + + // the number of edge-based nodes is mostly made up out of the edges in the node-based graph. + // Any edge in the node-based graph represents a node in the edge-based graph. In addition, we + // add a set of artificial edge-based nodes into the mix to model via-way turn restrictions. + std::uint64_t m_number_of_edge_based_nodes; const std::vector &m_coordinates; const extractor::PackedOSMIDs &m_osm_node_ids; std::shared_ptr m_node_based_graph; - std::shared_ptr m_restriction_map; const std::unordered_set &m_barrier_nodes; const std::unordered_set &m_traffic_lights; @@ -152,14 +157,16 @@ class EdgeBasedGraphFactory unsigned RenumberEdges(); - std::vector GenerateEdgeExpandedNodes(); + std::vector GenerateEdgeExpandedNodes(const WayRestrictionMap &way_restriction_map); void GenerateEdgeExpandedEdges(ScriptingEnvironment &scripting_environment, const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &turn_weight_penalties_filename, const std::string &turn_duration_penalties_filename, - const std::string &turn_penalties_index_filename); + const std::string &turn_penalties_index_filename, + const RestrictionMap &restriction_map, + const WayRestrictionMap &way_restriction_map); NBGToEBG InsertEdgeBasedNode(const NodeID u, const NodeID v); diff --git a/include/extractor/node_data_container.hpp b/include/extractor/node_data_container.hpp index 0b7b04d51de..4032dd694ef 100644 --- a/include/extractor/node_data_container.hpp +++ b/include/extractor/node_data_container.hpp @@ -104,6 +104,16 @@ template class EdgeBasedNodeDataContainerImpl util::inplacePermutation(classes.begin(), classes.end(), permutation); } + // all containers have the exact same size + std::size_t Size() const + { + BOOST_ASSERT(geometry_ids.size() == name_ids.size()); + BOOST_ASSERT(geometry_ids.size() == component_ids.size()); + BOOST_ASSERT(geometry_ids.size() == travel_modes.size()); + BOOST_ASSERT(geometry_ids.size() == classes.size()); + return geometry_ids.size(); + } + private: Vector geometry_ids; Vector name_ids; diff --git a/include/extractor/restriction.hpp b/include/extractor/restriction.hpp index 323846437be..9e1764278a7 100644 --- a/include/extractor/restriction.hpp +++ b/include/extractor/restriction.hpp @@ -25,9 +25,9 @@ namespace extractor // ab via b to bd struct InputNodeRestriction { - OSMEdgeID_weak from; - OSMNodeID_weak via; - OSMEdgeID_weak to; + OSMWayID from; + OSMNodeID via; + OSMWayID to; }; // A restriction that uses a single via-way in between @@ -39,9 +39,9 @@ struct InputNodeRestriction // ab via be to ef -- no u turn struct InputWayRestriction { - OSMEdgeID_weak from; - OSMEdgeID_weak via; - OSMEdgeID_weak to; + OSMWayID from; + OSMWayID via; + OSMWayID to; }; // Outside view of the variant, these are equal to the `which()` results @@ -52,35 +52,20 @@ enum RestrictionType NUM_RESTRICTION_TYPES = 2 }; -namespace restriction_details -{ - -// currently these bits only hold an `is_only` value. -struct Bits -{ // mostly unused, initialised to false by default - Bits() : is_only(false) {} - - bool is_only; - // when adding more bits, consider using bitfields just as in - // bool unused : 7; -}; - -} // namespace restriction - struct InputTurnRestriction { // keep in the same order as the turn restrictions below boost::variant node_or_way; - restriction_details::Bits flags; + bool is_only; - OSMEdgeID_weak From() const + OSMWayID From() const { return node_or_way.which() == RestrictionType::NODE_RESTRICTION ? boost::get(node_or_way).from : boost::get(node_or_way).from; } - OSMEdgeID_weak To() const + OSMWayID To() const { return node_or_way.which() == RestrictionType::NODE_RESTRICTION ? boost::get(node_or_way).to @@ -141,6 +126,11 @@ struct NodeRestriction return "From " + std::to_string(from) + " via " + std::to_string(via) + " to " + std::to_string(to); } + + 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 @@ -165,6 +155,12 @@ struct WayRestriction // case a way restrction is not fully collapsed NodeRestriction in_restriction; NodeRestriction out_restriction; + + bool operator==(const WayRestriction &other) const + { + return std::tie(in_restriction, out_restriction) == + std::tie(other.in_restriction, other.out_restriction); + } }; // Wrapper for turn restrictions that gives more information on its type / handles the switch @@ -173,20 +169,18 @@ struct TurnRestriction { // keep in the same order as the turn restrictions above boost::variant node_or_way; - restriction_details::Bits flags; + bool is_only; // construction for NodeRestrictions explicit TurnRestriction(NodeRestriction node_restriction, bool is_only = false) - : node_or_way(node_restriction) + : node_or_way(node_restriction), is_only(is_only) { - flags.is_only = is_only; } // construction for WayRestrictions explicit TurnRestriction(WayRestriction way_restriction, bool is_only = false) - : node_or_way(way_restriction) + : node_or_way(way_restriction), is_only(is_only) { - flags.is_only = is_only; } explicit TurnRestriction() @@ -239,6 +233,24 @@ struct TurnRestriction } } + 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(); + } + } + std::string ToString() const { std::string representation; @@ -253,7 +265,7 @@ struct TurnRestriction auto const &node = AsNodeRestriction(); representation = node.ToString(); } - representation += " is_only: " + std::to_string(flags.is_only); + representation += " is_only: " + std::to_string(is_only); return representation; } }; diff --git a/include/extractor/restriction_map.hpp b/include/extractor/restriction_map.hpp index 7a54e156d95..09396d6d9db 100644 --- a/include/extractor/restriction_map.hpp +++ b/include/extractor/restriction_map.hpp @@ -69,7 +69,6 @@ namespace osrm { namespace extractor { - /** \brief Efficent look up if an edge is the start + via node of a TurnRestriction EdgeBasedEdgeFactory decides by it if edges are inserted or geometry is compressed @@ -80,61 +79,8 @@ class RestrictionMap RestrictionMap() : m_count(0) {} RestrictionMap(const std::vector &restriction_list); - // Replace end v with w in each turn restriction containing u as via node - template - void FixupArrivingTurnRestriction(const NodeID node_u, - const NodeID node_v, - const NodeID node_w, - const GraphT &graph) - { - BOOST_ASSERT(node_u != SPECIAL_NODEID); - BOOST_ASSERT(node_v != SPECIAL_NODEID); - BOOST_ASSERT(node_w != SPECIAL_NODEID); - - if (!IsViaNode(node_u)) - { - return; - } - - // find all potential start edges. It is more efficient to get a (small) list - // of potential start edges than iterating over all buckets - std::vector predecessors; - for (const EdgeID current_edge_id : graph.GetAdjacentEdgeRange(node_u)) - { - const NodeID target = graph.GetTarget(current_edge_id); - if (node_v != target) - { - predecessors.push_back(target); - } - } - - for (const NodeID node_x : predecessors) - { - const auto restriction_iterator = m_restriction_map.find({node_x, node_u}); - if (restriction_iterator == m_restriction_map.end()) - { - continue; - } - - const unsigned index = restriction_iterator->second; - auto &bucket = m_restriction_bucket_list.at(index); - - for (RestrictionTarget &restriction_target : bucket) - { - if (node_v == restriction_target.target_node) - { - restriction_target.target_node = node_w; - } - } - } - } - bool IsViaNode(const NodeID node) const; - // Replaces start edge (v, w) with (u, w). Only start node changes. - void - FixupStartingTurnRestriction(const NodeID node_u, const NodeID node_v, const NodeID node_w); - // Check if edge (u, v) is the start of any turn restriction. // If so returns id of first target node. NodeID CheckForEmanatingIsOnlyTurn(const NodeID node_u, const NodeID node_v) const; diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp index 91f9852b8c5..25a2a95e0e0 100644 --- a/include/extractor/restriction_parser.hpp +++ b/include/extractor/restriction_parser.hpp @@ -3,7 +3,8 @@ #include "extractor/restriction.hpp" -#include +#include + #include #include @@ -37,7 +38,6 @@ class ScriptingEnvironment; * ...----(a)-----(via)------(b)----... * So it can be represented by the tripe (a, via, b). */ - class RestrictionParser { public: diff --git a/include/extractor/serialization.hpp b/include/extractor/serialization.hpp index 55217bb1e01..4885e591438 100644 --- a/include/extractor/serialization.hpp +++ b/include/extractor/serialization.hpp @@ -165,7 +165,7 @@ inline void write(storage::io::FileWriter &writer, const WayRestriction &restric inline void read(storage::io::FileReader &reader, TurnRestriction &restriction) { - reader.ReadInto(restriction.flags); + reader.ReadInto(restriction.is_only); if (restriction.Type() == RestrictionType::WAY_RESTRICTION) { WayRestriction way_restriction; @@ -183,7 +183,7 @@ inline void read(storage::io::FileReader &reader, TurnRestriction &restriction) inline void write(storage::io::FileWriter &writer, const TurnRestriction &restriction) { - writer.WriteOne(restriction.flags); + writer.WriteOne(restriction.is_only); if (restriction.Type() == RestrictionType::WAY_RESTRICTION) { write(writer, boost::get(restriction.node_or_way)); @@ -213,7 +213,7 @@ inline void read(storage::io::FileReader &reader, ConditionalTurnRestriction &re TurnRestriction base; read(reader, base); reinterpret_cast(restriction) = std::move(base); - auto num_conditions = reader.ReadElementCount64(); + const auto num_conditions = reader.ReadElementCount64(); restriction.condition.resize(num_conditions); for (uint64_t i = 0; i < num_conditions; i++) { @@ -230,19 +230,18 @@ inline void read(storage::io::FileReader &reader, std::vector & auto num_indices = reader.ReadElementCount64(); restrictions.reserve(num_indices); TurnRestriction restriction; - while (num_indices > 0) + while (num_indices-- > 0) { read(reader, restriction); restrictions.push_back(std::move(restriction)); - num_indices--; } } inline void write(storage::io::FileWriter &writer, const std::vector &restrictions) { - std::uint64_t num_indices = restrictions.size(); + const auto num_indices = restrictions.size(); writer.WriteElementCount64(num_indices); - auto const write_restriction = [&writer](const auto &restriction) { + const auto write_restriction = [&writer](const auto &restriction) { write(writer, restriction); }; std::for_each(restrictions.begin(), restrictions.end(), write_restriction); @@ -255,20 +254,19 @@ inline void read(storage::io::FileReader &reader, auto num_indices = reader.ReadElementCount64(); restrictions.reserve(num_indices); ConditionalTurnRestriction restriction; - while (num_indices > 0) + while (num_indices-- > 0) { read(reader, restriction); restrictions.push_back(std::move(restriction)); - num_indices--; } } inline void write(storage::io::FileWriter &writer, const std::vector &restrictions) { - std::uint64_t num_indices = restrictions.size(); + const auto num_indices = restrictions.size(); writer.WriteElementCount64(num_indices); - auto const write_restriction = [&writer](const auto &restriction) { + const auto write_restriction = [&writer](const auto &restriction) { write(writer, restriction); }; std::for_each(restrictions.begin(), restrictions.end(), write_restriction); diff --git a/include/extractor/way_restriction_map.hpp b/include/extractor/way_restriction_map.hpp new file mode 100644 index 00000000000..4e105c8af09 --- /dev/null +++ b/include/extractor/way_restriction_map.hpp @@ -0,0 +1,89 @@ +#ifndef OSRM_EXTRACTOR_WAY_RESTRICTION_MAP_HPP_ +#define OSRM_EXTRACTOR_WAY_RESTRICTION_MAP_HPP_ + +#include +#include + +// to access the turn restrictions +#include + +#include "extractor/restriction.hpp" +#include "util/integer_range.hpp" +#include "util/typedefs.hpp" + +// Given the compressed representation of via-way turn restrictions, we provide a fast access into +// the restrictions to indicate which turns may be restricted due to a way in between +namespace osrm +{ +namespace extractor +{ + +class WayRestrictionMap +{ + public: + struct ViaWay + { + std::size_t id; + NodeID from; + NodeID to; + }; + WayRestrictionMap(const std::vector &turn_restrictions); + + // check if an edge between two nodes is a restricted turn. The check needs to be performed + bool IsViaWay(const NodeID from, const NodeID to) const; + + // number of duplicated nodes + std::size_t NumberOfDuplicatedNodes() const; + + // returns a representative for the duplicated way, consisting of the representative ID (first + // ID of the nodes restrictions) and the from/to vertices of the via-way + // This is used to construct edge based nodes that act as intermediate nodes. + std::vector DuplicatedNodeRepresentatives() const; + + // Access all duplicated NodeIDs for a set of nodes indicating a via way + util::range DuplicatedNodeIDs(const NodeID from, const NodeID to) const; + + // check whether a turn onto a given node is restricted, when coming from a duplicated node + bool IsRestricted(std::size_t duplicated_node, const NodeID to) const; + + TurnRestriction const &GetRestriction(std::size_t) const; + + // changes edge_based_node to the correct duplicated_node_id in case node_based_from, + // node_based_via, node_based_to can be identified with a restriction group + NodeID RemapIfRestricted(const NodeID edge_based_node, + const NodeID node_based_from, + const NodeID node_based_via, + const NodeID node_based_to, + const NodeID number_of_edge_based_nodes) const; + + private: + std::size_t AsDuplicatedNodeID(const std::size_t restriction_id) const; + + // access all restrictions that have the same starting way and via way. Any duplicated node + // represents the same in-way + via-way combination. This vector contains data about all + // restrictions and their assigned duplicated nodes. It indicates the minimum restriciton ID + // that is represented by the next node. The ID of a node is defined as the position of the + // lower bound of the restrictions ID within this array + // + // a - b + // | + // y - c - x + // + // restriction nodes | restriction id + // a - b - c - x : 5 + // a - b - c - y : 6 + // + // EBN: 0 . | 2 | 3 | 4 ... + // duplicated node groups: ... | 5 | 7 | ... + std::vector duplicated_node_groups; + + boost::unordered_multimap, std::size_t> restriction_starts; + boost::unordered_multimap, std::size_t> restriction_ends; + + std::vector restriction_data; +}; + +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_WAY_RESTRICTION_MAP_HPP_ diff --git a/include/util/serialization.hpp b/include/util/serialization.hpp index 8fd406ded5b..ebece1a430b 100644 --- a/include/util/serialization.hpp +++ b/include/util/serialization.hpp @@ -66,7 +66,7 @@ template inline void read(storage::io::FileReader &reader, DynamicGraph &graph) { storage::serialization::read(reader, graph.node_array); - auto num_edges = reader.ReadElementCount64(); + const auto num_edges = reader.ReadElementCount64(); graph.edge_list.resize(num_edges); for (auto index : irange(0, num_edges)) { diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 06d52556eb6..1ba65b2c0f6 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -51,16 +51,17 @@ static_assert(std::is_pod(), "OSMNodeID is not a valid alias"); using OSMWayID = osrm::Alias; static_assert(std::is_pod(), "OSMWayID is not a valid alias"); -static const OSMNodeID SPECIAL_OSM_NODEID = OSMNodeID{std::numeric_limits::max()}; -static const OSMWayID SPECIAL_OSM_WAYID = OSMWayID{std::numeric_limits::max()}; - -static const OSMNodeID MAX_OSM_NODEID = OSMNodeID{std::numeric_limits::max()}; -static const OSMNodeID MIN_OSM_NODEID = OSMNodeID{std::numeric_limits::min()}; -static const OSMWayID MAX_OSM_WAYID = OSMWayID{std::numeric_limits::max()}; -static const OSMWayID MIN_OSM_WAYID = OSMWayID{std::numeric_limits::min()}; - -using OSMNodeID_weak = std::uint64_t; -using OSMEdgeID_weak = std::uint64_t; +static const OSMNodeID SPECIAL_OSM_NODEID = + OSMNodeID{std::numeric_limits::max()}; +static const OSMWayID SPECIAL_OSM_WAYID = + OSMWayID{std::numeric_limits::max()}; + +static const OSMNodeID MAX_OSM_NODEID = + OSMNodeID{std::numeric_limits::max()}; +static const OSMNodeID MIN_OSM_NODEID = + OSMNodeID{std::numeric_limits::min()}; +static const OSMWayID MAX_OSM_WAYID = OSMWayID{std::numeric_limits::max()}; +static const OSMWayID MIN_OSM_WAYID = OSMWayID{std::numeric_limits::min()}; using NodeID = std::uint32_t; using EdgeID = std::uint32_t; diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 34252656fb9..e7da76da654 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -46,15 +46,13 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory( CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, - std::shared_ptr restriction_map, const std::vector &coordinates, const extractor::PackedOSMIDs &osm_node_ids, ProfileProperties profile_properties, const util::NameTable &name_table, guidance::LaneDescriptionMap &lane_description_map) - : m_max_edge_id(0), m_coordinates(coordinates), m_osm_node_ids(osm_node_ids), - m_node_based_graph(std::move(node_based_graph)), - m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes), + : m_number_of_edge_based_nodes(0), m_coordinates(coordinates), m_osm_node_ids(osm_node_ids), + m_node_based_graph(std::move(node_based_graph)), m_barrier_nodes(barrier_nodes), m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container), profile_properties(std::move(profile_properties)), name_table(name_table), lane_description_map(lane_description_map) @@ -93,7 +91,10 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector &out swap(m_edge_based_node_weights, output_node_weights); } -EdgeID EdgeBasedGraphFactory::GetHighestEdgeID() { return m_max_edge_id; } +std::uint64_t EdgeBasedGraphFactory::GetNumberOfEdgeBasedNodes() const +{ + return m_number_of_edge_based_nodes; +} NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v) { @@ -176,8 +177,8 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N current_edge_target_coordinate_id, i); - m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || - reverse_data.startpoint); + m_edge_based_node_is_startpoint.push_back( + (forward_data.startpoint || reverse_data.startpoint)); current_edge_source_coordinate_id = current_edge_target_coordinate_id; } @@ -192,15 +193,17 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment, const std::string &turn_weight_penalties_filename, const std::string &turn_duration_penalties_filename, const std::string &turn_penalties_index_filename, - const std::string &cnbg_ebg_mapping_path) + const std::string &cnbg_ebg_mapping_path, + const RestrictionMap &restriction_map, + const WayRestrictionMap &way_restriction_map) { TIMER_START(renumber); - m_max_edge_id = RenumberEdges() - 1; + m_number_of_edge_based_nodes = RenumberEdges() + way_restriction_map.NumberOfDuplicatedNodes(); TIMER_STOP(renumber); TIMER_START(generate_nodes); { - auto mapping = GenerateEdgeExpandedNodes(); + auto mapping = GenerateEdgeExpandedNodes(way_restriction_map); files::writeNBGMapping(cnbg_ebg_mapping_path, mapping); } TIMER_STOP(generate_nodes); @@ -211,7 +214,9 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment, turn_lane_data_filename, turn_weight_penalties_filename, turn_duration_penalties_filename, - turn_penalties_index_filename); + turn_penalties_index_filename, + restriction_map, + way_restriction_map); TIMER_STOP(generate_edges); @@ -257,14 +262,19 @@ unsigned EdgeBasedGraphFactory::RenumberEdges() } /// Creates the nodes in the edge expanded graph from edges in the node-based graph. -std::vector EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() +std::vector +EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_restriction_map) { std::vector mapping; // Allocate memory for edge-based nodes - m_edge_based_node_container = EdgeBasedNodeDataContainer(m_max_edge_id + 1); + // In addition to the normal edges, allocate enough space for copied edges from + // via-way-restrictions + m_edge_based_node_container = EdgeBasedNodeDataContainer(m_number_of_edge_based_nodes); util::Log() << "Generating edge expanded nodes ... "; + // indicating a normal node within the edge-based graph. This node represents an edge in the + // node-based graph { util::UnbufferedLog log; util::Percent progress(log, m_node_based_graph->GetNumberOfNodes()); @@ -305,10 +315,55 @@ std::vector EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() } } + util::Log() << "Expanding via-way turn restrictions ... "; + // Add copies of the nodes + { + util::UnbufferedLog log; + const auto via_ways = way_restriction_map.DuplicatedNodeRepresentatives(); + util::Percent progress(log, via_ways.size()); + + NodeID edge_based_node_id = + NodeID(m_number_of_edge_based_nodes - way_restriction_map.NumberOfDuplicatedNodes()); + std::size_t progress_counter = 0; + // allocate enough space for the mapping + for (const auto way : via_ways) + { + const auto node_u = way.from; + const auto node_v = way.to; + // we know that the edge exists as non-reversed edge + const auto eid = m_node_based_graph->FindEdge(node_u, node_v); + + BOOST_ASSERT(m_node_based_graph->GetEdgeData(eid).edge_id != SPECIAL_NODEID); + + // merge edges together into one EdgeBasedNode + BOOST_ASSERT(node_u != SPECIAL_NODEID); + BOOST_ASSERT(node_v != SPECIAL_NODEID); + + // find node in the edge based graph, we only require one id: + const EdgeData &edge_data = m_node_based_graph->GetEdgeData(eid); + // what is this ID all about? :( + BOOST_ASSERT(edge_data.edge_id != SPECIAL_NODEID); + + BOOST_ASSERT(edge_data.edge_id < m_edge_based_node_container.Size()); + m_edge_based_node_container.SetData( + edge_based_node_id, + // fetch the known geometry ID + m_edge_based_node_container.GetGeometryID(static_cast(edge_data.edge_id)), + edge_data.name_id, + edge_data.travel_mode, + edge_data.classes); + + m_edge_based_node_weights.push_back(m_edge_based_node_weights[eid]); + + edge_based_node_id++; + progress.PrintStatus(progress_counter++); + } + } + BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size()); - BOOST_ASSERT(m_max_edge_id + 1 == m_edge_based_node_weights.size()); + BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size()); - util::Log() << "Generated " << (m_max_edge_id + 1) << " nodes and " + util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes and " << m_edge_based_node_segments.size() << " segments in edge-expanded graph"; return mapping; @@ -321,7 +376,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const std::string &turn_lane_data_filename, const std::string &turn_weight_penalties_filename, const std::string &turn_duration_penalties_filename, - const std::string &turn_penalties_index_filename) + const std::string &turn_penalties_index_filename, + const RestrictionMap &restriction_map, + const WayRestrictionMap &way_restriction_map) { util::Log() << "Generating edge-expanded edges "; @@ -342,7 +399,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( SuffixTable street_name_suffix_table(scripting_environment); guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_coordinates, - *m_restriction_map, + restriction_map, m_barrier_nodes, m_compressed_edge_container, name_table, @@ -410,7 +467,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // appended to the various output arrays/files by the `output_stage`. struct IntersectionData { - std::size_t nodes_processed = 0; std::vector turn_indexes; std::vector edges_list; std::vector turn_weight_penalties; @@ -418,13 +474,138 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::vector turn_data_container; }; + // same as IntersectionData, but grouped with edge to allow sorting after creating. Edges + // are out of order + struct EdgeWithData + { + EdgeBasedEdge edge; + lookup::TurnIndexBlock turn_index; + TurnPenalty turn_weight_penalty; + TurnPenalty turn_duration_penalty; + TurnData turn_data; + }; + + struct PipelineBuffer + { + std::size_t nodes_processed = 0; + IntersectionData continuous_data; + std::vector delayed_data; + }; + + // add into delayed data + const auto delayed_inserter = [](const auto &edge_with_data, auto &buffer) { + buffer.delayed_data.push_back(edge_with_data); + }; + + // add into main data + const auto continuous_inserter = [](const auto &edge_with_data, auto &buffer) { + buffer.continuous_data.edges_list.push_back(edge_with_data.edge); + buffer.continuous_data.turn_indexes.push_back(edge_with_data.turn_index); + buffer.continuous_data.turn_weight_penalties.push_back( + edge_with_data.turn_weight_penalty); + buffer.continuous_data.turn_duration_penalties.push_back( + edge_with_data.turn_duration_penalty); + buffer.continuous_data.turn_data_container.push_back(edge_with_data.turn_data); + }; + + // Generate edges for either artificial nodes or the main graph + const auto generate_edges = [this, &scripting_environment, weight_multiplier]( + // what nodes will be used? In most cases this will be the id stored in the edge_data. + // In case of duplicated nodes (e.g. due to via-way restrictions), one/both of these + // might refer to a newly added edge based node + const auto edge_based_node_from, + const auto edge_based_node_to, + // the situation of the turn + const auto node_along_road_entering, + const auto node_based_edge_from, + const auto node_at_center_of_intersection, + const auto node_based_edge_to, + const auto &intersection, + const auto &turn, + const auto entry_class_id, + // we require a sorted output, additional nodes are collected and added after the + // sorting is done Here we can specify how/where to add the data + auto inserter, + auto &output_buffer) { + const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(node_based_edge_from); + const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(node_based_edge_to); + + BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id); + BOOST_ASSERT(!edge_data1.reversed); + BOOST_ASSERT(!edge_data2.reversed); + + // the following is the core of the loop. + TurnData turn_data = {turn.instruction, + turn.lane_data_id, + entry_class_id, + util::guidance::TurnBearing(intersection[0].bearing), + util::guidance::TurnBearing(turn.bearing)}; + + // compute weight and duration penalties + auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection); + ExtractionTurn extracted_turn(turn, is_traffic_light); + extracted_turn.source_restricted = edge_data1.restricted; + extracted_turn.target_restricted = edge_data2.restricted; + scripting_environment.ProcessTurn(extracted_turn); + + // turn penalties are limited to [-2^15, 2^15) which roughly + // translates to 54 minutes and fits signed 16bit deci-seconds + auto weight_penalty = + boost::numeric_cast(extracted_turn.weight * weight_multiplier); + auto duration_penalty = boost::numeric_cast(extracted_turn.duration * 10.); + + BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); + BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); + + // auto turn_id = m_edge_based_edge_list.size(); + auto weight = boost::numeric_cast(edge_data1.weight + weight_penalty); + auto duration = boost::numeric_cast(edge_data1.duration + duration_penalty); + + EdgeBasedEdge edge_based_edge = { + edge_based_node_from, + edge_based_node_to, + SPECIAL_NODEID, // This will be updated once the main loop + // completes! + weight, + duration, + true, + false}; + + // We write out the mapping between the edge-expanded edges and + // the original nodes. Since each edge represents a possible + // maneuver, external programs can use this to quickly perform updates to edge + // weights in order to penalize certain turns. + + // If this edge is 'trivial' -- where the compressed edge + // corresponds exactly to an original OSM segment -- we can pull the turn's + // preceding node ID directly with `node_along_road_entering`; + // otherwise, we need to look up the node immediately preceding the turn + // from the compressed edge container. + const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from); + + const auto &from_node = + isTrivial ? node_along_road_entering + : m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from); + const auto &via_node = + m_compressed_edge_container.GetLastEdgeTargetID(node_based_edge_from); + const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid); + + lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node}; + + // insert data into the designated buffer + inserter( + EdgeWithData{ + edge_based_edge, turn_index_block, weight_penalty, duration_penalty, turn_data}, + output_buffer); + + }; + // Second part of the pipeline is where the intersection analysis is done for // each intersection - tbb::filter_t, std::shared_ptr> - processor_stage( + tbb::filter_t, std::shared_ptr> processor_stage( tbb::filter::parallel, [&](const tbb::blocked_range &intersection_node_range) { - auto buffer = std::make_shared(); + auto buffer = std::make_shared(); buffer->nodes_processed = intersection_node_range.end() - intersection_node_range.begin(); @@ -514,92 +695,105 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( bearing_class_by_node_based_node[node_at_center_of_intersection] = bearing_class_id; + // check if we are turning off a via way + const auto turning_off_via_way = way_restriction_map.IsViaWay( + node_along_road_entering, node_at_center_of_intersection); + for (const auto &turn : intersection) { // only keep valid turns if (!turn.entry_allowed) continue; - // only add an edge if turn is not prohibited const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(incoming_edge); const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid); - BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id); - BOOST_ASSERT(!edge_data1.reversed); - BOOST_ASSERT(!edge_data2.reversed); - - // the following is the core of the loop. - buffer->turn_data_container.push_back( - {turn.instruction, - turn.lane_data_id, - entry_class_id, - util::guidance::TurnBearing(intersection[0].bearing), - util::guidance::TurnBearing(turn.bearing)}); - - // compute weight and duration penalties - auto is_traffic_light = - m_traffic_lights.count(node_at_center_of_intersection); - ExtractionTurn extracted_turn(turn, is_traffic_light); - extracted_turn.source_restricted = edge_data1.restricted; - extracted_turn.target_restricted = edge_data2.restricted; - scripting_environment.ProcessTurn(extracted_turn); - - // turn penalties are limited to [-2^15, 2^15) which roughly - // translates to 54 minutes and fits signed 16bit deci-seconds - auto weight_penalty = boost::numeric_cast( - extracted_turn.weight * weight_multiplier); - auto duration_penalty = - boost::numeric_cast(extracted_turn.duration * 10.); - - BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); - BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); - - // auto turn_id = m_edge_based_edge_list.size(); - auto weight = - boost::numeric_cast(edge_data1.weight + weight_penalty); - auto duration = boost::numeric_cast(edge_data1.duration + - duration_penalty); - buffer->edges_list.emplace_back( - edge_data1.edge_id, + // In case a way restriction starts at a given location, add a turn onto + // every artificial node eminating here. + // + // e - f + // | + // a - b + // | + // c - d + // + // ab via bc to cd + // ab via be to ef + // + // has two artifical nodes (be/bc) with restrictions starting at `ab`. + // Since every restriction group (abc | abe) refers to the same + // artificial node, we simply have to find a single representative for + // the turn. Here we check whether the turn in question is the start of + // a via way restriction. If that should be the case, we switch + // edge_data2.edge_id to the ID of the duplicated node associated with + // the turn. (e.g. ab via bc switches bc to bc_dup) + auto const target_id = way_restriction_map.RemapIfRestricted( edge_data2.edge_id, - SPECIAL_NODEID, // This will be updated once the main loop - // completes! - weight, - duration, - true, - false); - - BOOST_ASSERT(buffer->turn_weight_penalties.size() == - buffer->edges_list.size() - 1); - buffer->turn_weight_penalties.push_back(weight_penalty); - BOOST_ASSERT(buffer->turn_duration_penalties.size() == - buffer->edges_list.size() - 1); - buffer->turn_duration_penalties.push_back(duration_penalty); - - // We write out the mapping between the edge-expanded edges and the - // original nodes. Since each edge represents a possible maneuver, - // external programs can use this to quickly perform updates to edge - // weights in order to penalize certain turns. - - // If this edge is 'trivial' -- where the compressed edge corresponds - // exactly to an original OSM segment -- we can pull the turn's - // preceding node ID directly with `node_along_road_entering`; - // otherwise, we need to look up the node immediately preceding the turn - // from the compressed edge container. - const bool isTrivial = - m_compressed_edge_container.IsTrivial(incoming_edge); - - const auto &from_node = - isTrivial ? node_along_road_entering - : m_compressed_edge_container.GetLastEdgeSourceID( - incoming_edge); - const auto &via_node = - m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge); - const auto &to_node = - m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid); - - buffer->turn_indexes.push_back({from_node, via_node, to_node}); + node_along_road_entering, + node_at_center_of_intersection, + m_node_based_graph->GetTarget(turn.eid), + m_number_of_edge_based_nodes); + + generate_edges(edge_data1.edge_id, + target_id, + node_along_road_entering, + incoming_edge, + node_at_center_of_intersection, + turn.eid, + intersection, + turn, + entry_class_id, + continuous_inserter, + *buffer); + + // when turning off a a via-way turn restriction, we need to not only + // handle the normal edges for the way, but also add turns for every + // duplicated node. This process is integrated here to avoid doing the + // turn analysis multiple times. + if (turning_off_via_way) + { + const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs( + node_along_road_entering, node_at_center_of_intersection); + + // next to the normal restrictions tracked in `entry_allowed`, via + // ways might introduce additional restrictions. These are handled + // here when turning off a via-way + const auto add_unrestricted_turns = + [&](const auto duplicated_node_id) { + const auto from_id = + m_number_of_edge_based_nodes - + way_restriction_map.NumberOfDuplicatedNodes() + + duplicated_node_id; + + auto const node_at_end_of_turn = + m_node_based_graph->GetTarget(turn.eid); + + const auto is_restricted = way_restriction_map.IsRestricted( + duplicated_node_id, node_at_end_of_turn); + + if (is_restricted) + return; + + generate_edges( + NodeID(from_id), + m_node_based_graph->GetEdgeData(turn.eid).edge_id, + node_along_road_entering, + incoming_edge, + node_at_center_of_intersection, + turn.eid, + intersection, + turn, + entry_class_id, + delayed_inserter, + *buffer); + + }; + + std::for_each(duplicated_nodes.begin(), + duplicated_nodes.end(), + add_unrestricted_turns); + } } } } @@ -616,36 +810,42 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::vector turn_indexes_write_buffer; turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE); - // Last part of the pipeline puts all the calculated data into the serial buffers - tbb::filter_t, void> output_stage( - tbb::filter::serial_in_order, [&](const std::shared_ptr buffer) { + std::vector delayed_data; - nodes_completed += buffer->nodes_processed; - progress.PrintStatus(nodes_completed); + auto const append_data_to_output = [&](IntersectionData const &data) { + // NOTE: potential overflow here if we hit 2^32 routable edges + m_edge_based_edge_list.append(data.edges_list.begin(), data.edges_list.end()); - // NOTE: potential overflow here if we hit 2^32 routable edges - m_edge_based_edge_list.append(buffer->edges_list.begin(), buffer->edges_list.end()); - BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits::max()); + BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits::max()); - turn_weight_penalties.insert(turn_weight_penalties.end(), - buffer->turn_weight_penalties.begin(), - buffer->turn_weight_penalties.end()); - turn_duration_penalties.insert(turn_duration_penalties.end(), - buffer->turn_duration_penalties.begin(), - buffer->turn_duration_penalties.end()); - turn_data_container.append(buffer->turn_data_container); + turn_weight_penalties.insert(turn_weight_penalties.end(), + data.turn_weight_penalties.begin(), + data.turn_weight_penalties.end()); + turn_duration_penalties.insert(turn_duration_penalties.end(), + data.turn_duration_penalties.begin(), + data.turn_duration_penalties.end()); + turn_data_container.append(data.turn_data_container); + turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(), + data.turn_indexes.begin(), + data.turn_indexes.end()); - turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(), - buffer->turn_indexes.begin(), - buffer->turn_indexes.end()); + // Buffer writes to reduce syscall count + if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE) + { + turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(), + turn_indexes_write_buffer.size()); + turn_indexes_write_buffer.clear(); + } + }; - // Buffer writes to reduce syscall count - if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE) - { - turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(), - turn_indexes_write_buffer.size()); - turn_indexes_write_buffer.clear(); - } + // Last part of the pipeline puts all the calculated data into the serial buffers + tbb::filter_t, void> output_stage( + tbb::filter::serial_in_order, [&](const std::shared_ptr buffer) { + nodes_completed += buffer->nodes_processed; + progress.PrintStatus(nodes_completed); + append_data_to_output(buffer->continuous_data); + delayed_data.insert( + delayed_data.end(), buffer->delayed_data.begin(), buffer->delayed_data.end()); }); // Now, execute the pipeline. The value of "5" here was chosen by experimentation @@ -656,6 +856,18 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5, generator_stage & processor_stage & output_stage); + std::sort(delayed_data.begin(), delayed_data.end(), [](auto const &lhs, auto const &rhs) { + return lhs.edge.source < rhs.edge.source; + }); + auto const transfer_data = [&](auto const &edge_with_data) { + m_edge_based_edge_list.push_back(edge_with_data.edge); + turn_weight_penalties.push_back(edge_with_data.turn_weight_penalty); + turn_duration_penalties.push_back(edge_with_data.turn_duration_penalty); + turn_data_container.push_back(edge_with_data.turn_data); + turn_indexes_write_buffer.push_back(edge_with_data.turn_index); + }; + std::for_each(delayed_data.begin(), delayed_data.end(), transfer_data); + // Flush the turn_indexes_write_buffer if it's not empty if (!turn_indexes_write_buffer.empty()) { @@ -715,7 +927,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( util::Log() << " contains " << m_edge_based_edge_list.size() << " edges"; util::Log() << " skips " << restricted_turns_counter << " turns, " "defined by " - << m_restriction_map->size() << " restrictions"; + << restriction_map.size() << " restrictions"; util::Log() << " skips " << skipped_uturns_counter << " U turns"; util::Log() << " skips " << skipped_barrier_turns_counter << " turns over barriers"; } diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 68c4ced4bdd..df805522e00 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -668,16 +668,16 @@ void ExtractionContainers::PrepareRestrictions() if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) { const auto &way = turn_restriction.AsWayRestriction(); - referenced_ways[OSMWayID{way.from}] = dummy_segment; - referenced_ways[OSMWayID{way.to}] = dummy_segment; - referenced_ways[OSMWayID{way.via}] = dummy_segment; + referenced_ways[way.from] = dummy_segment; + referenced_ways[way.to] = dummy_segment; + referenced_ways[way.via] = dummy_segment; } else { BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION); const auto &node = turn_restriction.AsNodeRestriction(); - referenced_ways[OSMWayID{node.from}] = dummy_segment; - referenced_ways[OSMWayID{node.to}] = dummy_segment; + referenced_ways[node.from] = dummy_segment; + referenced_ways[node.to] = dummy_segment; } }; @@ -702,7 +702,7 @@ void ExtractionContainers::PrepareRestrictions() auto const to_internal = [&](auto const osm_node) { auto internal = mapExternalToInternalNodeID( - used_node_id_list.begin(), used_node_id_list.end(), OSMNodeID{osm_node}); + used_node_id_list.begin(), used_node_id_list.end(), osm_node); if (internal == SPECIAL_NODEID) { util::Log(logDEBUG) << "Restriction references invalid node: " << osm_node; @@ -763,15 +763,15 @@ void ExtractionContainers::PrepareRestrictions() // be connected at a single location) auto const get_node_restriction_from_OSM_ids = [&]( auto const from_id, auto const to_id, const OSMNodeID via_node = MAX_OSM_NODEID) { - auto const from_segment_itr = referenced_ways.find(OSMWayID{from_id}); - if (from_segment_itr->second.way_id != OSMWayID{from_id}) + auto const from_segment_itr = referenced_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 = referenced_ways.find(OSMWayID{to_id}); - if (to_segment_itr->second.way_id != OSMWayID{to_id}) + auto const to_segment_itr = referenced_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}; @@ -781,7 +781,7 @@ void ExtractionContainers::PrepareRestrictions() // 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 = [&](auto const &external_type, auto &internal_type) { + const auto transform = [&](const auto &external_type, auto &internal_type) { if (external_type.Type() == RestrictionType::WAY_RESTRICTION) { auto const &external = external_type.AsWayRestriction(); @@ -808,12 +808,11 @@ void ExtractionContainers::PrepareRestrictions() auto const via_node = to_internal(external.via); // check if we were able to resolve all the involved ways - auto restriction = get_node_restriction_from_OSM_ids( - external.from, external.to, OSMNodeID{external.via}); + auto restriction = + get_node_restriction_from_OSM_ids(external.from, external.to, external.via); if (!restriction.Valid()) { - std::cout << " >>> Invalid" << std::endl; return false; } @@ -830,25 +829,26 @@ void ExtractionContainers::PrepareRestrictions() // wrapper function to handle distinction between conditional and unconditional turn // restrictions - const auto transform_into_internal_types = [&](auto &external_restriction) { - // unconditional restriction - if (external_restriction.condition.empty()) - { - TurnRestriction restriction; - restriction.flags = external_restriction.flags; - if (transform(external_restriction, restriction)) - unconditional_turn_restrictions.push_back(restriction); - } - // conditional turn restriction - else - { - ConditionalTurnRestriction restriction; - restriction.flags = external_restriction.flags; - restriction.condition = std::move(external_restriction.condition); - if (transform(external_restriction, restriction)) - conditional_turn_restrictions.push_back(restriction); - } - }; + const auto transform_into_internal_types = + [&](const InputConditionalTurnRestriction &external_restriction) { + // unconditional restriction + if (external_restriction.condition.empty()) + { + TurnRestriction restriction; + restriction.is_only = external_restriction.is_only; + if (transform(external_restriction, restriction)) + unconditional_turn_restrictions.push_back(restriction); + } + // conditional turn restriction + else + { + ConditionalTurnRestriction restriction; + restriction.is_only = external_restriction.is_only; + restriction.condition = std::move(external_restriction.condition); + if (transform(external_restriction, restriction)) + conditional_turn_restrictions.push_back(restriction); + } + }; // Transforming the restrictions into the dedicated internal types { diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index c75e77f6cb0..90eaa0ee0f2 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -24,6 +24,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/restriction_map.hpp" +#include "extractor/way_restriction_map.hpp" #include "util/static_graph.hpp" #include "util/static_rtree.hpp" @@ -140,7 +141,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) turn_lane_map); auto number_of_node_based_nodes = graph_size.first; - auto max_edge_id = graph_size.second; + auto max_edge_id = graph_size.second - 1; TIMER_STOP(expansion); @@ -470,26 +471,36 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, util::NameTable name_table(config.GetPath(".osrm.names").string()); - auto restriction_map = std::make_shared(turn_restrictions); - EdgeBasedGraphFactory edge_based_graph_factory( - node_based_graph, - compressed_edge_container, - barrier_nodes, - traffic_lights, - std::const_pointer_cast(restriction_map), - coordinates, - osm_node_ids, - scripting_environment.GetProfileProperties(), - name_table, - turn_lane_map); - - edge_based_graph_factory.Run(scripting_environment, - config.GetPath(".osrm.edges").string(), - config.GetPath(".osrm.tld").string(), - config.GetPath(".osrm.turn_weight_penalties").string(), - config.GetPath(".osrm.turn_duration_penalties").string(), - config.GetPath(".osrm.turn_penalties_index").string(), - config.GetPath(".osrm.cnbg_to_ebg").string()); + EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph, + compressed_edge_container, + barrier_nodes, + traffic_lights, + coordinates, + osm_node_ids, + scripting_environment.GetProfileProperties(), + name_table, + turn_lane_map); + + const auto create_edge_based_edges = [&]() { + // scoped to relase intermediate datastructures right after the call + RestrictionMap via_node_restriction_map(turn_restrictions); + WayRestrictionMap via_way_restriction_map(turn_restrictions); + turn_restrictions.clear(); + turn_restrictions.shrink_to_fit(); + + edge_based_graph_factory.Run(scripting_environment, + config.GetPath(".osrm.edges").string(), + config.GetPath(".osrm.tld").string(), + config.GetPath(".osrm.turn_weight_penalties").string(), + config.GetPath(".osrm.turn_duration_penalties").string(), + config.GetPath(".osrm.turn_penalties_index").string(), + config.GetPath(".osrm.cnbg_to_ebg").string(), + via_node_restriction_map, + via_way_restriction_map); + return edge_based_graph_factory.GetNumberOfEdgeBasedNodes(); + }; + + const auto number_of_edge_based_nodes = create_edge_based_edges(); compressed_edge_container.PrintStatistics(); @@ -531,7 +542,6 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights); - auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID(); const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes(); @@ -545,7 +555,7 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, TIMER_STOP(write_intersections); util::Log() << "ok, after " << TIMER_SEC(write_intersections) << "s"; - return std::make_pair(number_of_node_based_nodes, max_edge_id); + return std::make_pair(number_of_node_based_nodes, number_of_edge_based_nodes); } /** diff --git a/src/extractor/restriction_map.cpp b/src/extractor/restriction_map.cpp index 9b4230b3154..812ff504b99 100644 --- a/src/extractor/restriction_map.cpp +++ b/src/extractor/restriction_map.cpp @@ -47,7 +47,7 @@ RestrictionMap::RestrictionMap(const std::vector &restriction_l { continue; } - else if (restriction.flags.is_only) + else if (restriction.is_only) { // We are going to insert an is_only_*-restriction. There can be only one. m_count -= m_restriction_bucket_list.at(index).size(); @@ -55,8 +55,7 @@ RestrictionMap::RestrictionMap(const std::vector &restriction_l } } ++m_count; - m_restriction_bucket_list.at(index).emplace_back(node_restriction.to, - restriction.flags.is_only); + m_restriction_bucket_list.at(index).emplace_back(node_restriction.to, restriction.is_only); } } @@ -65,33 +64,6 @@ bool RestrictionMap::IsViaNode(const NodeID node) const return m_no_turn_via_node_set.find(node) != m_no_turn_via_node_set.end(); } -// Replaces start edge (v, w) with (u, w). Only start node changes. -void RestrictionMap::FixupStartingTurnRestriction(const NodeID node_u, - const NodeID node_v, - const NodeID node_w) -{ - BOOST_ASSERT(node_u != SPECIAL_NODEID); - BOOST_ASSERT(node_v != SPECIAL_NODEID); - BOOST_ASSERT(node_w != SPECIAL_NODEID); - - if (!IsSourceNode(node_v)) - { - return; - } - - const auto restriction_iterator = m_restriction_map.find({node_v, node_w}); - if (restriction_iterator != m_restriction_map.end()) - { - const unsigned index = restriction_iterator->second; - // remove old restriction start (v,w) - m_restriction_map.erase(restriction_iterator); - m_restriction_start_nodes.emplace(node_u); - // insert new restriction start (u,w) (pointing to index) - RestrictionSource new_source = {node_u, node_w}; - m_restriction_map.emplace(new_source, index); - } -} - // Check if edge (u, v) is the start of any turn restriction. // If so returns id of first target node. NodeID RestrictionMap::CheckForEmanatingIsOnlyTurn(const NodeID node_u, const NodeID node_v) const diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp index e495d6f20e1..996743eced8 100644 --- a/src/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -125,7 +125,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const // we pretend every restriction is a conditional restriction. If we do not find any restriction, // we can trim away the vector after parsing InputConditionalTurnRestriction restriction_container; - restriction_container.flags.is_only = is_only_restriction; + restriction_container.is_only = is_only_restriction; boost::optional from = boost::none, via = boost::none, to = boost::none; bool is_node_restriction; @@ -212,11 +212,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const { if (is_node_restriction) { - restriction_container.node_or_way = InputNodeRestriction{*from, *via, *to}; + // template struct requires bracket for ID initialisation :( + restriction_container.node_or_way = InputNodeRestriction{{*from}, {*via}, {*to}}; } else { - restriction_container.node_or_way = InputWayRestriction{*from, *via, *to}; + // template struct requires bracket for ID initialisation :( + restriction_container.node_or_way = InputWayRestriction{{*from}, {*via}, {*to}}; } return restriction_container; } diff --git a/src/extractor/way_restriction_map.cpp b/src/extractor/way_restriction_map.cpp new file mode 100644 index 00000000000..ae9dbe380d5 --- /dev/null +++ b/src/extractor/way_restriction_map.cpp @@ -0,0 +1,199 @@ +#include "extractor/way_restriction_map.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ + +namespace +{ +struct FindViaWay +{ + bool operator()(const std::tuple value, + const TurnRestriction &restriction) const + { + const auto &way = restriction.AsWayRestriction(); + return value < std::tie(way.in_restriction.via, way.out_restriction.via); + } + bool operator()(const TurnRestriction &restriction, + const std::tuple value) const + { + const auto &way = restriction.AsWayRestriction(); + return std::tie(way.in_restriction.via, way.out_restriction.via) < value; + } +}; + +} // namespace + +WayRestrictionMap::WayRestrictionMap(const std::vector &turn_restrictions) +{ + // get all way restrictions + const auto extract_restrictions = [this](const auto &turn_restriction) { + if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) + { + const auto &way = turn_restriction.AsWayRestriction(); + + // so far we can only handle restrictions that are not interrupted + if (way.in_restriction.via == way.out_restriction.from && + way.in_restriction.to == way.out_restriction.via) + restriction_data.push_back(turn_restriction); + } + }; + std::for_each(turn_restrictions.begin(), turn_restrictions.end(), extract_restrictions); + + const auto as_duplicated_node = + [](auto const &restriction) -> std::tuple { + auto &way = restriction.AsWayRestriction(); + // group restrictions by the via-way. On same via-ways group by from + return std::make_tuple( + way.in_restriction.via, way.out_restriction.via, way.in_restriction.from); + }; + + const auto by_duplicated_node = [&](auto const &lhs, auto const &rhs) { + return as_duplicated_node(lhs) < as_duplicated_node(rhs); + }; + + std::sort(restriction_data.begin(), restriction_data.end(), by_duplicated_node); + + std::size_t index = 0, duplication_id = 0; + // map all way restrictions into access containers + const auto prepare_way_restriction = [this, &index, &duplication_id, as_duplicated_node]( + const auto &restriction) { + const auto &way = restriction.AsWayRestriction(); + restriction_starts.insert( + std::make_pair(std::make_pair(way.in_restriction.from, way.in_restriction.via), index)); + ++index; + }; + std::for_each(restriction_data.begin(), restriction_data.end(), prepare_way_restriction); + + std::size_t offset = 1; + // the first group starts at 0 + if (!restriction_data.empty()) + duplicated_node_groups.push_back(0); + + auto const add_offset_on_new_groups = [&](auto const &lhs, auto const &rhs) { + BOOST_ASSERT(rhs == restriction_data[offset]); + // add a new lower bound for rhs + if (as_duplicated_node(lhs) != as_duplicated_node(rhs)) + duplicated_node_groups.push_back(offset); + ++offset; + return false; // continue until the end + }; + std::adjacent_find(restriction_data.begin(), restriction_data.end(), add_offset_on_new_groups); + duplicated_node_groups.push_back(restriction_data.size()); +} + +std::size_t WayRestrictionMap::NumberOfDuplicatedNodes() const +{ + return duplicated_node_groups.size() - 1; +} + +bool WayRestrictionMap::IsViaWay(const NodeID from, const NodeID to) const +{ + // safe-guards + if (restriction_data.empty()) + return false; + + const auto itr = std::lower_bound( + restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay()); + + // no fitting restriction + if (itr == restriction_data.end()) + return false; + + const auto &way = itr->AsWayRestriction(); + + return way.out_restriction.from == from && way.out_restriction.via == to; +} + +std::size_t WayRestrictionMap::AsDuplicatedNodeID(const std::size_t restriction_id) const +{ + return std::distance(duplicated_node_groups.begin(), + std::upper_bound(duplicated_node_groups.begin(), + duplicated_node_groups.end(), + restriction_id)) - + 1; +} + +util::range WayRestrictionMap::DuplicatedNodeIDs(const NodeID from, + const NodeID to) const +{ + const auto duplicated_node_range_itr = std::equal_range( + restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay()); + + const auto as_restriction_id = [this](const auto itr) { + return std::distance(restriction_data.begin(), itr); + }; + + return util::irange( + AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.first)), + AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.second))); +} + +bool WayRestrictionMap::IsRestricted(std::size_t duplicated_node, const NodeID to) const +{ + // loop over all restrictions associated with the node. Mark as restricted based on + // is_only/restricted targets + for (std::size_t restriction_index = duplicated_node_groups[duplicated_node]; + restriction_index != duplicated_node_groups[duplicated_node + 1]; + ++restriction_index) + { + const auto &restriction = restriction_data[restriction_index]; + const auto &way = restriction.AsWayRestriction(); + + if (restriction.is_only) + return way.out_restriction.to != to; + else if (to == way.out_restriction.to) + return true; + } + return false; +} + +TurnRestriction const &WayRestrictionMap::GetRestriction(const std::size_t id) const +{ + return restriction_data[id]; +} + +std::vector WayRestrictionMap::DuplicatedNodeRepresentatives() const +{ + std::vector result; + result.reserve(NumberOfDuplicatedNodes()); + std::transform(duplicated_node_groups.begin(), + duplicated_node_groups.end() - 1, + std::back_inserter(result), + [&](auto const representative_id) -> ViaWay { + auto &way = restriction_data[representative_id].AsWayRestriction(); + return {representative_id, way.in_restriction.via, way.out_restriction.via}; + }); + return result; +} + +NodeID WayRestrictionMap::RemapIfRestricted(const NodeID edge_based_node, + const NodeID node_based_from, + const NodeID node_based_via, + const NodeID node_based_to, + const NodeID number_of_edge_based_nodes) const +{ + auto range = restriction_starts.equal_range(std::make_pair(node_based_from, node_based_via)); + + // returns true if the ID saved in an iterator belongs to a turn restriction that references + // node_based_to as destination of the `in_restriction` + const auto restriction_targets_to = [node_based_to, this](const auto &pair) { + return restriction_data[pair.second].AsWayRestriction().in_restriction.to == node_based_to; + }; + const auto itr = std::find_if(range.first, range.second, restriction_targets_to); + + // in case we found a matching restriction, we can remap the edge_based_node + if (itr != range.second) + return number_of_edge_based_nodes - NumberOfDuplicatedNodes() + + AsDuplicatedNodeID(itr->second); + else + return edge_based_node; +} + +} // namespace extractor +} // namespace osrm diff --git a/src/updater/updater.cpp b/src/updater/updater.cpp index e3d3089b837..0377f94e813 100644 --- a/src/updater/updater.cpp +++ b/src/updater/updater.cpp @@ -511,7 +511,7 @@ updateConditionalTurns(const UpdaterConfig &config, // only add restrictions to the lookups if the restriction is valid now - if (node_or_way.flags.is_only) + if (node_or_way.is_only) { is_only_lookup.lookup.push_back({std::make_tuple(c.from, c.via), c.to}); }