Considering multiple small turns, right after each
other can result in a combined turn angle that is straight instead of turning left and right.
This commit is contained in:
		
							parent
							
								
									01a2c66472
								
							
						
					
					
						commit
						88c3f4c481
					
				@ -31,6 +31,36 @@ Feature: Simple Turns
 | 
			
		||||
            | f,a       | depart,arrive                   | road,road      |
 | 
			
		||||
            | e,a       | depart,turn slight right,arrive | turn,road,road |
 | 
			
		||||
 | 
			
		||||
    Scenario: Turning into splitting road
 | 
			
		||||
        Given the node map
 | 
			
		||||
        """
 | 
			
		||||
              a
 | 
			
		||||
            g-b
 | 
			
		||||
              /\
 | 
			
		||||
             /  \
 | 
			
		||||
            c   d
 | 
			
		||||
                |\
 | 
			
		||||
                | e
 | 
			
		||||
                |
 | 
			
		||||
                f
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name | highway | oneway |
 | 
			
		||||
            | ab    | road | primary | no     |
 | 
			
		||||
            | bc    | road | primary | yes    |
 | 
			
		||||
            | fdb   | road | primary | yes    |
 | 
			
		||||
            | de    | turn | primary | no     |
 | 
			
		||||
            | bg    | left | primary | yes    |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | waypoints | turns                                     | route               |
 | 
			
		||||
            | f,a       | depart,arrive                             | road,road           |
 | 
			
		||||
            | e,a       | depart,turn slight right,arrive           | turn,road,road      |
 | 
			
		||||
            | e,g       | depart,turn slight right,turn left,arrive | turn,road,left,left |
 | 
			
		||||
            | f,g       | depart,turn left,arrive                   | road,left,left      |
 | 
			
		||||
            | f,c       | depart,continue uturn,arrive              | road,road,road      |
 | 
			
		||||
 | 
			
		||||
    Scenario: Middle Island
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
@ -1288,3 +1288,90 @@ Feature: Simple Turns
 | 
			
		||||
            | waypoints | route              | turns                   |
 | 
			
		||||
            | a,c       | in,through,through | depart,turn left,arrive |
 | 
			
		||||
 | 
			
		||||
    # http://www.openstreetmap.org/#map=19/52.51556/13.41832
 | 
			
		||||
    Scenario: No Slight Right over Jannowitzbruecke
 | 
			
		||||
        Given the node map
 | 
			
		||||
        """
 | 
			
		||||
                  l   m
 | 
			
		||||
                  |   |
 | 
			
		||||
            f._   |   |
 | 
			
		||||
                ' g---h.
 | 
			
		||||
                  |   |  '.
 | 
			
		||||
                  |   |     i
 | 
			
		||||
            a_    |   |
 | 
			
		||||
               ''.b---c
 | 
			
		||||
                  |   |'d._
 | 
			
		||||
                  |   |    'e
 | 
			
		||||
                  j   k
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name          | highway   | oneway |
 | 
			
		||||
            | ab    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | bcde  | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | gf    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | ihg   | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | lgbj  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
            | kchm  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | waypoints | turns                            | route                                   |
 | 
			
		||||
            | a,e       | depart,new name straight,arrive  | Stralauer Str,Holzmarktstr,Holzmarktstr |
 | 
			
		||||
 | 
			
		||||
    Scenario: No Slight Right over Jannowitzbruecke -- less extreme
 | 
			
		||||
        Given the node map
 | 
			
		||||
         """
 | 
			
		||||
                  l   m
 | 
			
		||||
                  |   |
 | 
			
		||||
            f_    |   |
 | 
			
		||||
               ' 'g   h_
 | 
			
		||||
                  |   |  '\_
 | 
			
		||||
                  |   |     i
 | 
			
		||||
            a_    |   |
 | 
			
		||||
               '_ b   c_
 | 
			
		||||
                  |   |  \_
 | 
			
		||||
                  |   |     e
 | 
			
		||||
                  j   k
 | 
			
		||||
         """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name          | highway   | oneway |
 | 
			
		||||
            | ab    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | bce   | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | gf    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | ihg   | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | lgbj  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
            | kchm  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | waypoints | turns                           | route                                   |
 | 
			
		||||
            | a,e       | depart,new name straight,arrive | Stralauer Str,Holzmarktstr,Holzmarktstr |
 | 
			
		||||
 | 
			
		||||
    Scenario: No Slight Right over Jannowitzbruecke
 | 
			
		||||
        Given the node map
 | 
			
		||||
         """
 | 
			
		||||
                  l   m
 | 
			
		||||
                  |   |
 | 
			
		||||
                  |   |
 | 
			
		||||
              _ _ g   h_
 | 
			
		||||
            f'    |   |  '_
 | 
			
		||||
                  |   |     i
 | 
			
		||||
                  |   |
 | 
			
		||||
               _ _b   c__
 | 
			
		||||
            a'    |   |    'd
 | 
			
		||||
                  |   |
 | 
			
		||||
                  j   k
 | 
			
		||||
         """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name          | highway   | oneway |
 | 
			
		||||
            | ab    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | bcd   | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | gf    | Stralauer Str | tertiary  | yes    |
 | 
			
		||||
            | ihg   | Holzmarktstr  | secondary | yes    |
 | 
			
		||||
            | lgbj  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
            | kchm  | Alexanderstr  | primary   | yes    |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | waypoints | turns                           | route                                   |
 | 
			
		||||
            | a,d       | depart,new name straight,arrive | Stralauer Str,Holzmarktstr,Holzmarktstr |
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ class IntersectionGenerator
 | 
			
		||||
    Intersection GetConnectedRoads(const NodeID from_node, const EdgeID via_eid) const;
 | 
			
		||||
 | 
			
		||||
    // 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
 | 
			
		||||
    // intersection representation. See below for an example. Utility function for
 | 
			
		||||
    // MergeSegregatedRoads
 | 
			
		||||
    bool CanMerge(const NodeID intersection_node,
 | 
			
		||||
                  const Intersection &intersection,
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
#include "engine/guidance/post_processing.hpp"
 | 
			
		||||
#include "extractor/guidance/constants.hpp"
 | 
			
		||||
#include "extractor/guidance/toolkit.hpp"
 | 
			
		||||
#include "extractor/guidance/turn_instruction.hpp"
 | 
			
		||||
 | 
			
		||||
@ -567,16 +568,21 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (TurnType::Continue == current_step.maneuver.instruction.type ||
 | 
			
		||||
                (TurnType::Suppressed == current_step.maneuver.instruction.type &&
 | 
			
		||||
                 current_step.maneuver.instruction.direction_modifier !=
 | 
			
		||||
                     DirectionModifier::Straight))
 | 
			
		||||
                steps[step_index].maneuver.instruction.type = TurnType::Turn;
 | 
			
		||||
            const bool continue_or_suppressed =
 | 
			
		||||
                (TurnType::Continue == current_step.maneuver.instruction.type ||
 | 
			
		||||
                 (TurnType::Suppressed == current_step.maneuver.instruction.type &&
 | 
			
		||||
                  current_step.maneuver.instruction.direction_modifier !=
 | 
			
		||||
                      DirectionModifier::Straight));
 | 
			
		||||
 | 
			
		||||
            else if (TurnType::NewName == current_step.maneuver.instruction.type &&
 | 
			
		||||
                     current_step.maneuver.instruction.direction_modifier !=
 | 
			
		||||
                         DirectionModifier::Straight &&
 | 
			
		||||
                     one_back_step.intersections.front().bearings.size() > 2)
 | 
			
		||||
            const bool turning_name =
 | 
			
		||||
                (TurnType::NewName == current_step.maneuver.instruction.type &&
 | 
			
		||||
                 current_step.maneuver.instruction.direction_modifier !=
 | 
			
		||||
                     DirectionModifier::Straight &&
 | 
			
		||||
                 one_back_step.intersections.front().bearings.size() > 2);
 | 
			
		||||
 | 
			
		||||
            if (continue_or_suppressed)
 | 
			
		||||
                steps[step_index].maneuver.instruction.type = TurnType::Turn;
 | 
			
		||||
            else if (turning_name)
 | 
			
		||||
                steps[step_index].maneuver.instruction.type = TurnType::Turn;
 | 
			
		||||
            else if (TurnType::UseLane == current_step.maneuver.instruction.type &&
 | 
			
		||||
                     current_step.maneuver.instruction.direction_modifier !=
 | 
			
		||||
@ -584,9 +590,123 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
 | 
			
		||||
                     one_back_step.intersections.front().bearings.size() > 2)
 | 
			
		||||
                steps[step_index].maneuver.instruction.type = TurnType::Turn;
 | 
			
		||||
 | 
			
		||||
            const auto total_angle = findTotalTurnAngle(steps[one_back_index], current_step);
 | 
			
		||||
            steps[step_index].maneuver.instruction.direction_modifier =
 | 
			
		||||
                getTurnDirection(total_angle);
 | 
			
		||||
            // A new name with a continue/turning suppressed/name requires the adaption of the
 | 
			
		||||
            // direction modifier. The combination of the in-bearing and the out bearing gives the
 | 
			
		||||
            // new modifier for the turn
 | 
			
		||||
            if (continue_or_suppressed || turning_name)
 | 
			
		||||
            {
 | 
			
		||||
                const auto in_bearing = [](const RouteStep &step) {
 | 
			
		||||
                    return util::bearing::reverseBearing(
 | 
			
		||||
                        step.intersections.front().bearings[step.intersections.front().in]);
 | 
			
		||||
                };
 | 
			
		||||
                const auto out_bearing = [](const RouteStep &step) {
 | 
			
		||||
                    return step.intersections.front().bearings[step.intersections.front().out];
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                const auto first_angle =
 | 
			
		||||
                    turn_angle(in_bearing(one_back_step), out_bearing(one_back_step));
 | 
			
		||||
                const auto second_angle =
 | 
			
		||||
                    turn_angle(in_bearing(current_step), out_bearing(current_step));
 | 
			
		||||
                const auto bearing_turn_angle =
 | 
			
		||||
                    turn_angle(in_bearing(one_back_step), out_bearing(current_step));
 | 
			
		||||
 | 
			
		||||
                // When looking at an intersection, some angles, even though present, feel more like
 | 
			
		||||
                // a straight turn. This happens most often at segregated intersections.
 | 
			
		||||
                // We consider two cases
 | 
			
		||||
                // I) a shift in the road:
 | 
			
		||||
                //
 | 
			
		||||
                // a       g      h
 | 
			
		||||
                //     .   |      |
 | 
			
		||||
                //         b ---- c
 | 
			
		||||
                //         |      |    .
 | 
			
		||||
                //         f      e        d
 | 
			
		||||
                //
 | 
			
		||||
                // Where a-d technicall continues straight, even though the shift models it as a
 | 
			
		||||
                // slight left and a slight right turn.
 | 
			
		||||
                //
 | 
			
		||||
                // II) A curved road
 | 
			
		||||
                //
 | 
			
		||||
                //         g      h
 | 
			
		||||
                //         |      |
 | 
			
		||||
                //         b ---- c
 | 
			
		||||
                //    .    |      |    .
 | 
			
		||||
                // a       f      e        d
 | 
			
		||||
                //
 | 
			
		||||
                // where a-d is a curve passing by an intersection.
 | 
			
		||||
                //
 | 
			
		||||
                // We distinguish this case from other bearings though where the interpretation as
 | 
			
		||||
                // straight would end up disguising turns.
 | 
			
		||||
 | 
			
		||||
                // check if there is another similar turn next to the turn itself
 | 
			
		||||
                const auto hasSimilarAngle = [&](const std::size_t index,
 | 
			
		||||
                                                 const std::vector<short> &bearings) {
 | 
			
		||||
                    return (angularDeviation(bearings[index],
 | 
			
		||||
                                             bearings[(index + 1) % bearings.size()]) <
 | 
			
		||||
                            extractor::guidance::NARROW_TURN_ANGLE) ||
 | 
			
		||||
                           (angularDeviation(
 | 
			
		||||
                                bearings[index],
 | 
			
		||||
                                bearings[(index + bearings.size() - 1) % bearings.size()]) <
 | 
			
		||||
                            extractor::guidance::NARROW_TURN_ANGLE);
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                const auto is_shift_or_curve = [&]() -> bool {
 | 
			
		||||
                    // since we move an intersection modifier from a slight turn to a straight, we
 | 
			
		||||
                    // need to make sure that there is not a similar angle which could prevent this
 | 
			
		||||
                    // perception of angles to be true.
 | 
			
		||||
                    if (hasSimilarAngle(one_back_step.intersections.front().in,
 | 
			
		||||
                                        one_back_step.intersections.front().bearings) ||
 | 
			
		||||
                        hasSimilarAngle(current_step.intersections.front().out,
 | 
			
		||||
                                        current_step.intersections.front().bearings))
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    // Check if we are on a potential curve, both angles go in the same direction
 | 
			
		||||
                    if (angularDeviation(first_angle, second_angle) <
 | 
			
		||||
                        extractor::guidance::FUZZY_ANGLE_DIFFERENCE)
 | 
			
		||||
                    {
 | 
			
		||||
                        // We limit perceptive angles to narrow turns. If the total turn is going to
 | 
			
		||||
                        // be not-narrow, we assume it to be more than a simple curve.
 | 
			
		||||
                        return angularDeviation(bearing_turn_angle,
 | 
			
		||||
                                                extractor::guidance::STRAIGHT_ANGLE) <
 | 
			
		||||
                               extractor::guidance::NARROW_TURN_ANGLE;
 | 
			
		||||
                    }
 | 
			
		||||
                    // if one of the angles is a left turn and the other one is a right turn, we
 | 
			
		||||
                    // nearly reverse the angle
 | 
			
		||||
                    else if ((first_angle > extractor::guidance::STRAIGHT_ANGLE) !=
 | 
			
		||||
                             (second_angle > extractor::guidance::STRAIGHT_ANGLE))
 | 
			
		||||
                    {
 | 
			
		||||
                        // since we are not in a curve, we can check for a shift. If we are going
 | 
			
		||||
                        // nearly straight, we call it a shift.
 | 
			
		||||
                        return angularDeviation(bearing_turn_angle,
 | 
			
		||||
                                                extractor::guidance::STRAIGHT_ANGLE) <
 | 
			
		||||
                               extractor::guidance::NARROW_TURN_ANGLE;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }();
 | 
			
		||||
 | 
			
		||||
                // if the angles continue similar, it looks like we might be in a normal curve
 | 
			
		||||
                if (is_shift_or_curve)
 | 
			
		||||
                    steps[step_index].maneuver.instruction.direction_modifier =
 | 
			
		||||
                        DirectionModifier::Straight;
 | 
			
		||||
                else
 | 
			
		||||
                    steps[step_index].maneuver.instruction.direction_modifier =
 | 
			
		||||
                        util::guidance::getTurnDirection(bearing_turn_angle);
 | 
			
		||||
 | 
			
		||||
                // if the total direction of this turn is now straight, we can keep it suppressed/as
 | 
			
		||||
                // a new name. Else we have to interpret it as a turn.
 | 
			
		||||
                if (!is_shift_or_curve)
 | 
			
		||||
                    steps[step_index].maneuver.instruction.type = TurnType::Turn;
 | 
			
		||||
                else
 | 
			
		||||
                    steps[step_index].maneuver.instruction.type = TurnType::NewName;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const auto total_angle = findTotalTurnAngle(steps[one_back_index], current_step);
 | 
			
		||||
                steps[step_index].maneuver.instruction.direction_modifier =
 | 
			
		||||
                    getTurnDirection(total_angle);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
 | 
			
		||||
 | 
			
		||||
@ -549,6 +549,15 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
 | 
			
		||||
    for (std::size_t index = 1; index < intersection.size(); ++index)
 | 
			
		||||
    {
 | 
			
		||||
        auto &road = intersection[index];
 | 
			
		||||
        // 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 =
 | 
			
		||||
            GetConnectedRoads(node_at_intersection, road.turn.eid);
 | 
			
		||||
 | 
			
		||||
        if (next_intersection_along_road.size() <= 1)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        const auto node_at_next_intersection = node_based_graph.GetTarget(road.turn.eid);
 | 
			
		||||
        const util::Coordinate coordinate_at_next_intersection =
 | 
			
		||||
            node_info_list[node_at_next_intersection];
 | 
			
		||||
@ -569,12 +578,6 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
 | 
			
		||||
        if (range.size() <= 1)
 | 
			
		||||
            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 =
 | 
			
		||||
            GetConnectedRoads(node_at_intersection, road.turn.eid);
 | 
			
		||||
 | 
			
		||||
        // 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.
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
 | 
			
		||||
 | 
			
		||||
bool requiresAnnouncement(const EdgeData &from, const EdgeData &to)
 | 
			
		||||
{
 | 
			
		||||
    return !from.IsCompatibleTo(to);
 | 
			
		||||
    return !from.CanCombineWith(to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user