diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature index 76a1557f7..96c6f7558 100644 --- a/features/guidance/collapse.feature +++ b/features/guidance/collapse.feature @@ -528,3 +528,103 @@ Feature: Collapse | waypoints | turns | route | | a,d | depart,continue right,end of road right,arrive | road,road,road,road | | d,a | depart,continue left,end of road left,arrive | road,road,road,road | + + Scenario: Forking before a turn + Given the node map + | | | | g | | + | | | | | | + | | | | c | | + | a | | b | d | e | + | | | | | | + | | | | f | | + + And the ways + | nodes | name | oneway | highway | + | ab | road | yes | primary | + | bd | road | yes | primary | + | bc | road | yes | primary | + | de | road | yes | primary | + | fdcg | cross | no | secondary | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | fdcg | d | no_left_turn | + | restriction | bc | fdcg | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | + | a,g | road,cross,cross | depart,turn left,arrive | + | a,e | road,road | depart,arrive | + + Scenario: Forking before a turn (narrow) + Given the node map + | | | | g | | + | | | | | | + | | | | c | | + | a | b | | d | e | + | | | | | | + | | | | f | | + + And the ways + | nodes | name | oneway | highway | + | ab | road | yes | primary | + | bd | road | yes | primary | + | bc | road | yes | primary | + | de | road | yes | primary | + | fdcg | cross | no | secondary | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | fdcg | d | no_left_turn | + | restriction | bc | fdcg | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | + | a,g | road,cross,cross | depart,turn left,arrive | + | a,e | road,road | depart,arrive | + + Scenario: Forking before a turn (forky) + Given the node map + | | | | g | | | + | | | | | | | + | | | | c | | | + | a | b | | | | | + | | | | | d | | + | | | | | f | e | + + And the ways + | nodes | name | oneway | highway | + | ab | road | yes | primary | + | bd | road | yes | primary | + | bc | road | yes | primary | + | de | road | yes | primary | + | fdcg | cross | no | secondary | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bd | fdcg | d | no_left_turn | + | restriction | bc | fdcg | c | no_right_turn | + + When I route I should get + | waypoints | route | turns | + | a,g | road,cross,cross | depart,turn left,arrive | + | a,e | road,road,road | depart,continue slight right,arrive | + + Scenario: On-Off on Highway + Given the node map + | f | | | | + | a | b | c | d | + | | | | e | + + And the ways + | nodes | name | highway | oneway | + | abcd | Hwy | motorway | yes | + | fb | on | motorway_link | yes | + | ce | off | motorway_link | yes | + + When I route I should get + | waypoints | route | turns | + | a,d | Hwy,Hwy | depart,arrive | + | f,d | on,Hwy,Hwy | depart,merge slight right,arrive | + | f,e | on,Hwy,off,off | depart,merge slight right,off ramp right,arrive | + | a,e | Hwy,off,off | depart,off ramp right,arrive | diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature index c35d59403..d82b84928 100644 --- a/features/guidance/turn.feature +++ b/features/guidance/turn.feature @@ -805,3 +805,31 @@ Feature: Simple Turns | a,d | abc,bd,bd | depart,turn sharp right,arrive | | a,e | abc,be,be | depart,turn right,arrive | | a,f | abc,bf,bf | depart,turn slight right,arrive | + + Scenario: Turn Lane on Splitting up Road + Given the node map + | | | | | | | | | | | | | | | | + | g | | | | f | | | | | | | | | | | + | | | | | | | | | | | | | | | | + | | | | | | h | | | e | | | c | | | d | + | a | | | b | | | | | | | | | | | | + | | | | i | | | | | | | | | | | | + + And the ways + | nodes | highway | oneway | name | + | ab | secondary | yes | road | + | be | secondary | yes | road | + | ecd | secondary | no | road | + | efg | secondary | yes | road | + | ehb | secondary_link | yes | road | + | bi | tertiary | no | cross | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ehb | be | b | no_left_turn | + + When I route I should get + | waypoints | route | turns | + | a,d | road,road | depart,arrive | + | d,i | road,cross,cross | depart,turn left,arrive | + | d,g | road,road | depart,arrive | diff --git a/features/testbot/bearing_param.feature b/features/testbot/bearing_param.feature index b8450dfdc..c0c4072de 100644 --- a/features/testbot/bearing_param.feature +++ b/features/testbot/bearing_param.feature @@ -85,31 +85,31 @@ Feature: Bearing parameter | f | | | e | | | d | And the ways - | nodes | oneway | - | ia | yes | - | jb | yes | - | kc | yes | - | ld | yes | - | me | yes | - | nf | yes | - | og | yes | - | ph | yes | - | ab | yes | - | bc | yes | - | cd | yes | - | de | yes | - | ef | yes | - | fg | yes | - | gh | yes | - | ha | yes | + | nodes | oneway | name | + | ia | yes | ia | + | jb | yes | jb | + | kc | yes | kc | + | ld | yes | ld | + | me | yes | me | + | nf | yes | nf | + | og | yes | og | + | ph | yes | ph | + | ab | yes | ring | + | bc | yes | ring | + | cd | yes | ring | + | de | yes | ring | + | ef | yes | ring | + | fg | yes | ring | + | gh | yes | ring | + | ha | yes | ring | When I route I should get - | from | to | bearings | route | bearing | - | 0 | q | 0 90 | ia,ab,bc,cd,de,ef,fg,gh,ha,ha | 0->0,0->90,90->180,180->180,180->270,270->270,270->0,0->0,0->90,90->0 | - | 0 | a | 45 90 | jb,bc,cd,de,ef,fg,gh,ha,ha | 0->45,45->180,180->180,180->270,270->270,270->0,0->0,0->90,90->0 | - | 0 | q | 90 90 | kc,cd,de,ef,fg,gh,ha,ha | 0->90,90->180,180->270,270->270,270->0,0->0,0->90,90->0 | - | 0 | a | 135 90 | ld,de,ef,fg,gh,ha,ha | 0->135,135->270,270->270,270->0,0->0,0->90,90->0 | - | 0 | a | 180 90 | me,ef,fg,gh,ha,ha | 0->180,180->270,270->0,0->0,0->90,90->0 | - | 0 | a | 225 90 | nf,fg,gh,ha,ha | 0->225,225->0,0->0,0->90,90->0 | - | 0 | a | 270 90 | og,gh,ha,ha | 0->270,270->0,0->90,90->0 | - | 0 | a | 315 90 | ph,ha,ha | 0->315,315->90,90->0 | + | from | to | bearings | route | bearing | + | 0 | q | 0 90 | ia,ring,ring,ring,ring,ring | 0->0,0->90,180->270,270->0,0->90,90->0 | + | 0 | a | 45 90 | jb,ring,ring,ring,ring,ring | 0->45,45->180,180->270,270->0,0->90,90->0 | + | 0 | q | 90 90 | kc,ring,ring,ring,ring | 0->90,90->180,270->0,0->90,90->0 | + | 0 | a | 135 90 | ld,ring,ring,ring,ring | 0->135,135->270,270->0,0->90,90->0 | + | 0 | a | 180 90 | me,ring,ring,ring | 0->180,180->270,0->90,90->0 | + | 0 | a | 225 90 | nf,ring,ring,ring | 0->225,225->0,0->90,90->0 | + | 0 | a | 270 90 | og,ring,ring | 0->270,270->0,90->0 | + | 0 | a | 315 90 | ph,ring,ring | 0->315,315->90,90->0 | diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index d0fe8179c..d98f21a51 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -33,6 +33,16 @@ namespace { const constexpr double MAX_COLLAPSE_DISTANCE = 25; +inline bool choiceless(const RouteStep &step, const RouteStep &previous) +{ + // if the next turn is choiceless, we consider longer turn roads collapsable than usually + // accepted. We might need to improve this to find out whether we merge onto a through-street. + return previous.distance < 3 * MAX_COLLAPSE_DISTANCE && + 1 >= std::count(step.intersections.front().entry.begin(), + step.intersections.front().entry.end(), + true); +} + // List of types that can be collapsed, if all other restrictions pass bool isCollapsableInstruction(const TurnInstruction instruction) { @@ -41,6 +51,8 @@ bool isCollapsableInstruction(const TurnInstruction instruction) instruction.direction_modifier == DirectionModifier::Straight) || (instruction.type == TurnType::Turn && instruction.direction_modifier == DirectionModifier::Straight) || + (instruction.type == TurnType::Continue && + instruction.direction_modifier == DirectionModifier::Straight) || (instruction.type == TurnType::Merge); } @@ -387,7 +399,11 @@ void collapseTurnAt(std::vector &steps, BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty()); // Very Short New Name - if (collapsable(one_back_step)) + if (((collapsable(one_back_step) || + (isCollapsableInstruction(one_back_step.maneuver.instruction) && + choiceless(current_step, one_back_step))) && + !(one_back_step.maneuver.instruction.type == TurnType::Merge))) + // the check against merge is a workaround for motorways { BOOST_ASSERT(two_back_index < steps.size()); if (compatible(one_back_step, steps[two_back_index])) @@ -433,7 +449,11 @@ void collapseTurnAt(std::vector &steps, { steps[one_back_index].maneuver.instruction.type = TurnType::Continue; } - else if (TurnType::Merge == one_back_step.maneuver.instruction.type) + else if (TurnType::Merge == one_back_step.maneuver.instruction.type && + current_step.maneuver.instruction.type != + TurnType::Suppressed) // This suppressed is a check for highways. We might + // need a highway-suppressed to get the turn onto a + // highway... { steps[one_back_index].maneuver.instruction.direction_modifier = util::guidance::mirrorDirectionModifier( @@ -445,7 +465,8 @@ void collapseTurnAt(std::vector &steps, } } // Potential U-Turn - else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE && + else if ((one_back_step.distance <= MAX_COLLAPSE_DISTANCE || + choiceless(current_step, one_back_step)) && bearingsAreReversed(util::bearing::reverseBearing( one_back_step.intersections.front() .bearings[one_back_step.intersections.front().in]), @@ -529,6 +550,7 @@ std::vector removeNoTurnInstructions(std::vector steps) // that we come across. std::vector postProcess(std::vector steps) { + print(steps); // the steps should always include the first/last step in form of a location BOOST_ASSERT(steps.size() >= 2); if (steps.size() == 2) @@ -542,14 +564,12 @@ std::vector postProcess(std::vector steps) // required. We might end up with only one of them (e.g. starting within a roundabout) // or having a via-point in the roundabout. // In this case, exits are numbered from the start of the lag. - std::size_t last_valid_instruction = 0; for (std::size_t step_index = 0; step_index < steps.size(); ++step_index) { auto &step = steps[step_index]; const auto instruction = step.maneuver.instruction; if (entersRoundabout(instruction)) { - last_valid_instruction = step_index; has_entered_roundabout = setUpRoundabout(step); if (has_entered_roundabout && step_index + 1 < steps.size()) @@ -570,17 +590,11 @@ std::vector postProcess(std::vector steps) // in case the we are not on a roundabout, the very first instruction // after the depart will be transformed into a roundabout and become // the first valid instruction - last_valid_instruction = 1; } closeOffRoundabout(has_entered_roundabout, steps, step_index); has_entered_roundabout = false; on_roundabout = false; } - else if (!isSilent(instruction)) - { - // Remember the last non silent instruction - last_valid_instruction = step_index; - } } // unterminated roundabout @@ -648,6 +662,8 @@ std::vector collapseTurns(std::vector steps) for (std::size_t step_index = 1; step_index + 1 < steps.size(); ++step_index) { const auto ¤t_step = steps[step_index]; + if( current_step.maneuver.instruction.type == TurnType::NoTurn ) + continue; const auto one_back_index = getPreviousIndex(step_index); BOOST_ASSERT(one_back_index < steps.size()); @@ -748,7 +764,8 @@ std::vector collapseTurns(std::vector steps) invalidateStep(steps[step_index]); } } - else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE) + else if (choiceless(current_step, one_back_step) || + one_back_step.distance <= MAX_COLLAPSE_DISTANCE) { // check for one of the multiple collapse scenarios and, if possible, collapse the // turn @@ -757,7 +774,8 @@ std::vector collapseTurns(std::vector steps) collapseTurnAt(steps, two_back_index, one_back_index, step_index); } } - else if (one_back_index > 0 && one_back_step.distance <= MAX_COLLAPSE_DISTANCE) + else if (one_back_index > 0 && (one_back_step.distance <= MAX_COLLAPSE_DISTANCE || + choiceless(current_step, one_back_step))) { // check for one of the multiple collapse scenarios and, if possible, collapse the turn const auto two_back_index = getPreviousIndex(one_back_index); diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp index 462743e9a..cc81bc9dc 100644 --- a/src/extractor/guidance/motorway_handler.cpp +++ b/src/extractor/guidance/motorway_handler.cpp @@ -385,7 +385,7 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters { // circular order indicates a merge to the left (0-3 onto 4 if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < - NARROW_TURN_ANGLE) + 2*NARROW_TURN_ANGLE) intersection[1].turn.instruction = {TurnType::Merge, DirectionModifier::SlightLeft}; else // fallback @@ -407,12 +407,12 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph) && node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id != EMPTY_NAMEID && - node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id == - node_based_graph.GetEdgeData(intersection[0].turn.eid).name_id) + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id) { // circular order (5-0) onto 4 if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < - NARROW_TURN_ANGLE) + 2 * NARROW_TURN_ANGLE) intersection[2].turn.instruction = {TurnType::Merge, DirectionModifier::SlightRight}; else // fallback diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index 0bb7751d9..c87d443a2 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -1,5 +1,5 @@ -#include "extractor/guidance/turn_analysis.hpp" #include "extractor/guidance/constants.hpp" +#include "extractor/guidance/turn_analysis.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" @@ -127,15 +127,15 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, auto intersection_node_id = node_based_graph.GetTarget(source_edge_id); const auto linkTest = [this](const ConnectedRoad &road) { - return isLinkClass( - node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class) && - road.entry_allowed && - angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE; + return // isLinkClass( + // node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class) && + !node_based_graph.GetEdgeData(road.turn.eid).roundabout && road.entry_allowed && + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE; }; - bool hasRamp = + bool hasNarrow = std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end(); - if (!hasRamp) + if (!hasNarrow) return intersection; const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id); @@ -171,6 +171,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, const auto next_road_next_intersection = intersection_generator(intersection_node_id, next_road->turn.eid); + const auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid); + std::unordered_set target_road_names; for (const auto &road : next_road_next_intersection) @@ -187,7 +189,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, for (const auto &candidate_road : target_intersection) { const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid); - if (target_road_names.count(candidate_data.name_id) > 0) + if (target_road_names.count(candidate_data.name_id) > 0 && + node_based_graph.GetTarget(candidate_road.turn.eid) == next_intersection_node) { road.turn.instruction.type = TurnType::Sliproad; break; @@ -196,6 +199,30 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, } } + if (next_road->turn.instruction.type == TurnType::Fork) + { + const auto &next_data = node_based_graph.GetEdgeData(next_road->turn.eid); + if (next_data.name_id == source_edge_data.name_id) + { + if (angularDeviation(next_road->turn.angle, STRAIGHT_ANGLE) < 5) + next_road->turn.instruction.type = TurnType::Suppressed; + else + next_road->turn.instruction.type = TurnType::Continue; + next_road->turn.instruction.direction_modifier = + getTurnDirection(next_road->turn.angle); + } + else if (next_data.name_id != EMPTY_NAMEID) + { + next_road->turn.instruction.type = TurnType::NewName; + next_road->turn.instruction.direction_modifier = + getTurnDirection(next_road->turn.angle); + } + else + { + next_road->turn.instruction.type = TurnType::Suppressed; + } + } + return intersection; } diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp index 683df168a..652637ca1 100644 --- a/src/extractor/guidance/turn_handler.cpp +++ b/src/extractor/guidance/turn_handler.cpp @@ -1,7 +1,7 @@ -#include "extractor/guidance/turn_handler.hpp" #include "extractor/guidance/constants.hpp" #include "extractor/guidance/intersection_scenario_three_way.hpp" #include "extractor/guidance/toolkit.hpp" +#include "extractor/guidance/turn_handler.hpp" #include "util/guidance/toolkit.hpp" @@ -377,8 +377,12 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge, } const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid); + auto continue_class = node_based_graph.GetEdgeData(intersection[best_continue].turn.eid) + .road_classification.road_class; if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id && - deviation < best_continue_deviation) + (best_continue == 0 || continue_class > out_data.road_classification.road_class || + (deviation < best_continue_deviation && + out_data.road_classification.road_class == continue_class))) { best_continue_deviation = deviation; best_continue = i; @@ -392,7 +396,12 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge, return 0; // has no obvious continued road - if (best_continue == 0 || true) + if (best_continue == 0 || best_continue_deviation >= 2 * NARROW_TURN_ANGLE || + (node_based_graph.GetEdgeData(intersection[best_continue].turn.eid) + .road_classification.road_class == + node_based_graph.GetEdgeData(intersection[best].turn.eid) + .road_classification.road_class && + std::abs(best_continue_deviation) > 1 && best_deviation / best_continue_deviation < 0.75)) { // Find left/right deviation const double left_deviation = angularDeviation( @@ -422,8 +431,31 @@ std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge, return best; } } + else + { + const double deviation = + angularDeviation(intersection[best_continue].turn.angle, STRAIGHT_ANGLE); + const auto &continue_data = + node_based_graph.GetEdgeData(intersection[best_continue].turn.eid); + if (std::abs(deviation) < 1) + return best_continue; - return 0; // no obvious turn + // check if any other similar best continues exist + for (std::size_t i = 1; i < intersection.size(); ++i) + { + if (i == best_continue || !intersection[i].entry_allowed) + continue; + + if (angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE) / deviation < 1.1 && + continue_data.road_classification.road_class == + node_based_graph.GetEdgeData(intersection[i].turn.eid) + .road_classification.road_class) + return 0; + } + return best_continue; // no obvious turn + } + + return 0; } // Assignment of left turns hands of to right turns.