diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c5a3ab4..a6431ec3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # UNRELEASED - Changes from 5.15.1: - Guidance + - ADDED #4676: Support for maneuver override relation, allowing data-driven overrides for turn-by-turn instructions [#4676](https://github.com/Project-OSRM/osrm-backend/pull/4676) - CHANGED #4830: Announce reference change if names are empty - CHANGED #4835: MAXIMAL_ALLOWED_SEPARATION_WIDTH increased to 12 meters - Profile: diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index 63e85a092..80cb5b95e 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -1061,3 +1061,28 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | | a | d | ab,bc,bc,bge,de,de | + + + Scenario: Ambiguous ways + Given the node map + """ + x---a----b-----c---z + | + d + """ + + And the ways + | nodes | + | abc | + | bd | + | xa | + | cz | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | abc | b | no_left_turn | + + When I route I should get + | from | to | route | + | d | x | bd,abc,xa,xa | + | d | z | bd,abc,cz,cz | diff --git a/features/guidance/maneuver-tag.feature b/features/guidance/maneuver-tag.feature new file mode 100644 index 000000000..c2326aa85 --- /dev/null +++ b/features/guidance/maneuver-tag.feature @@ -0,0 +1,208 @@ +# The route results with #original are what the result should be if the maneuver tag is removed +@routing @guidance @maneuver +Feature: Maneuver tag support + + Background: + Given the profile "car" + Given a grid size of 5 meters + + Scenario: simple override #1 + Given the node map + """ + a--b---c----d---e + | + g + | + h------i--------j + """ + And the ways + | nodes | name | oneway | + | abc | A Street | no | + | cde | B Street | no | + | cgi | C Street | no | + | hij | J Street | no | + + And the relations + | type | way:from | node:via | way:to | maneuver | direction | + | maneuver | abc | c | cgi | turn | sharp_right | + | maneuver | hij | i | cde | turn | sharp_left | + | maneuver | abc | c | cde | turn | slight_left | + + When I route I should get + | waypoints | route | turns | + # Testing directly connected from/to + | a,j | A Street,C Street,J Street,J Street | depart,turn sharp right,turn left,arrive | + | b,g | A Street,C Street,C Street | depart,turn sharp right,arrive | + # Testing re-awakening suppressed turns + | a,e | A Street,B Street,B Street | depart,turn slight left,arrive | + + Scenario: single via-way + Given the node map + """" + a--b---c----d---e + | + g + | + h------i--------j + """ + + And the ways + | nodes | name | oneway | + | abc | A Street | no | + | cde | B Street | no | + | cgi | C Street | no | + | hi | J Street | no | + | ij | J Street | no | + + And the relations + | type | way:from | way:via | way:to | node:via | maneuver | direction | + | maneuver | abc | cgi | ij | c | turn | sharp_right | + + When I route I should get + | waypoints | route | turns | + | a,j | A Street,C Street,J Street,J Street | depart,turn sharp right,turn left,arrive | + + + Scenario: multiple via-way + Given the node map + """" + a--b---c----d---e + | + g-----k + | + h------i--------j + """ + + And the ways + | nodes | name | oneway | + | abc | A Street | no | + | cde | B Street | no | + | cg | C Street | no | + | gi | C Street | no | + | hi | J Street | no | + | ij | J Street | no | + | gk | G Street | no | + + And the relations + | type | way:from | way:via | way:via | way:to | node:via | maneuver | direction | + | maneuver | abc | cg | gi | ij | c | turn | sharp_right | + + When I route I should get + | waypoints | route | turns | + | a,j | A Street,C Street,J Street,J Street | depart,turn sharp right,end of road left,arrive | + + + Scenario: Use maneuver tag to announce a particular turn type + Given the node map + """ + f + * + * + * + * + * + * + * + * + * + t. .. * h + .. ....m** * + / * * + / * * * + / + / + | + | + \ + \ + o + """ + + And the ways + | nodes | name | oneway | highway | + | fm | CA-120 | no | secondary | + | mh | CA-120 | no | secondary | + | mt | Priest Rd | no | unclassified | + | mo | | no | service | + + And the relations + | type | way:from | node:via | way:to | maneuver | direction | + | maneuver | mh | m | mt | turn | left | + + When I route I should get + | waypoints | route | turns | + | h,t | CA-120,Priest Rd,Priest Rd | depart,turn left,arrive | + #original | h,t | CA-120,Priest Rd,Priest Rd | depart,turn straight,arrive | + + Scenario: Use maneuver tag to announce lane guidance + Given a grid size of 10 meters + Given the node map + """ + ad + / \ + / \ + / \ + | | + | | + | | + b-----c------e + | | + | | + | | + | | + r w + """ + + And the ways + | nodes | name | oneway | highway | + | ab | Marsh Rd | yes | secondary | + | br | Marsh Rd | yes | secondary | + | cd | Marsh Rd | yes | secondary | + | cw | Marsh Rd | yes | secondary | + | bc | service | no | service | + | ce | service | no | service | + + And the relations + | type | way:from | node:via | way:via | way:to | maneuver | + | maneuver | ab | c | bc | cd | uturn | + | maneuver | ab | b | bc | cd | suppress | + + When I route I should get + | waypoints | route | turns | + | a,d | Marsh Rd,Marsh Rd,Marsh Rd | depart,turn uturn,arrive | + #original | a,d | Marsh Rd,service,Marsh Rd,Marsh Rd | depart,turn left,turn left,arrive | + + Scenario: Use maneuver tag to suppress a turn + Given the node map + """ + c + | + | + v---y----------z + | + n---p----------k + |\ + | \ + b t + """ + + And the ways + | nodes | name | oneway | highway | + | zy | NY Ave | yes | primary | + | yv | NY Ave | yes | primary | + | np | NY Ave | yes | primary | + | pk | NY Ave | yes | primary | + | cp | 4th St | no | tertiary | + | yp | | no | motorway_link | + | pb | 4th St | no | primary | + | pt | 395 | no | primary | + + And the relations + | type | way:from | node:via | way:via | way:to | maneuver | + | maneuver | zy | p | yp | pt | suppress | + + When I route I should get + | waypoints | route | turns | + | z,t | NY Ave,395,395 | depart,on ramp left,arrive | + #original | z,t | NY Ave,,395,395 | depart,on ramp left,fork slight left,arrive | + diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js index c06fb14ce..f0760b643 100644 --- a/features/step_definitions/data.js +++ b/features/step_definitions/data.js @@ -185,31 +185,34 @@ module.exports = function () { let q = d3.queue(); - let addRelation = (row, cb) => { + let addRelation = (headers, row, cb) => { let relation = new OSM.Relation(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); var name = null; - for (let key in row) { + for (let index in row) { + + var key = headers[index]; + var value = row[index]; let isNode = key.match(/^node:?(.*)/), isWay = key.match(/^way:?(.*)/), isRelation = key.match(/^relation:?(.*)/), isColonSeparated = key.match(/^(.*):(.*)/); if (isNode) { - row[key].split(',').map(function(v) { return v.trim(); }).forEach((nodeName) => { + value.split(',').map(function(v) { return v.trim(); }).forEach((nodeName) => { if (nodeName.length !== 1) throw new Error(util.format('*** invalid relation node member "%s"', nodeName)); let node = this.findNodeByName(nodeName); if (!node) throw new Error(util.format('*** unknown relation node member "%s"', nodeName)); relation.addMember('node', node.id, isNode[1]); }); } else if (isWay) { - row[key].split(',').map(function(v) { return v.trim(); }).forEach((wayName) => { + value.split(',').map(function(v) { return v.trim(); }).forEach((wayName) => { let way = this.findWayByName(wayName); if (!way) throw new Error(util.format('*** unknown relation way member "%s"', wayName)); relation.addMember('way', way.id, isWay[1]); }); } else if (isRelation) { - row[key].split(',').map(function(v) { return v.trim(); }).forEach((relName) => { + value.split(',').map(function(v) { return v.trim(); }).forEach((relName) => { let otherrelation = this.findRelationByName(relName); if (!otherrelation) throw new Error(util.format('*** unknown relation relation member "%s"', relName)); relation.addMember('relation', otherrelation.id, isRelation[1]); @@ -217,8 +220,8 @@ module.exports = function () { } else if (isColonSeparated && isColonSeparated[1] !== 'restriction') { throw new Error(util.format('*** unknown relation member type "%s:%s", must be either "node" or "way"', isColonSeparated[1], isColonSeparated[2])); } else { - relation.addTag(key, row[key]); - if (key.match(/name/)) name = row[key]; + relation.addTag(key, value); + if (key.match(/name/)) name = value; } } relation.uid = this.OSM_UID; @@ -233,7 +236,8 @@ module.exports = function () { cb(); }; - table.hashes().forEach((row) => q.defer(addRelation, row)); + var headers = table.raw()[0]; + table.rows().forEach((row) => q.defer(addRelation, headers, row)); q.awaitAll(callback); }); diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 6f807af84..6b5242fc7 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -1,6 +1,7 @@ #ifndef ENGINE_API_ROUTE_HPP #define ENGINE_API_ROUTE_HPP +#include "extractor/maneuver_override.hpp" #include "engine/api/base_api.hpp" #include "engine/api/json_factory.hpp" #include "engine/api/route_parameters.hpp" @@ -19,6 +20,8 @@ #include "engine/internal_route_result.hpp" +#include "guidance/turn_instruction.hpp" + #include "util/coordinate.hpp" #include "util/integer_range.hpp" #include "util/json_util.hpp" @@ -130,6 +133,7 @@ class RouteAPI : public BaseAPI reversed_target, parameters.steps); + util::Log(logDEBUG) << "Assembling steps " << std::endl; if (parameters.steps) { auto steps = guidance::assembleSteps(BaseAPI::facade, @@ -140,6 +144,10 @@ class RouteAPI : public BaseAPI reversed_source, reversed_target); + // Apply maneuver overrides before any other post + // processing is performed + guidance::applyOverrides(BaseAPI::facade, steps, leg_geometry); + /* Perform step-based post-processing. * * Using post-processing on basis of route-steps for a single leg at a time diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 8b434c9c1..969c4d436 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -14,6 +14,7 @@ #include "extractor/datasources.hpp" #include "extractor/edge_based_node.hpp" #include "extractor/intersection_bearings_container.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/node_data_container.hpp" #include "extractor/packed_osm_ids.hpp" #include "extractor/profile_properties.hpp" @@ -205,6 +206,9 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade util::vector_view m_datasource_name_lengths; util::vector_view m_lane_tupel_id_pairs; + util::vector_view m_maneuver_overrides; + util::vector_view m_maneuver_override_node_sequences; + std::unique_ptr m_static_rtree; std::unique_ptr m_geospatial_query; boost::filesystem::path file_index_path; @@ -499,6 +503,21 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade m_entry_class_table = std::move(entry_class_table); } + void InitializeManeuverOverridePointers(storage::DataLayout &data_layout, char *memory_block) + { + auto maneuver_overrides_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::MANEUVER_OVERRIDES); + m_maneuver_overrides = util::vector_view( + maneuver_overrides_ptr, + data_layout.num_entries[storage::DataLayout::MANEUVER_OVERRIDES]); + + auto maneuver_override_node_sequences_ptr = data_layout.GetBlockPtr( + memory_block, storage::DataLayout::MANEUVER_OVERRIDE_NODE_SEQUENCES); + m_maneuver_override_node_sequences = util::vector_view( + maneuver_override_node_sequences_ptr, + data_layout.num_entries[storage::DataLayout::MANEUVER_OVERRIDE_NODE_SEQUENCES]); + } + void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block, const std::size_t exclude_index) @@ -515,6 +534,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade InitializeProfilePropertiesPointer(data_layout, memory_block, exclude_index); InitializeRTreePointers(data_layout, memory_block); InitializeIntersectionClassPointers(data_layout, memory_block); + InitializeManeuverOverridePointers(data_layout, memory_block); } public: @@ -890,6 +910,39 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade { return edge_based_node_data.IsSegregated(id); } + + std::vector + GetOverridesThatStartAt(const NodeID edge_based_node_id) const override final + { + std::vector results; + + // heterogeneous comparison: + struct Comp + { + bool operator()(const extractor::StorageManeuverOverride &s, NodeID i) const + { + return s.start_node < i; + } + bool operator()(NodeID i, const extractor::StorageManeuverOverride &s) const + { + return i < s.start_node; + } + }; + + auto found_range = std::equal_range( + m_maneuver_overrides.begin(), m_maneuver_overrides.end(), edge_based_node_id, Comp{}); + + std::for_each(found_range.first, found_range.second, [&](const auto & override) { + std::vector sequence( + m_maneuver_override_node_sequences.begin() + override.node_sequence_offset_begin, + m_maneuver_override_node_sequences.begin() + override.node_sequence_offset_end); + results.push_back(extractor::ManeuverOverride{std::move(sequence), + override.instruction_node, + override.override_type, + override.direction}); + }); + return results; + } }; template class ContiguousInternalMemoryDataFacade; diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index de1ab10c5..c1bc16564 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -10,6 +10,9 @@ #include "extractor/class_data.hpp" #include "extractor/edge_based_node_segment.hpp" +//#include "extractor/guidance/turn_lane_types.hpp" +#include "extractor/maneuver_override.hpp" +//#include "extractor/original_edge_data.hpp" #include "extractor/query_node.hpp" #include "extractor/travel_mode.hpp" #include "extractor/turn_lane_types.hpp" @@ -192,6 +195,9 @@ class BaseDataFacade virtual bool IsLeftHandDriving(const NodeID id) const = 0; virtual bool IsSegregated(const NodeID) const = 0; + + virtual std::vector + GetOverridesThatStartAt(const NodeID edge_based_node_id) const = 0; }; } } diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index a808c14dd..75344c063 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -127,7 +127,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa // intersections contain the classes of exiting road intersection.classes = facade.GetClasses(path_point.classes); - steps.push_back(RouteStep{step_name_id, + steps.push_back(RouteStep{path_point.from_edge_based_node, + step_name_id, is_segregated, name.to_string(), ref.to_string(), @@ -209,7 +210,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa // intersections contain the classes of exiting road intersection.classes = facade.GetClasses(facade.GetClassData(target_node_id)); BOOST_ASSERT(duration >= 0); - steps.push_back(RouteStep{step_name_id, + steps.push_back(RouteStep{leg_data[leg_data.size() - 1].from_edge_based_node, + step_name_id, is_segregated, facade.GetNameForID(step_name_id).to_string(), facade.GetRefForID(step_name_id).to_string(), @@ -253,7 +255,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa BOOST_ASSERT(target_duration >= source_duration || weight == 0); const EdgeWeight duration = std::max(0, target_duration - source_duration); - steps.push_back(RouteStep{source_name_id, + steps.push_back(RouteStep{source_node_id, + source_name_id, is_segregated, facade.GetNameForID(source_name_id).to_string(), facade.GetRefForID(source_name_id).to_string(), @@ -295,7 +298,8 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa 0}; BOOST_ASSERT(!leg_geometry.locations.empty()); - steps.push_back(RouteStep{target_name_id, + steps.push_back(RouteStep{target_node_id, + target_name_id, facade.IsSegregated(target_node_id), facade.GetNameForID(target_name_id).to_string(), facade.GetRefForID(target_name_id).to_string(), diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index 538f85810..7bf6967ad 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -1,6 +1,7 @@ #ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP #define ENGINE_GUIDANCE_POST_PROCESSING_HPP +#include "engine/datafacade/datafacade_base.hpp" #include "engine/guidance/leg_geometry.hpp" #include "engine/guidance/route_step.hpp" #include "engine/phantom_node.hpp" @@ -45,6 +46,18 @@ std::vector buildIntersections(std::vector steps); OSRM_ATTR_WARN_UNUSED LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector &steps); +/** + * Apply maneuver override relations to the selected route. + * Should be called before any other post-processing is performed + * to ensure that all sequences of edge-based-nodes are still in the + * steps list. + * + * @param steps the steps of the route + */ +void applyOverrides(const datafacade::BaseDataFacade &facade, + std::vector &steps, + const LegGeometry &geometry); + } // namespace guidance } // namespace engine } // namespace osrm diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp index bb06e743d..48a6d9b49 100644 --- a/include/engine/guidance/route_step.hpp +++ b/include/engine/guidance/route_step.hpp @@ -59,6 +59,7 @@ inline IntermediateIntersection getInvalidIntersection() struct RouteStep { + NodeID from_id; unsigned name_id; bool is_segregated; std::string name; diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index 0e0051fe0..cecab2c62 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -24,7 +24,9 @@ namespace engine struct PathData { - // id of via node of the turn + // from edge-based-node id + NodeID from_edge_based_node; + // the internal OSRM id of the OSM node id that is the via node of the turn NodeID turn_via_node; // name of the street that leads to the turn unsigned name_id; diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index b9fa5425f..dbabbf4ae 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -192,7 +192,8 @@ void annotatePath(const FacadeT &facade, BOOST_ASSERT(start_index < end_index); for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx) { - unpacked_path.push_back(PathData{id_vector[segment_idx + 1], + unpacked_path.push_back(PathData{*node_from, + id_vector[segment_idx + 1], name_index, is_segregated, weight_vector[segment_idx], @@ -266,7 +267,8 @@ void annotatePath(const FacadeT &facade, BOOST_ASSERT(segment_idx < id_vector.size() - 1); BOOST_ASSERT(facade.GetTravelMode(target_node_id) > 0); unpacked_path.push_back( - PathData{id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1], + PathData{target_node_id, + id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1], facade.GetNameIndex(target_node_id), facade.IsSegregated(target_node_id), weight_vector[segment_idx], diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 701031726..50e8ab388 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -8,6 +8,7 @@ #include "extractor/edge_based_edge.hpp" #include "extractor/edge_based_node_segment.hpp" #include "extractor/extraction_turn.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/nbg_to_ebg.hpp" #include "extractor/node_data_container.hpp" #include "extractor/query_node.hpp" @@ -83,9 +84,11 @@ class EdgeBasedGraphFactory const std::string &turn_penalties_index_filename, const std::string &cnbg_ebg_mapping_path, const std::string &conditional_penalties_filename, + const std::string &maneuver_overrides_filename, const RestrictionMap &node_restriction_map, const ConditionalRestrictionMap &conditional_restriction_map, - const WayRestrictionMap &way_restriction_map); + const WayRestrictionMap &way_restriction_map, + const std::vector &maneuver_overrides); // The following get access functions destroy the content in the factory void GetEdgeBasedEdges(util::DeallocatingVector &edges); @@ -168,14 +171,17 @@ class EdgeBasedGraphFactory // Edge-expanded edges are generate for all valid turns. The validity can be checked via the // restriction maps - void GenerateEdgeExpandedEdges(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 &conditional_turn_penalties_filename, - const RestrictionMap &node_restriction_map, - const ConditionalRestrictionMap &conditional_restriction_map, - const WayRestrictionMap &way_restriction_map); + void + GenerateEdgeExpandedEdges(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 &conditional_turn_penalties_filename, + const std::string &maneuver_overrides_filename, + const RestrictionMap &node_restriction_map, + const ConditionalRestrictionMap &conditional_restriction_map, + const WayRestrictionMap &way_restriction_map, + const std::vector &maneuver_overrides); NBGToEBG InsertEdgeBasedNode(const NodeID u, const NodeID v); diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp index 55c6bd498..7b5ae88ac 100644 --- a/include/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -23,6 +23,7 @@ namespace extractor class ExtractionContainers { void PrepareNodes(); + void PrepareManeuverOverrides(); void PrepareRestrictions(); void PrepareEdges(ScriptingEnvironment &scripting_environment); @@ -63,6 +64,9 @@ class ExtractionContainers std::vector conditional_turn_restrictions; std::vector unconditional_turn_restrictions; + std::vector external_maneuver_overrides_list; + std::vector internal_maneuver_overrides; + ExtractionContainers(); void PrepareData(ScriptingEnvironment &scripting_environment, diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index bf544e1b4..588488e6b 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "extractor/edge_based_graph_factory.hpp" #include "extractor/extractor_config.hpp" #include "extractor/graph_compressor.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/packed_osm_ids.hpp" #include "guidance/guidance_processing.hpp" @@ -62,7 +63,8 @@ class Extractor std::tuple, - std::vector> + std::vector, + std::vector> ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads); EdgeID BuildEdgeExpandedGraph( @@ -76,6 +78,7 @@ class Extractor const std::vector &conditional_turn_restrictions, const std::unordered_set &segregated_edges, const util::NameTable &name_table, + const std::vector &maneuver_overrides, const LaneDescriptionMap &turn_lane_map, // for calculating turn penalties ScriptingEnvironment &scripting_environment, diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index 6f51ac7d8..865f1e2ad 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -48,6 +48,7 @@ struct ExtractionWay; struct ExtractionRelation; struct ProfileProperties; struct InputConditionalTurnRestriction; +struct InputManeuverOverride; /** * This class is used by the extractor with the results of the @@ -90,6 +91,9 @@ class ExtractorCallbacks // warning: caller needs to take care of synchronization! void ProcessWay(const osmium::Way ¤t_way, const ExtractionWay &result_way); + + // warning: caller needs to take care of synchronization! + void ProcessManeuverOverride(const InputManeuverOverride & override); }; } } diff --git a/include/extractor/extractor_config.hpp b/include/extractor/extractor_config.hpp index 491b2b391..553c9b9ca 100644 --- a/include/extractor/extractor_config.hpp +++ b/include/extractor/extractor_config.hpp @@ -67,7 +67,8 @@ struct ExtractorConfig final : storage::IOConfig ".osrm.properties", ".osrm.icd", ".osrm.cnbg", - ".osrm.cnbg_to_ebg"}), + ".osrm.cnbg_to_ebg", + ".osrm.maneuver_overrides"}), requested_num_threads(0), parse_conditionals(false), use_locations_cache(true) diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp index e2418badf..99d4cf85b 100644 --- a/include/extractor/graph_compressor.hpp +++ b/include/extractor/graph_compressor.hpp @@ -4,6 +4,7 @@ #include "extractor/scripting_environment.hpp" #include "util/typedefs.hpp" +#include "extractor/maneuver_override.hpp" #include "util/node_based_graph.hpp" #include @@ -28,6 +29,7 @@ class GraphCompressor ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides, util::NodeBasedDynamicGraph &graph, const std::vector &node_data_container, CompressedEdgeContainer &geometry_compressor); diff --git a/include/extractor/maneuver_override.hpp b/include/extractor/maneuver_override.hpp new file mode 100644 index 000000000..34ac6230e --- /dev/null +++ b/include/extractor/maneuver_override.hpp @@ -0,0 +1,120 @@ +#ifndef MANUEVER_OVERRIDE_HPP +#define MANUEVER_OVERRIDE_HPP + +#include "guidance/turn_instruction.hpp" +#include "util/typedefs.hpp" + +#include "storage/shared_memory_ownership.hpp" +#include "util/vector_view.hpp" +#include +#include + +namespace osrm +{ +namespace extractor +{ + +// Data that is loaded from the OSM datafile directly +struct InputManeuverOverride +{ + std::vector via_ways; + OSMNodeID via_node; + std::string maneuver; + std::string direction; +}; + +// 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; +}; + +// Object returned by the datafacade +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; +}; + +struct NodeBasedTurn +{ + NodeID from; + NodeID via; + NodeID to; + + bool operator==(const NodeBasedTurn &other) const + { + return other.from == from && other.via == via && other.to == to; + } +}; + +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 + NodeID instruction_node; // node-based node ID + guidance::TurnType::Enum override_type; + guidance::DirectionModifier::Enum direction; + + // 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); + } +}; +} +} + +// custom specialization of std::hash can be injected in namespace std +namespace std +{ +template <> struct hash + +{ + typedef osrm::extractor::NodeBasedTurn argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const &s) const noexcept + { + + std::size_t seed = 0; + boost::hash_combine(seed, s.from); + boost::hash_combine(seed, s.via); + boost::hash_combine(seed, s.to); + + return seed; + } +}; +} + +#endif +/* +from=1 +to=3 +via=b + + 101 a 102 b 103 +---------------+---------------+-------------- (way 1) + 99 \ 98 \ 97 + 51 \ 2 50 \ 3 + \ \ +*/ diff --git a/include/extractor/maneuver_override_relation_parser.hpp b/include/extractor/maneuver_override_relation_parser.hpp new file mode 100644 index 000000000..902410a13 --- /dev/null +++ b/include/extractor/maneuver_override_relation_parser.hpp @@ -0,0 +1,65 @@ +#ifndef MANEUVER_OVERRIDE_RELATION_PARSER_HPP +#define MANEUVER_OVERRIDE_RELATION_PARSER_HPP + +#include "maneuver_override.hpp" + +#include +#include +#include + +namespace osmium +{ +class Relation; +} + +namespace osrm +{ +namespace extractor +{ + +class ScriptingEnvironment; + +/** + * Parses the relations that represents maneuver overrides. + * These are structured similarly to turn restrictions, with some slightly + * different fields. + * + * Simple, via-node overrides (the maneuver at the "via" point is overridden) + * + * + * + * + * + * + * + * + * + * Via-way descriptions are also supported - this is helpful if + * you only want to update an instruction if a certain sequence of + * road transitions are taken. + * + * + * + * + * + * + * + * + * + * + * + * For via-way restrictions, ways must be connected end-to-end, i.e. + * referenced ways must be split if the turn points are partway + * along the original way. + * + */ +class ManeuverOverrideRelationParser +{ + public: + ManeuverOverrideRelationParser(); + boost::optional TryParse(const osmium::Relation &relation) const; +}; +} +} + +#endif /* RESTRICTION_PARSER_HPP */ diff --git a/include/extractor/node_based_graph_factory.hpp b/include/extractor/node_based_graph_factory.hpp index 07facfdbe..77f6cb9af 100644 --- a/include/extractor/node_based_graph_factory.hpp +++ b/include/extractor/node_based_graph_factory.hpp @@ -2,6 +2,7 @@ #define OSRM_EXTRACTOR_NODE_BASED_GRAPH_FACTORY_HPP_ #include "extractor/compressed_edge_container.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/node_based_edge.hpp" #include "extractor/node_data_container.hpp" #include "extractor/packed_osm_ids.hpp" @@ -39,7 +40,8 @@ class NodeBasedGraphFactory NodeBasedGraphFactory(const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions); + std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides); auto const &GetGraph() const { return compressed_output_graph; } auto const &GetBarriers() const { return barriers; } @@ -67,7 +69,8 @@ class NodeBasedGraphFactory // edges into a single representative form void Compress(ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions); + std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides); // Most ways are bidirectional, making the geometry in forward and backward direction the same, // except for reversal. We make use of this fact by keeping only one representation of the diff --git a/include/extractor/restriction_compressor.hpp b/include/extractor/restriction_compressor.hpp index 60fd5ccf4..42675e716 100644 --- a/include/extractor/restriction_compressor.hpp +++ b/include/extractor/restriction_compressor.hpp @@ -1,6 +1,7 @@ #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" @@ -28,7 +29,8 @@ class RestrictionCompressor { public: RestrictionCompressor(std::vector &restrictions, - std::vector &conditional_turn_restrictions); + std::vector &conditional_turn_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); @@ -40,6 +42,9 @@ class RestrictionCompressor // node-restrictions, so we can focus on them alone boost::unordered_multimap starts; boost::unordered_multimap ends; + + boost::unordered_multimap maneuver_starts; + boost::unordered_multimap maneuver_ends; }; } // namespace extractor diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp index 9b28cf9b1..0fcc4a7c9 100644 --- a/include/extractor/scripting_environment.hpp +++ b/include/extractor/scripting_environment.hpp @@ -2,6 +2,7 @@ #define SCRIPTING_ENVIRONMENT_HPP #include "extractor/internal_extractor_edge.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/profile_properties.hpp" #include "extractor/restriction.hpp" @@ -31,6 +32,7 @@ namespace extractor { class RestrictionParser; +class ManeuverOverrideRelationParser; class ExtractionRelationContainer; struct ExtractionNode; struct ExtractionWay; @@ -62,10 +64,12 @@ class ScriptingEnvironment virtual void ProcessElements(const osmium::memory::Buffer &buffer, const RestrictionParser &restriction_parser, + const ManeuverOverrideRelationParser &maneuver_override_parser, const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions) = 0; + std::vector &resulting_restrictions, + std::vector &resulting_maneuver_overrides) = 0; virtual bool HasLocationDependentData() const = 0; }; diff --git a/include/extractor/scripting_environment_lua.hpp b/include/extractor/scripting_environment_lua.hpp index b9b0cf629..7714831aa 100644 --- a/include/extractor/scripting_environment_lua.hpp +++ b/include/extractor/scripting_environment_lua.hpp @@ -88,10 +88,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment void ProcessElements(const osmium::memory::Buffer &buffer, const RestrictionParser &restriction_parser, + const ManeuverOverrideRelationParser &maneuver_override_parser, const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions) override; + std::vector &resulting_restrictions, + std::vector &resulting_maneuver_overrides) override; bool HasLocationDependentData() const override { return !location_dependent_data.empty(); } diff --git a/include/extractor/serialization.hpp b/include/extractor/serialization.hpp index b4ed091a5..fb05225c2 100644 --- a/include/extractor/serialization.hpp +++ b/include/extractor/serialization.hpp @@ -4,6 +4,7 @@ #include "conditional_turn_penalty.hpp" #include "extractor/datasources.hpp" #include "extractor/intersection_bearings_container.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/nbg_to_ebg.hpp" #include "extractor/node_data_container.hpp" #include "extractor/profile_properties.hpp" @@ -298,6 +299,16 @@ inline void read(storage::io::FileReader &reader, for (auto &penalty : conditional_penalties) read(reader, penalty); } + +inline void write(storage::io::FileWriter &writer, + const std::vector &maneuver_overrides, + const std::vector &node_sequences) +{ + writer.WriteElementCount64(maneuver_overrides.size()); + writer.WriteElementCount64(node_sequences.size()); + writer.WriteFrom(maneuver_overrides); + writer.WriteFrom(node_sequences); +} } } } diff --git a/include/guidance/turn_instruction.hpp b/include/guidance/turn_instruction.hpp index c84b814e7..82afe8614 100644 --- a/include/guidance/turn_instruction.hpp +++ b/include/guidance/turn_instruction.hpp @@ -321,7 +321,8 @@ const constexpr char *modifier_names[] = {"uturn", "straight", "slight left", "left", - "sharp left"}; + "sharp left", + "UNDEFINED"}; /** * Human readable values for TurnType enum values @@ -363,13 +364,14 @@ const constexpr TurnTypeName turn_type_names[] = { {"roundabout turn", "roundabout turn"}, {"exit roundabout", "exit roundabout turn"}, {"invalid", "(stay on roundabout)"}, - {"invalid", "(sliproad)"}}; + {"invalid", "(sliproad)"}, + {"MAXVALUE", "MAXVALUE"}}; } // ns detail inline std::string instructionTypeToString(const TurnType::Enum type) { - static_assert(sizeof(detail::turn_type_names) / sizeof(detail::turn_type_names[0]) >= + static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >= TurnType::MaxTurnType, "Some turn types have no string representation."); return detail::turn_type_names[static_cast(type)].external_name; @@ -377,7 +379,7 @@ inline std::string instructionTypeToString(const TurnType::Enum type) inline std::string internalInstructionTypeToString(const TurnType::Enum type) { - static_assert(sizeof(detail::turn_type_names) / sizeof(detail::turn_type_names[0]) >= + static_assert((sizeof(detail::turn_type_names) + 1) / sizeof(detail::turn_type_names[0]) >= TurnType::MaxTurnType, "Some turn types have no string representation."); return detail::turn_type_names[static_cast(type)].internal_name; @@ -385,7 +387,7 @@ inline std::string internalInstructionTypeToString(const TurnType::Enum type) inline std::string instructionModifierToString(const DirectionModifier::Enum modifier) { - static_assert(sizeof(detail::modifier_names) / sizeof(detail::modifier_names[0]) >= + static_assert((sizeof(detail::modifier_names) + 1) / sizeof(detail::modifier_names[0]) >= DirectionModifier::MaxDirectionModifier, "Some direction modifiers have no string representation."); return detail::modifier_names[static_cast(modifier)]; diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp index e079bf840..dc27046ac 100644 --- a/include/storage/shared_datatype.hpp +++ b/include/storage/shared_datatype.hpp @@ -88,7 +88,9 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA", "MLD_CELL_LEVEL_OFFSETS", "MLD_GRAPH_NODE_LIST", "MLD_GRAPH_EDGE_LIST", - "MLD_GRAPH_NODE_TO_OFFSET"}; + "MLD_GRAPH_NODE_TO_OFFSET", + "MANEUVER_OVERRIDES", + "MANEUVER_OVERRIDE_NODE_SEQUENCES"}; struct DataLayout { @@ -165,6 +167,8 @@ struct DataLayout MLD_GRAPH_NODE_LIST, MLD_GRAPH_EDGE_LIST, MLD_GRAPH_NODE_TO_OFFSET, + MANEUVER_OVERRIDES, + MANEUVER_OVERRIDE_NODE_SEQUENCES, NUM_BLOCKS }; diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp index 6ea95b80f..916b47ffd 100644 --- a/include/storage/storage_config.hpp +++ b/include/storage/storage_config.hpp @@ -60,7 +60,8 @@ struct StorageConfig final : IOConfig ".osrm.datasource_names", ".osrm.names", ".osrm.properties", - ".osrm.icd"}, + ".osrm.icd", + ".osrm.maneuver_overrides"}, {".osrm.hsgr", ".osrm.nbg_nodes", ".osrm.ebg_nodes", diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 5f1758ff5..384d9a984 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -566,6 +566,150 @@ std::vector buildIntersections(std::vector steps) return removeNoTurnInstructions(std::move(steps)); } +void applyOverrides(const datafacade::BaseDataFacade &facade, + std::vector &steps, + const LegGeometry &leg_geometry) +{ + // Find overrides that match, and apply them + // The +/-1 here are to remove the depart and arrive steps, which + // we don't allow updates to + for (auto current_step_it = steps.begin(); current_step_it != steps.end(); ++current_step_it) + { + util::Log(logDEBUG) << "Searching for " << current_step_it->from_id << std::endl; + const auto overrides = facade.GetOverridesThatStartAt(current_step_it->from_id); + if (overrides.empty()) + continue; + util::Log(logDEBUG) << "~~~~ GOT A HIT, checking the rest ~~~" << std::endl; + for (const extractor::ManeuverOverride &maneuver_relation : overrides) + { + util::Log(logDEBUG) << "Override sequence is "; + for (auto &n : maneuver_relation.node_sequence) + { + util::Log(logDEBUG) << n << " "; + } + util::Log(logDEBUG) << std::endl; + util::Log(logDEBUG) << "Override type is " + << osrm::guidance::internalInstructionTypeToString( + maneuver_relation.override_type) + << std::endl; + util::Log(logDEBUG) << "Override direction is " + << osrm::guidance::instructionModifierToString( + maneuver_relation.direction) + << std::endl; + + util::Log(logDEBUG) << "Route sequence is "; + for (auto it = current_step_it; it != steps.end(); ++it) + { + util::Log(logDEBUG) << it->from_id << " "; + } + util::Log(logDEBUG) << std::endl; + + auto search_iter = maneuver_relation.node_sequence.begin(); + auto route_iter = current_step_it; + while (search_iter != maneuver_relation.node_sequence.end()) + { + if (route_iter == steps.end()) + break; + + if (*search_iter == route_iter->from_id) + { + ++search_iter; + ++route_iter; + continue; + } + // Skip over duplicated EBNs in the step array + // EBNs are sometime duplicated because guidance code inserts + // "fake" steps that it later removes. This hasn't happened yet + // at this point, but we can safely just skip past the dupes. + if ((route_iter - 1)->from_id == route_iter->from_id) + { + ++route_iter; + continue; + } + // If we get here, the values got out of sync so it's not + // a match. + break; + } + + // We got a match, update using the instruction_node + if (search_iter == maneuver_relation.node_sequence.end()) + { + util::Log(logDEBUG) << "Node sequence matched, looking for the step " + << "that has the via node" << std::endl; + const auto via_node_coords = + facade.GetCoordinateOfNode(maneuver_relation.instruction_node); + // Find the step that has the instruction_node at the intersection point + auto step_to_update = std::find_if( + current_step_it, + route_iter, + [&leg_geometry, &via_node_coords](const auto &step) { + util::Log(logDEBUG) << "Leg geom from " << step.geometry_begin << " to " + << step.geometry_end << std::endl; + + // iterators over geometry of current step + auto begin = leg_geometry.locations.begin() + step.geometry_begin; + auto end = leg_geometry.locations.begin() + step.geometry_end; + auto via_match = std::find_if(begin, end, [&](const auto &location) { + return location == via_node_coords; + }); + if (via_match != end) + { + util::Log(logDEBUG) + << "Found geometry match at " + << (std::distance(begin, end) - std::distance(via_match, end)) + << std::endl; + } + util::Log(logDEBUG) + << ((*(leg_geometry.locations.begin() + step.geometry_begin) == + via_node_coords) + ? "true" + : "false") + << std::endl; + return *(leg_geometry.locations.begin() + step.geometry_begin) == + via_node_coords; + // return via_match != end; + }); + // We found a step that had the intersection_node coordinate + // in its geometry + if (step_to_update != route_iter) + { + // Don't update the last step (it's an arrive instruction) + util::Log(logDEBUG) << "Updating step " + << std::distance(steps.begin(), steps.end()) - + std::distance(step_to_update, steps.end()) + << std::endl; + if (maneuver_relation.override_type != osrm::guidance::TurnType::MaxTurnType) + { + util::Log(logDEBUG) << " instruction was " + << osrm::guidance::internalInstructionTypeToString( + step_to_update->maneuver.instruction.type) + << " now " + << osrm::guidance::internalInstructionTypeToString( + maneuver_relation.override_type) + << std::endl; + step_to_update->maneuver.instruction.type = maneuver_relation.override_type; + } + if (maneuver_relation.direction != + osrm::guidance::DirectionModifier::MaxDirectionModifier) + { + util::Log(logDEBUG) + << " direction was " + << osrm::guidance::instructionModifierToString( + step_to_update->maneuver.instruction.direction_modifier) + << " now " << osrm::guidance::instructionModifierToString( + maneuver_relation.direction) + << std::endl; + step_to_update->maneuver.instruction.direction_modifier = + maneuver_relation.direction; + } + // step_to_update->is_overridden = true; + } + } + } + util::Log(logDEBUG) << "Done tweaking steps" << std::endl; + } +} + } // namespace guidance } // namespace engine } // namespace osrm diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index a6812718a..4b54cfefa 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -219,15 +220,18 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N return NBGToEBG{node_u, node_v, nbe_to_ebn_mapping[edge_id_1], nbe_to_ebn_mapping[edge_id_2]}; } -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 &conditional_penalties_filename, - const RestrictionMap &node_restriction_map, - const ConditionalRestrictionMap &conditional_node_restriction_map, - const WayRestrictionMap &way_restriction_map) +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 &conditional_penalties_filename, + const std::string &maneuver_overrides_filename, + const RestrictionMap &node_restriction_map, + const ConditionalRestrictionMap &conditional_node_restriction_map, + const WayRestrictionMap &way_restriction_map, + const std::vector &unresolved_maneuver_overrides) { TIMER_START(renumber); m_number_of_edge_based_nodes = @@ -252,9 +256,11 @@ void EdgeBasedGraphFactory::Run(ScriptingEnvironment &scripting_environment, turn_duration_penalties_filename, turn_penalties_index_filename, conditional_penalties_filename, + maneuver_overrides_filename, node_restriction_map, conditional_node_restriction_map, - way_restriction_map); + way_restriction_map, + unresolved_maneuver_overrides); TIMER_STOP(generate_edges); @@ -407,9 +413,11 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const std::string &turn_duration_penalties_filename, const std::string &turn_penalties_index_filename, const std::string &conditional_penalties_filename, + const std::string &maneuver_overrides_filename, const RestrictionMap &node_restriction_map, const ConditionalRestrictionMap &conditional_restriction_map, - const WayRestrictionMap &way_restriction_map) + const WayRestrictionMap &way_restriction_map, + const std::vector &unresolved_maneuver_overrides) { util::Log() << "Generating edge-expanded edges "; @@ -434,6 +442,10 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::vector turn_weight_penalties; std::vector turn_duration_penalties; + // Now, renumber all our maneuver overrides to use edge-based-nodes + std::vector storage_maneuver_overrides; + std::vector maneuver_override_sequences; + const auto weight_multiplier = scripting_environment.GetProfileProperties().GetWeightMultiplier(); @@ -482,12 +494,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( std::vector delayed_data; // may need this std::vector conditionals; + std::unordered_map> turn_to_ebn_map; + util::ConnectivityChecksum checksum; }; using EdgesPipelineBufferPtr = std::shared_ptr; m_connectivity_checksum = 0; + std::unordered_map> global_turn_to_ebn_map; + // going over all nodes (which form the center of an intersection), we compute all possible // turns along these intersections. NodeID current_node = 0; @@ -847,6 +863,30 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( m_node_based_graph.GetTarget(outgoing_edge.edge), m_number_of_edge_based_nodes); + /***************************/ + + const auto edgetarget = + 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) + { + for (auto &turn : override.turn_sequence) + { + if (turn.from == incoming_edge.node && + turn.via == intersection_node && turn.to == edgetarget) + { + 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); + } + } + } + { // scope to forget edge_with_data after const auto edge_with_data_and_condition = generate_edge(nbe_to_ebn_mapping[incoming_edge.edge], @@ -997,6 +1037,13 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // Copy via-way restrictions delayed data delayed_data.insert( delayed_data.end(), buffer->delayed_data.begin(), buffer->delayed_data.end()); + + std::for_each(buffer->turn_to_ebn_map.begin(), + buffer->turn_to_ebn_map.end(), + [&global_turn_to_ebn_map](const auto &p) { + // TODO: log conflicts here + global_turn_to_ebn_map.insert(p); + }); }); // Now, execute the pipeline. The value of "5" here was chosen by experimentation @@ -1010,6 +1057,39 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // NOTE: buffer.delayed_data and buffer.delayed_turn_data have the same index std::for_each(delayed_data.begin(), delayed_data.end(), transfer_data); + // 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) + { + 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; + + std::vector node_sequence(unresolved_override.turn_sequence.size() + 1, + SPECIAL_NODEID); + + 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()) + { + node_sequence[i] = v->second.first; + node_sequence[i + 1] = v->second.second; + } + } + 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); + } + // Flush the turn_indexes_write_buffer if it's not empty if (!turn_indexes_write_buffer.empty()) { @@ -1018,6 +1098,20 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( turn_indexes_write_buffer.clear(); } } + { + util::Log() << "Sorting and writing " << storage_maneuver_overrides.size() + << " maneuver overrides..."; + + // Sort by `from_node`, so that later lookups can be done with a binary search. + std::sort(storage_maneuver_overrides.begin(), + storage_maneuver_overrides.end(), + [](const auto &a, const auto &b) { return a.start_node < b.start_node; }); + // write conditional turn penalties into the restrictions file + storage::io::FileWriter writer(maneuver_overrides_filename, + storage::io::FileWriter::GenerateFingerprint); + extractor::serialization::write( + writer, storage_maneuver_overrides, maneuver_override_sequences); + } util::Log() << "done."; util::Log() << "Renumbering turns"; diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 86e440fa0..301199ba0 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -141,6 +141,19 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme WriteEdges(file_out); WriteMetadata(file_out); + /* Sort these so that searching is a bit faster later on */ + { + util::UnbufferedLog log; + log << "Sorting used ways ... "; + TIMER_START(sort_ways); + tbb::parallel_sort(way_start_end_id_list.begin(), + way_start_end_id_list.end(), + FirstAndLastSegmentOfWayCompare()); + TIMER_STOP(sort_ways); + log << "ok, after " << TIMER_SEC(sort_ways) << "s"; + } + + PrepareManeuverOverrides(); PrepareRestrictions(); WriteCharData(name_file_name); } @@ -651,20 +664,262 @@ void ExtractionContainers::WriteNodes(storage::io::FileWriter &file_out) const util::Log() << "Processed " << max_internal_node_id << " nodes"; } -void ExtractionContainers::PrepareRestrictions() +void ExtractionContainers::PrepareManeuverOverrides() { + std::unordered_map referenced_ways; + // prepare for extracting source/destination nodes for all maneuvers { util::UnbufferedLog log; - log << "Sorting used ways ... "; - TIMER_START(sort_ways); - tbb::parallel_sort(way_start_end_id_list.begin(), - way_start_end_id_list.end(), - FirstAndLastSegmentOfWayCompare()); - TIMER_STOP(sort_ways); - log << "ok, after " << TIMER_SEC(sort_ways) << "s"; + log << "Collecting start/end information on " << external_maneuver_overrides_list.size() + << " maneuver overrides..."; + TIMER_START(prepare_maneuver_overrides); + + const auto mark_ids = [&](auto const &external_maneuver_override) { + FirstAndLastSegmentOfWay dummy_segment{ + MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID}; + std::for_each(external_maneuver_override.via_ways.begin(), + external_maneuver_override.via_ways.end(), + [&referenced_ways, dummy_segment](const auto &element) { + referenced_ways[element] = dummy_segment; + }); + }; + + // First, make an empty hashtable keyed by the ways referenced + // by the maneuver overrides + std::for_each(external_maneuver_overrides_list.begin(), + external_maneuver_overrides_list.end(), + mark_ids); + + const auto set_ids = [&](auto const &start_end) { + auto itr = referenced_ways.find(start_end.way_id); + if (itr != referenced_ways.end()) + itr->second = start_end; + }; + + // Then, populate the values in that hashtable for only the ways + // referenced + std::for_each(way_start_end_id_list.cbegin(), way_start_end_id_list.cend(), set_ids); + + TIMER_STOP(prepare_maneuver_overrides); + log << "ok, after " << TIMER_SEC(prepare_maneuver_overrides) << "s"; } + auto const osm_node_to_internal_nbn = [&](auto const osm_node) { + auto internal = mapExternalToInternalNodeID( + used_node_id_list.begin(), used_node_id_list.end(), osm_node); + if (internal == SPECIAL_NODEID) + { + util::Log(logDEBUG) << "Maneuver override references invalid node: " << osm_node; + } + return internal; + }; + + // Given + // a -- b - ????????? - c -- d as via segment + // and either + // d -- e - ????????? - f -- g or + // h -- i - ????????? - j -- a + // return + // (d,e) or (j,a) as entry-segment + + /** + * Here's what these properties represent on the node-based-graph + * way "ABCD" way "AB" + * ----------------------------------------------------------------- + * ⬇ A first_segment_source_id + * ⬇ | + * ⬇︎ B first_segment_target_id A first_segment_source_id + * ⬇︎ | ⬇ | last_segment_source_id + * ⬇︎ | ⬇ | + * ⬇︎ | B first_segment_target_id + * ⬇︎ C last_segment_source_id last_segment_target_id + * ⬇︎ | + * ⬇︎ D last_segment_target_id + * + * Finds the point where two ways connect at the end, and returns the 3 + * node-based nodes that describe the turn (the node just before, the + * node at the turn, and the next node after the turn) + **/ + auto const find_turn_from_way_tofrom_nodes = [&](auto const &from_segment, + auto const &to_segment) { + + if (from_segment.first_segment_source_id == to_segment.first_segment_source_id) + { + return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.first_segment_target_id), + osm_node_to_internal_nbn(from_segment.first_segment_source_id), + osm_node_to_internal_nbn(to_segment.first_segment_target_id)}; + } + else if (from_segment.first_segment_source_id == to_segment.last_segment_target_id) + { + return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.first_segment_target_id), + osm_node_to_internal_nbn(from_segment.first_segment_source_id), + osm_node_to_internal_nbn(to_segment.last_segment_source_id)}; + } + else if (from_segment.last_segment_target_id == to_segment.first_segment_source_id) + { + return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.last_segment_source_id), + osm_node_to_internal_nbn(from_segment.last_segment_target_id), + osm_node_to_internal_nbn(to_segment.first_segment_target_id)}; + } + else if (from_segment.last_segment_target_id == to_segment.last_segment_target_id) + { + return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.last_segment_source_id), + osm_node_to_internal_nbn(from_segment.last_segment_target_id), + osm_node_to_internal_nbn(to_segment.last_segment_source_id)}; + } + util::Log(logDEBUG) << "Maneuver override ways " << from_segment.way_id << " and " + << to_segment.way_id << " are not connected"; + return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + }; + + auto const get_turn_from_way_pair = [&](const OSMWayID &from_id, const OSMWayID &to_id) { + auto const from_segment_itr = referenced_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 = referenced_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}; + } + + auto result = + find_turn_from_way_tofrom_nodes(from_segment_itr->second, to_segment_itr->second); + + return result; + }; + + 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, + guidance::DirectionModifier::MaxDirectionModifier); + + if (turn_string == "uturn") + { + result.first = guidance::TurnType::Turn; + result.second = guidance::DirectionModifier::UTurn; + } + else if (turn_string == "continue") + { + result.first = guidance::TurnType::Continue; + } + else if (turn_string == "turn") + { + result.first = guidance::TurnType::Turn; + } + else if (turn_string == "fork") + { + result.first = guidance::TurnType::Fork; + } + else if (turn_string == "suppress") + { + result.first = guidance::TurnType::Suppressed; + } + + // Directions + if (direction_string == "left") + { + result.second = guidance::DirectionModifier::Left; + } + else if (direction_string == "slight_left") + { + result.second = guidance::DirectionModifier::SlightLeft; + } + else if (direction_string == "sharp_left") + { + result.second = guidance::DirectionModifier::SharpLeft; + } + else if (direction_string == "sharp_right") + { + result.second = guidance::DirectionModifier::SharpRight; + } + else if (direction_string == "slight_right") + { + result.second = guidance::DirectionModifier::SlightRight; + } + else if (direction_string == "right") + { + result.second = guidance::DirectionModifier::Right; + } + + return result; + }; + + // Transform an InternalManeuverOverride (based on WayIDs) into an OSRM override (base on + // NodeIDs). + // 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) + { + // 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); + } + + // 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); + + 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; + }; + + const auto transform_into_internal_types = + [&](const InputManeuverOverride &external_maneuver_override) { + UnresolvedManeuverOverride internal_maneuver_override; + if (transform(external_maneuver_override, internal_maneuver_override)) + internal_maneuver_overrides.push_back(std::move(internal_maneuver_override)); + }; + + // Transforming the overrides into the dedicated internal types + { + util::UnbufferedLog log; + log << "Collecting start/end information on " << external_maneuver_overrides_list.size() + << " maneuver overrides..."; + TIMER_START(transform); + std::for_each(external_maneuver_overrides_list.begin(), + external_maneuver_overrides_list.end(), + transform_into_internal_types); + TIMER_STOP(transform); + log << "ok, after " << TIMER_SEC(transform) << "s"; + } +} + +void ExtractionContainers::PrepareRestrictions() +{ + // contain the start/end nodes of each way that is part of an restriction std::unordered_map referenced_ways; @@ -729,9 +984,16 @@ void ExtractionContainers::PrepareRestrictions() // (d,e) or (j,a) as entry-segment auto const find_node_restriction = [&](auto const &segment, auto const &via_segment, auto const via_node) { - // In case of way-restrictions, via-node will be set to MAX_OSM_NODEID to signal that + // In case of way-restrictions, via-node will be set to MAX_OSM_NODEID to signal + // that // the node is not present. // connected at the front of the segment + // Turn restrictions are described as a restriction between the two segments closest + // to + // the shared via-node on the from and to ways. Graph compression will later + // renumber + // the from and to internal node IDs as nodes are plucked out of the node-based + // graph. if (via_node == MAX_OSM_NODEID || segment.first_segment_source_id == via_node) { if (segment.first_segment_source_id == via_segment.first_segment_source_id) @@ -770,7 +1032,8 @@ void ExtractionContainers::PrepareRestrictions() return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; }; - // translate the turn from one segment onto another into a node restriction (the ways can only + // translate the turn from one segment onto another into a node restriction (the ways can + // only // 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) { @@ -792,7 +1055,8 @@ 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. - // Based on the auto type deduction, this transfor handles both conditional and unconditional + // Based on the auto type deduction, this transfor handles both conditional and + // unconditional // turn restrictions. const auto transform = [&](const auto &external_type, auto &internal_type) { if (external_type.Type() == RestrictionType::WAY_RESTRICTION) @@ -808,7 +1072,8 @@ void ExtractionContainers::PrepareRestrictions() if (!from_restriction.Valid() || !to_restriction.Valid()) return false; - // point located at both via and segment is alway on `second`, to FSSF is the order we + // point located at both via and segment is alway on `second`, to FSSF is the order + // we // need WayRestriction way_restriction{from_restriction, to_restriction}; internal_type.node_or_way = std::move(way_restriction); diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 6f0833f98..cb93dcc77 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -7,6 +7,7 @@ #include "extractor/extraction_way.hpp" #include "extractor/extractor_callbacks.hpp" #include "extractor/files.hpp" +#include "extractor/maneuver_override_relation_parser.hpp" #include "extractor/node_based_graph_factory.hpp" #include "extractor/raster_source.hpp" #include "extractor/restriction_filter.hpp" @@ -202,7 +203,11 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) LaneDescriptionMap turn_lane_map; std::vector turn_restrictions; std::vector conditional_turn_restrictions; - std::tie(turn_lane_map, turn_restrictions, conditional_turn_restrictions) = + std::vector unresolved_maneuver_overrides; + std::tie(turn_lane_map, + turn_restrictions, + conditional_turn_restrictions, + unresolved_maneuver_overrides) = ParseOSMData(scripting_environment, number_of_threads); // Transform the node-based graph that OSM is based on into an edge-based graph @@ -223,7 +228,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"), scripting_environment, turn_restrictions, - conditional_turn_restrictions); + conditional_turn_restrictions, + unresolved_maneuver_overrides); util::Log() << "Find segregated edges in node-based graph ..." << std::flush; TIMER_START(segregated); @@ -290,6 +296,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) conditional_turn_restrictions, segregated_edges, name_table, + unresolved_maneuver_overrides, turn_lane_map, scripting_environment, edge_based_nodes_container, @@ -367,7 +374,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) std::tuple, - std::vector> + std::vector, + std::vector> Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads) { @@ -432,6 +440,8 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, config.parse_conditionals, restrictions); + const ManeuverOverrideRelationParser maneuver_override_parser; + // OSM data reader using SharedBuffer = std::shared_ptr; struct ParsedBuffer @@ -441,6 +451,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, std::vector> resulting_ways; std::vector> resulting_relations; std::vector resulting_restrictions; + std::vector resulting_maneuver_overrides; }; ExtractionRelationContainer relations; @@ -482,10 +493,12 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, parsed_buffer.buffer = buffer; scripting_environment.ProcessElements(*buffer, restriction_parser, + maneuver_override_parser, relations, parsed_buffer.resulting_nodes, parsed_buffer.resulting_ways, - parsed_buffer.resulting_restrictions); + parsed_buffer.resulting_restrictions, + parsed_buffer.resulting_maneuver_overrides); return parsed_buffer; }); @@ -493,6 +506,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, unsigned number_of_nodes = 0; unsigned number_of_ways = 0; unsigned number_of_restrictions = 0; + unsigned number_of_maneuver_overrides = 0; tbb::filter_t buffer_storage( tbb::filter::serial_in_order, [&](const ParsedBuffer &parsed_buffer) { @@ -513,6 +527,13 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, { extractor_callbacks->ProcessRestriction(result); } + + number_of_maneuver_overrides = parsed_buffer.resulting_maneuver_overrides.size(); + for (const auto &result : parsed_buffer.resulting_maneuver_overrides) + { + extractor_callbacks->ProcessManeuverOverride(result); + } + }); tbb::filter_t> buffer_relation_cache( @@ -617,7 +638,8 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, return std::make_tuple(std::move(turn_lane_map), std::move(extraction_containers.unconditional_turn_restrictions), - std::move(extraction_containers.conditional_turn_restrictions)); + std::move(extraction_containers.conditional_turn_restrictions), + std::move(extraction_containers.internal_maneuver_overrides)); } void Extractor::FindComponents(unsigned number_of_edge_based_nodes, @@ -693,6 +715,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( const std::vector &conditional_turn_restrictions, const std::unordered_set &segregated_edges, const util::NameTable &name_table, + const std::vector &maneuver_overrides, const LaneDescriptionMap &turn_lane_map, // for calculating turn penalties ScriptingEnvironment &scripting_environment, @@ -736,9 +759,11 @@ EdgeID Extractor::BuildEdgeExpandedGraph( config.GetPath(".osrm.turn_penalties_index").string(), config.GetPath(".osrm.cnbg_to_ebg").string(), config.GetPath(".osrm.restrictions").string(), + config.GetPath(".osrm.maneuver_overrides").string(), via_node_restriction_map, conditional_node_restriction_map, - via_way_restriction_map); + via_way_restriction_map, + maneuver_overrides); return edge_based_graph_factory.GetNumberOfEdgeBasedNodes(); }; diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 199c98b35..ba6b8b5ab 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -75,6 +75,11 @@ void ExtractorCallbacks::ProcessRestriction(const InputConditionalTurnRestrictio // util::Log() << restriction.toString(); } +void ExtractorCallbacks::ProcessManeuverOverride(const InputManeuverOverride & override) +{ + external_memory.external_maneuver_overrides_list.push_back(override); +} + /** * Takes the geometry contained in the ```input_way``` and the tags computed * by the lua profile inside ```parsed_way``` and computes all edge segments. diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index c9d9feef2..fefda38e1 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -26,6 +26,7 @@ void GraphCompressor::Compress( ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides, util::NodeBasedDynamicGraph &graph, const std::vector &node_data_container, CompressedEdgeContainer &geometry_compressor) @@ -33,7 +34,8 @@ void GraphCompressor::Compress( const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); - RestrictionCompressor restriction_compressor(turn_restrictions, conditional_turn_restrictions); + RestrictionCompressor restriction_compressor( + turn_restrictions, conditional_turn_restrictions, maneuver_overrides); // we do not compress turn restrictions on degree two nodes. These nodes are usually used to // indicated `directed` barriers diff --git a/src/extractor/maneuver_override_relation_parser.cpp b/src/extractor/maneuver_override_relation_parser.cpp new file mode 100644 index 000000000..3dbc1896f --- /dev/null +++ b/src/extractor/maneuver_override_relation_parser.cpp @@ -0,0 +1,126 @@ +#include "extractor/maneuver_override_relation_parser.hpp" +#include "extractor/maneuver_override.hpp" + +#include "util/log.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +ManeuverOverrideRelationParser::ManeuverOverrideRelationParser() {} + +/** + * Parses the `type=maneuver` relation. Reads the fields, and puts data + * into an InputManeuverOverride object, if the relation is considered + * valid (i.e. has the minimum tags we expect). + */ +boost::optional +ManeuverOverrideRelationParser::TryParse(const osmium::Relation &relation) const +{ + osmium::tags::KeyFilter filter(false); + filter.add(true, "maneuver"); + + const osmium::TagList &tag_list = relation.tags(); + + osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); + osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); + + // if it's not a maneuver, continue; + if (std::distance(fi_begin, fi_end) == 0) + { + return boost::none; + } + + // we pretend every restriction is a conditional restriction. If we do not find any restriction, + // we can trim away the vector after parsing + InputManeuverOverride maneuver_override; + + maneuver_override.maneuver = relation.tags().get_value_by_key("maneuver", ""); + maneuver_override.direction = relation.tags().get_value_by_key("direction", ""); + + boost::optional from = boost::none, via = boost::none, to = boost::none; + std::vector via_ways; + + for (const auto &member : relation.members()) + { + const char *role = member.role(); + if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0) + { + continue; + } + + switch (member.type()) + { + case osmium::item_type::node: + { + + // Make sure nodes appear only in the role if a via node + if (0 == strcmp("from", role) || 0 == strcmp("to", role)) + { + continue; + } + BOOST_ASSERT(0 == strcmp("via", role)); + via = static_cast(member.ref()); + // set via node id + break; + } + case osmium::item_type::way: + BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) || + 0 == strcmp("via", role)); + if (0 == strcmp("from", role)) + { + from = static_cast(member.ref()); + } + else if (0 == strcmp("to", role)) + { + to = static_cast(member.ref()); + } + else if (0 == strcmp("via", role)) + { + via_ways.push_back(static_cast(member.ref())); + } + break; + case osmium::item_type::relation: + // not yet supported, but who knows what the future holds... + break; + default: + // shouldn't ever happen + break; + } + } + + if (from && (via || via_ways.size() > 0) && to) + { + via_ways.insert(via_ways.begin(), *from); + via_ways.push_back(*to); + if (via) + { + maneuver_override.via_node = {*via}; + } + for (const auto &n : via_ways) + { + maneuver_override.via_ways.push_back(OSMWayID{n}); + } + } + else + { + return boost::none; + } + return maneuver_override; +} +} +} diff --git a/src/extractor/node_based_graph_factory.cpp b/src/extractor/node_based_graph_factory.cpp index 8fc7b9d24..aef2d4491 100644 --- a/src/extractor/node_based_graph_factory.cpp +++ b/src/extractor/node_based_graph_factory.cpp @@ -19,10 +19,14 @@ NodeBasedGraphFactory::NodeBasedGraphFactory( const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions) + std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides) { LoadDataFromFile(input_file); - Compress(scripting_environment, turn_restrictions, conditional_turn_restrictions); + Compress(scripting_environment, + turn_restrictions, + conditional_turn_restrictions, + maneuver_overrides); CompressGeometry(); CompressAnnotationData(); } @@ -84,7 +88,8 @@ void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &inpu void NodeBasedGraphFactory::Compress( ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions) + std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides) { GraphCompressor graph_compressor; graph_compressor.Compress(barriers, @@ -92,6 +97,7 @@ void NodeBasedGraphFactory::Compress( scripting_environment, turn_restrictions, conditional_turn_restrictions, + maneuver_overrides, compressed_output_graph, annotation_data, compressed_edge_container); diff --git a/src/extractor/restriction_compressor.cpp b/src/extractor/restriction_compressor.cpp index b20ef9c55..506fd1ed4 100644 --- a/src/extractor/restriction_compressor.cpp +++ b/src/extractor/restriction_compressor.cpp @@ -12,7 +12,8 @@ namespace extractor RestrictionCompressor::RestrictionCompressor( std::vector &restrictions, - std::vector &conditional_turn_restrictions) + std::vector &conditional_turn_restrictions, + std::vector &maneuver_overrides) { // add a node restriction ptr to the starts/ends maps, needs to be a reference! auto index = [&](auto &element) { @@ -40,10 +41,23 @@ RestrictionCompressor::RestrictionCompressor( std::for_each(conditional_turn_restrictions.begin(), conditional_turn_restrictions.end(), index_starts_and_ends); + + auto index_maneuver = [&](auto &maneuver) { + for (auto &turn : maneuver.turn_sequence) + { + maneuver_starts.insert(std::make_pair(turn.from, &turn)); + maneuver_ends.insert(std::make_pair(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 startptrs and move them from via to from. auto all_starts_range = starts.equal_range(via); std::vector start_ptrs; @@ -104,6 +118,75 @@ void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const const auto reinsert_end = [&](auto ptr) { ends.insert(std::make_pair(ptr->to, ptr)); }; std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end); + + /**********************************************************************************************/ + + // 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(std::make_pair(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(std::make_pair(ptr->to, ptr)); + }; + std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), reinsert_end_mnvs); } } // namespace extractor diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index 9b802b0a0..885b47e61 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -7,6 +7,7 @@ #include "extractor/extraction_turn.hpp" #include "extractor/extraction_way.hpp" #include "extractor/internal_extractor_edge.hpp" +#include "extractor/maneuver_override_relation_parser.hpp" #include "extractor/profile_properties.hpp" #include "extractor/query_node.hpp" #include "extractor/raster_source.hpp" @@ -842,10 +843,12 @@ LuaScriptingContext &Sol2ScriptingEnvironment::GetSol2Context() void Sol2ScriptingEnvironment::ProcessElements( const osmium::memory::Buffer &buffer, const RestrictionParser &restriction_parser, + const ManeuverOverrideRelationParser &maneuver_override_parser, const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions) + std::vector &resulting_restrictions, + std::vector &resulting_maneuver_overrides) { ExtractionNode result_node; ExtractionWay result_way; @@ -885,6 +888,10 @@ void Sol2ScriptingEnvironment::ProcessElements( { resulting_restrictions.push_back(*result_res); } + else if (auto result_res = maneuver_override_parser.TryParse(relation)) + { + resulting_maneuver_overrides.push_back(*result_res); + } } break; default: diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 8a06d43da..3dde221a7 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -17,6 +17,7 @@ #include "extractor/edge_based_edge.hpp" #include "extractor/edge_based_node.hpp" #include "extractor/files.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/packed_osm_ids.hpp" #include "extractor/profile_properties.hpp" #include "extractor/query_node.hpp" @@ -440,6 +441,17 @@ void Storage::PopulateLayout(DataLayout &layout) lane_tuple_count); } + // load maneuver overrides + { + io::FileReader maneuver_overrides_file(config.GetPath(".osrm.maneuver_overrides"), + io::FileReader::VerifyFingerprint); + const auto number_of_overrides = maneuver_overrides_file.ReadElementCount64(); + layout.SetBlockSize(DataLayout::MANEUVER_OVERRIDES, + number_of_overrides); + const auto number_of_nodes = maneuver_overrides_file.ReadElementCount64(); + layout.SetBlockSize(DataLayout::MANEUVER_OVERRIDE_NODE_SEQUENCES, number_of_nodes); + } + { // Loading MLD Data if (boost::filesystem::exists(config.GetPath(".osrm.partition"))) @@ -1074,6 +1086,22 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr) " in " + config.GetPath(".osrm.edges").string()); } } + + // load maneuver overrides + { + io::FileReader maneuver_overrides_file(config.GetPath(".osrm.maneuver_overrides"), + io::FileReader::VerifyFingerprint); + const auto number_of_overrides = maneuver_overrides_file.ReadElementCount64(); + const auto number_of_nodes = maneuver_overrides_file.ReadElementCount64(); + const auto maneuver_overrides_ptr = + layout.GetBlockPtr( + memory_ptr, DataLayout::MANEUVER_OVERRIDES); + maneuver_overrides_file.ReadInto(maneuver_overrides_ptr, number_of_overrides); + + const auto maneuver_override_node_sequences_ptr = layout.GetBlockPtr( + memory_ptr, DataLayout::MANEUVER_OVERRIDE_NODE_SEQUENCES); + maneuver_overrides_file.ReadInto(maneuver_override_node_sequences_ptr, number_of_nodes); + } } } } diff --git a/unit_tests/engine/collapse_internal_route_result.cpp b/unit_tests/engine/collapse_internal_route_result.cpp index ded391b61..6e73cafbf 100644 --- a/unit_tests/engine/collapse_internal_route_result.cpp +++ b/unit_tests/engine/collapse_internal_route_result.cpp @@ -20,8 +20,8 @@ BOOST_AUTO_TEST_CASE(unchanged_collapse_route_result) PhantomNode target; source.forward_segment_id = {1, true}; target.forward_segment_id = {6, true}; - PathData pathy{2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; - PathData kathy{1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData pathy{0, 2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; + PathData kathy{0, 1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; InternalRouteResult one_leg_result; one_leg_result.unpacked_path_segments = {{pathy, kathy}}; one_leg_result.segment_end_coordinates = {PhantomNodes{source, target}}; @@ -38,9 +38,9 @@ BOOST_AUTO_TEST_CASE(unchanged_collapse_route_result) BOOST_AUTO_TEST_CASE(two_legs_to_one_leg) { - PathData pathy{2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; - PathData kathy{1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; - PathData cathy{3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData pathy{0, 2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; + PathData kathy{0, 1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData cathy{0, 3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; PhantomNode node_1; PhantomNode node_2; PhantomNode node_3; @@ -69,11 +69,11 @@ BOOST_AUTO_TEST_CASE(two_legs_to_one_leg) BOOST_AUTO_TEST_CASE(three_legs_to_two_legs) { - PathData pathy{2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; - PathData kathy{1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; - PathData qathy{5, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; - PathData cathy{3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; - PathData mathy{4, 18, false, 8, 9, 13, 4, 2, {}, 4, 2, {}, 2, {3.0}, {1.0}, false}; + PathData pathy{0, 2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; + PathData kathy{0, 1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData qathy{0, 5, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData cathy{0, 3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData mathy{0, 4, 18, false, 8, 9, 13, 4, 2, {}, 4, 2, {}, 2, {3.0}, {1.0}, false}; PhantomNode node_1; PhantomNode node_2; PhantomNode node_3; @@ -112,9 +112,9 @@ BOOST_AUTO_TEST_CASE(three_legs_to_two_legs) BOOST_AUTO_TEST_CASE(two_legs_to_two_legs) { - PathData pathy{2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; - PathData kathy{1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; - PathData cathy{3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData pathy{0, 2, 17, false, 2, 3, 4, 5, 0, {}, 4, 2, {}, 2, {1.0}, {1.0}, false}; + PathData kathy{0, 1, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; + PathData cathy{0, 3, 16, false, 1, 2, 3, 4, 1, {}, 3, 1, {}, 1, {2.0}, {3.0}, false}; PhantomNode node_1; PhantomNode node_2; PhantomNode node_3; diff --git a/unit_tests/engine/guidance_assembly.cpp b/unit_tests/engine/guidance_assembly.cpp index 5916db097..3f949b5cc 100644 --- a/unit_tests/engine/guidance_assembly.cpp +++ b/unit_tests/engine/guidance_assembly.cpp @@ -38,7 +38,8 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) {}}; // Check that duplicated coordinate in the end is removed - std::vector steps = {{324, + std::vector steps = {{0, + 324, false, "Central Park West", "", @@ -61,7 +62,8 @@ BOOST_AUTO_TEST_CASE(trim_short_segments) 3, {intersection1}, false}, - {324, + {0, + 324, false, "Central Park West", "", diff --git a/unit_tests/engine/offline_facade.cpp b/unit_tests/engine/offline_facade.cpp index 75c9cb487..a9f6c617d 100644 --- a/unit_tests/engine/offline_facade.cpp +++ b/unit_tests/engine/offline_facade.cpp @@ -366,6 +366,12 @@ class ContiguousInternalMemoryDataFacade util::guidance::EntryClass GetEntryClass(const EdgeID /*turn_id*/) const override { return {}; } bool IsLeftHandDriving(const NodeID /*id*/) const override { return false; } bool IsSegregated(const NodeID /*id*/) const override { return false; } + + std::vector + GetOverridesThatStartAt(const NodeID /* edge_based_node_id */) const override + { + return {}; + } }; } // datafacade diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index d4ff04f41..b402d795c 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -1,5 +1,6 @@ #include "extractor/graph_compressor.hpp" #include "extractor/compressed_edge_container.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/restriction.hpp" #include "util/node_based_graph.hpp" #include "util/typedefs.hpp" @@ -71,6 +72,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) std::vector annotations(1); CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; std::vector edges = {MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), @@ -91,6 +93,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -117,6 +120,7 @@ BOOST_AUTO_TEST_CASE(loop_test) CompressedEdgeContainer container; std::vector annotations(1); test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; std::vector edges = {MakeUnitEdge(0, 1), MakeUnitEdge(0, 5), @@ -151,6 +155,7 @@ BOOST_AUTO_TEST_CASE(loop_test) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -180,6 +185,7 @@ BOOST_AUTO_TEST_CASE(t_intersection) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; std::vector edges = {MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), @@ -200,6 +206,7 @@ BOOST_AUTO_TEST_CASE(t_intersection) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -223,6 +230,7 @@ BOOST_AUTO_TEST_CASE(street_name_changes) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; std::vector edges = { MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)}; @@ -239,6 +247,7 @@ BOOST_AUTO_TEST_CASE(street_name_changes) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -261,6 +270,7 @@ BOOST_AUTO_TEST_CASE(direction_changes) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; std::vector edges = { MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)}; @@ -273,6 +283,7 @@ BOOST_AUTO_TEST_CASE(direction_changes) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp index b1b1d50e2..e655c93ab 100644 --- a/unit_tests/extractor/intersection_analysis_tests.cpp +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -28,6 +28,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; TurnLanesIndexedArray turn_lanes_data{{0, 0, 3}, {TurnLaneType::uturn | TurnLaneType::left, @@ -90,6 +91,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -157,6 +159,7 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; TurnLanesIndexedArray turn_lanes_data; @@ -211,6 +214,7 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); @@ -261,6 +265,7 @@ BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; + std::vector maneuver_overrides; TurnLanesIndexedArray turn_lanes_data; @@ -301,6 +306,7 @@ BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) scripting_environment, restrictions, conditional_restrictions, + maneuver_overrides, graph, annotations, container); diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index 47656c430..dfbc34415 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -5,10 +5,12 @@ #include "contractor/query_edge.hpp" #include "extractor/class_data.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/travel_mode.hpp" #include "extractor/turn_lane_types.hpp" #include "guidance/turn_bearing.hpp" #include "guidance/turn_instruction.hpp" +#include "guidance/turn_instruction.hpp" #include "engine/algorithm.hpp" #include "engine/datafacade/algorithm_datafacade.hpp" @@ -264,6 +266,12 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade result.activate(3); return result; } + + std::vector + GetOverridesThatStartAt(const NodeID /* edge_based_node_id */) const override + { + return {}; + } }; template class MockAlgorithmDataFacade; diff --git a/unit_tests/mocks/mock_scripting_environment.hpp b/unit_tests/mocks/mock_scripting_environment.hpp index 8ca807717..8531176ad 100644 --- a/unit_tests/mocks/mock_scripting_environment.hpp +++ b/unit_tests/mocks/mock_scripting_environment.hpp @@ -3,6 +3,7 @@ #include "extractor/extraction_segment.hpp" #include "extractor/extraction_turn.hpp" +#include "extractor/maneuver_override.hpp" #include "extractor/profile_properties.hpp" #include "extractor/scripting_environment.hpp" @@ -37,10 +38,12 @@ class MockScriptingEnvironment : public extractor::ScriptingEnvironment void ProcessElements(const osmium::memory::Buffer &, const extractor::RestrictionParser &, + const extractor::ManeuverOverrideRelationParser &, const extractor::ExtractionRelationContainer &, std::vector> &, std::vector> &, - std::vector &) override final + std::vector &, + std::vector &) override final { }