Added post process logic to collapse segregated turn instructions (#4925)

* Added post process logic to collapse segregated turn instructions

* format updates

* Fixed coordinates to reflect reality
Updated left turn road name

* fixed coordinates to fix test

* Skip last step when processing segregated steps

* updated segregated turn test

* Updated segregated test

* Updated test: Segregated Intersection, Cross Belonging to Correct Street - features/guidance/collapse.feature:79

* Fixed all but one for features/guidance/collapse.feature:124

* Fixed Scenario: Partly Segregated Intersection, Two Segregated Roads, Intersection belongs to Second - features/guidance/collapse.feature:219

* Fixed 7 of th 9 failures for Scenario: Partly Segregated Intersection, Two Segregated Roads, Intersection belongs to Second - features/guidance/collapse.feature:219

* Fixed 7 of the 9 failures for Scenario: Segregated Intersection, Cross Belonging to Mixed Streets - Slight Angles (2) - features/guidance/collapse.feature:318

* Fixed Scenario: Segregated Intersection into Slight Turn - features/guidance/collapse.feature:581

* Updated Scenario: U-turn after a traffic light - features/guidance/turn-lanes.feature:1220

* Updated how we combine segregated steps

* Added test to Verify end of road left turn across divided roads

* Fixed divided highwat tests

* Fixed test failure

* fixed Scenario: Partitioned turn, Slight Curve - maxspeed - features/guidance/turn-lanes.feature:936

* Fixed Scenario: Partitioned turn, Slight Curve - features/guidance/turn-lanes.feature:961

* Added strategies to combine segrgated intersections

* Added setModifier alias for readability

* Added strategies to combine segrgated intersections

* Format updates

* Fixes segregated indentification to not mark `circular` edge as segregated

* Added intersection prior to turn so we still call out end of road

* updated expectation to be turn instead of continue

* Confirmed with @oxidase that the lane information if correct - updated the expectation

* Added logic to handle wider straights
Fixed tests

* Update CHANGELOG.md

Added #4925

* Removed TODO

* Process straight step prior to wider straight step
This commit is contained in:
Duane Gearhart
2018-03-30 07:43:56 -04:00
committed by GitHub
parent 0d9f18fe1f
commit 8a63ad9b4b
11 changed files with 449 additions and 121 deletions
+3
View File
@@ -148,6 +148,9 @@ class RouteAPI : public BaseAPI
// processing is performed
guidance::applyOverrides(BaseAPI::facade, steps, leg_geometry);
// Collapse segregated steps before others
steps = guidance::collapseSegregatedTurnInstructions(std::move(steps));
/* Perform step-based post-processing.
*
* Using post-processing on basis of route-steps for a single leg at a time
+24 -2
View File
@@ -13,13 +13,19 @@ namespace engine
namespace guidance
{
// Multiple possible reasons can result in unnecessary/confusing instructions
// Collapsing such turns into a single turn instruction, we give a clearer
// set of instructions that is not cluttered by unnecessary turns/name changes.
OSRM_ATTR_WARN_UNUSED
std::vector<RouteStep> collapseTurnInstructions(std::vector<RouteStep> steps);
// Multiple possible reasons can result in unnecessary/confusing instructions
// A prime example would be a segregated intersection. Turning around at this
// intersection would result in two instructions to turn left.
// Collapsing such turns into a single turn instruction, we give a clearer
// set of instructionst that is not cluttered by unnecessary turns/name changes.
// set of instructions that is not cluttered by unnecessary turns/name changes.
OSRM_ATTR_WARN_UNUSED
std::vector<RouteStep> collapseTurnInstructions(std::vector<RouteStep> steps);
std::vector<RouteStep> collapseSegregatedTurnInstructions(std::vector<RouteStep> steps);
// A combined turn is a set of two instructions that actually form a single turn, as far as we
// perceive it. A u-turn consisting of two left turns is one such example. But there are also lots
@@ -93,6 +99,22 @@ struct StaggeredTurnStrategy : CombineStrategy
const RouteStep &step_prior_to_intersection;
};
// Handling of consecutive segregated steps
struct CombineSegregatedStepsStrategy : CombineStrategy
{
void operator()(RouteStep &step_at_turn_location, const RouteStep &transfer_from_step) const;
};
// Handling of segregated intersections
struct SegregatedTurnStrategy : CombineStrategy
{
SegregatedTurnStrategy(const RouteStep &step_prior_to_intersection);
void operator()(RouteStep &step_at_turn_location, const RouteStep &transfer_from_step) const;
const RouteStep &step_prior_to_intersection;
};
// Signage Strategies
// Transfer the signage from the next step onto this step
@@ -23,6 +23,7 @@ const constexpr std::size_t MIN_END_OF_ROAD_INTERSECTIONS = std::size_t{2};
const constexpr double MAX_COLLAPSE_DISTANCE = 30.0;
// a bit larger than 100 to avoid oscillation in tests
const constexpr double NAME_SEGMENT_CUTOFF_LENGTH = 105.0;
const double constexpr STRAIGHT_ANGLE = 180.;
// check if a step is completely without turn type
inline bool hasTurnType(const RouteStep &step)
@@ -104,6 +105,12 @@ inline void setInstructionType(RouteStep &step, const osrm::guidance::TurnType::
step.maneuver.instruction.type = type;
}
// alias for readability
inline void setModifier(RouteStep &step, const osrm::guidance::DirectionModifier::Enum modifier)
{
step.maneuver.instruction.direction_modifier = modifier;
}
// alias for readability
inline bool haveSameMode(const RouteStep &lhs, const RouteStep &rhs)
{
@@ -215,6 +222,86 @@ inline double totalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_
return total_angle;
}
// check bearings for u-turns.
// since bearings are wrapped around at 0 (we only support 0,360), we need to do some minor math to
// check if bearings `a` and `b` go in opposite directions. In general we accept some minor
// deviations for u-turns.
inline bool bearingsAreReversed(const double bearing_in, const double bearing_out)
{
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
const double left_turn_angle = [&]() {
if (0 <= bearing_out && bearing_out <= bearing_in)
return bearing_in - bearing_out;
return bearing_in + 360 - bearing_out;
}();
return util::angularDeviation(left_turn_angle, 180) <= 35;
}
// Returns true if the specified step has only one intersection
inline bool hasSingleIntersection(const RouteStep &step)
{
return (step.intersections.size() == 1);
}
// Returns true if the specified angle is a wider straight turn
inline bool isWiderStraight(const double angle) { return (angle >= 125 && angle <= 235); }
// Returns the straightest intersecting edge turn for the specified step
inline double getStraightestIntersectingEdgeTurn(const RouteStep &step)
{
const auto &intersection = step.intersections.front();
const double bearing_in = util::bearing::reverse(intersection.bearings[intersection.in]);
double staightest_turn = 360.;
double staightest_delta = 360.;
for (std::size_t i = 0; i < intersection.bearings.size(); ++i)
{
// Skip the in, out, and non-traversable edges
if ((i == intersection.in) || (i == intersection.out) || !intersection.entry.at(i))
continue;
double intersecting_turn =
util::bearing::angleBetween(bearing_in, intersection.bearings.at(i));
double straight_delta = util::angularDeviation(intersecting_turn, STRAIGHT_ANGLE);
if (straight_delta < staightest_delta)
{
staightest_delta = straight_delta;
staightest_turn = intersecting_turn;
}
}
return staightest_turn;
}
// Returns true if the specified step has the straightest turn as compared to
// the intersecting edges
inline bool hasStraightestTurn(const RouteStep &step)
{
const auto &intersection = step.intersections.front();
const double path_turn =
util::bearing::angleBetween(util::bearing::reverse(intersection.bearings[intersection.in]),
intersection.bearings[intersection.out]);
// Path turn must be a wider straight
if (isWiderStraight(path_turn))
{
const double straightest_intersecting_turn = getStraightestIntersectingEdgeTurn(step);
const double path_straight_delta = util::angularDeviation(path_turn, STRAIGHT_ANGLE);
const double intersecting_straight_delta =
util::angularDeviation(straightest_intersecting_turn, STRAIGHT_ANGLE);
const double path_intersecting_delta =
util::angularDeviation(path_turn, straightest_intersecting_turn);
// Add some fuzz - the delta between the path and intersecting turn must be greater
// than 10 in order to consider using the intersecting turn as the straightest
return ((path_intersecting_delta > 10.)
? (path_straight_delta <= intersecting_straight_delta)
: true);
}
return false;
}
} /* namespace guidance */
} /* namespace engine */
} /* namespace osrm */