From 3b81b39998897b0b2e3ade58a0f0daeef1204778 Mon Sep 17 00:00:00 2001 From: Moritz Kobitzsch Date: Thu, 30 Jun 2016 09:31:08 +0200 Subject: [PATCH] turn lane handler moved to scenario based handling --- features/guidance/advanced-lanes.feature | 186 ++++++ features/guidance/turn-lanes.feature | 70 ++- .../extractor/edge_based_graph_factory.hpp | 10 +- include/extractor/extraction_containers.hpp | 7 +- include/extractor/extractor.hpp | 11 + include/extractor/extractor_callbacks.hpp | 8 +- include/extractor/guidance/turn_lane_data.hpp | 5 +- .../extractor/guidance/turn_lane_handler.hpp | 89 ++- .../extractor/guidance/turn_lane_matcher.hpp | 12 +- .../extractor/guidance/turn_lane_types.hpp | 6 + include/util/debug.hpp | 9 +- src/engine/guidance/post_processing.cpp | 17 +- src/extractor/edge_based_graph_factory.cpp | 26 +- src/extractor/extraction_containers.cpp | 36 +- src/extractor/extractor.cpp | 75 ++- src/extractor/extractor_callbacks.cpp | 19 +- src/extractor/guidance/turn_discovery.cpp | 25 +- .../guidance/turn_lane_augmentation.cpp | 9 + src/extractor/guidance/turn_lane_data.cpp | 25 +- src/extractor/guidance/turn_lane_handler.cpp | 573 +++++++++++++----- src/extractor/guidance/turn_lane_matcher.cpp | 29 +- 21 files changed, 955 insertions(+), 292 deletions(-) create mode 100644 features/guidance/advanced-lanes.feature diff --git a/features/guidance/advanced-lanes.feature b/features/guidance/advanced-lanes.feature new file mode 100644 index 000000000..6dd6c6c29 --- /dev/null +++ b/features/guidance/advanced-lanes.feature @@ -0,0 +1,186 @@ +@routing @guidance @turn-lanes +Feature: Turn Lane Guidance + + Background: + Given the profile "car" + Given a grid size of 3 meters + + #requires https://github.com/cucumber/cucumber-js/issues/417 + #Due to this, we use & as a pipe character. Switch them out for \| when 417 is fixed + @bug @WORKAROUND-FIXME + Scenario: Separate Turn Lanes + Given the node map + | | | | | | | | e | | + | a | | | b | | | | c | g | + | | | | | | | | d | | + | | | | | | | | f | | + + And the ways + | nodes | turn:lanes:forward | name | oneway | + | ab | | in | yes | + | bc | left\|through | in | yes | + | bd | right | in | yes | + | ec | | cross | no | + | cd | | cross | no | + | df | | cross | no | + | cg | | straight | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | cd | d | no_left_turn | + | restriction | bc | cd | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | lanes | + | a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, | + | a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, | + | a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, | + + @TODO @2650 @bug + Scenario: Sliproad with through lane + Given the node map + | | | | | | | | | | f | | | | + | | | | | | | | | | | | | | + | | | | | | | | g | | | | | | + | | | | | | | | | | | | | | + | | | | | | | | | | | | | e | + | | | | | | | | | | | | | | + | | | | | | | | | | d | | | | + | a | | | | | | | | | | | | | + | | | | | | b | | | | | | | | + | | | | | | | | | | c | | | | + | | | | | | | | | | | | | | + | | | | | | | | | | | | | | + | | | | | h | | | | | i | | | | + + And the ways + | nodes | name | oneway | turn:lanes:forward | + | ab | ghough | yes | | + | bc | ghough | yes | through\|none | + | bd | ghough | yes | none\|through | + | de | ghough | yes | | + | fgb | haight | yes | | + | bh | haight | yes | left\|none | + | fd | market | yes | | + | dc | market | yes | | + | ci | market | yes | | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | fgb | bd | b | no_left_turn | + | restriction | fgb | bc | b | no_left_turn | + + When I route I should get + | waypoints | route | turns | lanes | + | a,h | ghough,haight,haight | depart,turn right,arrive | | + | a,i | ghough,market,market | depart,turn right,arrive | | + | a,e | ghough,ghough,ghough | depart,continue slight left,arrive | | + + @TODO @2650 @bug + Scenario: Sliproad with through lane + Given the node map + | | | | | | | | f | | | + | | | | | | | | | | | + | | | | | | | g | | | | + | | | | | | | | | | | + | | | | | | | | | | e | + | | | | | | | | | | | + | | | | | | | | d | | | + | a | | | | | | | | | | + | | | | | b | | | | | | + | | | | | | | | c | | | + | | | | | | | | | | | + | | | | | | | | | | | + | | | | h | | | | i | | | + + And the ways + | nodes | name | oneway | turn:lanes:forward | + | ab | ghough | yes | | + | bc | ghough | yes | through\|none | + | bd | ghough | yes | none\|through | + | fgb | haight | yes | | + | bh | haight | yes | left\|none | + | fd | market | yes | | + | dc | market | yes | | + | ci | market | yes | | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | dc | d | no_right_turn | + | restriction | fgb | bd | b | no_left_turn | + | restriction | fgb | bc | b | no_left_turn | + + When I route I should get + | waypoints | route | turns | lanes | + | a,h | ghough,haight,haight | depart,turn right,arrive | | + | a,i | ghough,market,market | depart,turn right,arrive | | + | a,e | ghough,ghough,ghough | depart,continue slight left,arrive | | + + Scenario: Separate Turn Lanes + Given the node map + | | | | | | | | e | | + | a | | | b | | | | c | g | + | | | | | | | | d | | + | | | | | | | | f | | + + And the ways + | nodes | turn:lanes:forward | name | oneway | + | ab | | in | yes | + | bc | left\|through | in | yes | + | bd | right | in | yes | + | ec | | cross | no | + | cd | | cross | no | + | df | | cross | no | + | cg | | straight | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | cd | d | no_left_turn | + | restriction | bc | cd | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | lanes | + | a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, | + | a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, | + | a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, | + + @guidance @lanes @sliproads + Scenario: Separate Turn Lanes Next to other turns + Given the node map + | | | | | | | | e | | + | a | | | b | | | | c | g | + | | | | | | | | d | | + | | | | | | | | f | | + | | | | | | | | | | + | | | | | | | | | | + | | | | | | | | | | + | | | | | | | | | | + | | | | | | | | | | + | | | | | | | | | | + | i | | | h | | | | j | | + + And the ways + | nodes | turn:lanes:forward | name | oneway | + | ab | | in | yes | + | bc | left\|through | in | yes | + | bd | right | in | yes | + | ec | | cross | no | + | cd | | cross | no | + | df | | cross | no | + | cg | | straight | no | + | bh | left\|right | turn | yes | + | ihj | | other | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | cd | d | no_left_turn | + | restriction | bc | cd | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | lanes | + | a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, | + | a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, | + | a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, | + | a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, | + | a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, | + diff --git a/features/guidance/turn-lanes.feature b/features/guidance/turn-lanes.feature index 50e07e402..843ca440a 100644 --- a/features/guidance/turn-lanes.feature +++ b/features/guidance/turn-lanes.feature @@ -277,6 +277,7 @@ Feature: Turn Lane Guidance | a,j | road,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, | #this can happen due to traffic lights / lanes not drawn up to the intersection itself + @2654 Scenario: Turn Lanes Given earlier than actual turn Given the node map | a | | b | c | | d | @@ -295,6 +296,7 @@ Feature: Turn Lane Guidance | a,e | road,turn,turn | depart,turn right,arrive | ,none:false right:true, | | a,d | road,road | depart,arrive | , | + @2654 Scenario: Turn Lanes Given earlier than actual turn Given the node map | a | | b | c | d | | e | | f | g | h | | i | @@ -462,17 +464,19 @@ Feature: Turn Lane Guidance | | | | f | | And the ways - | nodes | name | turn:lanes:forward | oneway | highway | - | ab | road | left\|left\|through\|through | yes | primary | - | bd | road | through\|through | yes | primary | - | bc | road | left\|left | yes | primary | - | de | road | | yes | primary | - | fdcg | cross | | | secondary | + | nodes | name | turn:lanes:forward | oneway | highway | + | ab | road | left\|left\|through\|through\|right | yes | primary | + | bd | road | through\|through | yes | primary | + | bc | road | left\|left | yes | primary | + | de | road | | yes | primary | + | fd | cross | | | secondary | + | dc | cross | | | secondary | + | cg | cross | | | secondary | And the relations | type | way:from | way:to | node:via | restriction | - | restriction | bd | fdcg | d | no_left_turn | - | restriction | bc | fdcg | c | no_right_turn | + | restriction | bd | dc | d | no_left_turn | + | restriction | bc | dc | c | no_right_turn | When I route I should get | waypoints | route | turns | lanes | @@ -729,6 +733,7 @@ Feature: Turn Lane Guidance | waypoints | turns | route | lanes | | d,c | depart,merge slight left,arrive | ramp,Hwy,Hwy | ,slight right:true slight right:true, | + @2654 Scenario: Fork on motorway links - don't fork on through but use lane Given the node map | i | | | | | a | @@ -798,6 +803,27 @@ Feature: Turn Lane Guidance | waypoints | route | turns | lanes | | x,d | road,road | depart,arrive | , | + @partition + Scenario: Partitioned turn, Slight Curve + Given the node map + | | | f | | e | + | | | | | | + | | | | | | + | | | | | c | + | a | | b | | | + | | | g | | d | + + And the ways + | nodes | name | highway | oneway | turn:lanes:forward | + | ab | road | primary | yes | left\|right | + | bc | cross | primary | yes | | + | fbg | cross | primary | yes | | + | dce | cross | primary | yes | | + + When I route I should get + | waypoints | route | turns | lanes | + | a,g | road,cross,cross | depart,turn right,arrive | ,left:false right:true, | + | a,e | road,cross,cross | depart,turn left,arrive | ,left:true right:false, | Scenario: Lane Parsing Issue #2694 Given the node map @@ -930,3 +956,31 @@ Feature: Turn Lane Guidance | a,c | in,left,left | depart,turn left,arrive | ,left:true straight:false right;uturn:false, | | a,d | in,through,through | depart,new name straight,arrive | ,left:false straight:true right;uturn:false, | | a,e | in,right,right | depart,turn right,arrive | ,left:false straight:false right;uturn:true, | + + @todo @2654 + #https://github.com/Project-OSRM/osrm-backend/issues/2645 + #http://www.openstreetmap.org/export#map=19/52.56054/13.32152 + Scenario: Kurt-Schuhmacher-Damm + Given the node map + | | | | g | | f | + | | | | | | | + | j | | | h | | e | + | | | | | | | + | a | | | b | | c | + | | | | i | | d | + + And the ways + | nodes | name | highway | oneway | turn:lanes | + | ab | | motorway_link | yes | left\|none\|right | + | bc | | primary_link | yes | | + | cd | ksd | secondary | yes | | + | cef | ksd | primary | yes | | + | hj | | motorway_link | yes | | + | eh | | secondary_link | yes | | + | gh | ksd | primary | yes | | + | hbi | ksd | secondary | yes | | + + When I route I should get + | waypoints | route | turns | lanes | + | a,f | ,ksd,ksd | depart,turn left,arrive | ,left:true none:true right:false, | + | a,i | ,ksd,ksd | depart,turn right,arrive | ,left:false none:true right:true, | diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index eb9ec351a..e330f5a22 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -92,8 +92,9 @@ class EdgeBasedGraphFactory const std::vector &node_info_list, ProfileProperties profile_properties, const util::NameTable &name_table, - const std::vector &turn_lane_offsets, - const std::vector &turn_lane_masks); + std::vector &turn_lane_offsets, + std::vector &turn_lane_masks, + guidance::LaneDescriptionMap &lane_description_map); void Run(ScriptingEnvironment &scripting_environment, const std::string &original_edge_data_filename, @@ -154,8 +155,9 @@ class EdgeBasedGraphFactory ProfileProperties profile_properties; const util::NameTable &name_table; - const std::vector &turn_lane_offsets; - const std::vector &turn_lane_masks; + std::vector &turn_lane_offsets; + std::vector &turn_lane_masks; + guidance::LaneDescriptionMap &lane_description_map; void CompressGeometry(); unsigned RenumberEdges(); diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp index 77d6fd2cb..16ba0f32e 100644 --- a/include/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -39,11 +39,11 @@ class ExtractionContainers void WriteNodes(std::ofstream &file_out_stream) const; void WriteRestrictions(const std::string &restrictions_file_name) const; void WriteEdges(std::ofstream &file_out_stream) const; - void WriteCharData(const std::string &file_name); void WriteTurnLaneMasks(const std::string &file_name, const stxxl::vector &turn_lane_offsets, const stxxl::vector &turn_lane_masks) const; + void WriteCharData(const std::string &file_name); public: using STXXLNodeIDVector = stxxl::vector; @@ -60,8 +60,6 @@ class ExtractionContainers STXXLNameCharData name_char_data; STXXLNameOffsets name_offsets; // an adjacency array containing all turn lane masks - stxxl::vector turn_lane_offsets; - stxxl::vector turn_lane_masks; STXXLRestrictionsVector restrictions_list; STXXLWayIDStartEndVector way_start_end_id_list; std::unordered_map external_to_internal_node_id_map; @@ -72,8 +70,7 @@ class ExtractionContainers void PrepareData(ScriptingEnvironment &scripting_environment, const std::string &output_file_name, const std::string &restrictions_file_name, - const std::string &names_file_name, - const std::string &turn_lane_file_name); + const std::string &names_file_name); }; } } diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index 0aa3af545..09d1c9be6 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "util/guidance/bearing_class.hpp" #include "util/guidance/entry_class.hpp" +#include "util/guidance/turn_lanes.hpp" #include "util/typedefs.hpp" @@ -87,6 +88,16 @@ class Extractor const std::vector &node_based_intersection_classes, const std::vector &bearing_classes, const std::vector &entry_classes) const; + + void WriteTurnLaneData(const std::string &turn_lane_file) const; + + // globals persisting during the extraction process and the graph generation process + + // during turn lane analysis, we might have to combine lanes for roads that are modelled as two + // but are more or less experienced as one. This can be due to solid lines in between lanes, for + // example, that genereate a small separation between them. As a result, we might have to + // augment the turn lane map during processing, further adding more types. + guidance::LaneDescriptionMap turn_lane_map; }; } } diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index 8268766f0..5db7636a0 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -40,14 +40,12 @@ class ExtractorCallbacks using MapKey = std::pair; using MapVal = unsigned; std::unordered_map> string_map; - std::unordered_map - lane_description_map; + guidance::LaneDescriptionMap &lane_description_map; ExtractionContainers &external_memory; public: - explicit ExtractorCallbacks(ExtractionContainers &extraction_containers); + explicit ExtractorCallbacks(ExtractionContainers &extraction_containers, + guidance::LaneDescriptionMap &lane_description_map); ExtractorCallbacks(const ExtractorCallbacks &) = delete; ExtractorCallbacks &operator=(const ExtractorCallbacks &) = delete; diff --git a/include/extractor/guidance/turn_lane_data.hpp b/include/extractor/guidance/turn_lane_data.hpp index 6b88fe0db..a1f0e59a8 100644 --- a/include/extractor/guidance/turn_lane_data.hpp +++ b/include/extractor/guidance/turn_lane_data.hpp @@ -28,7 +28,7 @@ typedef std::vector LaneDataVector; // convertes a string given in the OSM format into a TurnLaneData vector OSRM_ATTR_WARN_UNUSED -LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description); +LaneDataVector laneDataFromDescription(TurnLaneDescription turn_lane_description); // Locate A Tag in a lane data vector (if multiple tags are set, the first one found is returned) LaneDataVector::const_iterator findTag(const TurnLaneType::Mask tag, const LaneDataVector &data); @@ -37,6 +37,9 @@ LaneDataVector::iterator findTag(const TurnLaneType::Mask tag, LaneDataVector &d // Returns true if any of the queried tags is contained bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data); +// Check if a set of lanes is a subset of a different set of lanes +bool isSubsetOf(const LaneDataVector &subset_candidate, const LaneDataVector &superset_candidate); + } // namespace lane_data_generation } // namespace guidance diff --git a/include/extractor/guidance/turn_lane_handler.hpp b/include/extractor/guidance/turn_lane_handler.hpp index 42df700e8..b0ace9d28 100644 --- a/include/extractor/guidance/turn_lane_handler.hpp +++ b/include/extractor/guidance/turn_lane_handler.hpp @@ -33,27 +33,75 @@ namespace lanes { class TurnLaneHandler { + typedef enum TurnLaneScenario { + SIMPLE, // a straightforward assignment + PARTITION_LOCAL, // an assignment that requires partitioning, using local turns + SIMPLE_PREVIOUS, // an assignemtnn using the turns specified at the previous road (e.g. + // traffic light, lanes not drawn up to the intersection) + PARTITION_PREVIOUS, // a set of lanes on a turn with a traffic island. The lanes for the + // turn end at the previous turn (parts of it remain valid without being + // shown again) + SLIPROAD, // Sliproads are simple assignments that, for better visual representation should + // include turns from other roads in their listings + MERGE, // Merging Lanes + NONE, // not a turn lane scenario at all + INVALID, // some error might have occurred + UNKNOWN, // UNKNOWN describes all cases that we are currently not able to handle + NUM_SCENARIOS + } TurnLaneScenario; + + const constexpr static char *scenario_names[TurnLaneScenario::NUM_SCENARIOS] = { + "Simple", + "Partition Local", + "Simple Previous", + "Partition Previous", + "Sliproad", + "Merge", + "None", + "Invalid", + "Unknown"}; + public: typedef std::vector LaneDataVector; TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph, - const std::vector &turn_lane_offsets, - const std::vector &turn_lane_masks, - const TurnAnalysis &turn_analysis); + std::vector &turn_lane_offsets, + std::vector &turn_lane_masks, + LaneDescriptionMap &lane_description_map, + const std::vector &node_info_list, + const TurnAnalysis &turn_analysis, + LaneDataIdMap &id_map); + + ~TurnLaneHandler(); OSRM_ATTR_WARN_UNUSED - Intersection assignTurnLanes(const NodeID at, - const EdgeID via_edge, - Intersection intersection, - LaneDataIdMap &id_map) const; + Intersection assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection); private: + unsigned *count_handled; + unsigned *count_called; // we need to be able to look at previous intersections to, in some cases, find the correct turn // lanes for a turn const util::NodeBasedDynamicGraph &node_based_graph; - const std::vector &turn_lane_offsets; - const std::vector &turn_lane_masks; + std::vector &turn_lane_offsets; + std::vector &turn_lane_masks; + LaneDescriptionMap &lane_description_map; + const std::vector &node_info_list; const TurnAnalysis &turn_analysis; + LaneDataIdMap &id_map; + + // Find out which scenario we have to handle + TurnLaneScenario deduceScenario(const NodeID at, + const EdgeID via_edge, + const Intersection &intersection, + // Output Parameters to reduce repeated creation + LaneDescriptionID &lane_description_id, + LaneDataVector &lane_data, + NodeID &previous_node, + EdgeID &previous_id, + Intersection &previous_intersection, + LaneDataVector &previous_lane_data, + LaneDescriptionID &previous_description_id); // check whether we can handle an intersection bool isSimpleIntersection(const LaneDataVector &turn_lane_data, @@ -63,21 +111,28 @@ class TurnLaneHandler OSRM_ATTR_WARN_UNUSED Intersection simpleMatchTuplesToTurns(Intersection intersection, const LaneDataVector &lane_data, - const LaneDescriptionID lane_string_id, - LaneDataIdMap &id_map) const; + const LaneDescriptionID lane_string_id); // partition lane data into lane data relevant at current turn and at next turn OSRM_ATTR_WARN_UNUSED std::pair partitionLaneData( const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const; - // if the current intersections turn string is empty, we check whether there is an incoming - // intersection whose turns might be related to this current intersection + // Sliproad turns have a separated lane to the right/left of other depicted lanes. These lanes + // are not necessarily separated clearly from the rest of the way. As a result, we combine both + // lane entries for our output, while performing the matching with the separated lanes only. OSRM_ATTR_WARN_UNUSED - Intersection handleTurnAtPreviousIntersection(const NodeID at, - const EdgeID via_edge, - Intersection intersection, - LaneDataIdMap &id_map) const; + Intersection handleSliproadTurn(Intersection intersection, + const LaneDescriptionID lane_description_id, + LaneDataVector lane_data, + const Intersection &previous_intersection, + const LaneDescriptionID &previous_lane_description_id, + const LaneDataVector &previous_lane_data); + + // get the lane data for an intersection + void extractLaneData(const EdgeID via_edge, + LaneDescriptionID &lane_description_id, + LaneDataVector &lane_data) const; }; } // namespace lanes diff --git a/include/extractor/guidance/turn_lane_matcher.hpp b/include/extractor/guidance/turn_lane_matcher.hpp index 594d18667..fc67b036b 100644 --- a/include/extractor/guidance/turn_lane_matcher.hpp +++ b/include/extractor/guidance/turn_lane_matcher.hpp @@ -22,16 +22,20 @@ namespace lanes { // Translate Turn Lane Tags into a matching modifier -DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag); +DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag); // check whether a match of a given tag and a turn instruction can be seen as valid -bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction); +bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction); // localisation of the best possible match for a tag -typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag, +typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag, const Intersection &intersection); + +// the quality of a matching to decide between first/second possibility on segregated intersections +double getMatchingQuality( const TurnLaneType::Mask tag, const ConnectedRoad &road ); + typename Intersection::const_iterator -findBestMatchForReverse(const TurnLaneType::Mask &leftmost_tag, const Intersection &intersection); +findBestMatchForReverse(const TurnLaneType::Mask leftmost_tag, const Intersection &intersection); // a match is trivial if all turns can be associated with their best match in a valid way and the // matches occur in order diff --git a/include/extractor/guidance/turn_lane_types.hpp b/include/extractor/guidance/turn_lane_types.hpp index d32a8e5c0..14d68567d 100644 --- a/include/extractor/guidance/turn_lane_types.hpp +++ b/include/extractor/guidance/turn_lane_types.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,11 @@ struct TurnLaneDescription_hash } }; +typedef std::unordered_map + LaneDescriptionMap; + } // guidance } // extractor } // osrm diff --git a/include/util/debug.hpp b/include/util/debug.hpp index f929a279c..843fc367d 100644 --- a/include/util/debug.hpp +++ b/include/util/debug.hpp @@ -25,8 +25,7 @@ inline void print(const engine::guidance::RouteStep &step) << static_cast(step.maneuver.waypoint_type) << " " << " Duration: " << step.duration << " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " " << step.geometry_end - << " exit: " << step.maneuver.exit << " Intersections: " << step.intersections.size() - << " ["; + << "\n\tIntersections: " << step.intersections.size() << " ["; for (const auto &intersection : step.intersections) { @@ -36,9 +35,9 @@ inline void print(const engine::guidance::RouteStep &step) std::cout << ", entry: "; for (auto entry : intersection.entry) std::cout << " " << (entry ? "true" : "false"); - std::cout << " Lanes: (" << static_cast(intersection.lanes.lanes_in_turn) << ", " - << static_cast(intersection.lanes.first_lane_from_the_right) << ")"; - std::cout << ")"; + const auto lanes = intersection.lanes; + std::cout<< " Lanes: (" << static_cast(lanes.lanes_in_turn) << ", " + << static_cast(lanes.first_lane_from_the_right) << "))"; } std::cout << "] name[" << step.name_id << "]: " << step.name; } diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index f7c0e0c5f..36ff919b1 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -331,8 +331,6 @@ void closeOffRoundabout(const bool on_roundabout, OSRM_ATTR_WARN_UNUSED RouteStep elongate(RouteStep step, const RouteStep &by_step) { - BOOST_ASSERT(step.mode == by_step.mode); - step.duration += by_step.duration; step.distance += by_step.distance; @@ -418,6 +416,11 @@ void collapseTurnAt(std::vector &steps, DirectionModifier::Straight && one_back_step.intersections.front().bearings.size() > 2) steps[step_index].maneuver.instruction.type = TurnType::Turn; + else if (TurnType::UseLane == current_step.maneuver.instruction.type && + current_step.maneuver.instruction.direction_modifier != + DirectionModifier::Straight && + one_back_step.intersections.front().bearings.size() > 2) + steps[step_index].maneuver.instruction.type = TurnType::Turn; steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step); // If the previous instruction asked to continue, the name change will have to @@ -741,7 +744,8 @@ std::vector collapseTurns(std::vector steps) if (current_step.maneuver.instruction.type == TurnType::Suppressed && compatible(one_back_step, current_step)) { - // Traffic light on the sliproad + // Traffic light on the sliproad, the road itself will be handled in the next + // iteration, when one-back-index again points to the sliproad. steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); invalidateStep(steps[step_index]); @@ -764,6 +768,13 @@ std::vector collapseTurns(std::vector steps) elongate(std::move(steps[one_back_index]), steps[step_index]); steps[one_back_index].name_id = steps[step_index].name_id; steps[one_back_index].name = steps[step_index].name; + steps[one_back_index].maneuver.instruction.type = TurnType::Turn; + // the turn lanes for this turn are on the sliproad itself, so we have to + // remember them + steps[one_back_index].intersections.front().lanes = + current_step.intersections.front().lanes; + steps[one_back_index].intersections.front().lane_description = + current_step.intersections.front().lane_description; const auto exit_intersection = steps[step_index].intersections.front(); const auto exit_bearing = exit_intersection.bearings[exit_intersection.out]; diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index d38e76211..184738e31 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -1,5 +1,5 @@ -#include "extractor/edge_based_graph_factory.hpp" #include "extractor/edge_based_edge.hpp" +#include "extractor/edge_based_graph_factory.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" #include "util/exception.hpp" @@ -41,14 +41,16 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory( const std::vector &node_info_list, ProfileProperties profile_properties, const util::NameTable &name_table, - const std::vector &turn_lane_offsets, - const std::vector &turn_lane_masks) + std::vector &turn_lane_offsets, + std::vector &turn_lane_masks, + guidance::LaneDescriptionMap &lane_description_map) : m_max_edge_id(0), m_node_info_list(node_info_list), m_node_based_graph(std::move(node_based_graph)), m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes), m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container), profile_properties(std::move(profile_properties)), name_table(name_table), - turn_lane_offsets(turn_lane_offsets), turn_lane_masks(turn_lane_masks) + turn_lane_offsets(turn_lane_offsets), turn_lane_masks(turn_lane_masks), + lane_description_map(lane_description_map) { } @@ -344,13 +346,19 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( name_table, street_name_suffix_table, profile_properties); - guidance::lanes::TurnLaneHandler turn_lane_handler( - *m_node_based_graph, turn_lane_offsets, turn_lane_masks, turn_analysis); + + guidance::LaneDataIdMap lane_data_map; + guidance::lanes::TurnLaneHandler turn_lane_handler(*m_node_based_graph, + turn_lane_offsets, + turn_lane_masks, + lane_description_map, + m_node_info_list, + turn_analysis, + lane_data_map); bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(), std::numeric_limits::max()); - guidance::LaneDataIdMap lane_data_map; for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) { progress.PrintStatus(node_u); @@ -367,8 +375,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( intersection = turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection)); - intersection = turn_lane_handler.assignTurnLanes( - node_u, edge_from_u, std::move(intersection), lane_data_map); + intersection = + turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection)); const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection); // the entry class depends on the turn, so we have to classify the interesction for diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 94dd876c3..91dfe9aab 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -116,11 +116,6 @@ ExtractionContainers::ExtractionContainers() name_offsets.push_back(0); // Insert the total length sentinel (corresponds to the next name string offset) name_offsets.push_back(0); - - // the offsets have to be initialized with two values, since we have the empty turn string for - // the first id - turn_lane_offsets.push_back(0); - turn_lane_offsets.push_back(0); } /** @@ -136,8 +131,7 @@ ExtractionContainers::ExtractionContainers() void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environment, const std::string &output_file_name, const std::string &restrictions_file_name, - const std::string &name_file_name, - const std::string &turn_lane_file_name) + const std::string &name_file_name) { std::ofstream file_out_stream; file_out_stream.open(output_file_name.c_str(), std::ios::binary); @@ -151,35 +145,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme PrepareRestrictions(); WriteRestrictions(restrictions_file_name); - WriteCharData(name_file_name); - WriteTurnLaneMasks(turn_lane_file_name, turn_lane_offsets, turn_lane_masks); -} - -void ExtractionContainers::WriteTurnLaneMasks( - const std::string &file_name, - const stxxl::vector &offsets, - const stxxl::vector &masks) const -{ - util::SimpleLogger().Write() << "Writing turn lane masks..."; - TIMER_START(turn_lane_timer); - - std::ofstream ofs(file_name, std::ios::binary); - - if (!util::serializeVector(ofs, offsets)) - { - util::SimpleLogger().Write(logWARNING) << "Error while writing."; - return; - } - - if (!util::serializeVector(ofs, masks)) - { - util::SimpleLogger().Write(logWARNING) << "Error while writing."; - return; - } - - TIMER_STOP(turn_lane_timer); - util::SimpleLogger().Write() << "done (" << TIMER_SEC(turn_lane_timer) << ")"; } void ExtractionContainers::WriteCharData(const std::string &file_name) diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 00f44d52d..8bc144183 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -45,6 +45,7 @@ #include #include #include +#include //partial_sum #include #include #include @@ -93,7 +94,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) util::SimpleLogger().Write() << "Threads: " << number_of_threads; ExtractionContainers extraction_containers; - auto extractor_callbacks = util::make_unique(extraction_containers); + auto extractor_callbacks = + util::make_unique(extraction_containers, turn_lane_map); const osmium::io::File input_file(config.input_path.string()); osmium::io::Reader reader(input_file); @@ -194,8 +196,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) extraction_containers.PrepareData(scripting_environment, config.output_file_name, config.restriction_file_name, - config.names_file_name, - config.turn_lane_descriptions_file_name); + config.names_file_name); WriteProfileProperties(config.profile_properties_output_path, scripting_environment.GetProfileProperties()); @@ -444,13 +445,21 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, util::NameTable name_table(config.names_file_name); - std::vector turn_lane_offsets; - std::vector turn_lane_masks; - if (!util::deserializeAdjacencyArray( - config.turn_lane_descriptions_file_name, turn_lane_offsets, turn_lane_masks)) - { - util::SimpleLogger().Write(logWARNING) << "Reading Turn Lane Masks failed."; - } + // could use some additional capacity? To avoid a copy during processing, though small data so + // probably not that important. + std::vector turn_lane_offsets(turn_lane_map.size() + 2); // empty ID + sentinel + for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry) + turn_lane_offsets[entry->second + 1] = entry->first.size(); + + // inplace prefix sum + std::partial_sum(turn_lane_offsets.begin(), turn_lane_offsets.end(), turn_lane_offsets.begin()); + + // allocate the current masks + std::vector turn_lane_masks(turn_lane_offsets.back()); + for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry) + std::copy(entry->first.begin(), + entry->first.end(), + turn_lane_masks.begin() + turn_lane_offsets[entry->second]); EdgeBasedGraphFactory edge_based_graph_factory( node_based_graph, @@ -462,7 +471,8 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, scripting_environment.GetProfileProperties(), name_table, turn_lane_offsets, - turn_lane_masks); + turn_lane_masks, + turn_lane_map); edge_based_graph_factory.Run(scripting_environment, config.edge_output_path, @@ -471,6 +481,8 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, config.edge_penalty_path, config.generate_edge_lookup); + WriteTurnLaneData(config.turn_lane_descriptions_file_name); + edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); @@ -633,5 +645,44 @@ void Extractor::WriteIntersectionClassificationData( << entry_classes.size() << " entry classes and " << total_bearings << " bearing values." << std::endl; } + +void Extractor::WriteTurnLaneData(const std::string &turn_lane_file) const +{ + // Write the turn lane data to file + std::vector turn_lane_offsets(turn_lane_map.size() + 2); // empty ID + sentinel + for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry) + turn_lane_offsets[entry->second + 1] = entry->first.size(); + + // inplace prefix sum + std::partial_sum(turn_lane_offsets.begin(), turn_lane_offsets.end(), turn_lane_offsets.begin()); + + // allocate the current masks + std::vector turn_lane_masks(turn_lane_offsets.back()); + for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry) + std::copy(entry->first.begin(), + entry->first.end(), + turn_lane_masks.begin() + turn_lane_offsets[entry->second]); + + util::SimpleLogger().Write() << "Writing turn lane masks..."; + TIMER_START(turn_lane_timer); + + std::ofstream ofs(turn_lane_file, std::ios::binary); + + if (!util::serializeVector(ofs, turn_lane_offsets)) + { + util::SimpleLogger().Write(logWARNING) << "Error while writing."; + return; + } + + if (!util::serializeVector(ofs, turn_lane_masks)) + { + util::SimpleLogger().Write(logWARNING) << "Error while writing."; + return; + } + + TIMER_STOP(turn_lane_timer); + util::SimpleLogger().Write() << "done (" << TIMER_SEC(turn_lane_timer) << ")"; } -} + +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 83cb358d0..2a1033ddd 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -31,11 +31,15 @@ namespace extractor using TurnLaneDescription = guidance::TurnLaneDescription; namespace TurnLaneType = guidance::TurnLaneType; -ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers) - : external_memory(extraction_containers) +ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers, + guidance::LaneDescriptionMap &lane_description_map) + : lane_description_map(lane_description_map), external_memory(extraction_containers) { // we reserved 0, 1, 2 for the empty case string_map[MapKey("", "")] = 0; + + // The map should be empty before we start initializing it + BOOST_ASSERT(lane_description_map.empty()); lane_description_map[TurnLaneDescription()] = 0; } @@ -165,6 +169,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti "reverse", "merge_to_left", "merge_to_right"}; + const constexpr TurnLaneType::Mask masks_by_osm_string[num_osm_tags + 1] = { TurnLaneType::none, TurnLaneType::straight, @@ -224,16 +229,6 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti const LaneDescriptionID new_id = boost::numeric_cast(lane_description_map.size()); lane_description_map[lane_description] = new_id; - - // since we are getting a new ID, we can augment the current offsets - - // and store the turn lane masks, sadly stxxl does not support insert - for (const auto mask : lane_description) - external_memory.turn_lane_masks.push_back(mask); - - external_memory.turn_lane_offsets.push_back(external_memory.turn_lane_offsets.back() + - lane_description.size()); - return new_id; } else diff --git a/src/extractor/guidance/turn_discovery.cpp b/src/extractor/guidance/turn_discovery.cpp index 0bd272844..5c69e11fc 100644 --- a/src/extractor/guidance/turn_discovery.cpp +++ b/src/extractor/guidance/turn_discovery.cpp @@ -1,5 +1,5 @@ -#include "extractor/guidance/turn_discovery.hpp" #include "extractor/guidance/constants.hpp" +#include "extractor/guidance/turn_discovery.hpp" namespace osrm { @@ -50,8 +50,10 @@ bool findPreviousIntersection(const NodeID node_v, // previous intersection. const auto straightmost_at_v_in_reverse = findClosestTurn(node_v_reverse_intersection, STRAIGHT_ANGLE); - if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) > - FUZZY_ANGLE_DIFFERENCE) + + // TODO evaluate if narrow turn is the right criterion here... Might be that other angles are + // valid + if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) > GROUP_ANGLE) return false; const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid); @@ -66,12 +68,25 @@ bool findPreviousIntersection(const NodeID node_v, // if the edge is not traversable, we obviously don't have a previous intersection or couldn't // find it. if (node_based_graph.GetEdgeData(result_via_edge).reversed) + { + result_via_edge = SPECIAL_EDGEID; + result_node = SPECIAL_NODEID; return false; + } result_intersection = turn_analysis.getIntersection(node_u, result_via_edge); - const auto check_via_edge = findClosestTurn(result_intersection, STRAIGHT_ANGLE)->turn.eid; - if (check_via_edge != via_edge) + const auto check_via_edge = + result_intersection.end() != + std::find_if(result_intersection.begin(), + result_intersection.end(), + [via_edge](const ConnectedRoad &road) { return road.turn.eid == via_edge; }); + + if (!check_via_edge) + { + result_via_edge = SPECIAL_EDGEID; + result_node = SPECIAL_NODEID; return false; + } result_intersection = turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection)); diff --git a/src/extractor/guidance/turn_lane_augmentation.cpp b/src/extractor/guidance/turn_lane_augmentation.cpp index 193351921..b1264fa72 100644 --- a/src/extractor/guidance/turn_lane_augmentation.cpp +++ b/src/extractor/guidance/turn_lane_augmentation.cpp @@ -1,3 +1,5 @@ +#include "util/debug.hpp" + #include "extractor/guidance/turn_lane_augmentation.hpp" #include "extractor/guidance/turn_lane_types.hpp" #include "util/simple_logger.hpp" @@ -280,6 +282,13 @@ LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data, // we have to reduce it, assigning it to neighboring turns else if (connection_count < lane_data.size()) { + if( connection_count+1 < lane_data.size() ){ + std::cout << "[error] failed assignment" << std::endl; + util::guidance::print(lane_data); + std::cout << "Intersection:\n"; + for( auto road : intersection ) + std::cout << "\t" << toString(road) << std::endl; + } // a pgerequisite is simple turns. Larger differences should not end up here // an additional line at the side is only reasonable if it is targeting public // service vehicles. Otherwise, we should not have it diff --git a/src/extractor/guidance/turn_lane_data.cpp b/src/extractor/guidance/turn_lane_data.cpp index f2116b1ee..fc58e1ad6 100644 --- a/src/extractor/guidance/turn_lane_data.cpp +++ b/src/extractor/guidance/turn_lane_data.cpp @@ -64,10 +64,12 @@ bool TurnLaneData::operator<(const TurnLaneData &other) const std::find(tag_by_modifier, tag_by_modifier + 8, other.tag); } -LaneDataVector laneDataFromDescription(const TurnLaneDescription &turn_lane_description) +LaneDataVector laneDataFromDescription(TurnLaneDescription turn_lane_description) { typedef std::unordered_map> LaneMap; + //TODO need to handle cases that have none-in between two identical values const auto num_lanes = boost::numeric_cast(turn_lane_description.size()); + const auto setLaneData = [&]( LaneMap &map, TurnLaneType::Mask full_mask, const LaneID current_lane) { const auto isSet = [&](const TurnLaneType::Mask test_mask) -> bool { @@ -151,6 +153,27 @@ bool hasTag(const TurnLaneType::Mask tag, const LaneDataVector &data) return findTag(tag, data) != data.cend(); } +bool isSubsetOf(const LaneDataVector &subset_candidate, const LaneDataVector &superset_candidate) +{ + auto location = superset_candidate.begin(); + for (const auto entry : subset_candidate) + { + location = + std::find_if(location, superset_candidate.end(), [entry](const TurnLaneData &lane_data) { + return lane_data.tag == entry.tag; + }); + + if (location == superset_candidate.end()) + return false; + + // compare the number of lanes TODO this might have be to be revisited for situations where + // a sliproad widens into multiple lanes + if ((location->to - location->from) != (entry.to - entry.from)) + return false; + } + return true; +} + } // namespace lanes } // namespace guidance } // namespace extractor diff --git a/src/extractor/guidance/turn_lane_handler.cpp b/src/extractor/guidance/turn_lane_handler.cpp index 8f9c123c0..35bfc12be 100644 --- a/src/extractor/guidance/turn_lane_handler.cpp +++ b/src/extractor/guidance/turn_lane_handler.cpp @@ -1,3 +1,5 @@ +#include "util/debug.hpp" + #include "extractor/guidance/constants.hpp" #include "extractor/guidance/turn_discovery.hpp" #include "extractor/guidance/turn_lane_augmentation.hpp" @@ -30,13 +32,31 @@ std::size_t getNumberOfTurns(const Intersection &intersection) } } // namespace +const constexpr char *TurnLaneHandler::scenario_names[TurnLaneScenario::NUM_SCENARIOS]; + TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph, - const std::vector &turn_lane_offsets, - const std::vector &turn_lane_masks, - const TurnAnalysis &turn_analysis) + std::vector &turn_lane_offsets, + std::vector &turn_lane_masks, + LaneDescriptionMap &lane_description_map, + const std::vector &node_info_list, + const TurnAnalysis &turn_analysis, + LaneDataIdMap &id_map) : node_based_graph(node_based_graph), turn_lane_offsets(turn_lane_offsets), - turn_lane_masks(turn_lane_masks), turn_analysis(turn_analysis) + turn_lane_masks(turn_lane_masks), lane_description_map(lane_description_map), + node_info_list(node_info_list), turn_analysis(turn_analysis), id_map(id_map) { + count_handled = new unsigned; + count_called = new unsigned; + *count_handled = *count_called = 0; +} + +TurnLaneHandler::~TurnLaneHandler() +{ + std::cout << "Handled: " << *count_handled << " of " << *count_called + << " lanes: " << (double)(*count_handled * 100) / (*count_called) << " %." + << std::endl; + delete count_called; + delete count_handled; } /* @@ -57,54 +77,199 @@ TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_g assignment onto the turns. For example: (130, turn slight right), (180, ramp straight), (320, turn sharp left). */ -Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, - const EdgeID via_edge, - Intersection intersection, - LaneDataIdMap &id_map) const +Intersection +TurnLaneHandler::assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection) { // if only a uturn exists, there is nothing we can do if (intersection.size() == 1) return intersection; - const auto &data = node_based_graph.GetEdgeData(via_edge); - // Extract a lane description for the ID + // A list of output parameters to be filled in during deduceScenario. + // Data for the current intersection + LaneDescriptionID lane_description_id = INVALID_LANE_DESCRIPTIONID; + LaneDataVector lane_data; + // Data for the previous intersection + NodeID previous_node = SPECIAL_NODEID; + EdgeID previous_via_edge = SPECIAL_EDGEID; + Intersection previous_intersection; + LaneDataVector previous_lane_data; + LaneDescriptionID previous_description_id = INVALID_LANE_DESCRIPTIONID; - const auto turn_lane_description = - data.lane_description_id != INVALID_LANE_DESCRIPTIONID - ? TurnLaneDescription( - turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id], - turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id + 1]) - : TurnLaneDescription(); + const auto scenario = deduceScenario(at, + via_edge, + intersection, + lane_description_id, + lane_data, + previous_node, + previous_via_edge, + previous_intersection, + previous_lane_data, + previous_description_id); - BOOST_ASSERT(turn_lane_description.empty() || - turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id + 1] - - turn_lane_offsets[data.lane_description_id])); + std::cout << "[turn lane] " << scenario_names[scenario] << std::endl; - // going straight, due to traffic signals, we can have uncompressed geometry - if (intersection.size() == 2 && - ((data.lane_description_id != INVALID_LANE_DESCRIPTIONID && - data.lane_description_id == - node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) || - angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)) + if (scenario != TurnLaneHandler::NONE) + (*count_called)++; + + switch (scenario) + { + // A turn based on current lane data + case TurnLaneScenario::SIMPLE: + case TurnLaneScenario::PARTITION_LOCAL: + lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection); + return simpleMatchTuplesToTurns(std::move(intersection), lane_data, lane_description_id); + + // Cases operating on data carried over from a previous lane + case TurnLaneScenario::SIMPLE_PREVIOUS: + case TurnLaneScenario::PARTITION_PREVIOUS: + previous_lane_data = + handleNoneValueAtSimpleTurn(std::move(previous_lane_data), intersection); + return simpleMatchTuplesToTurns( + std::move(intersection), previous_lane_data, previous_description_id); + + // Sliproads-turns that are to be handled as a single entity + case TurnLaneScenario::SLIPROAD: + return handleSliproadTurn(std::move(intersection), + lane_description_id, + std::move(lane_data), + previous_intersection, + previous_description_id, + previous_lane_data); + case TurnLaneScenario::MERGE: return intersection; + default: + // All different values that we cannot handle. For some me might want to print debug output + // later on, when the handling is actually improved to work in close to all cases. + // case TurnLaneScenario::UNKNOWN: + // case TurnLaneScenario::NONE: + // case TurnLaneScenario::INVALID: + { + /* + static int print_count = 0; + if (TurnLaneScenario::NONE != scenario && print_count++ < 10) + { + std::cout << "[Unhandled] " << (int)lane_description_id << " -- " + << (int)previous_description_id << "\n" + << std::endl; + util::guidance::printTurnAssignmentData( + at, lane_data, intersection, node_info_list); - auto lane_data = laneDataFromDescription(turn_lane_description); + if (previous_node != SPECIAL_NODEID) + util::guidance::printTurnAssignmentData( + previous_node, previous_lane_data, previous_intersection, node_info_list); + } + */ + } + return intersection; + } +} + +// Find out which scenario we have to handle +TurnLaneHandler::TurnLaneScenario +TurnLaneHandler::deduceScenario(const NodeID at, + const EdgeID via_edge, + const Intersection &intersection, + // Output Variables + LaneDescriptionID &lane_description_id, + LaneDataVector &lane_data, + NodeID &previous_node, + EdgeID &previous_via_edge, + Intersection &previous_intersection, + LaneDataVector &previous_lane_data, + LaneDescriptionID &previous_description_id) +{ + // if only a uturn exists, there is nothing we can do + if (intersection.size() == 1) + return TurnLaneHandler::NONE; + + extractLaneData(via_edge, lane_description_id, lane_data); + + // traffic lights are not compressed during our preprocessing. Due to this *shortcoming*, we can + // get to the following situation: + // + // d + // a - b - c + // e + // + // with a traffic light at b and a-b as well as b-c offering the same turn lanes. + // In such a situation, we don't need to handle the lanes at a-b, since we will get the same + // information at b-c, where the actual turns are taking place. + const bool is_going_straight_and_turns_continue = + (intersection.size() == 2 && + ((lane_description_id != INVALID_LANE_DESCRIPTIONID && + lane_description_id == + node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) || + angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)); + + if (is_going_straight_and_turns_continue) + return TurnLaneHandler::NONE; // if we see an invalid conversion, we stop immediately - if (!turn_lane_description.empty() && lane_data.empty()) - return intersection; + if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.empty()) + return TurnLaneScenario::INVALID; // might be reasonable to handle multiple turns, if we know of a sequence of lanes // e.g. one direction per lane, if three lanes and right, through, left available - if (!turn_lane_description.empty() && lane_data.size() == 1 && + if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.size() == 1 && lane_data[0].tag == TurnLaneType::none) - return intersection; + return TurnLaneScenario::NONE; + + // Due to sliproads, we might need access to the previous intersection at this point already; + previous_node = SPECIAL_NODEID; + previous_via_edge = SPECIAL_EDGEID; + if (findPreviousIntersection(at, + via_edge, + intersection, + turn_analysis, + node_based_graph, + previous_node, + previous_via_edge, + previous_intersection)) + { + extractLaneData(previous_via_edge, previous_description_id, previous_lane_data); + for (std::size_t road_index = 0; road_index < previous_intersection.size(); ++road_index) + { + const auto &road = previous_intersection[road_index]; + // in case of a sliproad that is connected to road of simlar angle, we handle the + // turn as a combined turn + if (road.turn.instruction.type == TurnType::Sliproad) + { + if (via_edge == road.turn.eid) + return TurnLaneScenario::SLIPROAD; + + const auto &closest_road = [&]() { + if (road_index + 1 == previous_intersection.size()) + { + BOOST_ASSERT(road_index > 1); + return previous_intersection[road_index - 1]; + } + else if (road_index == 1) + { + BOOST_ASSERT(road_index + 1 < previous_intersection.size()); + return previous_intersection[road_index + 1]; + } + else if (angularDeviation(road.turn.angle, + previous_intersection.at(road_index - 1).turn.angle) < + angularDeviation(road.turn.angle, + previous_intersection.at(road_index + 1).turn.angle)) + return previous_intersection[road_index - 1]; + else + return previous_intersection[road_index + 1]; + }(); + + if (via_edge == closest_road.turn.eid) + return TurnLaneScenario::SLIPROAD; + } + } + } const std::size_t possible_entries = getNumberOfTurns(intersection); // merge does not justify an instruction const bool has_merge_lane = hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, lane_data); + if (has_merge_lane) + return TurnLaneScenario::MERGE; // Dead end streets that don't have any left-tag. This can happen due to the fallbacks for // broken data/barriers. @@ -114,12 +279,13 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, lane_data) && lane_data.size() + 1 == possible_entries); - if (has_merge_lane || has_non_usable_u_turn) - return intersection; + if (has_non_usable_u_turn) + return TurnLaneScenario::INVALID; // check if a u-turn is allowed (for some reason) and is missing from the list of tags (u-turn // is often allowed from the `left` lane without an additional indication dedicated to u-turns). const bool is_missing_valid_u_turn = + !lane_data.empty() && canMatchTrivially(intersection, lane_data) && lane_data.size() != static_cast(( !hasTag(TurnLaneType::uturn, lane_data) && intersection[0].entry_allowed ? 1 : 0)) + @@ -132,18 +298,31 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, lane_data.push_back({TurnLaneType::uturn, lane_data.back().to, lane_data.back().to}); bool is_simple = isSimpleIntersection(lane_data, intersection); - // simple intersections can be assigned directly + if (is_simple) - { - lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection); - return simpleMatchTuplesToTurns( - std::move(intersection), lane_data, data.lane_description_id, id_map); - } - // if the intersection is not simple but we have lane data, we check for intersections with - // middle islands. We have two cases. The first one is providing lane data on the current - // segment and we only need to consider the part of the current segment. In this case we - // partition the data and only consider the first part. - else if (!lane_data.empty()) + return TurnLaneScenario::SIMPLE; + + // In case of intersections that don't offer all turns right away, we have to account for + // *delayed* turns. Consider the following example: + // + // e + // a - b - c - f + // d + // + // With lanes on a-b indicating: | left | through | right |. + // While right obviously refers to a-b-d, through and left refer to a-b-c-f and a-b-c-e + // respectively. While we are at a-b, we have to consider the right turn only. + // The turn a-b-c gets assigned the lanes of both *left* and *through*. + // At b-c, we get access to either a new set of lanes, or -- via the previous intersection + // -- to + // the second part of | left | through | right |. Lane anticipation can then deduce which + // lanes + // correspond to what and suppress unnecessary instructions. + // + // For our initial case, we consider only the turns that are available at the current + // location, + // which are given by partitioning the lane data and selecting the first part. + if (!lane_data.empty()) { if (lane_data.size() >= possible_entries) { @@ -155,115 +334,73 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, // check if we were successfull in trimming if (lane_data.size() == possible_entries && isSimpleIntersection(lane_data, intersection)) - { - lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection); - return simpleMatchTuplesToTurns( - std::move(intersection), lane_data, data.lane_description_id, id_map); - } + return TurnLaneScenario::PARTITION_LOCAL; } - } - // The second part does not provide lane data on the current segment, but on the segment prior - // to the turn. We try to partition the data and only consider the second part. - else if (turn_lane_description.empty()) - { - // acquire the lane data of a previous segment and, if possible, use it for the current - // intersection. - return handleTurnAtPreviousIntersection(at, via_edge, std::move(intersection), id_map); + // If partitioning doesn't solve the problem, we don't know how to handle it right now + return TurnLaneScenario::UNKNOWN; } - return intersection; + if (lane_description_id != INVALID_LANE_DESCRIPTIONID) + return TurnLaneScenario::UNKNOWN; + + // acquire the lane data of a previous segment and, if possible, use it for the current + // intersection. + if (previous_via_edge == SPECIAL_EDGEID) + return TurnLaneScenario::NONE; + + if (previous_lane_data.empty()) + return TurnLaneScenario::NONE; + + const bool previous_has_merge_lane = + hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, previous_lane_data); + if (previous_has_merge_lane) + return TurnLaneScenario::MERGE; + + const auto is_simple_previous = + isSimpleIntersection(previous_lane_data, intersection) && previous_intersection.size() == 2; + if (is_simple_previous) + return TurnLaneScenario::SIMPLE_PREVIOUS; + + // This is the second part of the previously described partitioning scenario. + if (previous_lane_data.size() >= getNumberOfTurns(previous_intersection) && + previous_intersection.size() != 2) + { + previous_lane_data = partitionLaneData(node_based_graph.GetTarget(previous_via_edge), + std::move(previous_lane_data), + previous_intersection) + .second; + + std::sort(previous_lane_data.begin(), previous_lane_data.end()); + + // check if we were successfull in trimming + if ((previous_lane_data.size() == possible_entries) && + isSimpleIntersection(previous_lane_data, intersection)) + return TurnLaneScenario::PARTITION_PREVIOUS; + } + + return TurnLaneScenario::UNKNOWN; } -// At segregated intersections, turn lanes will often only be specified up until the first turn. To -// actually take the turn, we need to look back to the edge we drove onto the intersection with. -Intersection TurnLaneHandler::handleTurnAtPreviousIntersection(const NodeID at, - const EdgeID via_edge, - Intersection intersection, - LaneDataIdMap &id_map) const +void TurnLaneHandler::extractLaneData(const EdgeID via_edge, + LaneDescriptionID &lane_description_id, + LaneDataVector &lane_data) const { - NodeID previous_node = SPECIAL_NODEID; - Intersection previous_intersection; - EdgeID previous_id = SPECIAL_EDGEID; - LaneDataVector lane_data; + const auto &edge_data = node_based_graph.GetEdgeData(via_edge); + // TODO access correct data + lane_description_id = edge_data.lane_description_id; + const auto lane_description = + lane_description_id != INVALID_LANE_DESCRIPTIONID + ? TurnLaneDescription(turn_lane_masks.begin() + turn_lane_offsets[lane_description_id], + turn_lane_masks.begin() + + turn_lane_offsets[lane_description_id + 1]) + : TurnLaneDescription(); - // Get the previous lane string. We only accept strings that stem from a not-simple intersection - // and are not empty. - const auto previous_lane_description = [&]() -> TurnLaneDescription { - if (!findPreviousIntersection(at, - via_edge, - intersection, - turn_analysis, - node_based_graph, - previous_node, - previous_id, - previous_intersection)) - return {}; + if (!lane_description.empty()) + lane_data = laneDataFromDescription(lane_description); - BOOST_ASSERT(previous_id != SPECIAL_EDGEID); - - const auto &previous_edge_data = node_based_graph.GetEdgeData(previous_id); - // TODO access correct data - const auto previous_description = - previous_edge_data.lane_description_id != INVALID_LANE_DESCRIPTIONID - ? TurnLaneDescription( - turn_lane_masks.begin() + - turn_lane_offsets[previous_edge_data.lane_description_id], - turn_lane_masks.begin() + - turn_lane_offsets[previous_edge_data.lane_description_id + 1]) - : TurnLaneDescription(); - if (previous_description.empty()) - return previous_description; - - previous_intersection = turn_analysis.assignTurnTypes( - previous_node, previous_id, std::move(previous_intersection)); - - lane_data = laneDataFromDescription(previous_description); - - if (isSimpleIntersection(lane_data, previous_intersection)) - return {}; - else - return previous_description; - }(); - - // no lane string, no problems - if (previous_lane_description.empty()) - return intersection; - - // stop on invalid lane data conversion - if (lane_data.empty()) - return intersection; - - const auto &previous_data = node_based_graph.GetEdgeData(previous_id); - const auto is_simple = isSimpleIntersection(lane_data, intersection); - if (is_simple) - { - lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection); - return simpleMatchTuplesToTurns( - std::move(intersection), lane_data, previous_data.lane_description_id, id_map); - } - else - { - if (lane_data.size() >= getNumberOfTurns(previous_intersection) && - previous_intersection.size() != 2) - { - lane_data = partitionLaneData(node_based_graph.GetTarget(previous_id), - std::move(lane_data), - previous_intersection) - .second; - - std::sort(lane_data.begin(), lane_data.end()); - - // check if we were successfull in trimming - if (lane_data.size() == getNumberOfTurns(intersection) && - isSimpleIntersection(lane_data, intersection)) - { - lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection); - return simpleMatchTuplesToTurns( - std::move(intersection), lane_data, previous_data.lane_description_id, id_map); - } - } - } - return intersection; + BOOST_ASSERT(lane_description.empty() || + lane_description.size() == (turn_lane_offsets[lane_description_id + 1] - + turn_lane_offsets[lane_description_id])); } /* A simple intersection does not depend on the next intersection coming up. This is important @@ -414,8 +551,10 @@ std::pair TurnLaneHandler::partitionLaneData( * case left) turn lane as if it were to continue straight onto the intersection and * look back between (1) and (2) to make sure we find the correct lane for the left-turn. * - * Intersections like these have two parts. Turns that can be made at the first intersection and - * turns that have to be made at the second. The partitioning returns the lane data split into + * Intersections like these have two parts. Turns that can be made at the first intersection + * and + * turns that have to be made at the second. The partitioning returns the lane data split + * into * two parts, one for the first and one for the second intersection. */ @@ -433,7 +572,8 @@ std::pair TurnLaneHandler::partitionLaneData( std::vector matched_at_first(turn_lane_data.size(), false); std::vector matched_at_second(turn_lane_data.size(), false); - // find out about the next intersection. To check for valid matches, we also need the turn types + // find out about the next intersection. To check for valid matches, we also need the turn + // types auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid); next_intersection = turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection)); @@ -447,7 +587,8 @@ std::pair TurnLaneHandler::partitionLaneData( continue; const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection); - if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction)) + if (best_match->entry_allowed && + isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction)) { matched_at_first[lane] = true; @@ -457,9 +598,20 @@ std::pair TurnLaneHandler::partitionLaneData( const auto best_match_at_next_intersection = findBestMatch(turn_lane_data[lane].tag, next_intersection); - if (isValidMatch(turn_lane_data[lane].tag, + if (best_match_at_next_intersection->entry_allowed && + isValidMatch(turn_lane_data[lane].tag, best_match_at_next_intersection->turn.instruction)) - matched_at_second[lane] = true; + { + if (!matched_at_first[lane] || turn_lane_data[lane].tag == TurnLaneType::straight || + getMatchingQuality(turn_lane_data[lane].tag, *best_match) > + getMatchingQuality(turn_lane_data[lane].tag, *best_match_at_next_intersection)) + { + if (turn_lane_data[lane].tag != TurnLaneType::straight) + matched_at_first[lane] = false; + + matched_at_second[lane] = true; + } + } // we need to match all items to either the current or the next intersection if (!(matched_at_first[lane] || matched_at_second[lane])) @@ -504,7 +656,6 @@ std::pair TurnLaneHandler::partitionLaneData( LaneDataVector first, second; for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane) { - if (matched_at_second[lane]) second.push_back(turn_lane_data[lane]); @@ -535,8 +686,7 @@ std::pair TurnLaneHandler::partitionLaneData( Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection, const LaneDataVector &lane_data, - const LaneDescriptionID lane_description_id, - LaneDataIdMap &id_map) const + const LaneDescriptionID lane_description_id) { if (lane_data.empty() || !canMatchTrivially(intersection, lane_data)) return intersection; @@ -545,10 +695,127 @@ Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection !hasTag(TurnLaneType::none | TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, lane_data)); + (*count_handled)++; + return triviallyMatchLanesToTurns( std::move(intersection), lane_data, node_based_graph, lane_description_id, id_map); } +Intersection +TurnLaneHandler::handleSliproadTurn(Intersection intersection, + const LaneDescriptionID lane_description_id, + LaneDataVector lane_data, + const Intersection &previous_intersection, + const LaneDescriptionID &previous_lane_description_id, + const LaneDataVector &previous_lane_data) +{ + const auto sliproad_index = + std::distance(previous_intersection.begin(), + std::find_if(previous_intersection.begin(), + previous_intersection.end(), + [](const ConnectedRoad &road) { + return road.turn.instruction.type == TurnType::Sliproad; + })); + + BOOST_ASSERT(sliproad_index <= previous_intersection.size()); + const auto &sliproad = previous_intersection[sliproad_index]; + + // code duplicatino with deduceScenario: TODO refactor + const auto &main_road = [&]() { + if (sliproad_index + 1 == previous_intersection.size()) + { + BOOST_ASSERT(sliproad_index > 1); + return previous_intersection[sliproad_index - 1]; + } + else if (sliproad_index == 1) + { + BOOST_ASSERT(sliproad_index + 1 < previous_intersection.size()); + return previous_intersection[sliproad_index + 1]; + } + else if (angularDeviation(sliproad.turn.angle, + previous_intersection.at(sliproad_index - 1).turn.angle) < + angularDeviation(sliproad.turn.angle, + previous_intersection.at(sliproad_index + 1).turn.angle)) + return previous_intersection[sliproad_index - 1]; + else + return previous_intersection[sliproad_index + 1]; + }(); + const auto main_description_id = + node_based_graph.GetEdgeData(main_road.turn.eid).lane_description_id; + const auto sliproad_description_id = + node_based_graph.GetEdgeData(sliproad.turn.eid).lane_description_id; + + if (main_description_id == INVALID_LANE_DESCRIPTIONID || + sliproad_description_id == INVALID_LANE_DESCRIPTIONID) + return intersection; + + TurnLaneDescription combined_description; + // is the sliproad going off to the right? + if (main_road.turn.angle > sliproad.turn.angle) + { + combined_description.insert( + combined_description.end(), + turn_lane_masks.begin() + turn_lane_offsets[main_description_id], + turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]); + + combined_description.insert( + combined_description.end(), + turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id], + turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]); + + // if we handle the main road, we have to adjust the lane-data + if (main_description_id == lane_description_id) + { + const auto offset = turn_lane_offsets[sliproad_description_id + 1] - + turn_lane_offsets[sliproad_description_id]; + for (auto &item : lane_data) + { + item.from += offset; + item.to += offset; + } + } + } + // or to the left? + else + { + combined_description.insert( + combined_description.end(), + turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id], + turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]); + combined_description.insert( + combined_description.end(), + turn_lane_masks.begin() + turn_lane_offsets[main_description_id], + turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]); + + // if we are handling the sliproad, we have to adjust its lane data + if (sliproad_description_id == lane_description_id) + { + const auto offset = + turn_lane_offsets[main_description_id + 1] - turn_lane_offsets[main_description_id]; + for (auto &item : lane_data) + { + item.from += offset; + item.to += offset; + } + } + } + + const auto combined_id = [&]() { + auto itr = lane_description_map.find(combined_description); + if (lane_description_map.find(combined_description) == lane_description_map.end()) + { + const auto new_id = boost::numeric_cast(lane_description_map.size()); + lane_description_map[combined_description] = new_id; + return new_id; + } + else + { + return itr->second; + } + }(); + return simpleMatchTuplesToTurns(std::move(intersection), lane_data, combined_id); +} + } // namespace lanes } // namespace guidance } // namespace extractor diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp index 32512becc..d8078fed0 100644 --- a/src/extractor/guidance/turn_lane_matcher.cpp +++ b/src/extractor/guidance/turn_lane_matcher.cpp @@ -17,7 +17,7 @@ namespace lanes { // Translate Turn Tags into a Matching Direction Modifier -DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag) +DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag) { const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn, TurnLaneType::sharp_right, @@ -51,7 +51,7 @@ DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask &tag) } // check whether a match of a given tag and a turn instruction can be seen as valid -bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instruction) +bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction) { using util::guidance::hasLeftModifier; using util::guidance::hasRightModifier; @@ -102,29 +102,34 @@ bool isValidMatch(const TurnLaneType::Mask &tag, const TurnInstruction instructi return false; } +double getMatchingQuality( const TurnLaneType::Mask tag, const ConnectedRoad &road) +{ + const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315}; + const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)]; + return angularDeviation(idealized_angle,road.turn.angle); +} + // Every tag is somewhat idealized in form of the expected angle. A through lane should go straight // (or follow a 180 degree turn angle between in/out segments.) The following function tries to find // the best possible match for every tag in a given intersection, considering a few corner cases // introduced to OSRM handling u-turns -typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask &tag, +typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag, const Intersection &intersection) { - const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315}; - const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)]; return std::min_element( intersection.begin(), intersection.end(), - [idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) { + [tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) { // prefer valid matches if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction)) return isValidMatch(tag, lhs.turn.instruction); + // if the entry allowed flags don't match, we select the one with // entry allowed set to true if (lhs.entry_allowed != rhs.entry_allowed) return lhs.entry_allowed; - return angularDeviation(idealized_angle, lhs.turn.angle) < - angularDeviation(idealized_angle, rhs.turn.angle); + return getMatchingQuality(tag,lhs) < getMatchingQuality(tag,rhs); }); } @@ -140,23 +145,21 @@ findBestMatchForReverse(const TurnLaneType::Mask &neighbor_tag, const Intersecti if (neighbor_itr + 1 == intersection.cend()) return intersection.begin(); - const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315}; const TurnLaneType::Mask tag = TurnLaneType::uturn; - const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)]; return std::min_element( intersection.begin() + std::distance(intersection.begin(), neighbor_itr), intersection.end(), - [idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) { + [tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) { // prefer valid matches if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction)) return isValidMatch(tag, lhs.turn.instruction); + // if the entry allowed flags don't match, we select the one with // entry allowed set to true if (lhs.entry_allowed != rhs.entry_allowed) return lhs.entry_allowed; - return angularDeviation(idealized_angle, lhs.turn.angle) < - angularDeviation(idealized_angle, rhs.turn.angle); + return getMatchingQuality(tag,lhs) < getMatchingQuality(tag,rhs); }); }