diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ace124a745..93379cc93ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ # UNRELEASED + - Changes from 5.14.3: + - Bugfixes: + - FIXED #4704: Fixed regression in bearings reordering introduced in 5.13 [#4704](https://github.com/Project-OSRM/osrm-backend/issues/4704) + - Guidance: + - CHANGED #4706: Guidance refactoring step to decouple intersection connectivity analysis and turn instructions generation [#4706](https://github.com/Project-OSRM/osrm-backend/pull/4706) + +# 5.14.3 - Changes from 5.14.2: - Bugfixes: - FIXED #4754: U-Turn penalties are applied to straight turns. diff --git a/features/car/conditional_restrictions.feature b/features/car/conditional_restrictions.feature index 1eb0c237154..bbc6fde88fe 100644 --- a/features/car/conditional_restrictions.feature +++ b/features/car/conditional_restrictions.feature @@ -884,7 +884,7 @@ Feature: Car - Turn restrictions | a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive | | a | e | albic,dobe,dobe | depart,turn left,arrive | - @no_turning @conditionals + @no_turning @conditionals @restriction-way Scenario: Car - Conditional restriction with multiple time windows Given the extract extra arguments "--parse-conditional-restrictions" # 5pm Wed 02 May, 2017 GMT @@ -1054,4 +1054,3 @@ Feature: Car - Turn restrictions | a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f | | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | - diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index ff6a4365027..d9fb76adff1 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -575,7 +575,7 @@ Feature: Car - Turn restrictions | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | - @restriction @overlap + @restriction-way @overlap Scenario: Car - prohibit turn Given the node map """ @@ -710,7 +710,7 @@ Feature: Car - Turn restrictions | a | j | left,first,right,right | | f | e | right,third,left,left | - @restriction + @restriction-way Scenario: Car - allow only turn Given the node map """ @@ -742,7 +742,7 @@ Feature: Car - Turn restrictions | c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d | | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | - @restriction + @restriction-way Scenario: Car - allow only turn Given the node map """ @@ -771,7 +771,7 @@ Feature: Car - Turn restrictions | from | to | route | | a | d | ab,be,de,de | - @restriction + @restriction-way Scenario: Multi Way restriction Given the node map """ @@ -808,7 +808,7 @@ Feature: Car - Turn restrictions | from | to | route | | a | h | horiz,vert,horiz,horiz | - @restriction + @restriction-way Scenario: Multi-Way overlapping single-way Given the node map """ @@ -847,7 +847,7 @@ Feature: Car - Turn restrictions | h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d | - @restriction + @restriction-way Scenario: Car - prohibit turn, traffic lights Given the node map """ @@ -890,7 +890,7 @@ Feature: Car - Turn restrictions | c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f | - @restriction @overlap @geometry + @restriction-way @overlap @geometry Scenario: Geometry Given the node map """ @@ -925,7 +925,7 @@ Feature: Car - Turn restrictions | c | d | bc,bge,de,de | | c | f | bc,bge,de,de,ef,ef | - @restriction @overlap @geometry @traffic-signals + @restriction-way @overlap @geometry @traffic-signals Scenario: Geometry Given the node map """ @@ -967,7 +967,7 @@ Feature: Car - Turn restrictions | c | f | bc,bge,de,de,ef,ef | # don't crash hard on invalid restrictions - @restriction @invalid + @restriction-way @invalid Scenario: Geometry Given the node map """ @@ -999,7 +999,7 @@ Feature: Car - Turn restrictions | a | f | ab,be,ef,ef | - @restriction @overlap @geometry + @restriction @restriction-way @overlap @geometry Scenario: Duplicated restriction Given the node map """ diff --git a/features/guidance/merge-segregated-roads.feature b/features/guidance/merge-segregated-roads.feature index a27862dc5c6..be4fad98871 100644 --- a/features/guidance/merge-segregated-roads.feature +++ b/features/guidance/merge-segregated-roads.feature @@ -393,31 +393,44 @@ Feature: Merge Segregated Roads """ a | - b + b-----z + / \ c h | | | | | | | | + | | + | | + | | + | | + | | d g + \ / e | f """ And the ways - | nodes | name | oneway | - | ab | road | no | - | ef | road | no | - | bcde | road | yes | - | eghb | road | yes | + | nodes | name | oneway | + | ab | road | no | + | ef | road | no | + | bcde | road | yes | + | eghb | road | yes | + | bz | cross | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bz | bcde | b | no_left_turn | When I route I should get - | waypoints | turns | route | - | a,f | depart,arrive | road,road | - | c,f | depart,arrive | road,road | - | f,a | depart,arrive | road,road | - | g,a | depart,arrive | road,road | + | waypoints | turns | route | + | a,f | depart,arrive | road,road | + | c,f | depart,arrive | road,road | + | f,a | depart,arrive | road,road | + | g,a | depart,arrive | road,road | + | z,a | depart,turn right,arrive | cross,road,road | Scenario: Traffic Island Given the node map @@ -588,10 +601,10 @@ Feature: Merge Segregated Roads When I route I should get | waypoints | route | turns | - | a,c | germ,ober | depart,arrive | - | a,g | germ,germ,germ | depart,continue right,arrive | - | a,1 | germ,germ,germ | depart,continue left,arrive | - | d,g | ober,germ,germ | depart,turn left,arrive | + | a,c | germ,ober | depart,arrive | + | a,g | germ,germ,germ | depart,continue right,arrive | + | a,1 | germ,germ,germ | depart,continue left,arrive | + | d,g | ober,germ,germ | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/51.32888/6.57059 Scenario: Places in presence of oneways @@ -623,16 +636,16 @@ Feature: Merge Segregated Roads | cf | albrecht | yes | When I route I should get - | waypoints | route | turns | - | a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive | - | a,j | schwert,luise,luise | depart,turn right,arrive | - | a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive | - | k,l | marianne,marianne | depart,arrive | - | k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive | - | k,d | marianne,schwert,schwert | depart,turn right,arrive | - | i,j | luise,luise | depart,arrive | - | i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive | - | i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive | + | waypoints | route | turns | + | a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive | + | a,j | schwert,luise,luise | depart,turn right,arrive | + | a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive | + | k,l | marianne,marianne | depart,arrive | + | k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive | + | k,d | marianne,schwert,schwert | depart,turn right,arrive | + | i,j | luise,luise | depart,arrive | + | i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive | + | i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive | # https://www.openstreetmap.org/#map=19/52.46339/13.40272 Scenario: Do not merge links between segregated roads diff --git a/features/guidance/turn-angles.feature b/features/guidance/turn-angles.feature index 54c202560cc..80973946450 100644 --- a/features/guidance/turn-angles.feature +++ b/features/guidance/turn-angles.feature @@ -961,12 +961,12 @@ Feature: Simple Turns g . . - . - . - f - h . - . . - . j + . + . + h f + . + . . + . j . . c . . . diff --git a/features/support/data_classes.js b/features/support/data_classes.js index 5aced6c3b4e..a383540d6e0 100644 --- a/features/support/data_classes.js +++ b/features/support/data_classes.js @@ -12,6 +12,10 @@ module.exports = { FuzzyMatch: class { match (got, want) { + // don't fail if bearings input and extected string is empty and actual result is undefined + if (want === '' && (got === '' || got === undefined)) + return true; + var matchPercent = want.match(/(.*)\s+~(.+)%$/), matchAbs = want.match(/(.*)\s+\+\-(.+)$/), matchRe = want.match(/^\/(.*)\/$/), diff --git a/include/extractor/geojson_debug_policies.hpp b/include/extractor/geojson_debug_policies.hpp index 4ba1f447491..693214e0d92 100644 --- a/include/extractor/geojson_debug_policies.hpp +++ b/include/extractor/geojson_debug_policies.hpp @@ -60,7 +60,7 @@ operator()(const NodeID intersection_node, const boost::optional &way_style) const { // request the number of lanes. This process needs to be in sync with what happens over at - // intersection_generator + // intersection analysis const auto intersection_lanes = intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph)); diff --git a/include/extractor/guidance/driveway_handler.hpp b/include/extractor/guidance/driveway_handler.hpp index 7fb1a24c57a..be43e7bc89c 100644 --- a/include/extractor/guidance/driveway_handler.hpp +++ b/include/extractor/guidance/driveway_handler.hpp @@ -14,10 +14,13 @@ namespace guidance class DrivewayHandler final : public IntersectionHandler { public: - DrivewayHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, + DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table); diff --git a/include/extractor/guidance/intersection.hpp b/include/extractor/guidance/intersection.hpp index 5a45e93e45f..faaf37f3ab2 100644 --- a/include/extractor/guidance/intersection.hpp +++ b/include/extractor/guidance/intersection.hpp @@ -44,14 +44,6 @@ inline auto makeCompareShapeDataByBearing(const double base_bearing) }; } -inline auto makeCompareShapeDataAngleToBearing(const double base_bearing) -{ - return [base_bearing](const auto &lhs, const auto &rhs) { - return util::bearing::angleBetween(lhs.bearing, base_bearing) < - util::bearing::angleBetween(rhs.bearing, base_bearing); - }; -} - inline auto makeCompareAngularDeviation(const double angle) { return [angle](const auto &lhs, const auto &rhs) { diff --git a/include/extractor/guidance/intersection_generator.hpp b/include/extractor/guidance/intersection_generator.hpp deleted file mode 100644 index e0f8c671b17..00000000000 --- a/include/extractor/guidance/intersection_generator.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ -#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ - -#include "extractor/compressed_edge_container.hpp" -#include "extractor/guidance/coordinate_extractor.hpp" -#include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_normalization_operation.hpp" -#include "extractor/query_node.hpp" -#include "extractor/restriction_index.hpp" -#include "util/attributes.hpp" -#include "util/node_based_graph.hpp" -#include "util/typedefs.hpp" - -#include -#include -#include - -#include - -namespace osrm -{ -namespace extractor -{ -namespace guidance -{ - -struct IntersectionGenerationParameters -{ - NodeID nid; - EdgeID via_eid; -}; - -// The Intersection Generator is given a turn location and generates an intersection representation -// from it. For this all turn possibilities are analysed. -// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile -// decisions. -class IntersectionGenerator -{ - public: - IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const RestrictionMap &restriction_map, - const std::unordered_set &barrier_nodes, - const std::vector &coordinates, - const CompressedEdgeContainer &compressed_edge_container); - - // For a source node `a` and a via edge `ab` creates an intersection at target `b`. - // - // a . . . b . . - // . - // . - // - IntersectionView operator()(const NodeID nid, const EdgeID via_eid) const; - - /* - * Compute the shape of an intersection, returning a set of connected roads, without any further - * concern for which of the entries are actually allowed. - * The shape also only comes with turn bearings, not with turn angles. All turn angles will be - * set to zero - */ - OSRM_ATTR_WARN_UNUSED - IntersectionShape - ComputeIntersectionShape(const NodeID center_node, - const boost::optional sorting_base = boost::none, - bool use_low_precision_angles = false) const; - - // Graph Compression cannot compress every setting. For example any barrier/traffic light cannot - // be compressed. As a result, a simple road of the form `a ----- b` might end up as having an - // intermediate intersection, if there is a traffic light in between. If we want to look farther - // down a road, finding the next actual decision requires the look at multiple intersections. - // Here we follow the road until we either reach a dead end or find the next intersection with - // more than a single next road. This function skips over degree two nodes to find coorect input - // for GetConnectedRoads. - OSRM_ATTR_WARN_UNUSED - IntersectionGenerationParameters SkipDegreeTwoNodes(const NodeID starting_node, - const EdgeID via_edge) const; - - // Allow access to the coordinate extractor for all owners - const CoordinateExtractor &GetCoordinateExtractor() const; - - // Check for restrictions/barriers and generate a list of valid and invalid turns present at - // the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed - // for their actual instructions later on. - // The switch for `use_low_precision_angles` enables a faster mode that will procude less - // accurate coordinates. It should be good enough to check order of turns, find straightmost - // turns. Even good enough to do some simple angle verifications. It is mostly available to - // allow for faster graph traversal in the extraction phase. - OSRM_ATTR_WARN_UNUSED - IntersectionView GetConnectedRoads(const NodeID from_node, - const EdgeID via_eid, - const bool use_low_precision_angles = false) const; - - /* - * To be used in the road network, we need to check for valid/restricted turns. These two - * functions transform a basic intersection / a normalised intersection into the - * correct view when entering via a given edge. - */ - OSRM_ATTR_WARN_UNUSED - IntersectionView - TransformIntersectionShapeIntoView(const NodeID previous_node, - const EdgeID entering_via_edge, - const IntersectionShape &intersection) const; - // version for normalised intersection - OSRM_ATTR_WARN_UNUSED - IntersectionView TransformIntersectionShapeIntoView( - const NodeID previous_node, - const EdgeID entering_via_edge, - const IntersectionShape &normalised_intersection, - const IntersectionShape &intersection, - const std::vector &merging_map) const; - - private: - const util::NodeBasedDynamicGraph &node_based_graph; - const EdgeBasedNodeDataContainer &node_data_container; - const RestrictionMap &restriction_map; - const std::unordered_set &barrier_nodes; - const std::vector &coordinates; - - // own state, used to find the correct coordinates along a road - const CoordinateExtractor coordinate_extractor; -}; - -} // namespace guidance -} // namespace extractor -} // namespace osrm - -#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */ diff --git a/include/extractor/guidance/intersection_handler.hpp b/include/extractor/guidance/intersection_handler.hpp index 9c63825c9cd..4247aacb65e 100644 --- a/include/extractor/guidance/intersection_handler.hpp +++ b/include/extractor/guidance/intersection_handler.hpp @@ -2,8 +2,8 @@ #define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_ #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/node_based_graph_walker.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "extractor/query_node.hpp" #include "extractor/suffix_table.hpp" @@ -34,10 +34,13 @@ class IntersectionHandler public: IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator); + const SuffixTable &street_name_suffix_table); virtual ~IntersectionHandler() = default; @@ -52,10 +55,13 @@ class IntersectionHandler protected: const util::NodeBasedDynamicGraph &node_based_graph; const EdgeBasedNodeDataContainer &node_data_container; - const std::vector &coordinates; + const std::vector &node_coordinates; + const extractor::CompressedEdgeContainer &compressed_geometries; + const RestrictionMap &node_restriction_map; + const std::unordered_set &barrier_nodes; + const guidance::TurnLanesIndexedArray &turn_lanes_data; const util::NameTable &name_table; const SuffixTable &street_name_suffix_table; - const IntersectionGenerator &intersection_generator; const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc. // Decide on a basic turn types @@ -567,11 +573,19 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, // try to find whether there is a turn going to the opposite direction of our obvious // turn, this should be alright. const auto previous_intersection = [&]() -> IntersectionView { - const auto parameters = intersection_generator.SkipDegreeTwoNodes( - node_at_intersection, intersection[0].eid); - if (node_based_graph.GetTarget(parameters.via_eid) == node_at_intersection) + const auto parameters = intersection::skipDegreeTwoNodes( + node_based_graph, {node_at_intersection, intersection[0].eid}); + if (node_based_graph.GetTarget(parameters.edge) == node_at_intersection) return {}; - return intersection_generator.GetConnectedRoads(parameters.nid, parameters.via_eid); + + return intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + parameters); }(); if (!previous_intersection.empty()) diff --git a/include/extractor/guidance/intersection_normalization_operation.hpp b/include/extractor/guidance/intersection_normalization_operation.hpp deleted file mode 100644 index 57750d262e8..00000000000 --- a/include/extractor/guidance/intersection_normalization_operation.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_ -#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_ - -#include "util/typedefs.hpp" - -namespace osrm -{ -namespace extractor -{ -namespace guidance -{ - -struct IntersectionNormalizationOperation -{ - // the source of the merge, not part of the intersection after the merge is performed. - EdgeID merged_eid; - // the edge that is covering the `merged_eid` - EdgeID into_eid; -}; - -} // namespace guidance -} // namespace extractor -} // namespace osrm - -#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_*/ diff --git a/include/extractor/guidance/intersection_normalizer.hpp b/include/extractor/guidance/intersection_normalizer.hpp deleted file mode 100644 index 95c07f3936c..00000000000 --- a/include/extractor/guidance/intersection_normalizer.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ -#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ - -#include "util/attributes.hpp" -#include "util/name_table.hpp" -#include "util/typedefs.hpp" - -#include "extractor/guidance/coordinate_extractor.hpp" -#include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" -#include "extractor/guidance/intersection_normalization_operation.hpp" -#include "extractor/guidance/mergable_road_detector.hpp" -#include "extractor/query_node.hpp" -#include "extractor/suffix_table.hpp" - -#include -#include - -namespace osrm -{ -namespace extractor -{ -namespace guidance -{ - -/* - * An intersection is a central part in computing guidance decisions. However the model in OSM and - * the view we want to use in guidance are not necessarily the same thing. We have to account for - * some models that are chosen explicitly in OSM and that don't actually describe how a human would - * experience an intersection. - * - * For example, if a small pedestrian island is located at a traffic light right in the middle of a - * road, OSM tends to model the road as two separate ways. A human would consider these two ways a - * single road, though. In this normalizer, we try to account for these subtle differences between - * OSM data and human perception to improve our decision base for guidance later on. - */ -class IntersectionNormalizer -{ - public: - struct NormalizationResult - { - IntersectionShape normalized_shape; - std::vector performed_merges; - }; - IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &node_coordinates, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator); - - // The function takes an intersection an converts it to a `perceived` intersection which closer - // represents how a human might experience the intersection - OSRM_ATTR_WARN_UNUSED - NormalizationResult operator()(const NodeID node_at_intersection, - IntersectionShape intersection) const; - - private: - const util::NodeBasedDynamicGraph &node_based_graph; - const IntersectionGenerator &intersection_generator; - const MergableRoadDetector mergable_road_detector; - - /* check if two indices in an intersection can be seen as a single road in the perceived - * intersection representation. See below for an example. Utility function for - * MergeSegregatedRoads. It also checks for neighboring merges. - * This is due possible segments where multiple roads could end up being merged into one. - * We only support merging two roads, not three or more, though. - * c c - * / / - * a - b -> a - b - (c,d) but not a - b d -> a,b,(cde) - * \ \ - * d e - */ - bool CanMerge(const NodeID intersection_node, - const IntersectionShape &intersection, - std::size_t first_index, - std::size_t second_index) const; - - // Perform an Actual Merge - IntersectionNormalizationOperation - DetermineMergeDirection(const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs) const; - IntersectionShapeData MergeRoads(const IntersectionShapeData &destination, - const IntersectionShapeData &source) const; - IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction, - const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs, - const double opposite_bearing) const; - - // Merge segregated roads to omit invalid turns in favor of treating segregated roads as - // one. - // This function combines roads the following way: - // - // * * - // * is converted to * - // v ^ + - // v ^ + - // - // The treatment results in a straight turn angle of 180º rather than a turn angle of approx - // 160 - OSRM_ATTR_WARN_UNUSED - NormalizationResult MergeSegregatedRoads(const NodeID intersection_node, - IntersectionShape intersection) const; - - // The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the - // intersection itself, it can also happen that intersections are connected to joining roads. - // - // * * - // * is converted to * - // v a --- a --- - // v ^ + - // v ^ + - // b - // - // for the local view of b at a. - OSRM_ATTR_WARN_UNUSED - IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection, - IntersectionShape intersection) const; -}; - -} // namespace guidance -} // namespace extractor -} // namespace osrm - -#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ */ diff --git a/include/extractor/guidance/mergable_road_detector.hpp b/include/extractor/guidance/mergable_road_detector.hpp index fa428d5ed02..adce8e6e9d6 100644 --- a/include/extractor/guidance/mergable_road_detector.hpp +++ b/include/extractor/guidance/mergable_road_detector.hpp @@ -1,7 +1,11 @@ #ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS #define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS +#include "extractor/compressed_edge_container.hpp" +#include "extractor/guidance/coordinate_extractor.hpp" #include "extractor/guidance/intersection.hpp" +#include "extractor/guidance/turn_lane_types.hpp" +#include "extractor/restriction_index.hpp" #include "util/coordinate.hpp" #include "util/node_based_graph.hpp" #include "util/typedefs.hpp" @@ -9,6 +13,7 @@ #include #include #include +#include #include namespace osrm @@ -39,8 +44,10 @@ class MergableRoadDetector MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &node_coordinates, - const IntersectionGenerator &intersection_generator, - const CoordinateExtractor &coordinate_extractor, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table); @@ -159,15 +166,19 @@ class MergableRoadDetector const util::NodeBasedDynamicGraph &node_based_graph; const EdgeBasedNodeDataContainer &node_data_container; const std::vector &node_coordinates; - const IntersectionGenerator &intersection_generator; - const CoordinateExtractor &coordinate_extractor; + const extractor::CompressedEdgeContainer &compressed_geometries; + const RestrictionMap &node_restriction_map; + const std::unordered_set &barrier_nodes; + const guidance::TurnLanesIndexedArray &turn_lanes_data; // name detection const util::NameTable &name_table; const SuffixTable &street_name_suffix_table; + const CoordinateExtractor coordinate_extractor; + // limit for detecting circles / parallel roads - const static double constexpr distance_to_extract = 150; + const static double constexpr distance_to_extract = 120; }; } // namespace guidance diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp index 036395dd452..edf62de2682 100644 --- a/include/extractor/guidance/motorway_handler.hpp +++ b/include/extractor/guidance/motorway_handler.hpp @@ -2,7 +2,6 @@ #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_ #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/query_node.hpp" @@ -26,9 +25,12 @@ class MotorwayHandler : public IntersectionHandler MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator); + const SuffixTable &street_name_suffix_table); ~MotorwayHandler() override final = default; diff --git a/include/extractor/guidance/node_based_graph_walker.hpp b/include/extractor/guidance/node_based_graph_walker.hpp index 806e1132999..4f1f8d131f0 100644 --- a/include/extractor/guidance/node_based_graph_walker.hpp +++ b/include/extractor/guidance/node_based_graph_walker.hpp @@ -2,7 +2,9 @@ #define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER #include "extractor/guidance/constants.hpp" -#include "extractor/guidance/intersection_generator.hpp" +#include "extractor/guidance/coordinate_extractor.hpp" +#include "extractor/guidance/turn_lane_data.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" #include "util/node_based_graph.hpp" @@ -29,7 +31,11 @@ class NodeBasedGraphWalker public: NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const IntersectionGenerator &intersection_generator); + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data); /* * the returned node-id, edge-id are either the last ones used, just prior accumulator @@ -48,7 +54,11 @@ class NodeBasedGraphWalker private: const util::NodeBasedDynamicGraph &node_based_graph; const EdgeBasedNodeDataContainer &node_data_container; - const IntersectionGenerator &intersection_generator; + const std::vector &node_coordinates; + const extractor::CompressedEdgeContainer &compressed_geometries; + const RestrictionMap &node_restriction_map; + const std::unordered_set &barrier_nodes; + const guidance::TurnLanesIndexedArray &turn_lanes_data; }; /* @@ -149,7 +159,13 @@ struct SelectStraightmostRoadByNameAndOnlyChoice struct IntersectionFinderAccumulator { IntersectionFinderAccumulator(const std::uint8_t hop_limit, - const IntersectionGenerator &intersection_generator); + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data); // true if the path has traversed enough distance bool terminate(); @@ -159,13 +175,19 @@ struct IntersectionFinderAccumulator std::uint8_t hops; const std::uint8_t hop_limit; - // we need to be able to look-up the intersection - const IntersectionGenerator &intersection_generator; - // the result we are looking for NodeID nid; EdgeID via_edge_id; IntersectionView intersection; + + private: + const util::NodeBasedDynamicGraph &node_based_graph; + const EdgeBasedNodeDataContainer &node_data_container; + const std::vector &node_coordinates; + const extractor::CompressedEdgeContainer &compressed_geometries; + const RestrictionMap &node_restriction_map; + const std::unordered_set &barrier_nodes; + const guidance::TurnLanesIndexedArray &turn_lanes_data; }; template @@ -199,9 +221,15 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id, return {}; // look at the next intersection - const constexpr auto LOW_PRECISION = true; - const auto next_intersection = intersection_generator.GetConnectedRoads( - current_node_id, current_edge_id, LOW_PRECISION); + const auto next_intersection = + intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {current_node_id, current_edge_id}); // don't follow u-turns or go past our initial intersection if (next_intersection.size() <= 1) diff --git a/include/extractor/guidance/roundabout_handler.hpp b/include/extractor/guidance/roundabout_handler.hpp index 72779b12ef0..b40c12fad4d 100644 --- a/include/extractor/guidance/roundabout_handler.hpp +++ b/include/extractor/guidance/roundabout_handler.hpp @@ -4,7 +4,6 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/guidance/coordinate_extractor.hpp" #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/guidance/roundabout_type.hpp" #include "extractor/query_node.hpp" @@ -42,10 +41,12 @@ class RoundaboutHandler : public IntersectionHandler RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, - const CompressedEdgeContainer &compressed_edge_container, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator); + const SuffixTable &street_name_suffix_table); ~RoundaboutHandler() override final = default; @@ -64,10 +65,6 @@ class RoundaboutHandler : public IntersectionHandler const EdgeID via_eid, const Intersection &intersection) const; - void invalidateExitAgainstDirection(const NodeID from_nid, - const EdgeID via_eid, - Intersection &intersection) const; - // decide whether we lookk at a roundabout or a rotary RoundaboutType getRoundaboutType(const NodeID nid) const; @@ -84,7 +81,6 @@ class RoundaboutHandler : public IntersectionHandler bool qualifiesAsRoundaboutIntersection(const std::unordered_set &roundabout_nodes) const; - const CompressedEdgeContainer &compressed_edge_container; const CoordinateExtractor coordinate_extractor; }; diff --git a/include/extractor/guidance/sliproad_handler.hpp b/include/extractor/guidance/sliproad_handler.hpp index 8dfa5d3eab4..8fb710b7b84 100644 --- a/include/extractor/guidance/sliproad_handler.hpp +++ b/include/extractor/guidance/sliproad_handler.hpp @@ -2,7 +2,6 @@ #define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_ #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/query_node.hpp" @@ -24,10 +23,13 @@ namespace guidance class SliproadHandler final : public IntersectionHandler { public: - SliproadHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, + SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table); @@ -78,6 +80,8 @@ class SliproadHandler final : public IntersectionHandler // The return value is guaranteed to not be larger than `threshold`. static double scaledThresholdByRoadClass(const double max_threshold, const RoadClassification &classification); + + const CoordinateExtractor coordinate_extractor; }; } // namespace guidance diff --git a/include/extractor/guidance/statistics_handler.hpp b/include/extractor/guidance/statistics_handler.hpp index 32c6ac140a0..735f53ad52b 100644 --- a/include/extractor/guidance/statistics_handler.hpp +++ b/include/extractor/guidance/statistics_handler.hpp @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include #include @@ -27,18 +27,24 @@ namespace guidance class StatisticsHandler final : public IntersectionHandler { public: - StatisticsHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, + StatisticsHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table) { } @@ -55,7 +61,7 @@ class StatisticsHandler final : public IntersectionHandler for (const auto &kv : type_hist) if (kv.second > 0) - util::Log() << std::fixed << std::setprecision(2) + util::Log() << " " << std::fixed << std::setprecision(2) << internalInstructionTypeToString(kv.first) << ": " << kv.second << " (" << (kv.second / static_cast(num_types) * 100.) << "%)"; @@ -63,7 +69,7 @@ class StatisticsHandler final : public IntersectionHandler for (const auto &kv : modifier_hist) if (kv.second > 0) - util::Log() << std::fixed << std::setprecision(2) + util::Log() << " " << std::fixed << std::setprecision(2) << instructionModifierToString(kv.first) << ": " << kv.second << " (" << (kv.second / static_cast(num_modifiers) * 100.) << "%)"; } @@ -84,12 +90,14 @@ class StatisticsHandler final : public IntersectionHandler // numbers closer to the handlers and see how often handlers ran. for (const auto &road : intersection) { - - const auto type = road.instruction.type; - const auto modifier = road.instruction.direction_modifier; - - type_hist[type] += 1; - modifier_hist[modifier] += 1; + if (road.entry_allowed) + { + const auto type = road.instruction.type; + const auto modifier = road.instruction.direction_modifier; + + type_hist[type] += 1; + modifier_hist[modifier] += 1; + } } return intersection; @@ -97,8 +105,8 @@ class StatisticsHandler final : public IntersectionHandler private: mutable std::mutex lock; - mutable std::unordered_map type_hist; - mutable std::unordered_map modifier_hist; + mutable std::map type_hist; + mutable std::map modifier_hist; }; } // namespace guidance diff --git a/include/extractor/guidance/suppress_mode_handler.hpp b/include/extractor/guidance/suppress_mode_handler.hpp index 7b3eb458cc2..a99061c793c 100644 --- a/include/extractor/guidance/suppress_mode_handler.hpp +++ b/include/extractor/guidance/suppress_mode_handler.hpp @@ -3,7 +3,6 @@ #include "extractor/guidance/constants.hpp" #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/travel_mode.hpp" #include "util/node_based_graph.hpp" @@ -21,10 +20,13 @@ namespace guidance class SuppressModeHandler final : public IntersectionHandler { public: - SuppressModeHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, + SuppressModeHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table); diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp index 9f405adfb4b..e9c637cba18 100644 --- a/include/extractor/guidance/turn_analysis.hpp +++ b/include/extractor/guidance/turn_analysis.hpp @@ -4,9 +4,6 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/guidance/driveway_handler.hpp" #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" -#include "extractor/guidance/intersection_normalization_operation.hpp" -#include "extractor/guidance/intersection_normalizer.hpp" #include "extractor/guidance/motorway_handler.hpp" #include "extractor/guidance/roundabout_handler.hpp" #include "extractor/guidance/sliproad_handler.hpp" @@ -43,10 +40,11 @@ class TurnAnalysis public: TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, + const std::vector &node_coordinates, + const CompressedEdgeContainer &compressed_edge_container, const RestrictionMap &restriction_map, const std::unordered_set &barrier_nodes, - const CompressedEdgeContainer &compressed_edge_container, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table); @@ -56,34 +54,14 @@ class TurnAnalysis Intersection operator()(const NodeID node_prior_to_intersection, const EdgeID entering_via_edge) const; - /* - * Returns a normalized intersection without any assigned turn types. - * This intersection can be used as input for intersection classification, turn lane assignment - * and similar. - */ - struct ShapeResult - { - // the basic shape, containing all turns - IntersectionShape intersection_shape; - // normalized shape, merged some roads into others, adjusted bearings - // see intersection_normalizer for further explanations - IntersectionNormalizer::NormalizationResult annotated_normalized_shape; - }; - OSRM_ATTR_WARN_UNUSED - ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const; - // Select turn types based on the intersection shape OSRM_ATTR_WARN_UNUSED Intersection AssignTurnTypes(const NodeID from_node, const EdgeID via_eid, const IntersectionView &intersection) const; - const IntersectionGenerator &GetIntersectionGenerator() const; - private: const util::NodeBasedDynamicGraph &node_based_graph; - const IntersectionGenerator intersection_generator; - const IntersectionNormalizer intersection_normalizer; const RoundaboutHandler roundabout_handler; const MotorwayHandler motorway_handler; const TurnHandler turn_handler; diff --git a/include/extractor/guidance/turn_discovery.hpp b/include/extractor/guidance/turn_discovery.hpp index ff8207c5fab..92cfe20c8a7 100644 --- a/include/extractor/guidance/turn_discovery.hpp +++ b/include/extractor/guidance/turn_discovery.hpp @@ -2,15 +2,27 @@ #define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_ #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" +#include "extractor/guidance/turn_lane_data.hpp" +#include "extractor/restriction_index.hpp" #include "util/typedefs.hpp" +#include + namespace osrm { +namespace util +{ +class Coordinate; +} + namespace extractor { + +class CompressedEdgeContainer; + namespace guidance { + namespace lanes { @@ -21,8 +33,13 @@ bool findPreviousIntersection( const NodeID node, const EdgeID via_edge, const Intersection &intersection, - const IntersectionGenerator &intersection_generator, const util::NodeBasedDynamicGraph &node_based_graph, // query edge data + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, // output parameters, will be in an arbitrary state on failure NodeID &result_node, EdgeID &result_via_edge, diff --git a/include/extractor/guidance/turn_handler.hpp b/include/extractor/guidance/turn_handler.hpp index b80edebad26..b8d5a227ee4 100644 --- a/include/extractor/guidance/turn_handler.hpp +++ b/include/extractor/guidance/turn_handler.hpp @@ -2,7 +2,6 @@ #define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_ #include "extractor/guidance/intersection.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/query_node.hpp" @@ -30,9 +29,12 @@ class TurnHandler : public IntersectionHandler TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator); + const SuffixTable &street_name_suffix_table); ~TurnHandler() override final = default; diff --git a/include/extractor/guidance/turn_lane_handler.hpp b/include/extractor/guidance/turn_lane_handler.hpp index 4f19e163b3d..c1e1fd760f5 100644 --- a/include/extractor/guidance/turn_lane_handler.hpp +++ b/include/extractor/guidance/turn_lane_handler.hpp @@ -74,6 +74,11 @@ class TurnLaneHandler TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, LaneDescriptionMap &lane_description_map, const TurnAnalysis &turn_analysis, util::guidance::LaneDataIdMap &id_map); @@ -90,6 +95,12 @@ class TurnLaneHandler // lanes for a turn const util::NodeBasedDynamicGraph &node_based_graph; const EdgeBasedNodeDataContainer &node_data_container; + const std::vector &node_coordinates; + const extractor::CompressedEdgeContainer &compressed_geometries; + const RestrictionMap &node_restriction_map; + const std::unordered_set &barrier_nodes; + const guidance::TurnLanesIndexedArray &turn_lanes_data; + std::vector turn_lane_offsets; std::vector turn_lane_masks; LaneDescriptionMap &lane_description_map; diff --git a/include/extractor/guidance/turn_lane_types.hpp b/include/extractor/guidance/turn_lane_types.hpp index 7c540416405..eebdd5b25dd 100644 --- a/include/extractor/guidance/turn_lane_types.hpp +++ b/include/extractor/guidance/turn_lane_types.hpp @@ -100,8 +100,10 @@ typedef util::ConcurrentIDMap LaneDescriptionMap; -inline std::tuple, std::vector> -transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map) +using TurnLanesIndexedArray = + std::tuple, std::vector>; + +inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map) { // could use some additional capacity? To avoid a copy during processing, though small data so // probably not that important. @@ -111,8 +113,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map) // // turn lane offsets points into the locations of the turn_lane_masks array. We use a standard // adjacency array like structure to store the turn lane masks. - std::vector turn_lane_offsets(turn_lane_map.data.size() + - 2); // empty ID + sentinel + std::vector turn_lane_offsets(turn_lane_map.data.size() + 1); // + sentinel for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry) turn_lane_offsets[entry->second + 1] = entry->first.size(); @@ -125,6 +126,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map) std::copy(entry->first.begin(), entry->first.end(), turn_lane_masks.begin() + turn_lane_offsets[entry->second]); + return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks)); } diff --git a/include/extractor/intersection/intersection_analysis.hpp b/include/extractor/intersection/intersection_analysis.hpp new file mode 100644 index 00000000000..09121f2b45a --- /dev/null +++ b/include/extractor/intersection/intersection_analysis.hpp @@ -0,0 +1,87 @@ +#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP +#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/guidance/mergable_road_detector.hpp" +#include "extractor/guidance/turn_lane_types.hpp" +#include "extractor/intersection/intersection_edge.hpp" +#include "extractor/restriction_index.hpp" + +#include "util/coordinate.hpp" +#include "util/node_based_graph.hpp" + +#include +#include + +namespace osrm +{ +namespace extractor +{ +namespace intersection +{ + +IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph, + const NodeID intersection); + +IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph, + const NodeID intersection); + +bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const IntersectionEdgeGeometries &geometries, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &from, + const IntersectionEdge &to); + +double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge); + +double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge); + +std::pair> +getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &node_coordinates, + const guidance::MergableRoadDetector &detector, + const NodeID intersection); + +guidance::IntersectionView +convertToIntersectionView(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const IntersectionEdgeGeometries &edge_geometries, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge, + const IntersectionEdges &outgoing_edges, + const std::unordered_set &merged_edges); + +// Check for restrictions/barriers and generate a list of valid and invalid turns present at +// the node reached from `incoming_edge`. The resulting candidates have to be analyzed +// for their actual instructions later on. +template +guidance::IntersectionView +getConnectedRoads(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge); + +// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot +// be compressed. As a result, a simple road of the form `a ----- b` might end up as having an +// intermediate intersection, if there is a traffic light in between. If we want to look farther +// down a road, finding the next actual decision requires the look at multiple intersections. +// Here we follow the road until we either reach a dead end or find the next intersection with +// more than a single next road. This function skips over degree two nodes to find correct input +// for getConnectedRoads. +IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph, + IntersectionEdge road); +} +} +} + +#endif diff --git a/include/extractor/intersection/intersection_edge.hpp b/include/extractor/intersection/intersection_edge.hpp new file mode 100644 index 00000000000..55620607baf --- /dev/null +++ b/include/extractor/intersection/intersection_edge.hpp @@ -0,0 +1,44 @@ +#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP +#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP + +#include "util/typedefs.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ +namespace intersection +{ + +// IntersectionEdge is an alias for incoming and outgoing node-based graph edges of an intersection +struct IntersectionEdge +{ + NodeID node; + EdgeID edge; + + bool operator<(const IntersectionEdge &other) const + { + return std::tie(node, edge) < std::tie(other.node, other.edge); + } +}; + +using IntersectionEdges = std::vector; + +struct IntersectionEdgeGeometry +{ + EdgeID edge; + double initial_bearing; + double perceived_bearing; + double length; + + bool operator<(const IntersectionEdgeGeometry &other) const { return edge < other.edge; } +}; + +using IntersectionEdgeGeometries = std::vector; +} +} +} + +#endif diff --git a/include/util/bearing.hpp b/include/util/bearing.hpp index fa68f0d17d4..c046bf417d5 100644 --- a/include/util/bearing.hpp +++ b/include/util/bearing.hpp @@ -144,18 +144,6 @@ inline double restrictAngleToValidRange(const double angle) return angle; } -// finds the angle between two angles, based on the minum difference between the two -inline double angleBetween(const double lhs, const double rhs) -{ - const auto difference = std::abs(lhs - rhs); - const auto is_clockwise_difference = difference <= 180; - const auto angle_between_candidate = .5 * (lhs + rhs); - if (is_clockwise_difference) - return angle_between_candidate; - else - return restrictAngleToValidRange(angle_between_candidate + 180); -} - } // namespace util } // namespace osrm diff --git a/package.json b/package.json index bc48c024a88..e706c411457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "osrm", - "version": "5.15.0-latest.1", + "version": "5.15.0-latest.4", "private": false, "description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.", "dependencies": { diff --git a/scripts/gdb_printers.py b/scripts/gdb_printers.py index bae67190370..ca9568c8341 100644 --- a/scripts/gdb_printers.py +++ b/scripts/gdb_printers.py @@ -71,10 +71,10 @@ def build_pretty_printer(): pp.add_printer('TurnLaneData', '::TurnLaneData$', TurnLaneDataPrinter) return pp -gdb.pretty_printers = [x for x in gdb.pretty_printers if x.name != 'OSRM'] # unregister OSRM pretty printer before (re)loading +## unregister OSRM pretty printer before (re)loading +gdb.pretty_printers = [x for x in gdb.pretty_printers if not isinstance(x, gdb.printing.RegexpCollectionPrettyPrinter) or x.name != 'OSRM'] gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer()) - import geojson import os import time diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index ba4c4a814af..43e92adfe5b 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -7,6 +7,8 @@ #include "extractor/scripting_environment.hpp" #include "extractor/suffix_table.hpp" +#include "extractor/intersection/intersection_analysis.hpp" + #include "extractor/serialization.hpp" #include "storage/io.hpp" @@ -421,22 +423,39 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( TurnDataExternalContainer turn_data_container; + SuffixTable street_name_suffix_table(scripting_environment); + const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map); + guidance::MergableRoadDetector mergable_road_detector(m_node_based_graph, + m_edge_based_node_container, + m_coordinates, + m_compressed_edge_container, + node_restriction_map, + m_barrier_nodes, + turn_lanes_data, + name_table, + street_name_suffix_table); + // Loop over all turns and generate new set of edges. // Three nested loop look super-linear, but we are dealing with a (kind of) // linear number of turns only. - SuffixTable street_name_suffix_table(scripting_environment); guidance::TurnAnalysis turn_analysis(m_node_based_graph, m_edge_based_node_container, m_coordinates, + m_compressed_edge_container, node_restriction_map, m_barrier_nodes, - m_compressed_edge_container, + turn_lanes_data, name_table, street_name_suffix_table); util::guidance::LaneDataIdMap lane_data_map; guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph, m_edge_based_node_container, + m_coordinates, + m_compressed_edge_container, + node_restriction_map, + m_barrier_nodes, + turn_lanes_data, lane_description_map, turn_analysis, lane_data_map); @@ -539,14 +558,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // the situation of the turn const auto node_along_road_entering, const auto node_based_edge_from, - const auto node_at_center_of_intersection, + const auto intersection_node, const auto node_based_edge_to, - const auto &intersection, + const auto incoming_bearing, const auto &turn, const auto entry_class_id) { const auto node_restricted = isRestricted(node_along_road_entering, - node_at_center_of_intersection, + intersection_node, m_node_based_graph.GetTarget(turn.eid), conditional_restriction_map); @@ -558,7 +577,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( conditional = {{edge_based_node_from, edge_based_node_to, {static_cast(-1), - m_coordinates[node_at_center_of_intersection], + m_coordinates[intersection_node], conditions}}}; } @@ -574,14 +593,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( TurnData turn_data = {turn.instruction, turn.lane_data_id, entry_class_id, - util::guidance::TurnBearing(intersection[0].bearing), + util::guidance::TurnBearing(incoming_bearing), util::guidance::TurnBearing(turn.bearing)}; // compute weight and duration penalties - auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection); + auto is_traffic_light = m_traffic_lights.count(intersection_node); ExtractionTurn extracted_turn( turn.angle, - m_node_based_graph.GetOutDegree(node_at_center_of_intersection), + m_node_based_graph.GetOutDegree(intersection_node), turn.instruction.IsUTurn(), is_traffic_light, edge_data1.flags.restricted, @@ -632,8 +651,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( : m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from); const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid); - lookup::TurnIndexBlock turn_index_block = { - from_node, node_at_center_of_intersection, to_node}; + lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node}; // insert data into the designated buffer return std::make_pair( @@ -655,17 +673,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( if (buffer->nodes_processed == 0) return buffer; - for (auto node_at_center_of_intersection = intersection_node_range.begin(), + for (auto intersection_node = intersection_node_range.begin(), end = intersection_node_range.end(); - node_at_center_of_intersection < end; - ++node_at_center_of_intersection) + intersection_node < end; + ++intersection_node) { - // We capture the thread-local work in these objects, then flush // them in a controlled manner at the end of the parallel range - - const auto shape_result = - turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection); + const auto &incoming_edges = + intersection::getIncomingEdges(m_node_based_graph, intersection_node); + const auto &outgoing_edges = + intersection::getOutgoingEdges(m_node_based_graph, intersection_node); + + intersection::IntersectionEdgeGeometries edge_geometries; + std::unordered_set merged_edge_ids; + std::tie(edge_geometries, merged_edge_ids) = + intersection::getIntersectionGeometries(m_node_based_graph, + m_compressed_edge_container, + m_coordinates, + mergable_road_detector, + intersection_node); // all nodes in the graph are connected in both directions. We check all // outgoing nodes to find the incoming edge. This is a larger search overhead, @@ -685,45 +712,33 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // From the flags alone, we cannot determine which nodes are connected to // `b` by an outgoing edge. Therefore, we have to search all connected edges for // edges entering `b` - for (const EdgeID outgoing_edge : - m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection)) - { - const NodeID node_along_road_entering = - m_node_based_graph.GetTarget(outgoing_edge); - - const auto incoming_edge = m_node_based_graph.FindEdge( - node_along_road_entering, node_at_center_of_intersection); - - if (m_node_based_graph.GetEdgeData(incoming_edge).reversed) - continue; + for (const auto &incoming_edge : incoming_edges) + { ++node_based_edge_counter; - auto intersection_with_flags_and_angles = - turn_analysis.GetIntersectionGenerator() - .TransformIntersectionShapeIntoView( - node_along_road_entering, - incoming_edge, - shape_result.annotated_normalized_shape.normalized_shape, - shape_result.intersection_shape, - shape_result.annotated_normalized_shape.performed_merges); - - auto intersection = - turn_analysis.AssignTurnTypes(node_along_road_entering, - incoming_edge, - intersection_with_flags_and_angles); - - OSRM_ASSERT(intersection.valid(), - m_coordinates[node_at_center_of_intersection]); - + const auto intersection_view = + convertToIntersectionView(m_node_based_graph, + m_edge_based_node_container, + node_restriction_map, + m_barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edges, + merged_edge_ids); + + auto intersection = turn_analysis.AssignTurnTypes( + incoming_edge.node, incoming_edge.edge, intersection_view); + + OSRM_ASSERT(intersection.valid(), m_coordinates[intersection_node]); intersection = turn_lane_handler.assignTurnLanes( - node_along_road_entering, incoming_edge, std::move(intersection)); + incoming_edge.node, incoming_edge.edge, std::move(intersection)); // the entry class depends on the turn, so we have to classify the - // interesction for - // every edge - const auto turn_classification = classifyIntersection( - intersection, m_coordinates[node_at_center_of_intersection]); + // interesction for every edge + const auto turn_classification = + classifyIntersection(intersection, m_coordinates[intersection_node]); const auto entry_class_id = entry_class_hash.ConcurrentFindOrAdd(turn_classification.first); @@ -734,19 +749,37 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // Note - this is strictly speaking not thread safe, but we know we // should never be touching the same element twice, so we should // be fine. - bearing_class_by_node_based_node[node_at_center_of_intersection] = - bearing_class_id; + bearing_class_by_node_based_node[intersection_node] = bearing_class_id; // check if we are turning off a via way - const auto turning_off_via_way = way_restriction_map.IsViaWay( - node_along_road_entering, node_at_center_of_intersection); + const auto turning_off_via_way = + way_restriction_map.IsViaWay(incoming_edge.node, intersection_node); - for (const auto &turn : intersection) + // Save reversed incoming bearing to compute turn angles + const auto reversed_incoming_bearing = util::bearing::reverse( + findEdgeBearing(edge_geometries, incoming_edge.edge)); + + for (const auto &outgoing_edge : outgoing_edges) { - // only keep valid turns - if (!turn.entry_allowed) + if (!intersection::isTurnAllowed(m_node_based_graph, + m_edge_based_node_container, + node_restriction_map, + m_barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edge)) continue; + const auto turn = + std::find_if(intersection.begin(), + intersection.end(), + [edge = outgoing_edge.edge](const auto &road) { + return road.eid == edge; + }); + OSRM_ASSERT(turn != intersection.end(), + m_coordinates[intersection_node]); + // In case a way restriction starts at a given location, add a turn onto // every artificial node eminating here. // @@ -768,22 +801,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // duplicated node associated with the turn. (e.g. ab via bc switches bc // to bc_dup) auto const target_id = way_restriction_map.RemapIfRestricted( - nbe_to_ebn_mapping[turn.eid], - node_along_road_entering, - node_at_center_of_intersection, - m_node_based_graph.GetTarget(turn.eid), + nbe_to_ebn_mapping[outgoing_edge.edge], + incoming_edge.node, + outgoing_edge.node, + m_node_based_graph.GetTarget(outgoing_edge.edge), m_number_of_edge_based_nodes); { // scope to forget edge_with_data after const auto edge_with_data_and_condition = - generate_edge(nbe_to_ebn_mapping[incoming_edge], + generate_edge(nbe_to_ebn_mapping[incoming_edge.edge], target_id, - node_along_road_entering, - incoming_edge, - node_at_center_of_intersection, - turn.eid, - intersection, - turn, + incoming_edge.node, + incoming_edge.edge, + outgoing_edge.node, + outgoing_edge.edge, + reversed_incoming_bearing, + *turn, entry_class_id); buffer->continuous_data.edges_list.push_back( @@ -810,7 +843,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( if (turning_off_via_way) { const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs( - node_along_road_entering, node_at_center_of_intersection); + incoming_edge.node, intersection_node); // next to the normal restrictions tracked in `entry_allowed`, via // ways might introduce additional restrictions. These are handled @@ -818,12 +851,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( for (auto duplicated_node_id : duplicated_nodes) { const auto from_id = - m_number_of_edge_based_nodes - - way_restriction_map.NumberOfDuplicatedNodes() + - duplicated_node_id; + NodeID(m_number_of_edge_based_nodes - + way_restriction_map.NumberOfDuplicatedNodes() + + duplicated_node_id); auto const node_at_end_of_turn = - m_node_based_graph.GetTarget(turn.eid); + m_node_based_graph.GetTarget(outgoing_edge.edge); const auto is_way_restricted = way_restriction_map.IsRestricted( duplicated_node_id, node_at_end_of_turn); @@ -838,14 +871,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // add into delayed data auto edge_with_data_and_condition = - generate_edge(NodeID(from_id), - nbe_to_ebn_mapping[turn.eid], - node_along_road_entering, - incoming_edge, - node_at_center_of_intersection, - turn.eid, - intersection, - turn, + generate_edge(from_id, + nbe_to_ebn_mapping[outgoing_edge.edge], + incoming_edge.node, + incoming_edge.edge, + outgoing_edge.node, + outgoing_edge.edge, + reversed_incoming_bearing, + *turn, entry_class_id); buffer->delayed_data.push_back( @@ -862,24 +895,24 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( { // add a new conditional for the edge we just created buffer->conditionals.push_back( - {NodeID(from_id), - nbe_to_ebn_mapping[turn.eid], + {from_id, + nbe_to_ebn_mapping[outgoing_edge.edge], {static_cast(-1), - m_coordinates[node_at_center_of_intersection], + m_coordinates[intersection_node], restriction.condition}}); } } else { auto edge_with_data_and_condition = - generate_edge(NodeID(from_id), - nbe_to_ebn_mapping[turn.eid], - node_along_road_entering, - incoming_edge, - node_at_center_of_intersection, - turn.eid, - intersection, - turn, + generate_edge(from_id, + nbe_to_ebn_mapping[outgoing_edge.edge], + incoming_edge.node, + incoming_edge.edge, + outgoing_edge.node, + outgoing_edge.edge, + reversed_incoming_bearing, + *turn, entry_class_id); buffer->delayed_data.push_back( diff --git a/src/extractor/guidance/driveway_handler.cpp b/src/extractor/guidance/driveway_handler.cpp index 3b1f0bac1d7..75d0642560f 100644 --- a/src/extractor/guidance/driveway_handler.cpp +++ b/src/extractor/guidance/driveway_handler.cpp @@ -12,18 +12,24 @@ namespace extractor namespace guidance { -DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, +DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table) { } @@ -64,12 +70,12 @@ operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersect }); (void)nid; - OSRM_ASSERT(road != intersection.end(), coordinates[nid]); + OSRM_ASSERT(road != intersection.end(), node_coordinates[nid]); if (road->instruction == TurnInstruction::INVALID()) return intersection; - OSRM_ASSERT(road->instruction.type == TurnType::Turn, coordinates[nid]); + OSRM_ASSERT(road->instruction.type == TurnType::Turn, node_coordinates[nid]); road->instruction.type = isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName; diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp deleted file mode 100644 index d1d6e207506..00000000000 --- a/src/extractor/guidance/intersection_generator.cpp +++ /dev/null @@ -1,488 +0,0 @@ -#include "extractor/guidance/intersection_generator.hpp" - -#include "extractor/geojson_debug_policies.hpp" - -#include "util/geojson_debug_logger.hpp" - -#include "util/assert.hpp" -#include "util/bearing.hpp" -#include "util/coordinate_calculation.hpp" -#include "util/log.hpp" - -#include -#include -#include // mem_fn -#include -#include -#include - -#include - -namespace osrm -{ -namespace extractor -{ -namespace guidance -{ -namespace -{ -const constexpr bool USE_LOW_PRECISION_MODE = true; -// the inverse of use low precision mode -const constexpr bool USE_HIGH_PRECISION_MODE = !USE_LOW_PRECISION_MODE; -} - -IntersectionGenerator::IntersectionGenerator( - const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const RestrictionMap &restriction_map, - const std::unordered_set &barrier_nodes, - const std::vector &coordinates, - const CompressedEdgeContainer &compressed_edge_container) - : node_based_graph(node_based_graph), node_data_container(node_data_container), - restriction_map(restriction_map), barrier_nodes(barrier_nodes), coordinates(coordinates), - coordinate_extractor(node_based_graph, compressed_edge_container, coordinates) -{ -} - -IntersectionView IntersectionGenerator::operator()(const NodeID from_node, - const EdgeID via_eid) const -{ - return GetConnectedRoads(from_node, via_eid, USE_HIGH_PRECISION_MODE); -} - -IntersectionShape -IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_intersection, - const boost::optional sorting_base, - const bool use_low_precision_angles) const -{ - const auto intersection_degree = node_based_graph.GetOutDegree(node_at_center_of_intersection); - const util::Coordinate turn_coordinate = coordinates[node_at_center_of_intersection]; - - // compute bearings in a relatively small circle to prevent wrong roads order with true bearings - struct RoadWithInitialBearing - { - double bearing; - IntersectionShapeData road; - }; - std::vector initial_roads_ordering; - // reserve enough items (+ the possibly missing u-turn edge) - initial_roads_ordering.reserve(intersection_degree); - - // number of lanes at the intersection changes how far we look down the road - const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection); - const auto max_lanes_intersection = - std::accumulate(edge_range.begin(), - edge_range.end(), - std::uint8_t{0}, - [this](const auto current_max, const auto current_eid) { - return std::max(current_max, - node_based_graph.GetEdgeData(current_eid) - .flags.road_classification.GetNumberOfLanes()); - }); - - for (const EdgeID edge_connected_to_intersection : - node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection)) - { - BOOST_ASSERT(edge_connected_to_intersection != SPECIAL_EDGEID); - const NodeID to_node = node_based_graph.GetTarget(edge_connected_to_intersection); - double bearing = 0.; - - auto coordinates = coordinate_extractor.GetCoordinatesAlongRoad( - node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node); - - const auto close_coordinate = - coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, coordinates); - const auto initial_bearing = - util::coordinate_calculation::bearing(turn_coordinate, close_coordinate); - - const auto segment_length = util::coordinate_calculation::getLength( - coordinates.begin(), - coordinates.end(), - util::coordinate_calculation::haversineDistance); - - const auto extract_coordinate = [&](const NodeID from_node, - const EdgeID via_eid, - const bool traversed_in_reverse, - const NodeID to_node) { - return (use_low_precision_angles || intersection_degree <= 2) - ? coordinate_extractor.GetCoordinateCloseToTurn( - from_node, via_eid, traversed_in_reverse, to_node) - : coordinate_extractor.ExtractRepresentativeCoordinate( - from_node, - via_eid, - traversed_in_reverse, - to_node, - max_lanes_intersection, - std::move(coordinates)); - }; - - // we have to look down the road a bit to get the correct turn - const auto coordinate_along_edge_leaving = extract_coordinate( - node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node); - - bearing = - util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving); - - // OSM data sometimes contains duplicated nodes with identical coordinates, or - // because of coordinate precision rounding, end up at the same coordinate. - // It's impossible to calculate a bearing between these, so we log a warning - // that the data should be checked. - // The bearing calculation should return 0 in these cases, which may not be correct, - // but is at least not random. - if (turn_coordinate == coordinate_along_edge_leaving) - { - util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving - << " could cause invalid intersection exit bearing."; - BOOST_ASSERT(std::abs(bearing) <= 0.1); - } - - initial_roads_ordering.push_back( - {initial_bearing, {edge_connected_to_intersection, bearing, segment_length}}); - } - - if (!initial_roads_ordering.empty()) - { - const auto base_initial_bearing = [&]() { - if (sorting_base) - { - const auto itr = std::find_if(initial_roads_ordering.begin(), - initial_roads_ordering.end(), - [&](const auto &data) { - return node_based_graph.GetTarget( - data.road.eid) == *sorting_base; - }); - if (itr != initial_roads_ordering.end()) - return util::bearing::reverse(itr->bearing); - } - return util::bearing::reverse(initial_roads_ordering.begin()->bearing); - }(); - - // sort roads with respect to the initial bearings, a tie-breaker for equal initial bearings - // is to order roads via final bearings to have roads in clockwise order - // - // rhs <---. lhs <----. - // / / - // lhs / rhs / - // - // lhs road is before rhs one rhs road is before lhs one - // bearing::angleBetween < 180 bearing::angleBetween > 180 - const auto initial_bearing_order = makeCompareShapeDataAngleToBearing(base_initial_bearing); - std::sort(initial_roads_ordering.begin(), - initial_roads_ordering.end(), - [&initial_bearing_order](const auto &lhs, const auto &rhs) { - return initial_bearing_order(lhs, rhs) || - (lhs.bearing == rhs.bearing && - util::bearing::angleBetween(lhs.road.bearing, rhs.road.bearing) < - 180); - }); - - // copy intersection data in the initial order - IntersectionShape intersection; - intersection.reserve(initial_roads_ordering.size()); - std::transform(initial_roads_ordering.begin(), - initial_roads_ordering.end(), - std::back_inserter(intersection), - [](const auto &entry) { return entry.road; }); - - if (intersection.size() > 2) - { // Check bearings ordering with respect to true bearings - const auto base_bearing = intersection.front().bearing; - const auto bearings_order = - makeCompareShapeDataAngleToBearing(util::bearing::reverse(base_bearing)); - for (auto curr = intersection.begin(), next = std::next(curr); - next != intersection.end(); - ++curr, ++next) - { - if (bearings_order(*next, *curr)) - { // If the true bearing is out of the initial order (next before current) then - // adjust the next bearing to keep the order. The adjustment angle is at most - // 0.5° or a half-angle between the current bearing and the base bearing. - // to prevent overlapping over base bearing + 360°. - const auto angle_adjustment = std::min( - .5, util::restrictAngleToValidRange(base_bearing - curr->bearing) / 2.); - next->bearing = - util::restrictAngleToValidRange(curr->bearing + angle_adjustment); - } - } - } - return intersection; - } - - return IntersectionShape{}; -} - -// a -// | -// | -// v -// For an intersection from_node --via_eid--> turn_node ----> c -// ^ -// | -// | -// b -// This functions returns _all_ turns as if the graph was undirected. -// That means we not only get (from_node, turn_node, c) in the above example -// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are -// marked as invalid and only needed for intersection classification. -IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node, - const EdgeID via_eid, - const bool use_low_precision_angles) const -{ - // make sure the via-eid is valid - BOOST_ASSERT([this](const NodeID from_node, const EdgeID via_eid) { - const auto range = node_based_graph.GetAdjacentEdgeRange(from_node); - return range.front() <= via_eid && via_eid <= range.back(); - }(from_node, via_eid)); - - auto intersection = ComputeIntersectionShape( - node_based_graph.GetTarget(via_eid), boost::none, use_low_precision_angles); - return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection)); -} - -IntersectionGenerationParameters -IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const -{ - NodeID query_node = starting_node; - EdgeID query_edge = via_edge; - - const auto get_next_edge = [this](const NodeID from, const EdgeID via) { - const NodeID new_node = node_based_graph.GetTarget(via); - BOOST_ASSERT(node_based_graph.GetOutDegree(new_node) == 2); - const EdgeID begin_edges_new_node = node_based_graph.BeginEdges(new_node); - return (node_based_graph.GetTarget(begin_edges_new_node) == from) ? begin_edges_new_node + 1 - : begin_edges_new_node; - }; - - std::unordered_set visited_nodes; - // skip trivial nodes without generating the intersection in between, stop at the very first - // intersection of degree > 2 - while (0 == visited_nodes.count(query_node) && - 2 == node_based_graph.GetOutDegree(node_based_graph.GetTarget(query_edge))) - { - visited_nodes.insert(query_node); - const auto next_node = node_based_graph.GetTarget(query_edge); - const auto next_edge = get_next_edge(query_node, query_edge); - - query_node = next_node; - query_edge = next_edge; - - // check if there is a relevant change in the graph - if (!CanBeCompressed(node_based_graph.GetEdgeData(query_edge), - node_based_graph.GetEdgeData(next_edge), - node_data_container) || - (node_based_graph.GetTarget(next_edge) == starting_node)) - break; - } - - return {query_node, query_edge}; -} - -IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView( - const NodeID previous_node, - const EdgeID entering_via_edge, - const IntersectionShape &intersection_shape) const -{ - // requires a copy of the intersection - return TransformIntersectionShapeIntoView(previous_node, - entering_via_edge, - intersection_shape, // creates a copy - intersection_shape, // reference to local - {}); // empty vector of performed merges -} - -IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView( - const NodeID previous_node, - const EdgeID entering_via_edge, - const IntersectionShape &normalized_intersection, - const IntersectionShape &intersection, - const std::vector &performed_merges) const -{ - const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge); - - // request all turn restrictions - auto const restrictions = restriction_map.Restrictions(previous_node, node_at_intersection); - - // check turn restrictions to find a node that is the only allowed target when coming from a - // node to an intersection - // d - // | - // a - b - c and `only_straight_on ab | bc would return `c` for `a,b` - const auto find_only_valid_turn = [&]() -> boost::optional { - const auto itr = std::find_if(restrictions.first, restrictions.second, [](auto pair) { - return pair.second->is_only; - }); - if (itr != restrictions.second) - return itr->second->AsNodeRestriction().to; - else - return boost::none; - }; - - const auto only_valid_turn = find_only_valid_turn(); - - // barriers change our behaviour regarding u-turns - const bool is_barrier_node = barrier_nodes.find(node_at_intersection) != barrier_nodes.end(); - - const auto connect_to_previous_node = [this, previous_node](const IntersectionShapeData road) { - return node_based_graph.GetTarget(road.eid) == previous_node; - }; - - // check which of the edges is the u-turn edge - const auto uturn_edge_itr = - std::find_if(intersection.begin(), intersection.end(), connect_to_previous_node); - - // there needs to be a connection, otherwise stuff went seriously wrong. Note that this is not - // necessarily the same id as `entering_via_edge`. - // In cases where parallel edges are present, we only remember the minimal edge. Both share - // exactly the same coordinates, so the u-turn is still the best choice here. - BOOST_ASSERT(uturn_edge_itr != intersection.end()); - - const auto is_restricted = [&](const NodeID destination) { - // check if we have a dedicated destination - if (only_valid_turn) - return *only_valid_turn != destination; - - // check if explicitly forbidden - return restrictions.second != - std::find_if(restrictions.first, restrictions.second, [&](const auto &restriction) { - return restriction.second->AsNodeRestriction().to == destination; - }); - }; - - const auto is_allowed_turn = [&](const IntersectionShapeData &road) { - const auto &road_data = node_based_graph.GetEdgeData(road.eid); - const NodeID road_destination_node = node_based_graph.GetTarget(road.eid); - // reverse edges are never valid turns because the resulting turn would look like this: - // from_node --via_edge--> node_at_intersection <--onto_edge-- to_node - // however we need this for capture intersection shape for incoming one-ways - return !road_data.reversed && - // we are not turning over a barrier - (!is_barrier_node || road_destination_node == previous_node) && - // don't allow restricted turns - !is_restricted(road_destination_node); - - }; - - // due to merging of roads, the u-turn might actually not be part of the intersection anymore - // uturn is a pair of {edge id, bearing} - const auto uturn = [&]() { - const auto merge_entry = std::find_if( - performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) { - return entry.merged_eid == uturn_edge_itr->eid; - }); - if (merge_entry != performed_merges.end()) - { - const auto merged_into_id = merge_entry->into_eid; - const auto merged_u_turn = std::find_if( - normalized_intersection.begin(), - normalized_intersection.end(), - [&](const IntersectionShapeData &road) { return road.eid == merged_into_id; }); - BOOST_ASSERT(merged_u_turn != normalized_intersection.end()); - return std::make_pair(merged_u_turn->eid, - util::bearing::reverse(merged_u_turn->bearing)); - } - else - { - const auto uturn_edge_at_normalized_intersection_itr = - std::find_if(normalized_intersection.begin(), - normalized_intersection.end(), - connect_to_previous_node); - BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr != - normalized_intersection.end()); - return std::make_pair( - uturn_edge_at_normalized_intersection_itr->eid, - util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing)); - } - }(); - - IntersectionView intersection_view; - intersection_view.reserve(normalized_intersection.size()); - std::transform(normalized_intersection.begin(), - normalized_intersection.end(), - std::back_inserter(intersection_view), - [&](const IntersectionShapeData &road) { - return IntersectionViewData( - road, - is_allowed_turn(road), - util::bearing::angleBetween(uturn.second, road.bearing)); - }); - - const auto uturn_edge_at_intersection_view_itr = - std::find_if(intersection_view.begin(), intersection_view.end(), connect_to_previous_node); - // number of found valid exit roads - const auto valid_count = - std::count_if(intersection_view.begin(), - intersection_view.end(), - [](const IntersectionViewData &road) { return road.entry_allowed; }); - // in general, we don't wan't to allow u-turns. If we don't look at a barrier, we have to check - // for dead end streets. These are the only ones that we allow uturns for, next to barriers - // (which are also kind of a dead end, but we don't have to check these again :)) - if (uturn_edge_at_intersection_view_itr != intersection_view.end() && - ((uturn_edge_at_intersection_view_itr->entry_allowed && !is_barrier_node && - valid_count != 1) || - valid_count == 0)) - { - const auto allow_uturn_at_dead_end = [&]() { - const auto &uturn_data = node_based_graph.GetEdgeData(uturn_edge_itr->eid); - - // we can't turn back onto oneway streets - if (uturn_data.reversed) - return false; - - // don't allow explicitly restricted turns - if (is_restricted(previous_node)) - return false; - - // we define dead ends as roads that can only be entered via the possible u-turn - const auto is_bidirectional = [&](const EdgeID entering_via_edge) { - const auto to_node = node_based_graph.GetTarget(entering_via_edge); - const auto reverse_edge = node_based_graph.FindEdge(to_node, node_at_intersection); - BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID); - return !node_based_graph.GetEdgeData(reverse_edge).reversed; - }; - - const auto bidirectional_edges = [&]() { - std::uint32_t count = 0; - for (const auto eid : node_based_graph.GetAdjacentEdgeRange(node_at_intersection)) - if (is_bidirectional(eid)) - ++count; - return count; - }(); - - // Checking for dead-end streets is kind of difficult. There is obvious dead ends - // (single road connected) - return bidirectional_edges <= 1; - }(); - uturn_edge_at_intersection_view_itr->entry_allowed = allow_uturn_at_dead_end; - } - std::sort(std::begin(intersection_view), - std::end(intersection_view), - std::mem_fn(&IntersectionViewData::CompareByAngle)); - - // Move entering_via_edge to intersection front and place all roads prior entering_via_edge - // at the end of the intersection view with 360° angle - auto entering_via_it = std::find_if(intersection_view.begin(), - intersection_view.end(), - [&uturn](auto &road) { return road.eid == uturn.first; }); - - OSRM_ASSERT(entering_via_it != intersection_view.end() && entering_via_it->angle >= 0. && - entering_via_it->angle < std::numeric_limits::epsilon(), - coordinates[node_at_intersection]); - - if (entering_via_it != intersection_view.begin() && entering_via_it != intersection_view.end()) - { - std::for_each( - intersection_view.begin(), entering_via_it, [](auto &road) { road.angle = 360.; }); - std::rotate(intersection_view.begin(), entering_via_it, intersection_view.end()); - } - - return intersection_view; -} - -const CoordinateExtractor &IntersectionGenerator::GetCoordinateExtractor() const -{ - return coordinate_extractor; -} - -} // namespace guidance -} // namespace extractor -} // namespace osrm diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp index 756ee321798..037375a58e0 100644 --- a/src/extractor/guidance/intersection_handler.cpp +++ b/src/extractor/guidance/intersection_handler.cpp @@ -1,5 +1,6 @@ #include "extractor/guidance/intersection_handler.hpp" #include "extractor/guidance/constants.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "util/coordinate_calculation.hpp" #include "util/guidance/name_announcements.hpp" @@ -45,17 +46,27 @@ inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_g } } // namespace detail -IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator) +IntersectionHandler::IntersectionHandler( + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const util::NameTable &name_table, + const SuffixTable &street_name_suffix_table) : node_based_graph(node_based_graph), node_data_container(node_data_container), - coordinates(coordinates), name_table(name_table), - street_name_suffix_table(street_name_suffix_table), - intersection_generator(intersection_generator), - graph_walker(node_based_graph, node_data_container, intersection_generator) + node_coordinates(node_coordinates), compressed_geometries(compressed_geometries), + node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes), + turn_lanes_data(turn_lanes_data), name_table(name_table), + street_name_suffix_table(street_name_suffix_table), graph_walker(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data) { } @@ -162,8 +173,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t // or actually follow the full road. When 2399 lands, we can exchange here for a // precalculated distance value. const auto distance = util::coordinate_calculation::haversineDistance( - coordinates[node_based_graph.GetTarget(via_edge)], - coordinates[node_based_graph.GetTarget(road.eid)]); + node_coordinates[node_based_graph.GetTarget(via_edge)], + node_coordinates[node_based_graph.GetTarget(road.eid)]); return {TurnType::Turn, (angularDeviation(road.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && @@ -462,17 +473,24 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons // Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c` // writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target. - const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via); + const auto intersection_parameters = + intersection::skipDegreeTwoNodes(node_based_graph, {at, via}); // This should never happen, guard against nevertheless - if (intersection_parameters.nid == SPECIAL_NODEID || - intersection_parameters.via_eid == SPECIAL_EDGEID) + if (intersection_parameters.node == SPECIAL_NODEID || + intersection_parameters.edge == SPECIAL_EDGEID) { return boost::none; } - auto intersection = - intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid); - auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid); + auto intersection = intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + intersection_parameters); + auto intersection_node = node_based_graph.GetTarget(intersection_parameters.edge); if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier()) { diff --git a/src/extractor/guidance/intersection_normalizer.cpp b/src/extractor/guidance/intersection_normalizer.cpp deleted file mode 100644 index ab7ef07476a..00000000000 --- a/src/extractor/guidance/intersection_normalizer.cpp +++ /dev/null @@ -1,430 +0,0 @@ -#include "extractor/guidance/intersection_normalizer.hpp" -#include "util/bearing.hpp" -#include "util/coordinate_calculation.hpp" - -#include -#include - -using osrm::util::angularDeviation; - -namespace osrm -{ -namespace extractor -{ -namespace guidance -{ - -IntersectionNormalizer::IntersectionNormalizer( - const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator) - : node_based_graph(node_based_graph), intersection_generator(intersection_generator), - mergable_road_detector(node_based_graph, - node_data_container, - coordinates, - intersection_generator, - intersection_generator.GetCoordinateExtractor(), - name_table, - street_name_suffix_table) -{ -} - -IntersectionNormalizer::NormalizationResult IntersectionNormalizer:: -operator()(const NodeID node_at_intersection, IntersectionShape intersection) const -{ - const auto intersection_copy = intersection; - auto merged_shape_and_merges = - MergeSegregatedRoads(node_at_intersection, std::move(intersection)); - merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination( - node_at_intersection, std::move(merged_shape_and_merges.normalized_shape)); - return merged_shape_and_merges; -} - -bool IntersectionNormalizer::CanMerge(const NodeID intersection_node, - const IntersectionShape &intersection, - std::size_t fist_index_in_ccw, - std::size_t second_index_in_ccw) const -{ - BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw); - - // don't merge on degree two, since it's most likely a bollard/traffic light or a round way - if (intersection.size() <= 2) - return false; - - const auto can_merge = mergable_road_detector.CanMergeRoad( - intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]); - - /* - * Merging should never depend on order/never merge more than two roads. To ensure that we don't - * merge anything that is impacted by neighboring roads (e.g. three roads of the same name as in - * parking lots/border checkpoints), we check if the neigboring roads would be merged as well. - * In that case, we cannot merge, since we would end up merging multiple items together - */ - const auto is_distinct = [&]() { - const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size(); - const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom( - intersection[second_index_in_ccw], intersection[next_index_in_ccw]); - const auto prev_index_in_ccw = - (fist_index_in_ccw + intersection.size() - 1) % intersection.size(); - const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom( - intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]); - return distinct_to_next_in_ccw && distinct_to_prev_in_ccw; - }; - - // use lazy evaluation to check only if mergable - return can_merge && is_distinct(); -} - -IntersectionNormalizationOperation -IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs) const -{ - if (node_based_graph.GetEdgeData(lhs.eid).reversed) - return {lhs.eid, rhs.eid}; - else - return {rhs.eid, lhs.eid}; -} - -IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into, - const IntersectionShapeData &from) const -{ - // we only merge small angles. If the difference between both is large, we are looking at a - // bearing leading north. Such a bearing cannot be handled via the basic average. In this - // case we actually need to shift the bearing by half the difference. - const auto aroundZero = [](const double first, const double second) { - return (std::max(first, second) - std::min(first, second)) >= 180; - }; - - // find the angle between two other angles - const auto combineAngles = [aroundZero](const double first, const double second) { - if (!aroundZero(first, second)) - return .5 * (first + second); - else - { - const auto offset = angularDeviation(first, second); - auto new_angle = std::max(first, second) + .5 * offset; - if (new_angle >= 360) - return new_angle - 360; - return new_angle; - } - }; - - auto result = into; - BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed); - result.bearing = combineAngles(into.bearing, from.bearing); - BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0); - return result; -} - -IntersectionShapeData -IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction, - const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs, - const double opposite_bearing) const -{ - // In some intersections, turning roads can introduce artificial turns if we merge here. - // Consider a scenario like: - //  - // a . g - f - // | . - // | . - // |. - // d-b--------e - // | - // c - //  - // Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight - // We don't change the angle, if such an opposite road exists - if (direction.merged_eid == lhs.eid) - { - // change the angle only if the opposite direction is not nearly straight - if (angularDeviation(opposite_bearing, rhs.bearing) > - (STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION)) - return rhs; - else - return MergeRoads(rhs, lhs); - } - else - { - if (angularDeviation(opposite_bearing, lhs.bearing) > - (STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION)) - return lhs; - else - return MergeRoads(lhs, rhs); - } -} - -/* - * Segregated Roads often merge onto a single intersection. - * While technically representing different roads, they are - * often looked at as a single road. - * Due to the merging, turn Angles seem off, wenn we compute them from the - * initial positions. - * - * bb>b>b(2)>b>b>b - * - * Would be seen as a slight turn going fro a to (2). A Sharp turn going from - * (1) to (2). - * - * In cases like these, we megre this segregated roads into a single road to - * end up with a case like: - * - * aaaaa-bbbbbb - * - * for the turn representation. - * Anything containing the first u-turn in a merge affects all other angles - * and is handled separately from all others. - */ -IntersectionNormalizer::NormalizationResult -IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node, - IntersectionShape intersection) const -{ - const auto getRight = [&](std::size_t index) { - return (index + intersection.size() - 1) % intersection.size(); - }; - - // This map stores for all edges that participated in a merging operation in which edge id they - // end up in the end. We only store what we have merged into other edges. - std::vector merging_map; - const auto merge = [this, &merging_map](const IntersectionShapeData &first, - const IntersectionShapeData &second, - const double opposite_bearing) { - - const auto direction = DetermineMergeDirection(first, second); - BOOST_ASSERT( - std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) { - return pair.merged_eid == direction.merged_eid; - }) == merging_map.end()); - merging_map.push_back(direction); - return MergeRoads(direction, first, second, opposite_bearing); - }; - - if (intersection.size() <= 1) - return {intersection, merging_map}; - - const auto intersection_copy = intersection; - const auto opposite_bearing = [this, intersection_copy](const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs) { - if (node_based_graph.GetEdgeData(lhs.eid).reversed) - { - return intersection_copy.FindClosestBearing(util::bearing::reverse(rhs.bearing)) - ->bearing; - } - else - { - BOOST_ASSERT(node_based_graph.GetEdgeData(rhs.eid).reversed); - return intersection_copy.FindClosestBearing(util::bearing::reverse(lhs.bearing)) - ->bearing; - } - }; - // check for merges including the basic u-turn - // these result in an adjustment of all other angles. This is due to how these angles are - // perceived. Considering the following example: - // - // c b - // Y - // a - // - // coming from a to b (given a road that splits at the fork into two one-ways), the turn is not - // considered as a turn but rather as going straight. - // Now if we look at the situation merging: - // - // a b - // \ / - // e - + - d - // | - // c - // - // With a,b representing the same road, the intersection itself represents a classif for way - // intersection so we handle it like - // - // (a),b - // | - // e - + - d - // | - // c - // - // To be able to consider this adjusted representation down the line, we merge some roads. - // If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are - // with respect to the now changed perceived location of a. If we move (a) to the left, we add - // the difference to all angles. Otherwise we subtract it. - // these result in an adjustment of all other angles - if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0)) - { - // moving `a` to the left - const auto opposite = opposite_bearing(intersection.front(), intersection.back()); - intersection[0] = merge(intersection.front(), intersection.back(), opposite); - // FIXME if we have a left-sided country, we need to switch this off and enable it - // below - intersection.pop_back(); - } - else if (CanMerge(intersection_node, intersection, 0, 1)) - { - const auto opposite = opposite_bearing(intersection.front(), intersection[1]); - intersection[0] = merge(intersection.front(), intersection[1], opposite); - intersection.erase(intersection.begin() + 1); - } - - // a merge including the first u-turn requires an adjustment of the turn angles - // therefore these are handled prior to this step - for (std::size_t index = 2; index < intersection.size(); ++index) - { - if (CanMerge(intersection_node, intersection, getRight(index), index)) - { - const auto opposite = - opposite_bearing(intersection[getRight(index)], intersection[index]); - intersection[getRight(index)] = - merge(intersection[getRight(index)], intersection[index], opposite); - intersection.erase(intersection.begin() + index); - --index; - } - } - return {intersection, merging_map}; -} - -// OSM can have some very steep angles for joining roads. Considering the following intersection: -// x -// | -// v __________c -// / -// a ---d -// \ __________b -// -// with c->d as a oneway -// and d->b as a oneway, the turn von x->d is actually a turn from x->a. So when looking at the -// intersection coming from x, we want to interpret the situation as -// x -// | -// a __ d __ v__________c -// | -// |_______________b -// -// Where we see the turn to `d` as a right turn, rather than going straight. -// We do this by adjusting the local turn angle at `x` to turn onto `d` to be reflective of this -// situation, where `v` would be the node at the intersection. -IntersectionShape -IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection, - IntersectionShape intersection) const -{ - // nothing to do for dead ends - if (intersection.size() <= 1) - return intersection; - - // we don't adjust any road that is longer than 30 meters (between centers of intersections), - // since the road is probably too long otherwise to impact perception. - const double constexpr PRUNING_DISTANCE = 30; - // never adjust u-turns - for (std::size_t index = 0; index < intersection.size(); ++index) - { - auto &road = intersection[index]; - // only consider roads that are close - if (road.segment_length > PRUNING_DISTANCE) - continue; - - // to find out about the above situation, we need to look at the next intersection (at d in - // the example). If the initial road can be merged to the left/right, we are about to adjust - // the angle. - const auto next_intersection_along_road = intersection_generator.ComputeIntersectionShape( - node_based_graph.GetTarget(road.eid), node_at_intersection); - - if (next_intersection_along_road.size() <= 1) - continue; - - const auto node_at_next_intersection = node_based_graph.GetTarget(road.eid); - - const auto adjustAngle = [](double angle, double offset) { - angle += offset; - if (angle > 360) - return angle - 360.; - else if (angle < 0) - return angle + 360.; - return angle; - }; - - const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection); - if (range.size() <= 1) - continue; - - // the order does not matter - const auto get_offset = [](const IntersectionShapeData &lhs, - const IntersectionShapeData &rhs) { - return 0.5 * angularDeviation(lhs.bearing, rhs.bearing); - }; - - // When offsetting angles in our turns, we don't want to get past the next turn. This - // function simply limits an offset to be at most half the distance to the next turn in the - // offfset direction - const auto get_corrected_offset = []( - const double offset, - const IntersectionShapeData &road, - const IntersectionShapeData &next_road_in_offset_direction) { - const auto offset_limit = - angularDeviation(road.bearing, next_road_in_offset_direction.bearing); - // limit the offset with an additional buffer - return (offset + MAXIMAL_ALLOWED_NO_TURN_DEVIATION > offset_limit) ? 0.5 * offset_limit - : offset; - }; - - // only if straighmost angles get smaller, we consider it an improvement - auto const improves_straightmost = [&](auto const index, auto const offset) { - const auto itr = next_intersection_along_road.FindClosestBearing( - util::bearing::reverse(next_intersection_along_road[index].bearing)); - const auto angle = util::bearing::angleBetween( - util::bearing::reverse(itr->bearing), next_intersection_along_road[index].bearing); - - return util::angularDeviation(angle, STRAIGHT_ANGLE) > - util::angularDeviation(angle + offset, STRAIGHT_ANGLE); - }; - - // check if the u-turn edge at the next intersection could be merged to the left/right. If - // this is the case and the road is not far away (see previous distance check), if - // influences the perceived angle. - if (CanMerge(node_at_next_intersection, next_intersection_along_road, 0, 1)) - { - const auto offset = - get_offset(next_intersection_along_road[0], next_intersection_along_road[1]); - - if (improves_straightmost(0, -offset) && improves_straightmost(1, offset)) - { - const auto corrected_offset = get_corrected_offset( - offset, - road, - intersection[(intersection.size() + index - 1) % intersection.size()]); - // at the target intersection, we merge to the right, so we need to shift the - // current - // angle to the left - road.bearing = adjustAngle(road.bearing, corrected_offset); - } - } - else if (CanMerge(node_at_next_intersection, - next_intersection_along_road, - next_intersection_along_road.size() - 1, - 0)) - { - const auto offset = - get_offset(next_intersection_along_road[0], - next_intersection_along_road[next_intersection_along_road.size() - 1]); - - if (improves_straightmost(0, offset) && - improves_straightmost(next_intersection_along_road.size() - 1, -offset)) - { - const auto corrected_offset = get_corrected_offset( - offset, road, intersection[(index + 1) % intersection.size()]); - // at the target intersection, we merge to the left, so we need to shift the current - // angle to the right - road.bearing = adjustAngle(road.bearing, -corrected_offset); - } - } - } - - return intersection; -} - -} // namespace guidance -} // namespace extractor -} // namespace osrm diff --git a/src/extractor/guidance/mergable_road_detector.cpp b/src/extractor/guidance/mergable_road_detector.cpp index 4773d631d6e..9bb31f70837 100644 --- a/src/extractor/guidance/mergable_road_detector.cpp +++ b/src/extractor/guidance/mergable_road_detector.cpp @@ -1,8 +1,7 @@ #include "extractor/guidance/mergable_road_detector.hpp" #include "extractor/guidance/constants.hpp" -#include "extractor/guidance/coordinate_extractor.hpp" -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/node_based_graph_walker.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "extractor/query_node.hpp" #include "extractor/suffix_table.hpp" @@ -52,17 +51,22 @@ inline auto makeCheckRoadForName(const NameID name_id, } } -MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &node_coordinates, - const IntersectionGenerator &intersection_generator, - const CoordinateExtractor &coordinate_extractor, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table) +MergableRoadDetector::MergableRoadDetector( + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const util::NameTable &name_table, + const SuffixTable &street_name_suffix_table) : node_based_graph(node_based_graph), node_data_container(node_data_container), - node_coordinates(node_coordinates), intersection_generator(intersection_generator), - coordinate_extractor(coordinate_extractor), name_table(name_table), - street_name_suffix_table(street_name_suffix_table) + node_coordinates(node_coordinates), compressed_geometries(compressed_geometries), + node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes), + turn_lanes_data(turn_lanes_data), name_table(name_table), + street_name_suffix_table(street_name_suffix_table), + coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates) { } @@ -170,8 +174,9 @@ bool MergableRoadDetector::EdgeDataSupportsMerge( bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node, const MergableRoadData &road) const { - const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid); - return intersection_node == node_based_graph.GetTarget(connection.via_eid); + const auto connection = + intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid}); + return intersection_node == node_based_graph.GetTarget(connection.edge); } bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node, @@ -180,8 +185,22 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node, { // selection data to the right and left const auto constexpr SMALL_RANDOM_HOPLIMIT = 5; - IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator), - right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator); + IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, + node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data), + right_accumulator(SMALL_RANDOM_HOPLIMIT, + node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data); /* Standard following the straightmost road * Since both items have the same id, we can `select` based on any setup @@ -193,8 +212,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node, /*requires entry=*/false, false); - NodeBasedGraphWalker graph_walker( - node_based_graph, node_data_container, intersection_generator); + NodeBasedGraphWalker graph_walker(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data); graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector); /* if the intersection does not have a right turn, we continue onto the next one once * (skipping over a single small side street) @@ -266,7 +290,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node, // check if both intersections are connected IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT, - intersection_generator); + node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data); graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id), connector_turn->eid, connect_accumulator, @@ -281,8 +311,13 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node, const MergableRoadData &lhs, const MergableRoadData &rhs) const { - NodeBasedGraphWalker graph_walker( - node_based_graph, node_data_container, intersection_generator); + NodeBasedGraphWalker graph_walker(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data); const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) { LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length); SelectStraightmostRoadByNameAndOnlyChoice selector( @@ -348,8 +383,13 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node, return false; // Find a coordinate following a road that is far away - NodeBasedGraphWalker graph_walker( - node_based_graph, node_data_container, intersection_generator); + NodeBasedGraphWalker graph_walker(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data); const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) { LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length); SelectStraightmostRoadByNameAndOnlyChoice selector( @@ -419,10 +459,16 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node, return false; // compare reference distance: - const auto distance_between_roads = util::coordinate_calculation::findClosestDistance( + const auto distance_mid_left_to_right = util::coordinate_calculation::findClosestDistance( coordinates_to_the_left[coordinates_to_the_left.size() / 2], coordinates_to_the_right.begin(), coordinates_to_the_right.end()); + const auto distance_mid_right_to_left = util::coordinate_calculation::findClosestDistance( + coordinates_to_the_right[coordinates_to_the_right.size() / 2], + coordinates_to_the_left.begin(), + coordinates_to_the_left.end()); + const auto distance_between_roads = + std::min(distance_mid_left_to_right, distance_mid_right_to_left); const auto lane_count_lhs = std::max( 1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes()); @@ -443,12 +489,12 @@ bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node, * location with the same name repeatet at least three times */ const auto left_connection = - intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid); + intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, lhs.eid}); const auto right_connection = - intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid); + intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, rhs.eid}); - const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid); - const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid); + const auto left_candidate = node_based_graph.GetTarget(left_connection.edge); + const auto right_candidate = node_based_graph.GetTarget(right_connection.edge); const auto candidate_is_valid = left_candidate == right_candidate && left_candidate != intersection_node; @@ -516,9 +562,16 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node, const MergableRoadData &road) const { const auto next_intersection_parameters = - intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid); - const auto next_intersection_along_road = intersection_generator.GetConnectedRoads( - next_intersection_parameters.nid, next_intersection_parameters.via_eid); + intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid}); + const auto next_intersection_along_road = + intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + next_intersection_parameters); const auto extract_name_id = [this](const MergableRoadData &road) { return node_data_container .GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data) @@ -543,7 +596,7 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node, // we cannot be looking at the same road we came from if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) == - next_intersection_parameters.nid) + next_intersection_parameters.node) return false; /* check if the opposite of the next road decision was sane. It could have been just as well our diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp index 4689286f1e8..c231c6e3388 100644 --- a/src/extractor/guidance/motorway_handler.cpp +++ b/src/extractor/guidance/motorway_handler.cpp @@ -2,6 +2,7 @@ #include "extractor/guidance/constants.hpp" #include "extractor/guidance/road_classification.hpp" +#include "util/assert.hpp" #include "util/bearing.hpp" #include "util/guidance/name_announcements.hpp" @@ -42,15 +43,21 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator) + const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table) { } @@ -271,17 +278,11 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in // handle motorway forks else if (exiting_motorways > 1) { - if (exiting_motorways == 2 && intersection.size() == 2) - { - intersection[1].instruction = - getInstructionForObvious(intersection.size(), - via_eid, - isThroughStreet(1, intersection), - intersection[1]); - intersection[0].entry_allowed = false; // UTURN on the freeway - } - else if (exiting_motorways == 2) + if (exiting_motorways == 2) { + OSRM_ASSERT(intersection.size() != 2, + node_coordinates[node_based_graph.GetTarget(via_eid)]); + // standard fork std::size_t first_valid = std::numeric_limits::max(), second_valid = std::numeric_limits::max(); diff --git a/src/extractor/guidance/node_based_graph_walker.cpp b/src/extractor/guidance/node_based_graph_walker.cpp index a84a0b01c0a..cc4d88f7b02 100644 --- a/src/extractor/guidance/node_based_graph_walker.cpp +++ b/src/extractor/guidance/node_based_graph_walker.cpp @@ -1,4 +1,5 @@ #include "extractor/guidance/node_based_graph_walker.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "util/bearing.hpp" #include "util/coordinate_calculation.hpp" @@ -14,11 +15,18 @@ namespace guidance { // --------------------------------------------------------------------------------- -NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const IntersectionGenerator &intersection_generator) +NodeBasedGraphWalker::NodeBasedGraphWalker( + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data) : node_based_graph(node_based_graph), node_data_container(node_data_container), - intersection_generator(intersection_generator) + node_coordinates(node_coordinates), compressed_geometries(compressed_geometries), + node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes), + turn_lanes_data(turn_lanes_data) { } @@ -235,8 +243,18 @@ operator()(const NodeID /*nid*/, // --------------------------------------------------------------------------------- IntersectionFinderAccumulator::IntersectionFinderAccumulator( - const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator) - : hops(0), hop_limit(hop_limit), intersection_generator(intersection_generator) + const std::uint8_t hop_limit, + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data) + : hops(0), hop_limit(hop_limit), node_based_graph(node_based_graph), + node_data_container(node_data_container), node_coordinates(node_coordinates), + compressed_geometries(compressed_geometries), node_restriction_map(node_restriction_map), + barrier_nodes(barrier_nodes), turn_lanes_data(turn_lanes_data) { } @@ -261,7 +279,14 @@ void IntersectionFinderAccumulator::update(const NodeID from_node, nid = from_node; via_edge_id = via_edge; - intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true); + intersection = intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {from_node, via_edge}); } } // namespace guidance diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp index 4bea638fe1f..4439436d565 100644 --- a/src/extractor/guidance/roundabout_handler.cpp +++ b/src/extractor/guidance/roundabout_handler.cpp @@ -23,21 +23,26 @@ namespace extractor namespace guidance { -RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, - const CompressedEdgeContainer &compressed_edge_container, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator) +RoundaboutHandler::RoundaboutHandler( + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const util::NameTable &name_table, + const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator), - compressed_edge_container(compressed_edge_container), - coordinate_extractor(node_based_graph, compressed_edge_container, coordinates) + street_name_suffix_table), + coordinate_extractor(node_based_graph, compressed_geometries, coordinates) { } @@ -56,7 +61,6 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid, Intersection RoundaboutHandler:: operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const { - invalidateExitAgainstDirection(from_nid, via_eid, intersection); const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection); const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid)); // find the radius of the roundabout @@ -107,77 +111,6 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags( return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately}; } -void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid, - const EdgeID via_eid, - Intersection &intersection) const -{ - const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags; - if (in_edge_class.roundabout || in_edge_class.circular) - return; - - // Find range in which exits that must be invalidated (shaded areas): - // exit..end exit..end begin..exit for ↺ roundabouts - // ************************************* - // * <--. ^ <--. / <--. * - // * | / | /░ | * - // * |/ |v░░ -->| * - // * |^ |\ ░ ░░░|\ * - // * |░\ |░\░ ░░░| \ * - // * --'░░░\ --'░░░v --' v * - // ************************************* - // - // begin..exit begin..exit exit..end for ↻ roundabouts - // ************************************* - // * --.░░░^ --.░░░/ --. ^ * - // * |░/░ |░/ ░░░| / * - // * |/░░ |v ░░░|/ * - // * |^░░ |\ -->| * - // * | \░ | \ | * - // * <--' \ <--' v <--' * - // ************************************* - bool roundabout_entry_first = false; - auto invalidate_from = intersection.end(), invalidate_to = intersection.end(); - for (auto road = intersection.begin(); road != intersection.end(); ++road) - { - const auto &edge = node_based_graph.GetEdgeData(road->eid); - if (edge.flags.roundabout || edge.flags.circular) - { - if (edge.reversed) - { - if (roundabout_entry_first) - { // invalidate turns in range exit..end - invalidate_from = road + 1; - invalidate_to = intersection.end(); - } - else - { // invalidate turns in range begin..exit - invalidate_from = intersection.begin() + 1; - invalidate_to = road; - } - } - else - { - roundabout_entry_first = true; - } - } - } - - OSRM_ASSERT(invalidate_from <= invalidate_to, coordinates[from_nid]); - - // Exiting roundabouts at an entry point is technically a data-modelling issue. - // This workaround handles cases in which an exit precedes and entry. The resulting - // u-turn against the roundabout direction is invalidated. - for (; invalidate_from != invalidate_to; ++invalidate_from) - { - const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid); - if (!edge.flags.roundabout && !edge.flags.circular && - node_based_graph.GetTarget(invalidate_from->eid) != from_nid) - { - invalidate_from->entry_allowed = false; - } - } -} - // If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a // dedicated turns. We are limiting it to four-way intersections with well distinct bearings. // All entry/roads and exit roads have to be simple. Not segregated roads. @@ -215,7 +148,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection( continue; // there is a single non-roundabout edge - const auto src_coordinate = coordinates[node]; + const auto src_coordinate = node_coordinates[node]; const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node); const auto number_of_lanes_at_intersection = std::accumulate( @@ -334,11 +267,11 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) { double length = 0.; - auto last_coord = coordinates[source_node]; - const auto &edge_bucket = compressed_edge_container.GetBucketReference(eid); + auto last_coord = node_coordinates[source_node]; + const auto &edge_bucket = compressed_geometries.GetBucketReference(eid); for (const auto &compressed_edge : edge_bucket) { - const auto next_coord = coordinates[compressed_edge.node_id]; + const auto next_coord = node_coordinates[compressed_edge.node_id]; length += util::coordinate_calculation::haversineDistance(last_coord, next_coord); last_coord = next_coord; } diff --git a/src/extractor/guidance/sliproad_handler.cpp b/src/extractor/guidance/sliproad_handler.cpp index abdc72297e4..bac7920513f 100644 --- a/src/extractor/guidance/sliproad_handler.cpp +++ b/src/extractor/guidance/sliproad_handler.cpp @@ -22,18 +22,25 @@ namespace extractor namespace guidance { -SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, +SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table), + coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates) { } @@ -243,7 +250,14 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter // Starting out at the intersection and going onto the Sliproad we skip artificial // degree two intersections and limit the max hop count in doing so. - IntersectionFinderAccumulator intersection_finder{10, intersection_generator}; + IntersectionFinderAccumulator intersection_finder{10, + node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data}; const SkipTrafficSignalBarrierRoadSelector road_selector{}; (void)graph_walker.TraverseRoad(intersection_node_id, // start node sliproad_edge, // onto edge @@ -372,8 +386,6 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter // // Sliproad Not a Sliproad { - const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor(); - const NodeID start = intersection_node_id; // b const EdgeID edge = sliproad_edge; // bd @@ -424,8 +436,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter // Only check for curvature and ~90 degree when it makes sense to do so. const constexpr auto MIN_LENGTH = 3.; - const auto length = haversineDistance(coordinates[intersection_node_id], - coordinates[main_road_intersection->node]); + const auto length = haversineDistance(node_coordinates[intersection_node_id], + node_coordinates[main_road_intersection->node]); const double minimal_crossroad_angle_of_intersection = 40.; @@ -546,8 +558,15 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter } else { - const auto skip_traffic_light_intersection = intersection_generator( - node_based_graph.GetTarget(sliproad_edge), candidate_road.eid); + const auto skip_traffic_light_intersection = intersection::getConnectedRoads( + node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {node_based_graph.GetTarget(sliproad_edge), candidate_road.eid}); if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() && node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) == main_road_intersection->node) @@ -656,8 +675,6 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg BOOST_ASSERT(start != SPECIAL_NODEID); BOOST_ASSERT(onto != SPECIAL_EDGEID); - const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor(); - // Base max distance threshold on the current road class we're on const auto &data = node_based_graph.GetEdgeData(onto).flags; const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down @@ -728,9 +745,9 @@ bool SliproadHandler::isValidSliproadArea(const double max_area, { using namespace util::coordinate_calculation; - const auto first = coordinates[a]; - const auto second = coordinates[b]; - const auto third = coordinates[c]; + const auto first = node_coordinates[a]; + const auto second = node_coordinates[b]; + const auto third = node_coordinates[c]; const auto length = haversineDistance(first, second); const auto heigth = haversineDistance(second, third); diff --git a/src/extractor/guidance/suppress_mode_handler.cpp b/src/extractor/guidance/suppress_mode_handler.cpp index 6a870ee984a..09b33629184 100644 --- a/src/extractor/guidance/suppress_mode_handler.cpp +++ b/src/extractor/guidance/suppress_mode_handler.cpp @@ -11,18 +11,25 @@ namespace extractor namespace guidance { -SuppressModeHandler::SuppressModeHandler(const IntersectionGenerator &intersection_generator, - const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, - const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table) +SuppressModeHandler::SuppressModeHandler( + const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const util::NameTable &name_table, + const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table) { } diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index b687c088295..8d1cb066929 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -23,89 +23,79 @@ using EdgeData = util::NodeBasedDynamicGraph::EdgeData; TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, - const std::vector &coordinates, + const std::vector &node_coordinates, + const CompressedEdgeContainer &compressed_edge_container, const RestrictionMap &restriction_map, const std::unordered_set &barrier_nodes, - const CompressedEdgeContainer &compressed_edge_container, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, const SuffixTable &street_name_suffix_table) - : node_based_graph(node_based_graph), intersection_generator(node_based_graph, - node_data_container, - restriction_map, - barrier_nodes, - coordinates, - compressed_edge_container), - intersection_normalizer(node_based_graph, - node_data_container, - coordinates, - name_table, - street_name_suffix_table, - intersection_generator), - roundabout_handler(node_based_graph, - node_data_container, - coordinates, - compressed_edge_container, - name_table, - street_name_suffix_table, - intersection_generator), + : node_based_graph(node_based_graph), roundabout_handler(node_based_graph, + node_data_container, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, + name_table, + street_name_suffix_table), motorway_handler(node_based_graph, node_data_container, - - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator), + street_name_suffix_table), turn_handler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator), - sliproad_handler(intersection_generator, - node_based_graph, + street_name_suffix_table), + sliproad_handler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, street_name_suffix_table), - suppress_mode_handler(intersection_generator, - node_based_graph, + suppress_mode_handler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, street_name_suffix_table), - driveway_handler(intersection_generator, - node_based_graph, + driveway_handler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, street_name_suffix_table), - statistics_handler(intersection_generator, - node_based_graph, + statistics_handler(node_based_graph, node_data_container, - coordinates, + node_coordinates, + compressed_edge_container, + restriction_map, + barrier_nodes, + turn_lanes_data, name_table, street_name_suffix_table) { } -Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection, - const EdgeID entering_via_edge) const -{ - TurnAnalysis::ShapeResult shape_result = - ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge)); - - // assign valid flags to normalized_shape - const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView( - node_prior_to_intersection, - entering_via_edge, - shape_result.annotated_normalized_shape.normalized_shape, - shape_result.intersection_shape, - shape_result.annotated_normalized_shape.performed_merges); - - // assign the turn types to the intersection - return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view); -} - Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection, const EdgeID entering_via_edge, const IntersectionView &intersection_view) const @@ -191,19 +181,6 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti return intersection; } -TurnAnalysis::ShapeResult -TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const -{ - ShapeResult intersection_shape; - intersection_shape.intersection_shape = - intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection); - - intersection_shape.annotated_normalized_shape = intersection_normalizer( - node_at_center_of_intersection, intersection_shape.intersection_shape); - - return intersection_shape; -} - // Sets basic turn types as fallback for otherwise unhandled turns Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection, const EdgeID, @@ -225,11 +202,6 @@ Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection, return intersection; } -const IntersectionGenerator &TurnAnalysis::GetIntersectionGenerator() const -{ - return intersection_generator; -} - } // namespace guidance } // namespace extractor } // namespace osrm diff --git a/src/extractor/guidance/turn_discovery.cpp b/src/extractor/guidance/turn_discovery.cpp index fc8f868d9bf..9f53c70c545 100644 --- a/src/extractor/guidance/turn_discovery.cpp +++ b/src/extractor/guidance/turn_discovery.cpp @@ -1,5 +1,7 @@ #include "extractor/guidance/turn_discovery.hpp" #include "extractor/guidance/constants.hpp" +#include "extractor/guidance/coordinate_extractor.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "util/bearing.hpp" #include "util/coordinate_calculation.hpp" @@ -14,16 +16,16 @@ namespace guidance namespace lanes { -namespace -{ -const constexpr bool USE_LOW_PRECISION_MODE = true; -} - bool findPreviousIntersection(const NodeID node_v, const EdgeID via_edge, const Intersection &intersection, - const IntersectionGenerator &intersection_generator, const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, // output parameters NodeID &result_node, EdgeID &result_via_edge, @@ -43,7 +45,9 @@ bool findPreviousIntersection(const NodeID node_v, */ const constexpr double COMBINE_DISTANCE_CUTOFF = 30; - const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor(); + const CoordinateExtractor coordinate_extractor( + node_based_graph, compressed_geometries, node_coordinates); + const auto coordinates_along_via_edge = coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge); const auto via_edge_length = @@ -71,7 +75,14 @@ bool findPreviousIntersection(const NodeID node_v, return false; const auto node_v_reverse_intersection = - intersection_generator.GetConnectedRoads(node_w, u_turn_at_node_w, USE_LOW_PRECISION_MODE); + intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {node_w, u_turn_at_node_w}); // Continue along the straightmost turn. If there is no straight turn, we cannot find a valid // previous intersection. const auto straightmost_at_v_in_reverse = @@ -83,8 +94,15 @@ bool findPreviousIntersection(const NodeID node_v, return false; const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->eid); - const auto node_u_reverse_intersection = intersection_generator.GetConnectedRoads( - node_v, straightmost_at_v_in_reverse->eid, USE_LOW_PRECISION_MODE); + const auto node_u_reverse_intersection = + intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {node_v, straightmost_at_v_in_reverse->eid}); // now check that the u-turn at the given intersection connects to via-edge // The u-turn at the now found intersection should, hopefully, represent the previous edge. @@ -102,7 +120,14 @@ bool findPreviousIntersection(const NodeID node_v, return false; } - result_intersection = intersection_generator(node_u, result_via_edge); + result_intersection = intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {node_u, result_via_edge}); const auto check_via_edge = result_intersection.end() != std::find_if(result_intersection.begin(), diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp index a4be7ede6aa..eda5f49845b 100644 --- a/src/extractor/guidance/turn_handler.cpp +++ b/src/extractor/guidance/turn_handler.cpp @@ -113,15 +113,21 @@ std::size_t TurnHandler::Fork::getLeftIndex() const TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, const std::vector &coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table, - const IntersectionGenerator &intersection_generator) + const SuffixTable &street_name_suffix_table) : IntersectionHandler(node_based_graph, node_data_container, coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, name_table, - street_name_suffix_table, - intersection_generator) + street_name_suffix_table) { } diff --git a/src/extractor/guidance/turn_lane_handler.cpp b/src/extractor/guidance/turn_lane_handler.cpp index 4f9f2ececf0..2110a3650d9 100644 --- a/src/extractor/guidance/turn_lane_handler.cpp +++ b/src/extractor/guidance/turn_lane_handler.cpp @@ -3,6 +3,7 @@ #include "extractor/guidance/turn_discovery.hpp" #include "extractor/guidance/turn_lane_augmentation.hpp" #include "extractor/guidance/turn_lane_matcher.hpp" +#include "extractor/intersection/intersection_analysis.hpp" #include "util/bearing.hpp" #include "util/log.hpp" #include "util/typedefs.hpp" @@ -35,14 +36,21 @@ std::size_t getNumberOfTurns(const Intersection &intersection) TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph, const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, LaneDescriptionMap &lane_description_map, const TurnAnalysis &turn_analysis, util::guidance::LaneDataIdMap &id_map) : node_based_graph(node_based_graph), node_data_container(node_data_container), - lane_description_map(lane_description_map), turn_analysis(turn_analysis), id_map(id_map) + node_coordinates(node_coordinates), compressed_geometries(compressed_geometries), + node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes), + turn_lanes_data(turn_lanes_data), lane_description_map(lane_description_map), + turn_analysis(turn_analysis), id_map(id_map) { - std::tie(turn_lane_offsets, turn_lane_masks) = - transformTurnLaneMapIntoArrays(lane_description_map); + std::tie(turn_lane_offsets, turn_lane_masks) = turn_lanes_data; count_handled = count_called = 0; } @@ -205,8 +213,13 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at, if (findPreviousIntersection(at, via_edge, intersection, - turn_analysis.GetIntersectionGenerator(), node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, previous_node, previous_via_edge, previous_intersection_view)) @@ -560,7 +573,16 @@ std::pair TurnLaneHandler::partitionLaneData( // find out about the next intersection. To check for valid matches, we also need the turn // types. We can skip merging/angle adjustments, though const auto next_intersection = turn_analysis.AssignTurnTypes( - at, straightmost->eid, turn_analysis.GetIntersectionGenerator()(at, straightmost->eid)); + at, + straightmost->eid, + intersection::getConnectedRoads(node_based_graph, + node_data_container, + node_coordinates, + compressed_geometries, + node_restriction_map, + barrier_nodes, + turn_lanes_data, + {at, straightmost->eid})); // check where we can match turn lanes std::size_t straightmost_tag_index = turn_lane_data.size(); diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp index acab3930b3a..eaac18b55ca 100644 --- a/src/extractor/guidance/turn_lane_matcher.cpp +++ b/src/extractor/guidance/turn_lane_matcher.cpp @@ -228,7 +228,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection, u_turn = 1; road_index = 2; } - intersection[u_turn].entry_allowed = true; intersection[u_turn].instruction.type = TurnType::Continue; intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn; @@ -268,7 +267,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection, } u_turn = intersection.size() - 1; } - intersection[u_turn].entry_allowed = true; intersection[u_turn].instruction.type = TurnType::Continue; intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn; diff --git a/src/extractor/intersection/intersection_analysis.cpp b/src/extractor/intersection/intersection_analysis.cpp new file mode 100644 index 00000000000..a4c3e5275e1 --- /dev/null +++ b/src/extractor/intersection/intersection_analysis.cpp @@ -0,0 +1,845 @@ +#include "extractor/intersection/intersection_analysis.hpp" + +#include "util/assert.hpp" +#include "util/bearing.hpp" +#include "util/coordinate_calculation.hpp" + +#include "extractor/guidance/coordinate_extractor.hpp" + +#include + +namespace osrm +{ +namespace extractor +{ +namespace intersection +{ + +IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph, + const NodeID intersection_node) +{ + IntersectionEdges result; + + for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node)) + { + const auto from_node = graph.GetTarget(outgoing_edge); + const auto incoming_edge = graph.FindEdge(from_node, intersection_node); + + if (!graph.GetEdgeData(incoming_edge).reversed) + { + result.push_back({from_node, incoming_edge}); + } + } + + // Enforce ordering of incoming edges + std::sort(result.begin(), result.end()); + return result; +} + +IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph, + const NodeID intersection_node) +{ + IntersectionEdges result; + + for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node)) + { + result.push_back({intersection_node, outgoing_edge}); + } + + BOOST_ASSERT(std::is_sorted(result.begin(), result.end())); + return result; +} + +std::vector +getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &node_coordinates, + const NodeID from_node, + const EdgeID edge, + const NodeID to_node) +{ + if (!compressed_geometries.HasEntryForID(edge)) + return {node_coordinates[from_node], node_coordinates[to_node]}; + + BOOST_ASSERT(from_node < node_coordinates.size()); + BOOST_ASSERT(to_node < node_coordinates.size()); + + // extracts the geometry in coordinates from the compressed edge container + std::vector result; + const auto &geometry = compressed_geometries.GetBucketReference(edge); + result.reserve(geometry.size() + 1); + + result.push_back(node_coordinates[from_node]); + std::transform(geometry.begin(), + geometry.end(), + std::back_inserter(result), + [&node_coordinates](const auto &compressed_edge) { + return node_coordinates[compressed_edge.node_id]; + }); + + // filter duplicated coordinates + result.erase(std::unique(result.begin(), result.end()), result.end()); + return result; +} + +namespace +{ +double findAngleBisector(double alpha, double beta) +{ + alpha *= M_PI / 180.; + beta *= M_PI / 180.; + const auto average = + 180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) / + M_PI; + return std::fmod(average + 360., 360.); +} + +double findClosestOppositeBearing(const IntersectionEdgeGeometries &edge_geometries, + const double bearing) +{ + BOOST_ASSERT(!edge_geometries.empty()); + const auto min = std::min_element( + edge_geometries.begin(), + edge_geometries.end(), + [bearing = util::bearing::reverse(bearing)](const auto &lhs, const auto &rhs) { + return util::angularDeviation(lhs.perceived_bearing, bearing) < + util::angularDeviation(rhs.perceived_bearing, bearing); + }); + return util::bearing::reverse(min->perceived_bearing); +} + +std::pair findMergedBearing(const util::NodeBasedDynamicGraph &graph, + const IntersectionEdgeGeometries &edge_geometries, + std::size_t lhs_index, + std::size_t rhs_index, + bool neighbor_intersection) +{ + // Function returns a pair with a flag and a value of bearing for merged roads + // If the flag is false the bearing must not be used as a merged value at neighbor intersections + + using guidance::STRAIGHT_ANGLE; + using guidance::MAXIMAL_ALLOWED_NO_TURN_DEVIATION; + using util::bearing::angleBetween; + using util::angularDeviation; + + const auto &lhs = edge_geometries[lhs_index]; + const auto &rhs = edge_geometries[rhs_index]; + BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed != graph.GetEdgeData(rhs.edge).reversed); + + const auto &entry = graph.GetEdgeData(lhs.edge).reversed ? rhs : lhs; + const auto opposite_bearing = + findClosestOppositeBearing(edge_geometries, entry.perceived_bearing); + const auto merged_bearing = findAngleBisector(rhs.perceived_bearing, lhs.perceived_bearing); + + if (angularDeviation(angleBetween(opposite_bearing, entry.perceived_bearing), STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // In some intersections, turning roads can introduce artificial turns if we merge here. + // Consider a scenario like: + //  + // a . g - f + // | . + // | . + // |. + // d-b--------e + // | + // c + //  + // Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight + // We don't change the angle, if such an opposite road exists + return {false, entry.perceived_bearing}; + } + + if (neighbor_intersection) + { + // Check that the merged bearing makes both turns closer to straight line + const auto turn_angle_lhs = angleBetween(opposite_bearing, lhs.perceived_bearing); + const auto turn_angle_rhs = angleBetween(opposite_bearing, rhs.perceived_bearing); + const auto turn_angle_new = angleBetween(opposite_bearing, merged_bearing); + + if (util::angularDeviation(turn_angle_lhs, STRAIGHT_ANGLE) < + util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE) || + util::angularDeviation(turn_angle_rhs, STRAIGHT_ANGLE) < + util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE)) + return {false, opposite_bearing}; + } + + return {true, merged_bearing}; +} + +bool isRoadsPairMergeable(const guidance::MergableRoadDetector &detector, + const IntersectionEdgeGeometries &edge_geometries, + const NodeID intersection_node, + const std::size_t index) +{ + const auto size = edge_geometries.size(); + BOOST_ASSERT(index < size); + + const auto &llhs = edge_geometries[(index + size - 1) % size]; + const auto &lhs = edge_geometries[index]; + const auto &rhs = edge_geometries[(index + 1) % size]; + const auto &rrhs = edge_geometries[(index + 2) % size]; + + // TODO: check IsDistinctFrom - it is an angle and name-only check + // also check CanMergeRoad for all merging scenarios + return detector.IsDistinctFrom({llhs.edge, llhs.perceived_bearing, llhs.length}, + {lhs.edge, lhs.perceived_bearing, lhs.length}) && + detector.CanMergeRoad(intersection_node, + {lhs.edge, lhs.perceived_bearing, lhs.length}, + {rhs.edge, rhs.perceived_bearing, rhs.length}) && + detector.IsDistinctFrom({rhs.edge, rhs.perceived_bearing, rhs.length}, + {rrhs.edge, rrhs.perceived_bearing, rrhs.length}); +} + +auto getIntersectionLanes(const util::NodeBasedDynamicGraph &graph, const NodeID intersection_node) +{ + std::uint8_t max_lanes_intersection = 0; + for (auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node)) + { + max_lanes_intersection = + std::max(max_lanes_intersection, + graph.GetEdgeData(outgoing_edge).flags.road_classification.GetNumberOfLanes()); + } + return max_lanes_intersection; +} + +template +IntersectionEdgeGeometries +getIntersectionOutgoingGeometries(const util::NodeBasedDynamicGraph &graph, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &node_coordinates, + const NodeID intersection_node) +{ + IntersectionEdgeGeometries edge_geometries; + + // TODO: keep CoordinateExtractor to reproduce bearings, simplify later + const guidance::CoordinateExtractor coordinate_extractor( + graph, compressed_geometries, node_coordinates); + + const auto max_lanes_intersection = getIntersectionLanes(graph, intersection_node); + + // Collect outgoing edges + for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node)) + { + const auto remote_node = graph.GetTarget(outgoing_edge); + + const auto &geometry = getEdgeCoordinates( + compressed_geometries, node_coordinates, intersection_node, outgoing_edge, remote_node); + + // OSRM_ASSERT(geometry.size() >= 2, node_coordinates[intersection_node]); + + const auto close_coordinate = + coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, geometry); + const auto initial_bearing = + util::coordinate_calculation::bearing(geometry[0], close_coordinate); + + const auto representative_coordinate = + USE_CLOSE_COORDINATE || graph.GetOutDegree(intersection_node) <= 2 + ? coordinate_extractor.GetCoordinateCloseToTurn( + intersection_node, outgoing_edge, false, remote_node) + : coordinate_extractor.ExtractRepresentativeCoordinate(intersection_node, + outgoing_edge, + false, + remote_node, + max_lanes_intersection, + geometry); + const auto perceived_bearing = + util::coordinate_calculation::bearing(geometry[0], representative_coordinate); + + const auto edge_length = util::coordinate_calculation::getLength( + geometry.begin(), geometry.end(), util::coordinate_calculation::haversineDistance); + + edge_geometries.push_back({outgoing_edge, initial_bearing, perceived_bearing, edge_length}); + } + + // Sort edges in the clockwise bearings order + std::sort(edge_geometries.begin(), edge_geometries.end(), [](const auto &lhs, const auto &rhs) { + return lhs.perceived_bearing < rhs.perceived_bearing; + }); + return edge_geometries; +} +} + +std::pair> +getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &node_coordinates, + const guidance::MergableRoadDetector &detector, + const NodeID intersection_node) +{ + IntersectionEdgeGeometries edge_geometries = getIntersectionOutgoingGeometries( + graph, compressed_geometries, node_coordinates, intersection_node); + + const auto edges_number = edge_geometries.size(); + + std::vector merged_edges(edges_number, false); + + // TODO: intersection views do not contain merged and not allowed edges + // but contain other restricted edges that are used in TurnAnalysis, + // to be deleted after TurnAnalysis refactoring + std::unordered_set merged_edge_ids; + + if (edges_number >= 3) + { // Adjust bearings of mergeable roads + for (std::size_t index = 0; index < edges_number; ++index) + { + if (isRoadsPairMergeable(detector, edge_geometries, intersection_node, index)) + { // Merge bearings of roads left & right + const auto next = (index + 1) % edges_number; + auto &lhs = edge_geometries[index]; + auto &rhs = edge_geometries[next]; + merged_edges[index] = true; + merged_edges[next] = true; + + const auto merge = findMergedBearing(graph, edge_geometries, index, next, false); + + lhs.perceived_bearing = lhs.initial_bearing = merge.second; + rhs.perceived_bearing = rhs.initial_bearing = merge.second; + + // Only one of the edges must be reversed, mark it as merged to remove from + // intersection view + BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed ^ + graph.GetEdgeData(rhs.edge).reversed); + merged_edge_ids.insert(graph.GetEdgeData(lhs.edge).reversed ? lhs.edge : rhs.edge); + } + } + } + + if (edges_number >= 2) + { // Adjust bearings of roads that will be merged at the neighbor intersections + const double constexpr PRUNING_DISTANCE = 30.; + + for (std::size_t index = 0; index < edges_number; ++index) + { + auto &edge_geometry = edge_geometries[index]; + + // Don't adjust bearings of roads that were merged at the current intersection + // or have neighbor intersection farer than the pruning distance + if (merged_edges[index] || edge_geometry.length > PRUNING_DISTANCE) + continue; + + const auto neighbor_intersection_node = graph.GetTarget(edge_geometry.edge); + + const auto neighbor_geometries = getIntersectionOutgoingGeometries( + graph, compressed_geometries, node_coordinates, neighbor_intersection_node); + + const auto neighbor_edges = neighbor_geometries.size(); + if (neighbor_edges <= 1) + continue; + + const auto neighbor_curr = std::distance( + neighbor_geometries.begin(), + std::find_if(neighbor_geometries.begin(), + neighbor_geometries.end(), + [&graph, &intersection_node](const auto &road) { + return graph.GetTarget(road.edge) == intersection_node; + })); + BOOST_ASSERT(static_cast(neighbor_curr) != neighbor_geometries.size()); + const auto neighbor_prev = (neighbor_curr + neighbor_edges - 1) % neighbor_edges; + const auto neighbor_next = (neighbor_curr + 1) % neighbor_edges; + + if (isRoadsPairMergeable( + detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev)) + { // Neighbor intersection has mergable neighbor_prev and neighbor_curr roads + BOOST_ASSERT(!isRoadsPairMergeable( + detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr)); + + // TODO: merge with an angle bisector, but not a reversed closed turn, to be + // checked as a difference with the previous implementation + const auto merge = findMergedBearing( + graph, neighbor_geometries, neighbor_prev, neighbor_curr, true); + + if (merge.first) + { + const auto offset = util::angularDeviation( + merge.second, neighbor_geometries[neighbor_curr].perceived_bearing); + + // Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr) + // and BC (neighbor_prev) will be merged and will have merged bearing Bb. + // The adjustment value is ∠bBA with negative sign (counter-clockwise) to Aa + // A ~~~ a + // \  + // b --- B --- + // / + // C + edge_geometry.perceived_bearing = edge_geometry.initial_bearing = + std::fmod(edge_geometry.perceived_bearing + 360. - offset, 360.); + } + } + else if (isRoadsPairMergeable( + detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr)) + { // Neighbor intersection has mergable neighbor_curr and neighbor_next roads + BOOST_ASSERT(!isRoadsPairMergeable( + detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev)); + + // TODO: merge with an angle bisector, but not a reversed closed turn, to be + // checked as a difference with the previous implementation + const auto merge = findMergedBearing( + graph, neighbor_geometries, neighbor_curr, neighbor_next, true); + if (merge.first) + { + const auto offset = util::angularDeviation( + merge.second, neighbor_geometries[neighbor_curr].perceived_bearing); + + // Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr) + // and BC (neighbor_next) will be merged and will have merged bearing Bb. + // The adjustment value is ∠bBA with positive sign (clockwise) to Aa + // a ~~~ A + // / + // --- B --- b + // \  + // C + edge_geometry.perceived_bearing = edge_geometry.initial_bearing = + std::fmod(edge_geometry.perceived_bearing + offset, 360.); + } + } + } + } + + // Add incoming edges with reversed bearings + edge_geometries.resize(2 * edges_number); + for (std::size_t index = 0; index < edges_number; ++index) + { + const auto &geometry = edge_geometries[index]; + const auto remote_node = graph.GetTarget(geometry.edge); + const auto incoming_edge = graph.FindEdge(remote_node, intersection_node); + edge_geometries[edges_number + index] = {incoming_edge, + util::bearing::reverse(geometry.initial_bearing), + util::bearing::reverse(geometry.perceived_bearing), + geometry.length}; + } + + // Enforce ordering of edges by IDs + std::sort(edge_geometries.begin(), edge_geometries.end()); + + return std::make_pair(edge_geometries, merged_edge_ids); +} + +inline auto findEdge(const IntersectionEdgeGeometries &geometries, const EdgeID &edge) +{ + const auto it = std::lower_bound( + geometries.begin(), geometries.end(), edge, [](const auto &geometry, const auto edge) { + return geometry.edge < edge; + }); + BOOST_ASSERT(it != geometries.end() && it->edge == edge); + return it; +} + +double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge) +{ + return findEdge(geometries, edge)->perceived_bearing; +} + +double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge) +{ + return findEdge(geometries, edge)->length; +} + +template +bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to) +{ + // Check turn restrictions to find a node that is the only allowed target when coming from a + // node to an intersection + // d + // | + // a - b - c and `only_straight_on ab | bc would return `c` for `a,b` + const auto is_only = std::find_if(restrictions.first, + restrictions.second, + [](const auto &pair) { return pair.second->is_only; }); + if (is_only != restrictions.second) + return is_only->second->AsNodeRestriction().to != to; + + // Check if explicitly forbidden + const auto no_turn = + std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) { + return restriction.second->AsNodeRestriction().to == to; + }); + + return no_turn != restrictions.second; +} + +bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const IntersectionEdgeGeometries &geometries, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &from, + const IntersectionEdge &to) +{ + BOOST_ASSERT(graph.GetTarget(from.edge) == to.node); + + // TODO: to use TurnAnalysis all outgoing edges are required, to be removed later + if (graph.GetEdgeData(from.edge).reversed || graph.GetEdgeData(to.edge).reversed) + return false; + + const auto intersection_node = to.node; + const auto destination_node = graph.GetTarget(to.edge); + auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node); + + // Check if turn is explicitly restricted by a turn restriction + if (isTurnRestricted(restrictions, destination_node)) + return false; + + // Precompute reversed bearing of the `from` edge + const auto from_edge_reversed_bearing = + util::bearing::reverse(findEdgeBearing(geometries, from.edge)); + + // Collect some information about the intersection + // 1) number of allowed exits and adjacent bidirectional edges + std::uint32_t allowed_exits = 0, bidirectional_edges = 0; + // 2) edge IDs of roundabouts edges + EdgeID roundabout_from = SPECIAL_EDGEID, roundabout_to = SPECIAL_EDGEID; + double roundabout_from_angle = 0., roundabout_to_angle = 0.; + + for (const auto eid : graph.GetAdjacentEdgeRange(intersection_node)) + { + const auto &edge_data = graph.GetEdgeData(eid); + const auto &edge_class = edge_data.flags; + const auto to_node = graph.GetTarget(eid); + const auto reverse_edge = graph.FindEdge(to_node, intersection_node); + BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID); + + const auto is_exit_edge = !edge_data.reversed && !isTurnRestricted(restrictions, to_node); + const auto is_bidirectional = !graph.GetEdgeData(reverse_edge).reversed; + allowed_exits += is_exit_edge; + bidirectional_edges += is_bidirectional; + + if (edge_class.roundabout || edge_class.circular) + { + if (edge_data.reversed) + { + // "Linked Roundabouts" is an example of tie between two linked roundabouts + // A tie breaker for that maximizes ∠(roundabout_from_bearing, ¬from_edge_bearing) + const auto angle = util::bearing::angleBetween( + findEdgeBearing(geometries, reverse_edge), from_edge_reversed_bearing); + if (angle > roundabout_from_angle) + { + roundabout_from = reverse_edge; + roundabout_from_angle = angle; + } + } + else + { + // a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing) + const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing, + findEdgeBearing(geometries, eid)); + if (angle > roundabout_to_angle) + { + roundabout_to = eid; + roundabout_to_angle = angle; + } + } + } + } + + // 3) if the intersection has a barrier + const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end(); + + // Check a U-turn + if (from.node == destination_node) + { + // Allow U-turns before barrier nodes + if (is_barrier_node) + return true; + + // Allow U-turns at dead-ends + if (graph.GetAdjacentEdgeRange(intersection_node).size() == 1) + return true; + + // Allow U-turns at dead-ends if there is at most one bidirectional road at the intersection + // The condition allows U-turns d→a→d and c→b→c ("Bike - Around the Block" test) + // a→b + // ↕ ↕ + // d↔c + if (allowed_exits == 1 || bidirectional_edges <= 1) + return true; + + // Allow U-turn if the incoming edge has a U-turn lane + // TODO: revisit the use-case, related PR #2753 + const auto &incoming_edge_annotation_id = graph.GetEdgeData(from.edge).annotation_data; + const auto lane_description_id = static_cast( + node_data_container.GetAnnotation(incoming_edge_annotation_id).lane_description_id); + if (lane_description_id != INVALID_LANE_DESCRIPTIONID) + { + const auto &turn_lane_offsets = std::get<0>(turn_lanes_data); + const auto &turn_lanes = std::get<1>(turn_lanes_data); + BOOST_ASSERT(lane_description_id + 1 < turn_lane_offsets.size()); + + if (std::any_of(turn_lanes.begin() + turn_lane_offsets[lane_description_id], + turn_lanes.begin() + turn_lane_offsets[lane_description_id + 1], + [](const auto &lane) { return lane & guidance::TurnLaneType::uturn; })) + return true; + } + + // Don't allow U-turns on usual intersections + return false; + } + + // Don't allow turns via barriers for not U-turn maneuvers + if (is_barrier_node) + return false; + + // Check for roundabouts exits in the opposite direction of roundabout flow + if (roundabout_from != SPECIAL_EDGEID && roundabout_to != SPECIAL_EDGEID) + { + // Get bearings of edges + const auto roundabout_from_bearing = findEdgeBearing(geometries, roundabout_from); + const auto roundabout_to_bearing = findEdgeBearing(geometries, roundabout_to); + const auto to_edge_bearing = findEdgeBearing(geometries, to.edge); + + // Get angles from the roundabout edge to three other edges + const auto roundabout_angle = + util::bearing::angleBetween(roundabout_from_bearing, roundabout_to_bearing); + const auto roundabout_from_angle = + util::bearing::angleBetween(roundabout_from_bearing, from_edge_reversed_bearing); + const auto roundabout_to_angle = + util::bearing::angleBetween(roundabout_from_bearing, to_edge_bearing); + + // Restrict turning over a roundabout if `roundabout_to_angle` is in + // a sector between `roundabout_from_bearing` to `from_bearing` (shaded area) + // + // roundabout_angle = 270° roundabout_angle = 90° + // roundabout_from_angle = 150° roundabout_from_angle = 150° + // roundabout_to_angle = 90° roundabout_to_angle = 270° + // + // 150° 150° + // v░░░░░░ ░░░░░░░░░v + // v░░░░░░░ ░░░░░░░░v + // 270° <-ooo- v -ttt-> 90° 270° <-ttt- v -ooo-> 90° + // ^░░░░░░░ ░░░░░░░^ + // r░░░░░░░ ░░░░░░░r + // r░░░░░░░ ░░░░░░░r + if ((roundabout_from_angle < roundabout_angle && + roundabout_to_angle < roundabout_from_angle) || + (roundabout_from_angle > roundabout_angle && + roundabout_to_angle > roundabout_from_angle)) + return false; + } + + return true; +} + +// The function adapts intersection geometry data to TurnAnalysis +guidance::IntersectionView +convertToIntersectionView(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const RestrictionMap &restriction_map, + const std::unordered_set &barrier_nodes, + const IntersectionEdgeGeometries &edge_geometries, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge, + const IntersectionEdges &outgoing_edges, + const std::unordered_set &merged_edges) +{ + using util::bearing::angleBetween; + + const auto edge_it = findEdge(edge_geometries, incoming_edge.edge); + const auto incoming_bearing = edge_it->perceived_bearing; + const auto initial_incoming_bearing = edge_it->initial_bearing; + + using IntersectionViewDataWithAngle = std::pair; + std::vector pre_intersection_view; + guidance::IntersectionViewData uturn{{SPECIAL_EDGEID, 0., 0.}, false, 0.}; + std::size_t allowed_uturns_number = 0; + for (const auto &outgoing_edge : outgoing_edges) + { + const auto is_uturn = [](const auto angle) { + return std::fabs(angle) < std::numeric_limits::epsilon(); + }; + + const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge); + const auto segment_length = edge_it->length; + const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0; + const auto is_turn_allowed = intersection::isTurnAllowed(graph, + node_data_container, + restriction_map, + barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edge); + + // Compute angles + const auto outgoing_bearing = edge_it->perceived_bearing; + const auto initial_outgoing_bearing = edge_it->initial_bearing; + auto turn_angle = std::fmod( + std::round(angleBetween(incoming_bearing, outgoing_bearing) * 1e8) / 1e8, 360.); + auto initial_angle = angleBetween(initial_incoming_bearing, initial_outgoing_bearing); + + // If angle of the allowed turn is in a neighborhood of 0° (±15°) but the initial OSM angle + // is in the opposite semi-plane then assume explicitly a U-turn to avoid incorrect + // adjustments due to numerical noise in selection of representative_coordinate + if (is_turn_allowed && + ((turn_angle < 15 && initial_angle > 180) || (turn_angle > 345 && initial_angle < 180))) + { + turn_angle = 0; + initial_angle = 0; + } + + const auto is_uturn_angle = is_uturn(turn_angle); + + guidance::IntersectionViewData road{ + {outgoing_edge.edge, outgoing_bearing, segment_length}, is_turn_allowed, turn_angle}; + + if (graph.GetTarget(outgoing_edge.edge) == incoming_edge.node) + { // Save the true U-turn road to add later if no allowed U-turns will be added + uturn = road; + } + else if (is_turn_allowed || (!is_merged && !is_uturn_angle)) + { // Add roads that have allowed entry or not U-turns and not merged + allowed_uturns_number += is_uturn_angle; + + // Adjust computed initial turn angle for non-U-turn road edge cases: + // 1) use 0° or 360° if the road has 0° initial angle + // 2) use turn angle if the smallest arc between turn and initial angles passes 0° + const auto use_turn_angle = (turn_angle > 270 && initial_angle < 90) || + (turn_angle < 90 && initial_angle > 270); + const auto adjusted_angle = is_uturn(initial_angle) + ? (turn_angle > 180. ? 360. : 0.) + : use_turn_angle ? turn_angle : initial_angle; + pre_intersection_view.push_back({road, adjusted_angle}); + } + } + + BOOST_ASSERT(uturn.eid != SPECIAL_EDGEID); + if (uturn.entry_allowed || allowed_uturns_number == 0) + { // Add the true U-turn if it is allowed or no other U-turns found + pre_intersection_view.insert(pre_intersection_view.begin(), {uturn, 0}); + } + + // Order roads in counter-clockwise order starting from the U-turn edge in the OSM order + std::stable_sort(pre_intersection_view.begin(), + pre_intersection_view.end(), + [](const auto &lhs, const auto &rhs) { + return std::tie(lhs.second, lhs.first.angle) < + std::tie(rhs.second, rhs.first.angle); + }); + + // Adjust perceived bearings to keep the initial OSM order with respect to the first edge + for (auto curr = pre_intersection_view.begin(), next = std::next(curr); + next != pre_intersection_view.end(); + ++curr, ++next) + { + // Check that the perceived angles order is the same as the initial OSM one + if (next->first.angle < curr->first.angle) + { // If the true bearing is out of the initial order (next before current) then + // adjust the next road angle to keep the order. The adjustment angle is at most + // 0.5° or a half-angle between the current angle and 360° to prevent overlapping + const auto angle_adjustment = + std::min(.5, util::restrictAngleToValidRange(360. - curr->first.angle) / 2.); + next->first.angle = + util::restrictAngleToValidRange(curr->first.angle + angle_adjustment); + } + } + + // Copy intersection view data + guidance::IntersectionView intersection_view; + intersection_view.reserve(pre_intersection_view.size()); + std::transform(pre_intersection_view.begin(), + pre_intersection_view.end(), + std::back_inserter(intersection_view), + [](const auto &road) { return road.first; }); + + return intersection_view; +} + +// a +// | +// | +// v +// For an intersection from_node --via_eid--> turn_node ----> c +// ^ +// | +// | +// b +// This functions returns _all_ turns as if the graph was undirected. +// That means we not only get (from_node, turn_node, c) in the above example +// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are +// marked as invalid and only needed for intersection classification. +template +guidance::IntersectionView +getConnectedRoads(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge) +{ + const auto intersection_node = graph.GetTarget(incoming_edge.edge); + const auto &outgoing_edges = intersection::getOutgoingEdges(graph, intersection_node); + auto edge_geometries = getIntersectionOutgoingGeometries( + graph, compressed_geometries, node_coordinates, intersection_node); + + // Add incoming edges with reversed bearings + const auto edges_number = edge_geometries.size(); + edge_geometries.resize(2 * edges_number); + for (std::size_t index = 0; index < edges_number; ++index) + { + const auto &geometry = edge_geometries[index]; + const auto remote_node = graph.GetTarget(geometry.edge); + const auto incoming_edge = graph.FindEdge(remote_node, intersection_node); + edge_geometries[edges_number + index] = {incoming_edge, + util::bearing::reverse(geometry.initial_bearing), + util::bearing::reverse(geometry.perceived_bearing), + geometry.length}; + } + + // Enforce ordering of edges by IDs + std::sort(edge_geometries.begin(), edge_geometries.end()); + + return convertToIntersectionView(graph, + node_data_container, + node_restriction_map, + barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edges, + std::unordered_set()); +} + +template guidance::IntersectionView +getConnectedRoads(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge); + +template guidance::IntersectionView +getConnectedRoads(const util::NodeBasedDynamicGraph &graph, + const EdgeBasedNodeDataContainer &node_data_container, + const std::vector &node_coordinates, + const extractor::CompressedEdgeContainer &compressed_geometries, + const RestrictionMap &node_restriction_map, + const std::unordered_set &barrier_nodes, + const guidance::TurnLanesIndexedArray &turn_lanes_data, + const IntersectionEdge &incoming_edge); + +IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph, IntersectionEdge road) +{ + std::unordered_set visited_nodes; + (void)visited_nodes; + + // Skip trivial nodes without generating the intersection in between, stop at the very first + // intersection of degree > 2 + const auto starting_node = road.node; + auto next_node = graph.GetTarget(road.edge); + while (graph.GetOutDegree(next_node) == 2 && next_node != starting_node) + { + BOOST_ASSERT(visited_nodes.insert(next_node).second); + const auto next_edge = graph.BeginEdges(next_node); + road.edge = graph.GetTarget(next_edge) == road.node ? next_edge + 1 : next_edge; + road.node = next_node; + next_node = graph.GetTarget(road.edge); + } + + return road; +} +} +} +} diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index c3c207a579d..ddee2e8ff2e 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -26,10 +26,8 @@ namespace // creates a default edge of unit weight inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to) { - // src, tgt, dist, edge_id, name_id, fwd, bkwd, roundabout, circular, startpoint, local access, - // split edge, travel_mode - return {from, - to, + return {from, // source + to, // target 1, // weight 1, // duration GeometryID{0, false}, // geometry_id diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp new file mode 100644 index 00000000000..1692a1cc4e0 --- /dev/null +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -0,0 +1,315 @@ +#include "extractor/intersection/intersection_analysis.hpp" + +#include "extractor/graph_compressor.hpp" + +#include "../common/range_tools.hpp" +#include "../unit_tests/mocks/mock_scripting_environment.hpp" + +#include +#include + +BOOST_AUTO_TEST_SUITE(intersection_analysis_tests) + +using namespace osrm; +using namespace osrm::extractor; +using namespace osrm::extractor::guidance; +using namespace osrm::extractor::intersection; +using InputEdge = util::NodeBasedDynamicGraph::InputEdge; +using Graph = util::NodeBasedDynamicGraph; + +BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) +{ + std::unordered_set barrier_nodes{6}; + std::unordered_set traffic_lights; + std::vector annotations{ + {EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}, + {EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}}; + std::vector restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}}; + std::vector conditional_restrictions; + CompressedEdgeContainer container; + test::MockScriptingEnvironment scripting_environment; + + TurnLanesIndexedArray turn_lanes_data{{0, 0, 3}, + {TurnLaneType::uturn | TurnLaneType::left, + TurnLaneType::straight, + TurnLaneType::straight | TurnLaneType::right}}; + + // Graph with an additional turn restriction 0→2→1 and bollard at 6 + // 0→5↔6↔7 + // ↕ + // 1↔2←3 + // ↓ + // 4 + const auto unit_edge = + [](const NodeID from, const NodeID to, bool allowed, AnnotationID annotation) { + return InputEdge{from, + to, + 1, + 1, + GeometryID{0, false}, + !allowed, + NodeBasedEdgeClassification(), + annotation}; + }; + + std::vector edges = {unit_edge(0, 2, true, 1), + unit_edge(0, 5, true, 0), + unit_edge(1, 2, true, 0), + unit_edge(2, 0, true, 0), + unit_edge(2, 1, true, 0), + unit_edge(2, 3, false, 0), + unit_edge(2, 4, true, 0), + unit_edge(3, 2, true, 0), + unit_edge(4, 2, false, 0), + unit_edge(5, 0, false, 0), + unit_edge(5, 6, true, 0), + unit_edge(6, 5, true, 0), + unit_edge(6, 7, true, 0), + unit_edge(7, 6, true, 0)}; + IntersectionEdgeGeometries edge_geometries{ + {0, 180, 180, 10.}, // 0→2 + {1, 90, 90, 10.}, // 0→5 + {2, 90, 90, 10.}, // 1→2 + {3, 0, 0, 10.}, // 2→0 + {4, 270, 270, 10.}, // 2→1 + {5, 90, 90, 10.}, // 2→3 + {6, 180, 180, 10.}, // 2→4 + {7, 270, 270, 10.}, // 3→2 + {8, 0, 0, 10.}, // 4→2 + {9, 270, 270, 10.}, // 5→0 + {10, 90, 90, 10.}, // 5→6 + {11, 270, 270, 10.}, // 6→5 + {12, 90, 90, 10.}, // 6→7 + {13, 270, 270, 10.} // 7→6 + }; + + Graph graph(8, edges); + + GraphCompressor().Compress(barrier_nodes, + traffic_lights, + scripting_environment, + restrictions, + conditional_restrictions, + graph, + annotations, + container); + + REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3); + REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4); + + EdgeBasedNodeDataContainer node_data_container( + std::vector(graph.GetNumberOfEdges()), annotations); + RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia()); + + const auto connectivity_matrix = [&](NodeID node) { + std::vector result; + const auto incoming_edges = getIncomingEdges(graph, node); + const auto outgoing_edges = getOutgoingEdges(graph, node); + for (const auto incoming_edge : incoming_edges) + { + for (const auto outgoing_edge : outgoing_edges) + { + result.push_back(isTurnAllowed(graph, + node_data_container, + restriction_map, + barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edge)); + } + } + return result; + }; + + CHECK_EQUAL_RANGE(connectivity_matrix(0), 1, 1); // from node 2 allowed U-turn and to node 5 + CHECK_EQUAL_RANGE(connectivity_matrix(1), 1); // from node 2 allowed U-turn + CHECK_EQUAL_RANGE(connectivity_matrix(2), + // clang-format off + 1, 0, 0, 1, // from node 0 to node 4 and a U-turn at 2 + 1, 0, 0, 1, // from node 1 to nodes 0 and 4 + 1, 1, 0, 1 // from node 3 to nodes 0, 1 and 4 + // clang-format on + ); + REQUIRE_SIZE_RANGE(connectivity_matrix(3), 0); // no incoming edges, empty matrix + CHECK_EQUAL_RANGE(connectivity_matrix(4), 0); // from node 2 not allowed U-turn + CHECK_EQUAL_RANGE(connectivity_matrix(5), + // clang-format off + 0, 1, // from node 0 to node 6 + 0, 1, // from node 6 a U-turn to node 6 + // clang-format on + ); + + CHECK_EQUAL_RANGE(connectivity_matrix(6), + // clang-format off + 1, 0, // from node 5 a U-turn to node 5 + 0, 1, // from node 7 a U-turn to node 7 + // clang-format on + ); +} + +BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) +{ + std::unordered_set barrier_nodes; + std::unordered_set traffic_lights; + std::vector annotations; + std::vector restrictions; + std::vector conditional_restrictions; + CompressedEdgeContainer container; + test::MockScriptingEnvironment scripting_environment; + + TurnLanesIndexedArray turn_lanes_data; + + // Graph with roundabout edges 5→0→2 + // 1 2 3 + // ↘ ↑ ↙ + // 0 + // ↙ ↑ ↘ + // 4 5 6 + const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed, bool roundabout) { + return InputEdge{ + from, + to, + 1, + 1, + GeometryID{0, false}, + !allowed, + NodeBasedEdgeClassification{true, false, false, roundabout, false, false, false, {}}, + 0}; + }; + std::vector edges = {unit_edge(0, 1, false, false), + unit_edge(0, 2, true, true), + unit_edge(0, 3, false, false), + unit_edge(0, 4, true, false), + unit_edge(0, 5, false, true), + unit_edge(0, 6, true, false), + unit_edge(1, 0, true, false), + unit_edge(2, 0, false, true), + unit_edge(3, 0, true, false), + unit_edge(4, 0, false, false), + unit_edge(5, 0, true, true), + unit_edge(6, 0, false, false)}; + IntersectionEdgeGeometries edge_geometries{ + {0, 315, 315, 10}, // 0→1 + {1, 0, 0, 10}, // 0→2 + {2, 45, 45, 10}, // 0→3 + {3, 225, 225, 10}, // 0→4 + {4, 180, 180, 10}, // 0→5 + {5, 135, 135, 10}, // 0→6 + {6, 135, 135, 10}, // 1→0 + {7, 180, 180, 10}, // 2→0 + {8, 225, 225, 10}, // 3→0 + {9, 45, 45, 10}, // 4→0 + {10, 0, 0, 10}, // 5→0 + {11, 315, 315, 10} // 6→0 + }; + + Graph graph(7, edges); + + GraphCompressor().Compress(barrier_nodes, + traffic_lights, + scripting_environment, + restrictions, + conditional_restrictions, + graph, + annotations, + container); + + REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3); + REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6); + + EdgeBasedNodeDataContainer node_data_container( + std::vector(graph.GetNumberOfEdges()), annotations); + RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia()); + + const auto connectivity_matrix = [&](NodeID node) { + std::vector result; + const auto incoming_edges = getIncomingEdges(graph, node); + const auto outgoing_edges = getOutgoingEdges(graph, node); + for (const auto incoming_edge : incoming_edges) + { + for (const auto outgoing_edge : outgoing_edges) + { + result.push_back(isTurnAllowed(graph, + node_data_container, + restriction_map, + barrier_nodes, + edge_geometries, + turn_lanes_data, + incoming_edge, + outgoing_edge)); + } + } + return result; + }; + + CHECK_EQUAL_RANGE(connectivity_matrix(0), + // clang-format off + 0, 1, 0, 0, 0, 1, // from node 1 to nodes 2 and 6 + 0, 1, 0, 1, 0, 0, // from node 3 to nodes 2 and 4 + 0, 1, 0, 1, 0, 1 // from node 5 to nodes 2, 4 and 6 + // clang-format on + ); +} + +BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) +{ + std::unordered_set barrier_nodes{1}; + std::unordered_set traffic_lights{2}; + std::vector annotations(1); + std::vector restrictions; + std::vector conditional_restrictions; + CompressedEdgeContainer container; + test::MockScriptingEnvironment scripting_environment; + + TurnLanesIndexedArray turn_lanes_data; + + // Graph + // + // 0↔1→2↔3↔4→5 7 + // ↑ ↕ ↕ + // 6 8 ↔ 9 + // + const auto unit_edge = [](const NodeID from, const NodeID to, bool allowed) { + return InputEdge{ + from, to, 1, 1, GeometryID{0, false}, !allowed, NodeBasedEdgeClassification{}, 0}; + }; + std::vector edges = {unit_edge(0, 1, true), // 0 + unit_edge(1, 0, true), + unit_edge(1, 2, true), + unit_edge(2, 1, false), + unit_edge(2, 3, true), + unit_edge(3, 2, true), // 5 + unit_edge(3, 4, true), + unit_edge(4, 3, true), + unit_edge(4, 5, true), + unit_edge(4, 6, false), + unit_edge(5, 4, false), // 10 + unit_edge(6, 4, true), + // Circle + unit_edge(7, 8, true), // 12 + unit_edge(7, 9, true), + unit_edge(8, 7, true), + unit_edge(8, 9, true), + unit_edge(9, 7, true), + unit_edge(9, 8, true)}; + + Graph graph(10, edges); + + GraphCompressor().Compress(barrier_nodes, + traffic_lights, + scripting_environment, + restrictions, + conditional_restrictions, + graph, + annotations, + container); + + BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {0, 0}).edge), 4); + BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {4, 7}).edge), 0); + BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {5, 10}).edge), 4); + BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {6, 11}).edge), 4); + BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {7, 12}).edge), 7); +} + +BOOST_AUTO_TEST_SUITE_END()