From 6d64f93463e03013d63c8f7cf2b71ca5229512ee Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 26 Apr 2022 18:28:05 +0300 Subject: [PATCH 01/28] [Routing] Refactoring Unnesesary checks removed Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 6a1c0246d7..919edbc3b9 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -282,21 +282,6 @@ bool KeepRoundaboutTurnByHighwayClass(TurnCandidates const & possibleTurns, return false; } -bool KeepTurnByIngoingEdges(m2::PointD const & junctionPoint, - m2::PointD const & ingoingPointOneSegment, - m2::PointD const & outgoingPoint, bool hasMultiTurns, - size_t const ingoingEdgesCount) -{ - double const turnAngle = - base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPointOneSegment, outgoingPoint)); - bool const isGoStraightOrSlightTurn = IsGoStraightOrSlightTurn(IntermediateDirection(turnAngle)); - - // The code below is responsible for cases when there is only one way to leave the junction. - // Such junction has to be kept as a turn when it's not a slight turn and it has ingoing edges - // (one or more); - return hasMultiTurns || (!isGoStraightOrSlightTurn && ingoingEdgesCount > 0); -} - bool FixupLaneSet(CarDirection turn, vector & lanes, function checker) { @@ -1177,17 +1162,6 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex if (IsGoStraightOrSlightTurn(turn.m_turn)) { - auto const notSoCloseToTheTurnPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_notSoCloseMaxPointsCount, - vehicleSettings.m_notSoCloseMaxDistMeters, false /* forward */); - - // Removing a slight turn if there's only one way to leave the turn and there's no ingoing edges. - if (!KeepTurnByIngoingEdges(junctionPoint, notSoCloseToTheTurnPoint, outgoingPoint, hasMultiTurns, ingoingCount)) - { - turn.m_turn = CarDirection::None; - return; - } - // Removing a slight turn if ingoing and outgoing edges are not links and all other // possible ways out (except of uturn) are links. if (!turnInfo.m_ingoing.m_isLink && !turnInfo.m_outgoing.m_isLink && -- 2.45.3 From 48acd889c0e9a88e4648d1b63c1ddde6e6bbaccb Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 15:54:24 +0300 Subject: [PATCH 02/28] [Routing] Refactoring Tests updates. Tuning of algorithm to pass the tests. Signed-off-by: Anton Makouski --- .../bicycle_turn_test.cpp | 39 ++-- .../routing_test_tools.cpp | 3 + .../routing_integration_tests/turn_test.cpp | 61 +++--- .../routing_tests/turns_generator_test.cpp | 17 +- routing/turns_generator.cpp | 192 ++++++++++++++---- routing/turns_generator.hpp | 2 +- 6 files changed, 217 insertions(+), 97 deletions(-) diff --git a/routing/routing_integration_tests/bicycle_turn_test.cpp b/routing/routing_integration_tests/bicycle_turn_test.cpp index 8879d75af9..252c1be174 100644 --- a/routing/routing_integration_tests/bicycle_turn_test.cpp +++ b/routing/routing_integration_tests/bicycle_turn_test.cpp @@ -63,7 +63,7 @@ UNIT_TEST(RussiaMoscowSalameiNerisPossibleTurnCorrectionBicycleWayTurnTest) integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -UNIT_TEST(RussiaMoscowSalameiNerisNoUTurnBicycleWayTurnTest) +UNIT_TEST(Russia_Moscow_SalameiNerisNoUTurnBicycleWay_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Bicycle), @@ -74,11 +74,20 @@ UNIT_TEST(RussiaMoscowSalameiNerisNoUTurnBicycleWayTurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo This route goes not as expected after adding surface=ground here: + /// @note This route goes not as expected after adding surface=ground here: /// https://www.openstreetmap.org/way/605548369. - integration::TestTurnCount(route, 2 /* expectedTurnCount */); + + // Test for unexpected but factual route. + integration::TestTurnCount(route, 3 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); + // Test for expected but not factual route. + /* + integration::TestTurnCount(route, 2); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); + */ } UNIT_TEST(RussiaMoscowSevTushinoParkBicycleOnePointOnewayRoadTurnTest) @@ -101,7 +110,7 @@ UNIT_TEST(RussiaMoscowSevTushinoParkBicycleOnePointTwowayRoadTurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); } -UNIT_TEST(RussiaMoscowTatishchevaOnewayCarRoadTurnTest) +UNIT_TEST(Russia_Moscow_TatishchevaOnewayCarRoad_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Bicycle), @@ -116,13 +125,14 @@ UNIT_TEST(RussiaMoscowTatishchevaOnewayCarRoadTurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); + // @todo No potential additioanal GoStraight turn 2. Currently is kept because of 35 degrees TurnSlightLeft alternative. integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnRight); integration::TestRouteLength(route, 320.0); } -UNIT_TEST(RussiaMoscowSvobodiOnewayBicycleWayTurnTest) +UNIT_TEST(Russia_Moscow_SvobodiOnewayBicycleWay_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Bicycle), @@ -133,15 +143,14 @@ UNIT_TEST(RussiaMoscowSvobodiOnewayBicycleWayTurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 6 /* expectedTurnCount */); + integration::TestTurnCount(route, 5 /* expectedTurnCount */); - integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); - integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnSlightRight); - integration::GetNthTurn(route, 3).TestValid().TestOneOfDirections( + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightRight); + integration::GetNthTurn(route, 2).TestValid().TestOneOfDirections( {CarDirection::TurnSlightLeft, CarDirection::TurnLeft}); - integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnSlightLeft); - integration::GetNthTurn(route, 5).TestValid().TestDirection(CarDirection::TurnLeft); - integration::GetNthTurn(route, 6).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnLeft); integration::TestRouteLength(route, 768.0); } @@ -157,12 +166,12 @@ UNIT_TEST(TurnsNearAltufievskoeShosseLongFakeSegmentTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 4 /* expectedTurnCount */); + integration::TestTurnCount(route, 3 /* expectedTurnCount */); + /// @todo For some reason for both nodes.candidates of (turn_m_index == 3) angles are 0, but it is expected to be -90 and +90. integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); - integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnSlightLeft); - integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); integration::TestRouteLength(route, 279.0); } diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index 7de64e7dab..6afb4a9728 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -297,7 +297,10 @@ TestTurn GetNthTurn(routing::Route const & route, uint32_t turnNumber) vector turns; route.GetTurnsForTesting(turns); if (turnNumber >= turns.size()) + { + ASSERT(false, ()); return TestTurn(); + } TurnItem const & turn = turns[turnNumber]; return TestTurn(route.GetPoly().GetPoint(turn.m_index), turn.m_turn, turn.m_exitNum); diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 1e55ec1e92..59a61a75e6 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -45,7 +45,7 @@ UNIT_TEST(StPetersburgSideRoadPenaltyTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaMoscowLenigradskiy39UturnTurnTest) +UNIT_TEST(Russia_Moscow_Lenigradskiy39Uturn_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -56,17 +56,12 @@ UNIT_TEST(RussiaMoscowLenigradskiy39UturnTurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo Current algorithm starts from private service road (it's closer) - /// but not from Leningradskiy prospekt (it's more correct), so it adds 1 additional turn. - integration::TestTurnCount(route, 3 /* expectedTurnCount */); + integration::TestTurnCount(route, 4 /* expectedTurnCount */); - integration::GetNthTurn(route, 0) - .TestValid() - .TestDirection(CarDirection::UTurnLeft); - integration::GetNthTurn(route, 1) - .TestValid() - .TestDirection(CarDirection::TurnRight); - integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::UTurnLeft); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnLeft); integration::TestRouteLength(route, 2050.); } @@ -167,7 +162,7 @@ UNIT_TEST(Russia_Moscow_PlanetnayaOnlyStraight_TurnTest) integration::TestRouteLength(route, 418.0); } -UNIT_TEST(RussiaMoscowNoTurnsOnMKADTurnTest) +UNIT_TEST(Russia_Moscow_NoTurnsOnMKAD_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -237,7 +232,7 @@ UNIT_TEST(RussiaMoscowParallelResidentalUTurnAvoiding) integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); } -UNIT_TEST(RussiaMoscowPankratevskiPerBolshaySuharedskazPloschadTurnTest) +UNIT_TEST(Russia_Moscow_PankratevskiPerBolshaySuharedskazPloschad_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -251,8 +246,13 @@ UNIT_TEST(RussiaMoscowPankratevskiPerBolshaySuharedskazPloschadTurnTest) std::vector t; route.GetTurnsForTesting(t); - // It's not possible to get destination with less number of turns due to oneway roads. - TEST_GREATER_OR_EQUAL(t.size(), 5, ()); + + integration::TestTurnCount(route, 5 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnSlightLeft); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnSharpLeft); } UNIT_TEST(RussiaMoscowMKADPutilkovskeShosseTurnTest) @@ -355,7 +355,7 @@ UNIT_TEST(RussiaMoscowMKADLeningradkaTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(BelarusMKADShosseinai) +UNIT_TEST(Belarus_Vitebsk_Shosseinai_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -396,11 +396,10 @@ UNIT_TEST(Russia_Moscow_VarshavskoeShosseMKAD_TurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 2 /* expectedTurnCount */); + integration::TestTurnCount(route, 1 /* expectedTurnCount */); - /// @todo TurnSlightLeft or GoStraight or no-turn-instruction when keep highway and don't turn on link? - integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); - integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::ExitHighwayToRight); + /// @todo No potential additioanal turn 0 (1 alternative way is link another is much smaller (service)). + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } UNIT_TEST(RussiaMoscowBolshayaNikitskayaOkhotnyRyadTest) @@ -540,7 +539,7 @@ UNIT_TEST(RussiaMoscowSvobodaStTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -UNIT_TEST(RussiaTiinskTest) +UNIT_TEST(Russia_Tiinsk_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -570,7 +569,7 @@ UNIT_TEST(NetherlandsGorinchemBridgeTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaVoronezhProspTrudaTest) +UNIT_TEST(Russia_Voronezh_ProspTruda_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -583,8 +582,7 @@ UNIT_TEST(RussiaVoronezhProspTrudaTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); - integration::GetNthTurn(route, 0).TestValid().TestOneOfDirections( - {CarDirection::TurnSlightRight, CarDirection::TurnRight}); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); } UNIT_TEST(GermanyFrankfurtAirportTest) @@ -737,18 +735,21 @@ UNIT_TEST(RussiaMoscowMKADToSvobodaTest) } // Test that there's no turns if to follow MKAD. -UNIT_TEST(RussiaMoscowMKADTest) +UNIT_TEST(Netherlands_Crazy_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), mercator::FromLatLon(52.15866, 5.56538), {0., 0.}, - mercator::FromLatLon(52.16668, 5.55665)); + mercator::FromLatLon(52.17042, 5.55834)); Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 1 /* expectedTurnCount */); + /// @todo Reasonable solution from GraphHopper: + // https://www.openstreetmap.org/directions?engine=graphhopper_car&route=52.16783%2C5.56589%3B52.16940%2C5.56270#map=19/52.16916/5.56537 + integration::TestTurnCount(route, 1); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); } UNIT_TEST(NetherlandsBarneveldTest) @@ -780,11 +781,11 @@ UNIT_TEST(Belarus_Minsk_TurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); - integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); } // Test on building route from a point close to mwm border to a point close to a mwm border. -UNIT_TEST(EnglandLondonExitToLeftTest) +UNIT_TEST(England_London_ExitToLeft_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -793,7 +794,7 @@ UNIT_TEST(EnglandLondonExitToLeftTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; - + /// @note Important test since different mwms for one segment are used and this can cause extra GoStraight. TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToLeft); diff --git a/routing/routing_tests/turns_generator_test.cpp b/routing/routing_tests/turns_generator_test.cpp index a4e551195b..9c4a4c7da7 100644 --- a/routing/routing_tests/turns_generator_test.cpp +++ b/routing/routing_tests/turns_generator_test.cpp @@ -436,44 +436,45 @@ UNIT_TEST(GetNextRoutePointIndex) // Forward direction. TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, nextIndex), ()); + NumMwmIds(), true /* forward */, true, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, nextIndex), ()); + NumMwmIds(), true /* forward */, true, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), ()); // Trying to get next item after the last item of the first segment. + // False because of too sharp turn angle. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, nextIndex), ()); + NumMwmIds(), true /* forward */, true, nextIndex), ()); // Trying to get point about the end of the route. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({1 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, nextIndex), ()); + NumMwmIds(), true /* forward */, true, nextIndex), ()); // Backward direction. // Moving in backward direction it's possible to get index of the first item of a segment. TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({1 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, nextIndex), ()); + NumMwmIds(), false /* forward */, true, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({1 /* m_segmentIndex */, 0 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, nextIndex), ()); + NumMwmIds(), false /* forward */, true, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, nextIndex), ()); + NumMwmIds(), false /* forward */, true, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), ()); // Trying to get point before the beginning. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, nextIndex), ()); + NumMwmIds(), false /* forward */, true, nextIndex), ()); } } // namespace diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 919edbc3b9..e82e5355da 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -375,7 +375,7 @@ m2::PointD GetPointByIndex(TUnpackedPathSegments const & segments, RoutePointInd */ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegmentIndex, NumMwmIds const & numMwmIds, size_t const maxPointsCount, - double const maxDistMeters, bool forward) + double const maxDistMeters, bool const forward) { auto const & segments = result.GetSegments(); ASSERT_LESS(outgoingSegmentIndex, segments.size(), ()); @@ -396,9 +396,9 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment size_t count = 0; double curDistanceMeters = 0.0; - ASSERT(GetNextRoutePointIndex(result, index, numMwmIds, forward, nextIndex), ()); + ASSERT(GetNextRoutePointIndex(result, index, numMwmIds, forward, false, nextIndex), ()); - while (GetNextRoutePointIndex(result, index, numMwmIds, forward, nextIndex)) + do { nextPoint = GetPointByIndex(segments, nextIndex); @@ -415,6 +415,7 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment point = nextPoint; index = nextIndex; } + while (GetNextRoutePointIndex(result, index, numMwmIds, forward, true, nextIndex)); return nextPoint; } @@ -440,6 +441,17 @@ double GetOneSegmentTurnAngle(TurnInfo const & turnInfo) turnInfo.m_outgoing.m_path[1].GetPoint())); } +double GetPathTurnAngle(LoadedPathSegment const & segment, size_t const pathIndex) +{ + ASSERT_GREATER_OR_EQUAL(segment.m_path.size(), 3, ()); + ASSERT_GREATER(pathIndex, 0, ()); + ASSERT_LESS(pathIndex, segment.m_path.size() - 1, ()); + + return base::RadToDeg(PiMinusTwoVectorsAngle(segment.m_path[pathIndex].GetPoint(), + segment.m_path[pathIndex - 1].GetPoint(), + segment.m_path[pathIndex + 1].GetPoint())); +} + /*! * \brief Calculates |nextIndex| which is an index of next route point at result.GetSegments() * in forward direction. @@ -453,7 +465,7 @@ double GetOneSegmentTurnAngle(TurnInfo const & turnInfo) * if |index| points at the first or intermediate point in turn segment returns the next one. * \returns true if |nextIndex| fills correctly and false otherwise. */ -bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, +bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, bool const smoothOnly, NumMwmIds const & numMwmIds, RoutePointIndex & nextIndex) { auto const & segments = result.GetSegments(); @@ -476,7 +488,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde double const oneSegmentTurnAngle = GetOneSegmentTurnAngle(turnInfo); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); - if (!IsGoStraightOrSlightTurn(oneSegmentDirection)) + if (smoothOnly && !IsGoStraightOrSlightTurn(oneSegmentDirection)) return false; // Too sharp turn angle. size_t ingoingCount = 0; @@ -510,11 +522,20 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde return false; } -bool GetPrevInSegmentRoutePoint(RoutePointIndex const & index, RoutePointIndex & nextIndex) +bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, bool const smoothOnly, RoutePointIndex & nextIndex) { if (index.m_pathIndex == 0) return false; + auto const & segments = result.GetSegments(); + if (smoothOnly && segments[index.m_segmentIndex].m_path.size() >= 3 && index.m_pathIndex < segments[index.m_segmentIndex].m_path.size() - 1) + { + double const oneSegmentTurnAngle = GetPathTurnAngle(segments[index.m_segmentIndex], index.m_pathIndex); + CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); + if (!IsGoStraightOrSlightTurn(oneSegmentDirection)) + return false; // Too sharp turn angle. + } + nextIndex = {index.m_segmentIndex, index.m_pathIndex - 1}; return true; } @@ -525,12 +546,17 @@ bool GetPrevInSegmentRoutePoint(RoutePointIndex const & index, RoutePointIndex & * the route) is checked. If the other way is "go straight" or "slight turn", turn.m_turn is set * to |turnToSet|. */ -void GoStraightCorrection(TurnCandidate const & notRouteCandidate, CarDirection turnToSet, +void GoStraightCorrection(TurnCandidate const & notRouteCandidate, double const routeAngle, CarDirection const & turnToSet, TurnItem & turn) { if (turn.m_turn != CarDirection::GoStraight) return; + double const kMinAngleDiffToNotConfuse = 25.0; + + if (abs(notRouteCandidate.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuse) + return; + if (!IsGoStraightOrSlightTurn(IntermediateDirection(notRouteCandidate.m_angle))) return; @@ -572,16 +598,17 @@ bool TurnInfo::IsSegmentsValid() const } bool GetNextRoutePointIndex(IRoutingResult const & result, RoutePointIndex const & index, - NumMwmIds const & numMwmIds, bool forward, RoutePointIndex & nextIndex) + NumMwmIds const & numMwmIds, bool const forward, bool const smoothOnly, + RoutePointIndex & nextIndex) { if (forward) { - if (!GetNextCrossSegmentRoutePoint(result, index, numMwmIds, nextIndex)) + if (!GetNextCrossSegmentRoutePoint(result, index, smoothOnly, numMwmIds, nextIndex)) return false; } else { - if (!GetPrevInSegmentRoutePoint(index, nextIndex)) + if (!GetPrevInSegmentRoutePoint(result, index, smoothOnly, nextIndex)) return false; } @@ -934,7 +961,7 @@ CarDirection InvertDirection(CarDirection dir) CarDirection RightmostDirection(const double angle) { static vector> const kLowerBounds = { - {157., CarDirection::TurnSharpRight}, + {145., CarDirection::TurnSharpRight}, {50., CarDirection::TurnRight}, {10., CarDirection::TurnSlightRight}, // For sure it's incorrect to give directions TurnLeft or TurnSlighLeft if we need the rightmost turn. @@ -953,12 +980,12 @@ CarDirection LeftmostDirection(const double angle) CarDirection IntermediateDirection(const double angle) { static vector> const kLowerBounds = { - {157., CarDirection::TurnSharpRight}, + {145., CarDirection::TurnSharpRight}, {50., CarDirection::TurnRight}, {10., CarDirection::TurnSlightRight}, {-10., CarDirection::GoStraight}, {-50., CarDirection::TurnSlightLeft}, - {-157., CarDirection::TurnLeft}, + {-145., CarDirection::TurnLeft}, {-180., CarDirection::TurnSharpLeft}}; return FindDirectionByAngle(kLowerBounds, angle); @@ -1047,7 +1074,70 @@ bool PathIsFakeLoop(std::vector const & path) return path.size() == 2 && path[0] == path[1]; } -void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex, +double CalcTurnAngle(IRoutingResult const & result, + size_t const outgoingSegmentIndex, + bool const isStartFakeLoop, + TurnInfo const & turnInfo, + NumMwmIds const & numMwmIds, + RoutingSettings const & vehicleSettings) +{ + m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); + size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; + + m2::PointD const ingoingPoint = GetPointForTurn( + result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_maxIngoingPointsCount, + vehicleSettings.m_minIngoingDistMeters, false /* forward */); + m2::PointD const outgoingPoint = GetPointForTurn( + result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, + vehicleSettings.m_minOutgoingDistMeters, true /* forward */); + + double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); + return turnAngle; +} + +double CalcTurnBasicAngle(IRoutingResult const & result, + size_t const outgoingSegmentIndex, + bool const isStartFakeLoop, + TurnInfo const & turnInfo, + NumMwmIds const & numMwmIds) +{ + m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); + size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; + + m2::PointD const ingoingPoint = GetPointForTurn( + result, segmentIndexForIngoingPoint, numMwmIds, 1, + 100, false /* forward */); + m2::PointD const outgoingPoint = GetPointForTurn( + result, outgoingSegmentIndex, numMwmIds, 1, + 100, true /* forward */); + + double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); + return turnAngle; +} + +void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, + size_t const outgoingSegmentIndex, + bool const isStartFakeLoop, + TurnInfo const & turnInfo, + NumMwmIds const & numMwmIds, + Segment const & firstOutgoingSeg, + std::vector & candidates) +{ + auto IsFirstOutgoingSeg = [&firstOutgoingSeg](TurnCandidate const & turnCandidate) { return turnCandidate.m_segment == firstOutgoingSeg; }; + if (find_if(candidates.begin(), candidates.end(), IsFirstOutgoingSeg) == candidates.end()) + { + double const turnAngle = CalcTurnBasicAngle(result, outgoingSegmentIndex, isStartFakeLoop, turnInfo, numMwmIds); + auto DoesAngleMatch = [&turnAngle](TurnCandidate const & turnCandidate) { return base::AlmostEqualAbs(turnCandidate.m_angle, turnAngle, 0.001); }; + auto it = find_if(candidates.begin(), candidates.end(), DoesAngleMatch); + if (it != candidates.end()) + { + ASSERT(it->m_segment.GetMwmId() != firstOutgoingSeg.GetMwmId(), ()); + it->m_segment = firstOutgoingSeg; + } + } +} + +void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmentIndex, NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, TurnItem & turn) { @@ -1086,19 +1176,6 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex turnInfo.m_outgoing.m_path.front().GetPoint()), kFeaturesNearTurnMeters, ()); - m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); - size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; - - m2::PointD const ingoingPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_maxIngoingPointsCount, - vehicleSettings.m_minIngoingDistMeters, false /* forward */); - m2::PointD const outgoingPoint = GetPointForTurn( - result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, - vehicleSettings.m_minOutgoingDistMeters, true /* forward */); - - double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); - CarDirection const intermediateDirection = IntermediateDirection(turnAngle); - turn.m_keepAnyway = (!turnInfo.m_ingoing.m_isLink && turnInfo.m_outgoing.m_isLink); turn.m_sourceName = turnInfo.m_ingoing.m_name; turn.m_targetName = turnInfo.m_outgoing.m_name; @@ -1107,6 +1184,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex ASSERT_GREATER(turnInfo.m_ingoing.m_path.size(), 1, ()); TurnCandidates nodes; size_t ingoingCount; + m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); result.GetPossibleTurns(turnInfo.m_ingoing.m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { @@ -1118,6 +1196,15 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex if (nodes.candidates.size() == 0) return; + Segment firstOutgoingSeg; + bool const isFirstOutgoingSegValid = + turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + + // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. + // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are + // from different mwms. + CandidatesSegmentCorrectionByOutgoing(result, outgoingSegmentIndex, isStartFakeLoop, turnInfo, numMwmIds, firstOutgoingSeg, nodes.candidates); + bool const hasMultiTurns = HasMultiTurns(numMwmIds, nodes, turnInfo); RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); auto const & turnCandidates = nodes.candidates; @@ -1135,10 +1222,14 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex return; } + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, isStartFakeLoop, + turnInfo, numMwmIds, vehicleSettings); + CarDirection const intermediateDirection = IntermediateDirection(turnAngle); + // Checking for exits from highways. - Segment firstOutgoingSeg; - bool const isFirstOutgoingSegValid = - turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + // Segment firstOutgoingSeg; + //bool const isFirstOutgoingSegValid = + // turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); if (isFirstOutgoingSegValid && IsExit(nodes, turnInfo, firstOutgoingSeg, intermediateDirection, turn.m_turn)) { @@ -1164,6 +1255,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex { // Removing a slight turn if ingoing and outgoing edges are not links and all other // possible ways out (except of uturn) are links. + /// @todo Add condition of links m_highwayClass to be higher. if (!turnInfo.m_ingoing.m_isLink && !turnInfo.m_outgoing.m_isLink && turnInfo.m_ingoing.m_highwayClass == turnInfo.m_outgoing.m_highwayClass && GetLinkCount(turnCandidates) + 1 == turnCandidates.size()) @@ -1183,23 +1275,37 @@ void GetTurnDirection(IRoutingResult const & result, size_t outgoingSegmentIndex // to avoid ambiguity: 2 or more almost straight turns and GoStraight direction. // turnCandidates are sorted by angle from leftmost to rightmost. - if (turnCandidates.front().m_segment == firstOutgoingSeg) + // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. + if (adjacent_find(nodes.candidates.begin(), nodes.candidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == nodes.candidates.end()) { - // The route goes along the leftmost candidate. - turn.m_turn = LeftmostDirection(turnAngle); - // Compare with the closest left candidate. - GoStraightCorrection(turnCandidates[1], CarDirection::TurnSlightLeft, turn); + if (turnCandidates.front().m_segment == firstOutgoingSeg) + { + // The route goes along the leftmost candidate. + turn.m_turn = LeftmostDirection(turnAngle); + // Compare with the closest left candidate. + GoStraightCorrection(turnCandidates[1], turnCandidates.front().m_angle, CarDirection::TurnSlightLeft, turn); + } + else if (turnCandidates.back().m_segment == firstOutgoingSeg) + { + // The route goes along the rightmost candidate. + turn.m_turn = RightmostDirection(turnAngle); + // Compare with the closest right candidate. + GoStraightCorrection(turnCandidates[turnCandidates.size() - 2], turnCandidates.back().m_angle, CarDirection::TurnSlightRight, turn); + } } - else if (turnCandidates.back().m_segment == firstOutgoingSeg) + else + LOG(LWARNING, ("nodes.candidates are not expected to have same m_angle.")); + } + else // turnCandidates.size() == 1 + { + if (nodes.candidates.front().m_segment == firstOutgoingSeg) { - // The route goes along the rightmost candidate. - turn.m_turn = RightmostDirection(turnAngle); - // Compare with the closest right candidate. - GoStraightCorrection(turnCandidates[turnCandidates.size() - 2], CarDirection::TurnSlightRight, turn); + if (ingoingCount <= 1) + { + turn.m_turn = CarDirection::None; + return; + } } - // Note. It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. - // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are - // from different mwms. } } diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index ff1871bda2..91d2496ff4 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -84,7 +84,7 @@ struct TurnInfo * of a segment. But it's impossible moving in forward direction. */ bool GetNextRoutePointIndex(IRoutingResult const & result, RoutePointIndex const & index, - NumMwmIds const & numMwmIds, bool forward, RoutePointIndex & nextIndex); + NumMwmIds const & numMwmIds, bool const forward, bool const smoothOnly, RoutePointIndex & nextIndex); /*! * \brief Compute turn and time estimation structs for the abstract route result. -- 2.45.3 From 9d04ee602c4cd93ffa49cfeb7cd156ccc9df2060 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 16:38:07 +0300 Subject: [PATCH 03/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 54 +++++++++++++------------------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index e82e5355da..f5421bf417 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1076,40 +1076,22 @@ bool PathIsFakeLoop(std::vector const & path) double CalcTurnAngle(IRoutingResult const & result, size_t const outgoingSegmentIndex, - bool const isStartFakeLoop, - TurnInfo const & turnInfo, - NumMwmIds const & numMwmIds, - RoutingSettings const & vehicleSettings) + m2::PointD const & junctionPoint, + NumMwmIds const & numMwmIds, + size_t maxIngoingPointsCount, + double const maxIngoingDistMeters, + size_t maxOutgoingPointsCount, + double const maxOutgoingDistMeters) { - m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); + bool const isStartFakeLoop = PathIsFakeLoop(result.GetSegments()[outgoingSegmentIndex - 1].m_path); size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; m2::PointD const ingoingPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_maxIngoingPointsCount, - vehicleSettings.m_minIngoingDistMeters, false /* forward */); + result, segmentIndexForIngoingPoint, numMwmIds, maxIngoingPointsCount, + maxIngoingDistMeters, false /* forward */); m2::PointD const outgoingPoint = GetPointForTurn( - result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, - vehicleSettings.m_minOutgoingDistMeters, true /* forward */); - - double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); - return turnAngle; -} - -double CalcTurnBasicAngle(IRoutingResult const & result, - size_t const outgoingSegmentIndex, - bool const isStartFakeLoop, - TurnInfo const & turnInfo, - NumMwmIds const & numMwmIds) -{ - m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); - size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; - - m2::PointD const ingoingPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, 1, - 100, false /* forward */); - m2::PointD const outgoingPoint = GetPointForTurn( - result, outgoingSegmentIndex, numMwmIds, 1, - 100, true /* forward */); + result, outgoingSegmentIndex, numMwmIds, maxOutgoingPointsCount, + maxOutgoingDistMeters, true /* forward */); double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); return turnAngle; @@ -1117,8 +1099,7 @@ double CalcTurnBasicAngle(IRoutingResult const & result, void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, size_t const outgoingSegmentIndex, - bool const isStartFakeLoop, - TurnInfo const & turnInfo, + m2::PointD const & junctionPoint, NumMwmIds const & numMwmIds, Segment const & firstOutgoingSeg, std::vector & candidates) @@ -1126,7 +1107,7 @@ void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, auto IsFirstOutgoingSeg = [&firstOutgoingSeg](TurnCandidate const & turnCandidate) { return turnCandidate.m_segment == firstOutgoingSeg; }; if (find_if(candidates.begin(), candidates.end(), IsFirstOutgoingSeg) == candidates.end()) { - double const turnAngle = CalcTurnBasicAngle(result, outgoingSegmentIndex, isStartFakeLoop, turnInfo, numMwmIds); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, 1, 0.0, 1, 0.0); auto DoesAngleMatch = [&turnAngle](TurnCandidate const & turnCandidate) { return base::AlmostEqualAbs(turnCandidate.m_angle, turnAngle, 0.001); }; auto it = find_if(candidates.begin(), candidates.end(), DoesAngleMatch); if (it != candidates.end()) @@ -1203,7 +1184,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are // from different mwms. - CandidatesSegmentCorrectionByOutgoing(result, outgoingSegmentIndex, isStartFakeLoop, turnInfo, numMwmIds, firstOutgoingSeg, nodes.candidates); + CandidatesSegmentCorrectionByOutgoing(result, outgoingSegmentIndex, junctionPoint, numMwmIds, firstOutgoingSeg, nodes.candidates); bool const hasMultiTurns = HasMultiTurns(numMwmIds, nodes, turnInfo); RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); @@ -1222,8 +1203,11 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen return; } - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, isStartFakeLoop, - turnInfo, numMwmIds, vehicleSettings); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, + vehicleSettings.m_maxIngoingPointsCount, + vehicleSettings.m_minIngoingDistMeters, + vehicleSettings.m_maxOutgoingPointsCount, + vehicleSettings.m_minOutgoingDistMeters); CarDirection const intermediateDirection = IntermediateDirection(turnAngle); // Checking for exits from highways. -- 2.45.3 From ca8ee94526f9321836795dcbf039de88ee2de771 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 16:55:42 +0300 Subject: [PATCH 04/28] [Routing] Refactoring TurnTest names unification Signed-off-by: Anton Makouski --- .../routing_integration_tests/turn_test.cpp | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 59a61a75e6..eed3254ae5 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -10,7 +10,7 @@ using namespace routing; using namespace routing::turns; -UNIT_TEST(RussiaMoscowNagatinoUturnTurnTest) +UNIT_TEST(Russia_Moscow_NagatinoUturn_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -31,7 +31,7 @@ UNIT_TEST(RussiaMoscowNagatinoUturnTurnTest) } // Secondary should be preferred against residential. -UNIT_TEST(StPetersburgSideRoadPenaltyTest) +UNIT_TEST(StPetersburg_SideRoadPenalty_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -66,7 +66,7 @@ UNIT_TEST(Russia_Moscow_Lenigradskiy39Uturn_TurnTest) integration::TestRouteLength(route, 2050.); } -UNIT_TEST(RussiaMoscowSalameiNerisUturnTurnTest) +UNIT_TEST(Russia_Moscow_SalameiNerisUturn_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -100,7 +100,7 @@ UNIT_TEST(RussiaMoscowSalameiNerisUturnTurnTest) } // Fails because consider service roads are roundabout exits. -UNIT_TEST(RussiaMoscowTrikotagniAndPohodniRoundaboutTurnTest) +UNIT_TEST(Russia_Moscow_TrikotagniAndPohodniRoundabout_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -121,7 +121,7 @@ UNIT_TEST(RussiaMoscowTrikotagniAndPohodniRoundaboutTurnTest) integration::TestRouteLength(route, 387.); } -UNIT_TEST(SwedenBarlangeRoundaboutTurnTest) +UNIT_TEST(Sweden_BarlangeRoundabout_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -182,7 +182,7 @@ UNIT_TEST(Russia_Moscow_NoTurnsOnMKAD_TurnTest) integration::TestRouteLength(route, 43233.7); } -UNIT_TEST(RussiaMoscowTTKVarshavskoeShosseOutTurnTest) +UNIT_TEST(Russia_Moscow_TTKVarshavskoeShosseOut_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -197,7 +197,7 @@ UNIT_TEST(RussiaMoscowTTKVarshavskoeShosseOutTurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(RussiaMoscowTTKUTurnTest) +UNIT_TEST(Russia_Moscow_TTKU_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -215,7 +215,7 @@ UNIT_TEST(RussiaMoscowTTKUTurnTest) integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -UNIT_TEST(RussiaMoscowParallelResidentalUTurnAvoiding) +UNIT_TEST(Russia_Moscow_ParallelResidentalUTurnAvoiding_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -255,7 +255,7 @@ UNIT_TEST(Russia_Moscow_PankratevskiPerBolshaySuharedskazPloschad_TurnTest) integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnSharpLeft); } -UNIT_TEST(RussiaMoscowMKADPutilkovskeShosseTurnTest) +UNIT_TEST(Russia_Moscow_MKADPutilkovskeShosse_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -270,7 +270,7 @@ UNIT_TEST(RussiaMoscowMKADPutilkovskeShosseTurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(RussiaMoscowPetushkovaShodniaReverTurnTest) +UNIT_TEST(Russia_Moscow_PetushkovaShodniaRever_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -284,7 +284,7 @@ UNIT_TEST(RussiaMoscowPetushkovaShodniaReverTurnTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaHugeRoundaboutTurnTest) +UNIT_TEST(Russia_HugeRoundabout_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -306,7 +306,7 @@ UNIT_TEST(RussiaHugeRoundaboutTurnTest) .TestRoundAboutExitNum(5); } -UNIT_TEST(BelarusMiskProspNezavisimostiMKADTurnTest) +UNIT_TEST(Belarus_Misk_ProspNezavisimostiMKAD_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -323,7 +323,7 @@ UNIT_TEST(BelarusMiskProspNezavisimostiMKADTurnTest) // Test case: turning form one street to another one with the same name. // An end user shall be informed about this manoeuvre. -UNIT_TEST(RussiaMoscowPetushkovaPetushkovaTest) +UNIT_TEST(Russia_Moscow_PetushkovaPetushkova_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -340,7 +340,7 @@ UNIT_TEST(RussiaMoscowPetushkovaPetushkovaTest) // Test case: a route goes straight along a unnamed big link road when joined a small road. // An end user shall not be informed about such manoeuvres. -UNIT_TEST(RussiaMoscowMKADLeningradkaTest) +UNIT_TEST(Russia_Moscow_MKADLeningradka_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -370,7 +370,7 @@ UNIT_TEST(Belarus_Vitebsk_Shosseinai_TurnTest) } // A route goes straight along a big road ignoring joined small roads. -UNIT_TEST(ThailandPhuketNearPrabarameeRoad) +UNIT_TEST(Thailand_Phuket_NearPrabarameeRoad_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -402,7 +402,7 @@ UNIT_TEST(Russia_Moscow_VarshavskoeShosseMKAD_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(RussiaMoscowBolshayaNikitskayaOkhotnyRyadTest) +UNIT_TEST(Russia_Moscow_BolshayaNikitskayaOkhotnyRyad_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -418,7 +418,7 @@ UNIT_TEST(RussiaMoscowBolshayaNikitskayaOkhotnyRyadTest) integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightRight); } -UNIT_TEST(RussiaMoscowTverskajaOkhotnyRyadTest) +UNIT_TEST(Russia_Moscow_TverskajaOkhotnyRyad_Test) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -433,7 +433,7 @@ UNIT_TEST(RussiaMoscowTverskajaOkhotnyRyadTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); } -UNIT_TEST(RussiaMoscowBolshoyKislovskiyPerBolshayaNikitinskayaUlTest) +UNIT_TEST(Russia_Moscow_BolshoyKislovskiyPerBolshayaNikitinskayaUl_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -449,7 +449,7 @@ UNIT_TEST(RussiaMoscowBolshoyKislovskiyPerBolshayaNikitinskayaUlTest) } // Test case: a route goes in Moscow along Leningradskiy Prpt (towards city center). -UNIT_TEST(RussiaMoscowLeningradskiyPrptToTheCenterUTurnTest) +UNIT_TEST(Russia_Moscow_LeningradskiyPrptToTheCenterUTurn_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -478,7 +478,7 @@ UNIT_TEST(Switzerland_SamstagernBergstrasse_TurnTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaMoscowMikoiankNoUTurnTest) +UNIT_TEST(Russia_Moscow_MikoiankNoUTurn_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -524,7 +524,7 @@ UNIT_TEST(Russia_Moscow_LeningradskiyPrptDublToTTK_TurnTest) integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightRight); } -UNIT_TEST(RussiaMoscowSvobodaStTest) +UNIT_TEST(Russia_Moscow_SvobodaSt_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -555,7 +555,7 @@ UNIT_TEST(Russia_Tiinsk_TurnTest) {CarDirection::TurnSlightLeft, CarDirection::TurnLeft}); } -UNIT_TEST(NetherlandsGorinchemBridgeTest) +UNIT_TEST(Netherlands_GorinchemBridge_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -585,7 +585,7 @@ UNIT_TEST(Russia_Voronezh_ProspTruda_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); } -UNIT_TEST(GermanyFrankfurtAirportTest) +UNIT_TEST(Germany_FrankfurtAirport_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -603,7 +603,7 @@ UNIT_TEST(GermanyFrankfurtAirportTest) } -UNIT_TEST(GermanyFrankfurtAirport2Test) +UNIT_TEST(Germany_FrankfurtAirport2_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -622,7 +622,7 @@ UNIT_TEST(GermanyFrankfurtAirport2Test) // Test on absence of unnecessary turn which may appear between two turns in the test. -UNIT_TEST(RussiaKubinkaTest) +UNIT_TEST(Russia_Kubinka_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -645,7 +645,7 @@ UNIT_TEST(RussiaKubinkaTest) } // Test on absence of unnecessary turn. -UNIT_TEST(AustriaKitzbuhelTest) +UNIT_TEST(Austria_Kitzbuhel_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -660,7 +660,7 @@ UNIT_TEST(AustriaKitzbuhelTest) } // Test on absence of unnecessary turn. -UNIT_TEST(AustriaKitzbuhel2Test) +UNIT_TEST(Austria_Kitzbuhel2_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -675,7 +675,7 @@ UNIT_TEST(AustriaKitzbuhel2Test) } // Test on absence of unnecessary turn in case of fake ingoing segment. -UNIT_TEST(AustriaKitzbuhel3Test) +UNIT_TEST(Austria_Kitzbuhel3_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -704,7 +704,7 @@ UNIT_TEST(Austria_BrixentalStrasse_TurnTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaMoscowLeningradkaToMKADTest) +UNIT_TEST(Russia_Moscow_LeningradkaToMKAD_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -719,7 +719,7 @@ UNIT_TEST(RussiaMoscowLeningradkaToMKADTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(RussiaMoscowMKADToSvobodaTest) +UNIT_TEST(Russia_Moscow_MKADToSvoboda_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -752,7 +752,7 @@ UNIT_TEST(Netherlands_Crazy_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -UNIT_TEST(NetherlandsBarneveldTest) +UNIT_TEST(Netherlands_Barneveld_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -801,7 +801,7 @@ UNIT_TEST(England_London_ExitToLeft_TurnTest) } // Test on the route from Leninsky prospect to its frontage road and turns generated on the route. -UNIT_TEST(RussiaMoscowLeninskyProspTest) +UNIT_TEST(Russia_Moscow_LeninskyProsp_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -818,7 +818,7 @@ UNIT_TEST(RussiaMoscowLeninskyProspTest) } // Test on the route from TTK (primary) to a link. -UNIT_TEST(RussiaMoscowTTKToLinkTest) +UNIT_TEST(Russia_Moscow_TTKToLink_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -834,7 +834,7 @@ TRouteResult const routeResult = } // Test on the turn from TTK (primary) to a secondary road. -UNIT_TEST(RussiaMoscowTTKToBegovayAlleyaTest) +UNIT_TEST(Russia_Moscow_TTKToBegovayAlleya_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -850,7 +850,7 @@ UNIT_TEST(RussiaMoscowTTKToBegovayAlleyaTest) } // Test on the turn from TTK (primary) to a service road. The angle of the turn is not slight. -UNIT_TEST(RussiaMoscowTTKToServiceTest) +UNIT_TEST(Russia_Moscow_TTKToService_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -866,7 +866,7 @@ UNIT_TEST(RussiaMoscowTTKToServiceTest) } // Test on a turn from TTK (primary) to an unclassified road. The angle of the turn is slight. -UNIT_TEST(RussiaMoscowTTKToNMaslovkaTest) +UNIT_TEST(Russia_Moscow_TTKToNMaslovka_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -881,7 +881,7 @@ UNIT_TEST(RussiaMoscowTTKToNMaslovkaTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } -UNIT_TEST(RussiaMoscowComplicatedTurnTest) +UNIT_TEST(Russia_Moscow_Complicated_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -898,7 +898,7 @@ UNIT_TEST(RussiaMoscowComplicatedTurnTest) integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); } -UNIT_TEST(USATampaTest) +UNIT_TEST(USA_Tampa_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -915,7 +915,7 @@ UNIT_TEST(USATampaTest) } // Test on go straight direction if it's possible to go through a roundabout. -UNIT_TEST(RussiaMoscowMinskia1TurnTest) +UNIT_TEST(Russia_Moscow_Minskia1_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -929,7 +929,7 @@ UNIT_TEST(RussiaMoscowMinskia1TurnTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaMoscowMinskia2TurnTest) +UNIT_TEST(Russia_Moscow_Minskia2_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -946,7 +946,7 @@ UNIT_TEST(RussiaMoscowMinskia2TurnTest) // This test on getting correct (far enough) outgoing turn point (result of method GetPointForTurn()) // despite the fact that a small road adjoins immediately after the turn point. -UNIT_TEST(RussiaMoscowBarikadnaiTurnTest) +UNIT_TEST(Russia_Moscow_Barikadnai_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -962,7 +962,7 @@ UNIT_TEST(RussiaMoscowBarikadnaiTurnTest) } // This test on getting correct (far enough) outgoing turn point (result of method GetPointForTurn()). -UNIT_TEST(RussiaMoscowKomsomolskyTurnTest) +UNIT_TEST(Russia_Moscow_Komsomolsky_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -978,7 +978,7 @@ UNIT_TEST(RussiaMoscowKomsomolskyTurnTest) } // Test on no go straight direction in case of a route along a big road and pass smaller ones. -UNIT_TEST(RussiaMoscowTTKNoGoStraightTurnTest) +UNIT_TEST(Russia_Moscow_TTKNoGoStraight_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -992,7 +992,7 @@ UNIT_TEST(RussiaMoscowTTKNoGoStraightTurnTest) integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -UNIT_TEST(RussiaMoscowLeninskyProsp2Test) +UNIT_TEST(Russia_MoscowLeninskyProsp2_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -1008,7 +1008,7 @@ UNIT_TEST(RussiaMoscowLeninskyProsp2Test) } /* -UNIT_TEST(RussiaMoscow_OnlyUTurnTest_1) +UNIT_TEST(Russia_Moscow_OnlyUTurnTest1_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -1022,7 +1022,7 @@ UNIT_TEST(RussiaMoscow_OnlyUTurnTest_1) integration::TestRouteLength(route, 3854.44); } -UNIT_TEST(RussiaMoscow_OnlyUTurnTest_1_WithDirection) +UNIT_TEST(Russia_Moscow_OnlyUTurnTest1WithDirection_TurnTest) { auto const startDir = mercator::FromLatLon(55.90423, 37.40176); auto const endDir = mercator::FromLatLon(55.90218, 37.40433); -- 2.45.3 From a98b1109e4bd45019f4d4aed7761dac7b614ed26 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 17:08:44 +0300 Subject: [PATCH 05/28] [Routing] Refactoring Invalid TurnTest removed. Signed-off-by: Anton Makouski --- .../routing_integration_tests/turn_test.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index eed3254ae5..168e57c75d 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -735,7 +735,7 @@ UNIT_TEST(Russia_Moscow_MKADToSvoboda_TurnTest) } // Test that there's no turns if to follow MKAD. -UNIT_TEST(Netherlands_Crazy_TurnTest) +UNIT_TEST(Netherlands_Barneveld_TurnTest) { TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), @@ -752,23 +752,6 @@ UNIT_TEST(Netherlands_Crazy_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -UNIT_TEST(Netherlands_Barneveld_TurnTest) -{ - TRouteResult const routeResult = - integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), - mercator::FromLatLon(52.15866, 5.56538), {0., 0.}, - mercator::FromLatLon(52.16667, 5.55663)); - - Route const & route = *routeResult.first; - RouterResultCode const result = routeResult.second; - - TEST_EQUAL(result, RouterResultCode::NoError, ()); - - /// @todo iOS app makes some strange route here. The test is valid! - integration::TestTurnCount(route, 1 /* expectedTurnCount */); - integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); -} - UNIT_TEST(Belarus_Minsk_TurnTest) { TRouteResult const routeResult = -- 2.45.3 From 027bf8adaed7c962db36c9225a639d375b9ed8c1 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 17:20:37 +0300 Subject: [PATCH 06/28] [Routing] Refactoring Extra Assert. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index f5421bf417..a6af7d4b12 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1113,6 +1113,7 @@ void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, if (it != candidates.end()) { ASSERT(it->m_segment.GetMwmId() != firstOutgoingSeg.GetMwmId(), ()); + ASSERT(it->m_segment.GetSegmentIdx() == firstOutgoingSeg.GetSegmentIdx() && it->m_segment.IsForward() == firstOutgoingSeg.IsForward(), ()); it->m_segment = firstOutgoingSeg; } } -- 2.45.3 From 132e3ca48ea0401037878b806ec7fb27734b7e97 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Fri, 29 Apr 2022 19:32:26 +0300 Subject: [PATCH 07/28] [Routing] Refactoring GetTurnInfo() added struct TurnInfo changed from refs to ptrs Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 174 ++++++++++++++++-------------------- routing/turns_generator.hpp | 10 ++- 2 files changed, 82 insertions(+), 102 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index a6af7d4b12..308a2a932b 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -59,8 +59,8 @@ bool IsExit(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, if (!possibleTurns.isCandidatesAngleValid) return false; - if (!IsHighway(turnInfo.m_ingoing.m_highwayClass, turnInfo.m_ingoing.m_isLink) || - !(turnInfo.m_outgoing.m_isLink || (IsSmallRoad(turnInfo.m_outgoing.m_highwayClass) && + if (!IsHighway(turnInfo.m_ingoing->m_highwayClass, turnInfo.m_ingoing->m_isLink) || + !(turnInfo.m_outgoing->m_isLink || (IsSmallRoad(turnInfo.m_outgoing->m_highwayClass) && IsGoStraightOrSlightTurn(intermediateDirection)))) { return false; @@ -112,11 +112,11 @@ bool GetTurnHighwayClasses(TurnCandidates const & possibleTurns, TurnInfo const // The turn should be kept if there's no any information about feature id of outgoing segment // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. Segment firstOutgoingSegment; - if (!turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) + if (!turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) return true; Segment inversedLastIngoingSegment; - if (!turnInfo.m_ingoing.m_segmentRange.GetLastSegment(numMwmIds, inversedLastIngoingSegment)) + if (!turnInfo.m_ingoing->m_segmentRange.GetLastSegment(numMwmIds, inversedLastIngoingSegment)) return true; inversedLastIngoingSegment.Inverse(); @@ -148,8 +148,8 @@ bool GetTurnHighwayClasses(TurnCandidates const & possibleTurns, TurnInfo const // @note The bigger is the road, the lesser is HighwayClass value. turnHighwayClasses.m_smallestRouteRoadClass = - static_cast(max(static_cast(turnInfo.m_ingoing.m_highwayClass), - static_cast(turnInfo.m_outgoing.m_highwayClass))); + static_cast(max(static_cast(turnInfo.m_ingoing->m_highwayClass), + static_cast(turnInfo.m_outgoing->m_highwayClass))); if (turnHighwayClasses.m_smallestRouteRoadClass == ftypes::HighwayClass::Error) { @@ -215,13 +215,13 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, { double constexpr kMaxAbsAngleSameRoadClass = 70.0; - ftypes::HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing.m_highwayClass; - ftypes::HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing.m_highwayClass; + ftypes::HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; + ftypes::HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; // The turn should be kept if there's no any information about feature id of outgoing segment // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. Segment firstOutgoingSegment; - if (!turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) + if (!turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) return false; for (auto const & t : turnCandidates) @@ -270,7 +270,7 @@ bool KeepRoundaboutTurnByHighwayClass(TurnCandidates const & possibleTurns, { Segment firstOutgoingSegment; bool const validFirstOutgoingSeg = - turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment); + turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment); for (auto const & t : possibleTurns.candidates) { @@ -433,12 +433,12 @@ size_t GetLinkCount(vector const & candidates) double GetOneSegmentTurnAngle(TurnInfo const & turnInfo) { - ASSERT_GREATER_OR_EQUAL(turnInfo.m_ingoing.m_path.size(), 2, ()); - ASSERT_GREATER_OR_EQUAL(turnInfo.m_outgoing.m_path.size(), 2, ()); + ASSERT_GREATER_OR_EQUAL(turnInfo.m_ingoing->m_path.size(), 2, ()); + ASSERT_GREATER_OR_EQUAL(turnInfo.m_outgoing->m_path.size(), 2, ()); - return base::RadToDeg(PiMinusTwoVectorsAngle(turnInfo.m_ingoing.m_path.back().GetPoint(), - turnInfo.m_ingoing.m_path[turnInfo.m_ingoing.m_path.size() - 2].GetPoint(), - turnInfo.m_outgoing.m_path[1].GetPoint())); + return base::RadToDeg(PiMinusTwoVectorsAngle(turnInfo.m_ingoing->m_path.back().GetPoint(), + turnInfo.m_ingoing->m_path[turnInfo.m_ingoing->m_path.size() - 2].GetPoint(), + turnInfo.m_outgoing->m_path[1].GetPoint())); } double GetPathTurnAngle(LoadedPathSegment const & segment, size_t const pathIndex) @@ -484,7 +484,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde if (index.m_segmentIndex + 1 == segments.size()) return false; // The end of the route is reached. - TurnInfo const turnInfo(segments[index.m_segmentIndex], segments[index.m_segmentIndex + 1]); + TurnInfo const turnInfo(&segments[index.m_segmentIndex], &segments[index.m_segmentIndex + 1]); double const oneSegmentTurnAngle = GetOneSegmentTurnAngle(turnInfo); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); @@ -493,7 +493,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde size_t ingoingCount = 0; TurnCandidates possibleTurns; - result.GetPossibleTurns(turnInfo.m_ingoing.m_segmentRange, GetPointByIndex(segments, index), + result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, GetPointByIndex(segments, index), ingoingCount, possibleTurns); if (possibleTurns.candidates.empty()) @@ -589,7 +589,7 @@ bool RoutePointIndex::operator==(RoutePointIndex const & index) const // TurnInfo ---------------------------------------------------------------------------------------- bool TurnInfo::IsSegmentsValid() const { - if (m_ingoing.m_path.empty() || m_outgoing.m_path.empty()) + if (m_ingoing->m_path.empty() || m_outgoing->m_path.empty()) { LOG(LWARNING, ("Some turns can't load the geometry.")); return false; @@ -1007,7 +1007,7 @@ bool OneOfTurnCandidatesGoesAlongIngoingSegment(NumMwmIds const & numMwmIds, TurnInfo const & turnInfo) { Segment ingoingSegment; - if (!turnInfo.m_ingoing.m_segmentRange.GetLastSegment(numMwmIds, ingoingSegment)) + if (!turnInfo.m_ingoing->m_segmentRange.GetLastSegment(numMwmIds, ingoingSegment)) return false; for (auto const & c : turnCandidates.candidates) @@ -1039,7 +1039,7 @@ bool HasMultiTurns(NumMwmIds const & numMwmIds, TurnCandidates const & turnCandi void RemoveUTurnCandidate(TurnInfo const & turnInfo, NumMwmIds const & numMwmIds, std::vector & turnCandidates) { Segment lastIngoingSegment; - if (turnInfo.m_ingoing.m_segmentRange.GetLastSegment(numMwmIds, lastIngoingSegment)) + if (turnInfo.m_ingoing->m_segmentRange.GetLastSegment(numMwmIds, lastIngoingSegment)) { if (turnCandidates.front().m_segment.IsInverse(lastIngoingSegment)) turnCandidates.erase(turnCandidates.begin()); @@ -1119,55 +1119,67 @@ void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, } } -void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmentIndex, - NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, - TurnItem & turn) +bool GetTurnInfo(IRoutingResult const & result, size_t const outgoingSegmentIndex, + RoutingSettings const & vehicleSettings, + TurnInfo & turnInfo) { auto const & segments = result.GetSegments(); CHECK_LESS(outgoingSegmentIndex, segments.size(), ()); CHECK_GREATER(outgoingSegmentIndex, 0, ()); if (PathIsFakeLoop(segments[outgoingSegmentIndex].m_path)) - return; + return false; bool const isStartFakeLoop = PathIsFakeLoop(segments[outgoingSegmentIndex - 1].m_path); if (isStartFakeLoop && outgoingSegmentIndex < 2) - return; + return false; size_t const prevIndex = isStartFakeLoop ? outgoingSegmentIndex - 2 : outgoingSegmentIndex - 1; - auto const turnInfo = TurnInfo(segments[prevIndex], segments[outgoingSegmentIndex]); + turnInfo = TurnInfo(&segments[prevIndex], &segments[outgoingSegmentIndex]); - if (!turnInfo.IsSegmentsValid() || turnInfo.m_ingoing.m_segmentRange.IsEmpty()) - return; + if (!turnInfo.IsSegmentsValid() || turnInfo.m_ingoing->m_segmentRange.IsEmpty()) + return false; if (isStartFakeLoop) { - if (mercator::DistanceOnEarth(turnInfo.m_ingoing.m_path.front().GetPoint(), - turnInfo.m_ingoing.m_path.back().GetPoint()) < vehicleSettings.m_minIngoingDistMeters || - mercator::DistanceOnEarth(turnInfo.m_outgoing.m_path.front().GetPoint(), - turnInfo.m_outgoing.m_path.back().GetPoint()) < vehicleSettings.m_minOutgoingDistMeters) + if (mercator::DistanceOnEarth(turnInfo.m_ingoing->m_path.front().GetPoint(), + turnInfo.m_ingoing->m_path.back().GetPoint()) < vehicleSettings.m_minIngoingDistMeters || + mercator::DistanceOnEarth(turnInfo.m_outgoing->m_path.front().GetPoint(), + turnInfo.m_outgoing->m_path.back().GetPoint()) < vehicleSettings.m_minOutgoingDistMeters) { - return; + return false; } } - ASSERT(!turnInfo.m_ingoing.m_path.empty(), ()); - ASSERT(!turnInfo.m_outgoing.m_path.empty(), ()); - ASSERT_LESS(mercator::DistanceOnEarth(turnInfo.m_ingoing.m_path.back().GetPoint(), - turnInfo.m_outgoing.m_path.front().GetPoint()), + ASSERT(!turnInfo.m_ingoing->m_path.empty(), ()); + ASSERT(!turnInfo.m_outgoing->m_path.empty(), ()); + ASSERT_LESS(mercator::DistanceOnEarth(turnInfo.m_ingoing->m_path.back().GetPoint(), + turnInfo.m_outgoing->m_path.front().GetPoint()), kFeaturesNearTurnMeters, ()); - turn.m_keepAnyway = (!turnInfo.m_ingoing.m_isLink && turnInfo.m_outgoing.m_isLink); - turn.m_sourceName = turnInfo.m_ingoing.m_name; - turn.m_targetName = turnInfo.m_outgoing.m_name; + return true; +} + +void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmentIndex, + NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, + TurnItem & turn) +{ + TurnInfo turnInfo; + bool validTurnInfo = GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo); + if (!validTurnInfo) + return; + + turn.m_keepAnyway = (!turnInfo.m_ingoing->m_isLink && turnInfo.m_outgoing->m_isLink); + turn.m_sourceName = turnInfo.m_ingoing->m_name; + turn.m_targetName = turnInfo.m_outgoing->m_name; turn.m_turn = CarDirection::None; - ASSERT_GREATER(turnInfo.m_ingoing.m_path.size(), 1, ()); + ASSERT_GREATER(turnInfo.m_ingoing->m_path.size(), 1, ()); TurnCandidates nodes; size_t ingoingCount; - m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); - result.GetPossibleTurns(turnInfo.m_ingoing.m_segmentRange, junctionPoint, ingoingCount, nodes); + m2::PointD const junctionPoint = turnInfo.m_ingoing->m_path.back().GetPoint(); + result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates @@ -1180,7 +1192,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen Segment firstOutgoingSeg; bool const isFirstOutgoingSegValid = - turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are @@ -1194,11 +1206,11 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen ("hasMultiTurns is true if there are two or more possible ways which don't go along an ingoing segment and false otherwise")); // Check for enter or exit to/from roundabout. - if (turnInfo.m_ingoing.m_onRoundabout || turnInfo.m_outgoing.m_onRoundabout) + if (turnInfo.m_ingoing->m_onRoundabout || turnInfo.m_outgoing->m_onRoundabout) { bool const keepTurnByHighwayClass = KeepRoundaboutTurnByHighwayClass(nodes, turnInfo, numMwmIds); - turn.m_turn = GetRoundaboutDirection(turnInfo.m_ingoing.m_onRoundabout, - turnInfo.m_outgoing.m_onRoundabout, + turn.m_turn = GetRoundaboutDirection(turnInfo.m_ingoing->m_onRoundabout, + turnInfo.m_outgoing->m_onRoundabout, hasMultiTurns, keepTurnByHighwayClass); return; @@ -1214,7 +1226,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // Checking for exits from highways. // Segment firstOutgoingSeg; //bool const isFirstOutgoingSegValid = - // turnInfo.m_outgoing.m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + // turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); if (isFirstOutgoingSegValid && IsExit(nodes, turnInfo, firstOutgoingSeg, intermediateDirection, turn.m_turn)) { @@ -1241,8 +1253,8 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // Removing a slight turn if ingoing and outgoing edges are not links and all other // possible ways out (except of uturn) are links. /// @todo Add condition of links m_highwayClass to be higher. - if (!turnInfo.m_ingoing.m_isLink && !turnInfo.m_outgoing.m_isLink && - turnInfo.m_ingoing.m_highwayClass == turnInfo.m_outgoing.m_highwayClass && + if (!turnInfo.m_ingoing->m_isLink && !turnInfo.m_outgoing->m_isLink && + turnInfo.m_ingoing->m_highwayClass == turnInfo.m_outgoing->m_highwayClass && GetLinkCount(turnCandidates) + 1 == turnCandidates.size()) { turn.m_turn = CarDirection::None; @@ -1298,63 +1310,27 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, TurnItem & turn) { - auto const & segments = result.GetSegments(); - CHECK_LESS(outgoingSegmentIndex, segments.size(), ()); - CHECK_GREATER(outgoingSegmentIndex, 0, ()); - - if (PathIsFakeLoop(segments[outgoingSegmentIndex].m_path)) + TurnInfo turnInfo; + bool validTurnInfo = GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo); + if (!validTurnInfo) return; - bool const isStartFakeLoop = PathIsFakeLoop(segments[outgoingSegmentIndex - 1].m_path); + m2::PointD const junctionPoint = turnInfo.m_ingoing->m_path.back().GetPoint(); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, + vehicleSettings.m_maxIngoingPointsCount, + vehicleSettings.m_minIngoingDistMeters, + vehicleSettings.m_maxOutgoingPointsCount, + vehicleSettings.m_minOutgoingDistMeters); - if (isStartFakeLoop && outgoingSegmentIndex < 2) - return; - - size_t const prevIndex = isStartFakeLoop ? outgoingSegmentIndex - 2 : outgoingSegmentIndex - 1; - auto const turnInfo = TurnInfo(segments[prevIndex], segments[outgoingSegmentIndex]); - - if (!turnInfo.IsSegmentsValid() || turnInfo.m_ingoing.m_segmentRange.IsEmpty()) - return; - - if (isStartFakeLoop) - { - if (mercator::DistanceOnEarth(turnInfo.m_ingoing.m_path.front().GetPoint(), - turnInfo.m_ingoing.m_path.back().GetPoint()) < vehicleSettings.m_minIngoingDistMeters || - mercator::DistanceOnEarth(turnInfo.m_outgoing.m_path.front().GetPoint(), - turnInfo.m_outgoing.m_path.back().GetPoint()) < vehicleSettings.m_minOutgoingDistMeters) - { - return; - } - } - - ASSERT(!turnInfo.m_ingoing.m_path.empty(), ()); - ASSERT(!turnInfo.m_outgoing.m_path.empty(), ()); - ASSERT_LESS(mercator::DistanceOnEarth(turnInfo.m_ingoing.m_path.back().GetPoint(), - turnInfo.m_outgoing.m_path.front().GetPoint()), - kFeaturesNearTurnMeters, ()); - - m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint(); - size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; - - m2::PointD const ingoingPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_maxIngoingPointsCount, - vehicleSettings.m_minIngoingDistMeters, false /* forward */); - m2::PointD const outgoingPoint = GetPointForTurn( - result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, - vehicleSettings.m_minOutgoingDistMeters, true /* forward */); - - double const turnAngle = - base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); - - turn.m_sourceName = turnInfo.m_ingoing.m_name; - turn.m_targetName = turnInfo.m_outgoing.m_name; + turn.m_sourceName = turnInfo.m_ingoing->m_name; + turn.m_targetName = turnInfo.m_outgoing->m_name; turn.m_pedestrianTurn = PedestrianDirection::None; - ASSERT_GREATER(turnInfo.m_ingoing.m_path.size(), 1, ()); + ASSERT_GREATER(turnInfo.m_ingoing->m_path.size(), 1, ()); TurnCandidates nodes; size_t ingoingCount = 0; - result.GetPossibleTurns(turnInfo.m_ingoing.m_segmentRange, junctionPoint, ingoingCount, nodes); + result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates.end(), diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index 91d2496ff4..8fa5fae2a7 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -58,10 +58,14 @@ struct RoutePointIndex */ struct TurnInfo { - LoadedPathSegment const & m_ingoing; - LoadedPathSegment const & m_outgoing; + LoadedPathSegment const * m_ingoing; + LoadedPathSegment const * m_outgoing; - TurnInfo(LoadedPathSegment const & ingoingSegment, LoadedPathSegment const & outgoingSegment) + TurnInfo() : m_ingoing(nullptr), m_outgoing(nullptr) + { + } + + TurnInfo(LoadedPathSegment const * ingoingSegment, LoadedPathSegment const * outgoingSegment) : m_ingoing(ingoingSegment), m_outgoing(outgoingSegment) { } -- 2.45.3 From 2b9c981dc65bb5eb5207580bce6c42723c3a9f8e Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sat, 30 Apr 2022 10:27:36 +0300 Subject: [PATCH 08/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 77 +++++++++---------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 308a2a932b..06a8cec18f 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1001,41 +1001,6 @@ PedestrianDirection IntermediateDirectionPedestrian(const double angle) return FindDirectionByAngle(kLowerBounds, angle); } -/// \returns true if one of the turn candidates goes along the ingoing route segment. -bool OneOfTurnCandidatesGoesAlongIngoingSegment(NumMwmIds const & numMwmIds, - TurnCandidates const & turnCandidates, - TurnInfo const & turnInfo) -{ - Segment ingoingSegment; - if (!turnInfo.m_ingoing->m_segmentRange.GetLastSegment(numMwmIds, ingoingSegment)) - return false; - - for (auto const & c : turnCandidates.candidates) - { - if (c.m_segment.IsInverse(ingoingSegment)) - return true; - } - return false; -} - -/// \returns true if there are two or more possible ways which don't go along an ingoing segment -/// and false otherwise. -/// \example If a route goes along such graph edges: -/// ...-->*<------>*<--->*<---------------->*<---... -/// for each point which is drawn above HasMultiTurns() returns false -/// despite the fact that for each point it's possible to go to two directions. -bool HasMultiTurns(NumMwmIds const & numMwmIds, TurnCandidates const & turnCandidates, - TurnInfo const & turnInfo) -{ - size_t const numTurnCandidates = turnCandidates.candidates.size(); - if (numTurnCandidates <= 1) - return false; - if (numTurnCandidates > 2) - return true; - - return !OneOfTurnCandidatesGoesAlongIngoingSegment(numMwmIds, turnCandidates, turnInfo); -} - void RemoveUTurnCandidate(TurnInfo const & turnInfo, NumMwmIds const & numMwmIds, std::vector & turnCandidates) { Segment lastIngoingSegment; @@ -1182,36 +1147,38 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { - ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates - .end(), base::LessBy(&TurnCandidate::m_angle)), + ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates.end(), base::LessBy(&TurnCandidate::m_angle)), ("Turn candidates should be sorted by its angle field.")); } if (nodes.candidates.size() == 0) return; + // No turns are needed on transported road. + if (turnInfo.m_ingoing->m_highwayClass == ftypes::HighwayClass::Transported && + turnInfo.m_outgoing->m_highwayClass == ftypes::HighwayClass::Transported) + return; + Segment firstOutgoingSeg; - bool const isFirstOutgoingSegValid = - turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + bool const isFirstOutgoingSegValid = turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); + if (!isFirstOutgoingSegValid) + return; // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are // from different mwms. CandidatesSegmentCorrectionByOutgoing(result, outgoingSegmentIndex, junctionPoint, numMwmIds, firstOutgoingSeg, nodes.candidates); - bool const hasMultiTurns = HasMultiTurns(numMwmIds, nodes, turnInfo); RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); auto const & turnCandidates = nodes.candidates; - ASSERT(hasMultiTurns == (turnCandidates.size() >= 2), - ("hasMultiTurns is true if there are two or more possible ways which don't go along an ingoing segment and false otherwise")); - + // Check for enter or exit to/from roundabout. if (turnInfo.m_ingoing->m_onRoundabout || turnInfo.m_outgoing->m_onRoundabout) { bool const keepTurnByHighwayClass = KeepRoundaboutTurnByHighwayClass(nodes, turnInfo, numMwmIds); turn.m_turn = GetRoundaboutDirection(turnInfo.m_ingoing->m_onRoundabout, turnInfo.m_outgoing->m_onRoundabout, - hasMultiTurns, + turnCandidates.size() > 1, keepTurnByHighwayClass); return; } @@ -1224,11 +1191,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen CarDirection const intermediateDirection = IntermediateDirection(turnAngle); // Checking for exits from highways. - // Segment firstOutgoingSeg; - //bool const isFirstOutgoingSegValid = - // turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSeg); - if (isFirstOutgoingSegValid && - IsExit(nodes, turnInfo, firstOutgoingSeg, intermediateDirection, turn.m_turn)) + if (IsExit(nodes, turnInfo, firstOutgoingSeg, intermediateDirection, turn.m_turn)) { return; } @@ -1273,7 +1236,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // turnCandidates are sorted by angle from leftmost to rightmost. // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. - if (adjacent_find(nodes.candidates.begin(), nodes.candidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == nodes.candidates.end()) + if (adjacent_find(turnCandidates.begin(), turnCandidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == turnCandidates.end()) { if (turnCandidates.front().m_segment == firstOutgoingSeg) { @@ -1295,13 +1258,11 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen } else // turnCandidates.size() == 1 { - if (nodes.candidates.front().m_segment == firstOutgoingSeg) + ASSERT(turnCandidates.front().m_segment == firstOutgoingSeg, ()); + if (ingoingCount <= 1) { - if (ingoingCount <= 1) - { - turn.m_turn = CarDirection::None; - return; - } + turn.m_turn = CarDirection::None; + return; } } } @@ -1349,11 +1310,11 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe return; } - bool const roadForks = HasMultiTurns(numMwmIds, nodes, turnInfo); + RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); // If there is no fork on the road we don't need to generate any turn. It is pointless because // there is no possibility of leaving the route. - if (!roadForks || (std::fabs(GetOneSegmentTurnAngle(turnInfo)) < kMaxForwardAngleActual && HasSingleForwardTurn(nodes))) + if (nodes.candidates.size() <= 1 || (std::fabs(GetOneSegmentTurnAngle(turnInfo)) < kMaxForwardAngleActual && HasSingleForwardTurn(nodes))) turn.m_pedestrianTurn = PedestrianDirection::None; } -- 2.45.3 From acc42e50e39c740ce2514b45d51435680fc9fd3a Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sat, 30 Apr 2022 15:56:47 +0300 Subject: [PATCH 09/28] [Routing] Refactoring Extra tests. Signed-off-by: Anton Makouski --- .../routing_integration_tests/turn_test.cpp | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 168e57c75d..3116eeec7d 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -398,7 +398,7 @@ UNIT_TEST(Russia_Moscow_VarshavskoeShosseMKAD_TurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); - /// @todo No potential additioanal turn 0 (1 alternative way is link another is much smaller (service)). + /// @todo No potential additioanal turn 0 (one alternative way is link, another is much smaller (service)). integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } @@ -990,6 +990,90 @@ UNIT_TEST(Russia_MoscowLeninskyProsp2_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightRight); } +UNIT_TEST(Germany_ShuttleTrain_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(54.78152, 8.83952), {0., 0.}, + mercator::FromLatLon(54.79281, 8.83466)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + // No turns on shutte train road. + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 2 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightLeft); +} + +UNIT_TEST(Germany_ShuttleTrain2_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(54.90181, 8.32472), {0., 0.}, + mercator::FromLatLon(54.91681, 8.31346)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + // No turns on shutte train road. + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 5 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSharpRight); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnLeft); +} + +UNIT_TEST(Cyprus_Nicosia_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(35.12459, 33.34449), {0., 0.}, + mercator::FromLatLon(35.14353, 33.34387)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + // No SlightTurns at not straight junctions. Issue #2262. + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 0 /* expectedTurnCount */); +} + +UNIT_TEST(Cyprus_Nicosia2_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(35.14528, 33.34014), {0., 0.}, + mercator::FromLatLon(35.13930, 33.34171)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + // No SlightTurns at not straight junctions. + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 2 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); +} + +UNIT_TEST(Cyprus_NicosiaPresidentialPark_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(35.15992, 33.34625), {0., 0.}, + mercator::FromLatLon(35.15837, 33.35058)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 1 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); +} + /* UNIT_TEST(Russia_Moscow_OnlyUTurnTest1_TurnTest) { -- 2.45.3 From 28790e137fd49f36b3af68fcd8893331058843e9 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sat, 30 Apr 2022 16:37:15 +0300 Subject: [PATCH 10/28] [Routing] Refactoring Proper ingoingCount consideration. Signed-off-by: Anton Makouski --- routing/directions_engine.cpp | 1 + routing/loaded_path_segment.hpp | 1 + routing/routing_integration_tests/bicycle_turn_test.cpp | 3 ++- routing/routing_integration_tests/turn_test.cpp | 3 +++ routing/turns_generator.cpp | 5 ++++- 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/routing/directions_engine.cpp b/routing/directions_engine.cpp index 64de4c6aa0..857fe5d2ad 100644 --- a/routing/directions_engine.cpp +++ b/routing/directions_engine.cpp @@ -65,6 +65,7 @@ void DirectionsEngine::LoadPathAttributes(FeatureID const & featureId, pathSegment.m_highwayClass = highwayClass; pathSegment.m_isLink = ftypes::IsLinkChecker::Instance()(*ft); pathSegment.m_onRoundabout = ftypes::IsRoundAboutChecker::Instance()(*ft); + pathSegment.m_isOneWay = ftypes::IsOneWayChecker::Instance()(*ft); pathSegment.m_name = ft->GetName(StringUtf8Multilang::kDefaultCode); } diff --git a/routing/loaded_path_segment.hpp b/routing/loaded_path_segment.hpp index 23533aadb6..3a3b178866 100644 --- a/routing/loaded_path_segment.hpp +++ b/routing/loaded_path_segment.hpp @@ -29,6 +29,7 @@ struct LoadedPathSegment ftypes::HighwayClass m_highwayClass = ftypes::HighwayClass::Undefined; bool m_onRoundabout = false; bool m_isLink = false; + bool m_isOneWay = false; bool IsValid() const { return m_path.size() > 1; } }; diff --git a/routing/routing_integration_tests/bicycle_turn_test.cpp b/routing/routing_integration_tests/bicycle_turn_test.cpp index 252c1be174..994f96e929 100644 --- a/routing/routing_integration_tests/bicycle_turn_test.cpp +++ b/routing/routing_integration_tests/bicycle_turn_test.cpp @@ -168,7 +168,8 @@ UNIT_TEST(TurnsNearAltufievskoeShosseLongFakeSegmentTest) integration::TestTurnCount(route, 3 /* expectedTurnCount */); - /// @todo For some reason for both nodes.candidates of (turn_m_index == 3) angles are 0, but it is expected to be -90 and +90. + /// @todo Problem with outgoingTurns from RoutingEngineResult::GetPossibleTurns at (turn_m_index == 3) + /// For some reason for both of outgoingTurns angles are 0, but it is expected to be -90 and +90. integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 3116eeec7d..b5b8b9695a 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -443,6 +443,9 @@ UNIT_TEST(Russia_Moscow_BolshoyKislovskiyPerBolshayaNikitinskayaUl_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; + /// @todo Problem with outgoingTurns from RoutingEngineResult::GetPossibleTurns at (turn_m_index == 4) + /// For some reason it contains only one possible turn (+90), but it is expected that it will be two of them (-90 and +90). + /// This is the reason why the RightTurn is discarded. TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 06a8cec18f..8738e27f98 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1259,7 +1259,10 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen else // turnCandidates.size() == 1 { ASSERT(turnCandidates.front().m_segment == firstOutgoingSeg, ()); - if (ingoingCount <= 1) + ASSERT(!IsGoStraightOrSlightTurn(turn.m_turn), ("GoStraightOrSlightTurn are discarded before if turnCandidates.size() == 1")); + // IngoingCount includes ingoing segment and reversed outgoing (if it is not one-way). + // Both of them should be isnored. If any other one is present, turn is kept to prevent user from going to oneway alternative. + if (ingoingCount <= 1 + size_t(!turnInfo.m_outgoing->m_isOneWay)) { turn.m_turn = CarDirection::None; return; -- 2.45.3 From efaee68912914797a37fe9707f6cd04a9efd503f Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sat, 30 Apr 2022 18:45:39 +0300 Subject: [PATCH 11/28] [Routing] Refactoring Extra tests. Signed-off-by: Anton Makouski --- .../routing_integration_tests/turn_test.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index b5b8b9695a..dcf6dcc2e0 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -1055,7 +1055,7 @@ UNIT_TEST(Cyprus_Nicosia2_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; - // No SlightTurns at not straight junctions. + // No SlightTurns at not straight junctions. Issue #2262. TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 2 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); @@ -1072,11 +1072,28 @@ UNIT_TEST(Cyprus_NicosiaPresidentialPark_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; + // Issue #2438 TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); } +UNIT_TEST(Cyprus_NicosiaSchoolParking_TurnTest) +{ + TRouteResult const routeResult = + integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(35.15395, 33.35000), {0., 0.}, + mercator::FromLatLon(35.15159, 33.34961)); + + Route const & route = *routeResult.first; + RouterResultCode const result = routeResult.second; + + // Issue #2438 + TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestTurnCount(route, 1 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); +} + /* UNIT_TEST(Russia_Moscow_OnlyUTurnTest1_TurnTest) { -- 2.45.3 From 995bb5e0eb0b5925091e4e863144ff7a17550026 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sun, 1 May 2022 19:44:57 +0300 Subject: [PATCH 12/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- .../routing_integration_tests/turn_test.cpp | 6 +- routing/turns_generator.cpp | 92 ++++++++++++------- 2 files changed, 61 insertions(+), 37 deletions(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index dcf6dcc2e0..7833e6a261 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -1005,9 +1005,11 @@ UNIT_TEST(Germany_ShuttleTrain_TurnTest) // No turns on shutte train road. TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 2 /* expectedTurnCount */); + integration::TestTurnCount(route, 3 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); - integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightLeft); + integration::GetNthTurn(route, 1).TestValid().TestOneOfDirections( + {CarDirection::GoStraight, CarDirection::TurnSlightRight}); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnSlightLeft); } UNIT_TEST(Germany_ShuttleTrain2_TurnTest) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 8738e27f98..b36a70af86 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -22,6 +22,7 @@ namespace // Angles in degrees for finding route segments with no actual forks. double constexpr kMaxForwardAngleCandidates = 95.0; double constexpr kMaxForwardAngleActual = 60.0; +double constexpr kMinAngleDiffToNotConfuseStraightAndAlternative = 25.0; /// \brief Contains information about highway classes of the route goes through a turn /// and about highway classes of possible ways from the turn. @@ -208,7 +209,8 @@ bool DiscardTurnByHighwayClass(TurnCandidates const & possibleTurns, TurnInfo co /// \param turnCandidates is all possible ways out from a junction except uturn. /// \param turnInfo is information about ingoing and outgoing segments of the route. /// it is supposed that current turn is GoStraight or SlightTurn -bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, +bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, + double routeAngle, std::vector const & turnCandidates, TurnInfo const & turnInfo, NumMwmIds const & numMwmIds) @@ -232,7 +234,7 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, if (turnRoute.m_turn == CarDirection::GoStraight && static_cast(outgoingRouteRoadClass) >= static_cast(ingoingRouteRoadClass)) { - if (IsGoStraightOrSlightTurn(IntermediateDirection(t.m_angle))) + if (abs(t.m_angle) < abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) return false; } else @@ -551,10 +553,8 @@ void GoStraightCorrection(TurnCandidate const & notRouteCandidate, double const { if (turn.m_turn != CarDirection::GoStraight) return; - - double const kMinAngleDiffToNotConfuse = 25.0; - if (abs(notRouteCandidate.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuse) + if (abs(notRouteCandidate.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) return; if (!IsGoStraightOrSlightTurn(IntermediateDirection(notRouteCandidate.m_angle))) @@ -808,7 +808,7 @@ double CalculateMercatorDistanceAlongPath(uint32_t startPointIndex, uint32_t end void FixupTurns(vector const & junctions, Route::TTurns & turnsDir) { - double const kMergeDistMeters = 30.0; + double const kMergeDistMeters = 15.0; // For turns that are not EnterRoundAbout exitNum is always equal to zero. // If a turn is EnterRoundAbout exitNum is a number of turns between two junctions: // (1) the route enters to the roundabout; @@ -1126,6 +1126,55 @@ bool GetTurnInfo(IRoutingResult const & result, size_t const outgoingSegmentInde return true; } +// If the route goes along the rightmost or the leftmost way among available ones: +// 1. RightmostDirection or LeftmostDirection is selected +// 2. If the turn direction is |CarDirection::GoStraight| +// and there's another GoStraight or SlightTurn turn +// GoStraight is corrected to TurnSlightRight/TurnSlightLeft +// to avoid ambiguity: 2 or more almost straight turns and GoStraight direction. + +// turnCandidates are sorted by angle from leftmost to rightmost. +// Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. +void RightmostAndLeftmostCorrection(std::vector const & turnCandidates, + Segment const & firstOutgoingSeg, + double turnAngle, + TurnItem & turn) +{ + if (adjacent_find(turnCandidates.begin(), turnCandidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == turnCandidates.end()) + { + for (auto candidate = turnCandidates.begin(); candidate != turnCandidates.end(); ++candidate) + { + if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.end()) + { + // The route goes along the leftmost candidate. + turn.m_turn = LeftmostDirection(turnAngle); + // Compare with the next candidate to the right. + GoStraightCorrection(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightLeft, turn); + break; + } + // Ignoring too sharp alternative candidates. + if (candidate->m_angle > -90) + break; + } + for (auto candidate = turnCandidates.rbegin(); candidate != turnCandidates.rend(); ++candidate) + { + if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.rend()) + { + // The route goes along the rightmost candidate. + turn.m_turn = RightmostDirection(turnAngle); + // Compare with the next candidate to the left. + GoStraightCorrection(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightRight, turn); + break; + } + // Ignoring too sharp alternative candidates. + if (candidate->m_angle < 90) + break; + }; + } + else + LOG(LWARNING, ("nodes.candidates are not expected to have same m_angle.")); +} + void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmentIndex, NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, TurnItem & turn) @@ -1204,7 +1253,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (!turn.m_keepAnyway && IsGoStraightOrSlightTurn(turn.m_turn)) { if (DiscardTurnByHighwayClass(nodes, turnInfo, numMwmIds, turn.m_turn) || - DiscardTurnByNoAlignedAlternatives(turn, turnCandidates, turnInfo, numMwmIds)) + DiscardTurnByNoAlignedAlternatives(turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) { turn.m_turn = CarDirection::None; return; @@ -1227,34 +1276,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (turnCandidates.size() >= 2) { - // If the route goes along the rightmost or the leftmost way among available ones: - // 1. RightmostDirection or LeftmostDirection is selected - // 2. If the turn direction is |CarDirection::GoStraight| - // and there's another GoStraight or SlightTurn turn - // GoStraight is corrected to TurnSlightRight/TurnSlightLeft - // to avoid ambiguity: 2 or more almost straight turns and GoStraight direction. - - // turnCandidates are sorted by angle from leftmost to rightmost. - // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. - if (adjacent_find(turnCandidates.begin(), turnCandidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == turnCandidates.end()) - { - if (turnCandidates.front().m_segment == firstOutgoingSeg) - { - // The route goes along the leftmost candidate. - turn.m_turn = LeftmostDirection(turnAngle); - // Compare with the closest left candidate. - GoStraightCorrection(turnCandidates[1], turnCandidates.front().m_angle, CarDirection::TurnSlightLeft, turn); - } - else if (turnCandidates.back().m_segment == firstOutgoingSeg) - { - // The route goes along the rightmost candidate. - turn.m_turn = RightmostDirection(turnAngle); - // Compare with the closest right candidate. - GoStraightCorrection(turnCandidates[turnCandidates.size() - 2], turnCandidates.back().m_angle, CarDirection::TurnSlightRight, turn); - } - } - else - LOG(LWARNING, ("nodes.candidates are not expected to have same m_angle.")); + RightmostAndLeftmostCorrection(turnCandidates, firstOutgoingSeg, turnAngle, turn); } else // turnCandidates.size() == 1 { -- 2.45.3 From 27dadd5afdfabe2a0f55f4b71b20b0fa955dc759 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Sun, 1 May 2022 19:50:26 +0300 Subject: [PATCH 13/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index b36a70af86..2abec416ea 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1062,7 +1062,11 @@ double CalcTurnAngle(IRoutingResult const & result, return turnAngle; } -void CandidatesSegmentCorrectionByOutgoing(IRoutingResult const & result, +// It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. +// It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are +// from different mwms. +// Let's identify it turnCandidates by angle and update according turnCandidate. +void CorrectCandidatesSegmentByOutgoing(IRoutingResult const & result, size_t const outgoingSegmentIndex, m2::PointD const & junctionPoint, NumMwmIds const & numMwmIds, @@ -1135,7 +1139,7 @@ bool GetTurnInfo(IRoutingResult const & result, size_t const outgoingSegmentInde // turnCandidates are sorted by angle from leftmost to rightmost. // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. -void RightmostAndLeftmostCorrection(std::vector const & turnCandidates, +void CorrectRightmostAndLeftmost(std::vector const & turnCandidates, Segment const & firstOutgoingSeg, double turnAngle, TurnItem & turn) @@ -1213,10 +1217,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (!isFirstOutgoingSegValid) return; - // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. - // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are - // from different mwms. - CandidatesSegmentCorrectionByOutgoing(result, outgoingSegmentIndex, junctionPoint, numMwmIds, firstOutgoingSeg, nodes.candidates); + CorrectCandidatesSegmentByOutgoing(result, outgoingSegmentIndex, junctionPoint, numMwmIds, firstOutgoingSeg, nodes.candidates); RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); auto const & turnCandidates = nodes.candidates; @@ -1276,7 +1277,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (turnCandidates.size() >= 2) { - RightmostAndLeftmostCorrection(turnCandidates, firstOutgoingSeg, turnAngle, turn); + CorrectRightmostAndLeftmost(turnCandidates, firstOutgoingSeg, turnAngle, turn); } else // turnCandidates.size() == 1 { -- 2.45.3 From 11c27deef6b5aaa197e5425e1020374b0de961ff Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Mon, 2 May 2022 11:46:19 +0300 Subject: [PATCH 14/28] [Routing] Refactoring Time limit in GetPointForTurn() Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 2abec416ea..96f0f92366 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -354,6 +354,23 @@ m2::PointD GetPointByIndex(TUnpackedPathSegments const & segments, RoutePointInd return segments[index.m_segmentIndex].m_path[index.m_pathIndex].GetPoint(); } +double CalcEstimatedTimeToPass(double distance, ftypes::HighwayClass highwayClass) +{ + double speed = 0; + switch (highwayClass) + { + case ftypes::HighwayClass::Trunk: speed = 100000.0/60/60; break; + case ftypes::HighwayClass::Primary: speed = 70000.0/60/60; break; + case ftypes::HighwayClass::Secondary: speed = 70000.0/60/60; break; + case ftypes::HighwayClass::Tertiary: speed = 50000.0/60/60; break; + case ftypes::HighwayClass::LivingStreet: speed = 20000.0/60/60; break; + case ftypes::HighwayClass::Service: speed = 10000.0/60/60; break; + case ftypes::HighwayClass::Pedestrian: speed = 50000.0/60/60; break; + default: speed = 500000.0/60/60; break; + } + return distance / speed; +} + /*! * \brief Returns ingoing point or outgoing point for turns. * These points belong to the route but they often are not neighbor of junction point. @@ -397,6 +414,11 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment m2::PointD nextPoint; size_t count = 0; double curDistanceMeters = 0.0; + double curTime = 0.0; + + // There is no need to go too far for low-speed roads. + // So additional time limit is applied. + double maxTime = 3.0; ASSERT(GetNextRoutePointIndex(result, index, numMwmIds, forward, false, nextIndex), ()); @@ -410,8 +432,11 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment if (point == nextPoint && outgoingSegmentIndex + 1 == segments.size()) return nextPoint; - curDistanceMeters += mercator::DistanceOnEarth(point, nextPoint); - if (curDistanceMeters > maxDistMeters || ++count >= maxPointsCount) + double distanceMeters = mercator::DistanceOnEarth(point, nextPoint); + curDistanceMeters += distanceMeters; + curTime += CalcEstimatedTimeToPass(distanceMeters, segments[nextIndex.m_segmentIndex].m_highwayClass); + + if (curTime > maxTime || ++count >= maxPointsCount || curDistanceMeters > maxDistMeters) return nextPoint; point = nextPoint; -- 2.45.3 From c87e9111e97a3ebc8652e3e804266784fda2ec1a Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Mon, 2 May 2022 12:36:21 +0300 Subject: [PATCH 15/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- routing/routing_settings.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/routing_settings.hpp b/routing/routing_settings.hpp index bfed66fb23..b63f75b3f8 100644 --- a/routing/routing_settings.hpp +++ b/routing/routing_settings.hpp @@ -57,8 +57,8 @@ public: double m_minOutgoingDistMeters; size_t m_maxIngoingPointsCount; double m_minIngoingDistMeters; - // These 2 parameters are used during turns generation to calculate the angle between the turn - // point and one of the neighbour points to find out if the route turns significantly or not. + /// @todo These 2 parameters are not used anymore. + /// It should me removed. But desktop crashes after removal. size_t m_notSoCloseMaxPointsCount; double m_notSoCloseMaxDistMeters; }; -- 2.45.3 From 129c16848068d754af482dc3464d2f29cc68c897 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Mon, 2 May 2022 14:56:48 +0300 Subject: [PATCH 16/28] [Routing] Refactoring Tests update Signed-off-by: Anton Makouski --- .../bicycle_turn_test.cpp | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/routing/routing_integration_tests/bicycle_turn_test.cpp b/routing/routing_integration_tests/bicycle_turn_test.cpp index 994f96e929..0f02cf6c6c 100644 --- a/routing/routing_integration_tests/bicycle_turn_test.cpp +++ b/routing/routing_integration_tests/bicycle_turn_test.cpp @@ -19,12 +19,13 @@ UNIT_TEST(RussiaMoscowSevTushinoParkBicycleWayTurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo 5 turns is ok here for now, IMHO. Probably the second turn is redundant. - integration::TestTurnCount(route, 5 /* expectedTurnCount */); - integration::GetNthTurn(route, 0).TestValid().TestOneOfDirections( - {CarDirection::TurnSlightRight}); + /// @todo Turn 3 is discarded since first segment of alternative way is too sharp. + integration::TestTurnCount(route, 4 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightRight); integration::GetNthTurn(route, 1).TestValid().TestOneOfDirections( - {CarDirection::TurnLeft}); + {CarDirection::TurnSlightLeft, CarDirection::TurnLeft}); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnSlightRight); integration::TestRouteLength(route, 753.0); } @@ -125,7 +126,6 @@ UNIT_TEST(Russia_Moscow_TatishchevaOnewayCarRoad_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnRight); - // @todo No potential additioanal GoStraight turn 2. Currently is kept because of 35 degrees TurnSlightLeft alternative. integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnRight); @@ -170,6 +170,7 @@ UNIT_TEST(TurnsNearAltufievskoeShosseLongFakeSegmentTest) /// @todo Problem with outgoingTurns from RoutingEngineResult::GetPossibleTurns at (turn_m_index == 3) /// For some reason for both of outgoingTurns angles are 0, but it is expected to be -90 and +90. + /// But this does not prevent from proper directions. integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); @@ -206,7 +207,16 @@ UNIT_TEST(TurnsNearMKAD85kmShortFakeSegmentTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestTurnCount(route, 8 /* expectedTurnCount */); + integration::TestTurnCount(route, 9 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::GoStraight); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::EnterRoundAbout); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::LeaveRoundAbout); + integration::GetNthTurn(route, 5).TestValid().TestDirection(CarDirection::EnterRoundAbout); + integration::GetNthTurn(route, 6).TestValid().TestDirection(CarDirection::LeaveRoundAbout); + integration::GetNthTurn(route, 7).TestValid().TestDirection(CarDirection::TurnRight); + integration::GetNthTurn(route, 8).TestValid().TestDirection(CarDirection::TurnLeft); integration::TestRouteLength(route, 1704.21); } -- 2.45.3 From 938226849c7e5a354fbb22c77b5534e012401e6c Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Mon, 2 May 2022 16:11:43 +0300 Subject: [PATCH 17/28] [Routing] Refactoring Tuning. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 96f0f92366..b9d83a79d6 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -215,7 +215,7 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, TurnInfo const & turnInfo, NumMwmIds const & numMwmIds) { - double constexpr kMaxAbsAngleSameRoadClass = 70.0; + double constexpr kMinAbsAngleDiffSameRoadClass = 35.0; ftypes::HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; ftypes::HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; @@ -234,6 +234,7 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, if (turnRoute.m_turn == CarDirection::GoStraight && static_cast(outgoingRouteRoadClass) >= static_cast(ingoingRouteRoadClass)) { + // Can't discard turn if alternative cadidate's angle is not enough sharp compared to the route. if (abs(t.m_angle) < abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) return false; } @@ -248,9 +249,8 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, } else if (highwayClass == outgoingRouteRoadClass) { - // Any alternative turn to a similar road keeps the turn direction if the turn's angle is less than - // kMaxAbsAngleSameRoadClass degrees (wider than SlightTurn). - if (fabs(t.m_angle) < kMaxAbsAngleSameRoadClass) + // Can't discard turn if alternative cadidate's angle is not enough sharp compared to the route. + if (abs(t.m_angle) < abs(routeAngle) + kMinAbsAngleDiffSameRoadClass) return false; } else // static_cast(highwayClass) > static_cast(outgoingRouteRoadClass) @@ -579,6 +579,7 @@ void GoStraightCorrection(TurnCandidate const & notRouteCandidate, double const if (turn.m_turn != CarDirection::GoStraight) return; + // No need to coorect turn if alternative cadidate's angle is sharp enough compared to the route. if (abs(notRouteCandidate.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) return; -- 2.45.3 From 5cdd2ddb648e03375b99c43f41d513d341d7009d Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Mon, 2 May 2022 20:12:51 +0300 Subject: [PATCH 18/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- .../routing_tests/turns_generator_test.cpp | 20 - routing/turns_generator.cpp | 452 ++++++++---------- routing/turns_generator.hpp | 12 - 3 files changed, 187 insertions(+), 297 deletions(-) diff --git a/routing/routing_tests/turns_generator_test.cpp b/routing/routing_tests/turns_generator_test.cpp index 9c4a4c7da7..f87164a22f 100644 --- a/routing/routing_tests/turns_generator_test.cpp +++ b/routing/routing_tests/turns_generator_test.cpp @@ -290,26 +290,6 @@ UNIT_TEST(TestGetRoundaboutDirection) TEST_EQUAL(GetRoundaboutDirection(true, false, false, false), CarDirection::LeaveRoundAbout, ()); } -UNIT_TEST(TestCheckRoundaboutEntrance) -{ - // The signature of CheckRoundaboutEntrance function is - // CheckRoundaboutEntrance(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout) - TEST(!CheckRoundaboutEntrance(true, true), ()); - TEST(!CheckRoundaboutEntrance(false, false), ()); - TEST(!CheckRoundaboutEntrance(true, false), ()); - TEST(CheckRoundaboutEntrance(false, true), ()); -} - -UNIT_TEST(TestCheckRoundaboutExit) -{ - // The signature of GetRoundaboutDirection function is - // CheckRoundaboutExit(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout) - TEST(!CheckRoundaboutExit(true, true), ()); - TEST(!CheckRoundaboutExit(false, false), ()); - TEST(CheckRoundaboutExit(true, false), ()); - TEST(!CheckRoundaboutExit(false, true), ()); -} - UNIT_TEST(TestInvertDirection) { TEST_EQUAL(InvertDirection(CarDirection::TurnSlightRight), CarDirection::TurnSlightLeft, ()); diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index b9d83a79d6..9c730f710b 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -16,6 +16,7 @@ using namespace routing; using namespace routing::turns; using namespace std; +using namespace ftypes; namespace { @@ -24,47 +25,33 @@ double constexpr kMaxForwardAngleCandidates = 95.0; double constexpr kMaxForwardAngleActual = 60.0; double constexpr kMinAngleDiffToNotConfuseStraightAndAlternative = 25.0; -/// \brief Contains information about highway classes of the route goes through a turn -/// and about highway classes of possible ways from the turn. -struct TurnHighwayClasses +bool IsHighway(HighwayClass hwClass, bool isLink) { - // When a route goes through a turn there's an ingoing and outgoing segments. - // |m_smallestRouteRoadClass| is equal to the less important road between ingoing and outgoing - // segments. - ftypes::HighwayClass m_smallestRouteRoadClass = ftypes::HighwayClass::Error; - // Let's consider all possible ways from the turn except for the way along the route and - // the way which let us to make a U-turn by going along the ingoing segment. - // |m_biggestPossibleTurnRoadClass| should be equal to the class of the biggest road of such roads. - ftypes::HighwayClass m_biggestPossibleTurnRoadClass = ftypes::HighwayClass::Error; -}; - -bool IsHighway(ftypes::HighwayClass hwClass, bool isLink) -{ - return (hwClass == ftypes::HighwayClass::Trunk || hwClass == ftypes::HighwayClass::Primary) && + return (hwClass == HighwayClass::Trunk || hwClass == HighwayClass::Primary) && !isLink; } -bool IsSmallRoad(ftypes::HighwayClass hwClass) +bool IsSmallRoad(HighwayClass hwClass) { - return hwClass == ftypes::HighwayClass::LivingStreet || - hwClass == ftypes::HighwayClass::Service || hwClass == ftypes::HighwayClass::Pedestrian; + return hwClass == HighwayClass::LivingStreet || + hwClass == HighwayClass::Service || hwClass == HighwayClass::Pedestrian; } -/// \brief Fills |turn| with |CarDirection::ExitHighwayToRight| or |CarDirection::ExitHighwayToLeft| -/// and returns true. Or does not change |turn| and returns false. -/// \note The function makes a decision about |turn| based on geometry of the route and turn +/// \brief Return |CarDirection::ExitHighwayToRight| or |CarDirection::ExitHighwayToLeft| +/// or return |CarDirection::None|. +/// \note The function makes a decision about turn based on geometry of the route and turn /// candidates, so it works correctly for both left and right hand traffic. -bool IsExit(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, - Segment const & firstOutgoingSeg, CarDirection intermediateDirection, CarDirection & turn) +CarDirection TryToGetExitDirection(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, + Segment const & firstOutgoingSeg, CarDirection const intermediateDirection) { if (!possibleTurns.isCandidatesAngleValid) - return false; + return CarDirection::None; if (!IsHighway(turnInfo.m_ingoing->m_highwayClass, turnInfo.m_ingoing->m_isLink) || !(turnInfo.m_outgoing->m_isLink || (IsSmallRoad(turnInfo.m_outgoing->m_highwayClass) && IsGoStraightOrSlightTurn(intermediateDirection)))) { - return false; + return CarDirection::None; } // At this point it is known that the route goes form a highway to a link road or to a small road // which has a slight angle with the highway. @@ -93,132 +80,42 @@ bool IsExit(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, { ++highwayCandidateNumber; if (highwayCandidateNumber >= 2) - return false; // There are two or more highway candidates from the junction. + return CarDirection::None; // There are two or more highway candidates from the junction. isHighwayCandidateBeforeOutgoing = isCandidateBeforeOutgoing; } } if (highwayCandidateNumber == 1) { - turn = isHighwayCandidateBeforeOutgoing ? CarDirection::ExitHighwayToRight + return isHighwayCandidateBeforeOutgoing ? CarDirection::ExitHighwayToRight : CarDirection::ExitHighwayToLeft; - return true; } - return false; + return CarDirection::None; } -/// \brief If it's possible fills |turnHighwayClasses| and returns true, and returns false otherwise. -bool GetTurnHighwayClasses(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, - NumMwmIds const & numMwmIds, TurnHighwayClasses & turnHighwayClasses) +int CalcDiffRoadClasses(HighwayClass const left, HighwayClass const right) { - // The turn should be kept if there's no any information about feature id of outgoing segment - // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. - Segment firstOutgoingSegment; - if (!turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) - return true; - - Segment inversedLastIngoingSegment; - if (!turnInfo.m_ingoing->m_segmentRange.GetLastSegment(numMwmIds, inversedLastIngoingSegment)) - return true; - inversedLastIngoingSegment.Inverse(); - - turnHighwayClasses.m_biggestPossibleTurnRoadClass = ftypes::HighwayClass::Count; - for (auto const & t : possibleTurns.candidates) - { - // Let's consider all outgoing segments except for - // (1) route outgoing segment - // (2) a U-turn segment (inversedLastIngoingSegment). - if (t.m_segment == firstOutgoingSegment || t.m_segment == inversedLastIngoingSegment) - continue; - ftypes::HighwayClass const highwayClass = t.m_highwayClass; - // @note The bigger is the road, the lesser is HighwayClass value. - if (static_cast(highwayClass) < static_cast(turnHighwayClasses.m_biggestPossibleTurnRoadClass)) - turnHighwayClasses.m_biggestPossibleTurnRoadClass = highwayClass; - } - if (turnHighwayClasses.m_biggestPossibleTurnRoadClass == ftypes::HighwayClass::Error) - { - ASSERT_GREATER(possibleTurns.candidates.size(), 1, ("No turn candidates or there's only one turn candidate.")); - ASSERT(false, ("One of possible turns follows along an undefined HighwayClass.")); - return true; - } - - if (turnHighwayClasses.m_biggestPossibleTurnRoadClass == ftypes::HighwayClass::Undefined) - return false; // Fake edges have HighwayClass::Undefined. - - if (turnHighwayClasses.m_biggestPossibleTurnRoadClass == ftypes::HighwayClass::Count) - return false; // No outgoing segments except for the route. - - // @note The bigger is the road, the lesser is HighwayClass value. - turnHighwayClasses.m_smallestRouteRoadClass = - static_cast(max(static_cast(turnInfo.m_ingoing->m_highwayClass), - static_cast(turnInfo.m_outgoing->m_highwayClass))); - - if (turnHighwayClasses.m_smallestRouteRoadClass == ftypes::HighwayClass::Error) - { - ASSERT(false, ("The route contains undefined HighwayClass.")); - return false; - } - if (turnHighwayClasses.m_smallestRouteRoadClass == ftypes::HighwayClass::Undefined) - return false; // Fake edges have HighwayClass::Undefined. - - return true; + return static_cast(left) - static_cast(right); } /// * \returns true when /// * - the route leads from one big road to another one; /// * - and the other possible turns lead to small roads; /// * Returns false otherwise. -/// \param possibleTurns is all possible ways out from a junction. +/// \param turnCandidates is all possible ways out from a junction. /// \param turnInfo is information about ingoing and outgoing segments of the route. -/// \param carDirection is a direction about the current turn if it's available. -/// If not, CarDirection::None should be passed. -bool DiscardTurnByHighwayClass(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, - NumMwmIds const & numMwmIds, CarDirection carDirection) +bool CanDiscardTurnByHighwayClass(std::vector const & turnCandidates, + TurnInfo const & turnInfo, + NumMwmIds const & numMwmIds) { // Maximum difference between HighwayClasses of the route segments and // the possible way segments to keep the junction as a turn. - int constexpr kMaxHighwayClassDiff = 2; - int constexpr kMaxHighwayClassDiffForGoStraight = 1; - int constexpr kMaxHighwayClassDiffForService = 1; + int constexpr kMinHighwayClassDiff = -2; + int constexpr kMinHighwayClassDiffForService = -1; - TurnHighwayClasses turnHighwayClasses; - // Looks like a bug. If no info received turn should not be discarded. - if (!GetTurnHighwayClasses(possibleTurns, turnInfo, numMwmIds, turnHighwayClasses)) - return true; + HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; + HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; - int const diff = - static_cast(turnHighwayClasses.m_biggestPossibleTurnRoadClass) - static_cast(turnHighwayClasses.m_smallestRouteRoadClass); - if (diff >= kMaxHighwayClassDiff || - (diff >= kMaxHighwayClassDiffForGoStraight && carDirection == CarDirection::GoStraight) || - (diff >= kMaxHighwayClassDiffForService && turnHighwayClasses.m_biggestPossibleTurnRoadClass == ftypes::HighwayClass::Service)) - { - // The turn shall be removed if the route goes near small roads. - return true; - } - return false; -} - -/// * \returns false when -/// * for routes which go straight and don't go to a smaller road: -/// * - any alternative GoStraight or SlightTurn turn -/// * for other routes: -/// * - any alternative turn to a bigger road -/// * - or any alternative turn to a similar road if the turn's angle is less than kMaxAbsAngleSameRoadClass degrees (wider than SlightTurn) -/// * - or any alternative turn to a smaller road if it's GoStraight or SlightTurn. -/// * Returns true otherwise. -/// \param turnRoute is current route turn -/// \param turnCandidates is all possible ways out from a junction except uturn. -/// \param turnInfo is information about ingoing and outgoing segments of the route. -/// it is supposed that current turn is GoStraight or SlightTurn -bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, - double routeAngle, - std::vector const & turnCandidates, - TurnInfo const & turnInfo, - NumMwmIds const & numMwmIds) -{ - double constexpr kMinAbsAngleDiffSameRoadClass = 35.0; - - ftypes::HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; - ftypes::HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; + HighwayClass maxRouteRoadClass = static_cast(max(static_cast(ingoingRouteRoadClass), static_cast(outgoingRouteRoadClass))); // The turn should be kept if there's no any information about feature id of outgoing segment // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. @@ -232,33 +129,116 @@ bool DiscardTurnByNoAlignedAlternatives(TurnItem const & turnRoute, if (t.m_segment == firstOutgoingSegment) continue; - if (turnRoute.m_turn == CarDirection::GoStraight && static_cast(outgoingRouteRoadClass) >= static_cast(ingoingRouteRoadClass)) + HighwayClass candidateRoadClass = t.m_highwayClass; + + // If route's road is significantly larger than candidate's road, the candidate can be ignored. + if (CalcDiffRoadClasses(maxRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiff) + continue; + + // If route's road is significantly larger than candidate's service road, the candidate can be ignored. + if (CalcDiffRoadClasses(maxRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) + continue; + + return false; + } + + return true; +} + +/// * \returns false when +/// * for routes which go straight and don't go to a smaller road: +/// * - any alternative GoStraight or SlightTurn turn +/// * for other routes: +/// * - any alternative turn to a bigger road +/// * - or any alternative turn to a similar road if the turn's angle is less than kMaxAbsAngleSameRoadClass degrees (wider than SlightTurn) +/// * - or any alternative turn to a smaller road if it's GoStraight or SlightTurn. +/// * Returns true otherwise. +/// \param routeDirection is route direction +/// \param routeAngle is route angle +/// \param turnCandidates is all possible ways out from a junction except uturn. +/// \param turnInfo is information about ingoing and outgoing segments of the route. +bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, + double routeAngle, + std::vector const & turnCandidates, + TurnInfo const & turnInfo, + NumMwmIds const & numMwmIds) +{ + if (!IsGoStraightOrSlightTurn(routeDirection)) + { + ASSERT(false, ("It is supposed that current turn is GoStraight or SlightTurn")); + return false; + } + + // Minimum difference between alternative cadidate's angle and the route to ignore this candidate. + double constexpr kMinAbsAngleDiffSameRoadClass = 35.0; + // Maximum difference between HighwayClasses of the route segments and + // the possible way segments to keep the junction as a turn. + int constexpr kMinHighwayClassDiff = -2; + int constexpr kMinHighwayClassDiffForGoStraight = -1; + int constexpr kMinHighwayClassDiffForService = -1; + + HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; + HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; +// HighwayClass maxRouteRoadClass = static_cast(max(static_cast(ingoingRouteRoadClass), static_cast(outgoingRouteRoadClass))); + + // The turn should be kept if there's no any information about feature id of outgoing segment + // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. + Segment firstOutgoingSegment; + if (!turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment)) + return false; + + for (auto const & t : turnCandidates) + { + // Let's consider all outgoing segments except for route outgoing segment. + if (t.m_segment == firstOutgoingSegment) + continue; + + HighwayClass candidateRoadClass = t.m_highwayClass; + + // If outgoing route road is significantly larger than candidate, the candidate can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiff) + continue; + + // If igngoing and outgoing edges are not links and candidate-link can be ignored. + /// @todo Maybe add condition of links m_highwayClass to be higher. + if (!turnInfo.m_ingoing->m_isLink && !turnInfo.m_outgoing->m_isLink && + turnInfo.m_ingoing->m_highwayClass == turnInfo.m_outgoing->m_highwayClass && + t.m_isLink) + continue; + + if (routeDirection == CarDirection::GoStraight) { - // Can't discard turn if alternative cadidate's angle is not enough sharp compared to the route. - if (abs(t.m_angle) < abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) + // If outgoing route road is significantly larger than candidate, the candidate can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForGoStraight) + continue; + + // If outgoing route road is the same or large than ingoing + // and candidate's angle is sharp enough compared to the route it can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, ingoingRouteRoadClass) <= 0 && + abs(t.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) + continue; + } + + // If outgoing route road is smaller than candidate candidate turn cant be discarded. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) > 0) + { + return false; + } + else if (outgoingRouteRoadClass == candidateRoadClass) + { + // If alternative cadidate's road size is the same and it's angle is not enough sharp compared to the route turn can't be discarded. + if (abs(t.m_angle) < abs(routeAngle) + kMinAbsAngleDiffSameRoadClass) return false; } - else + else // Outgoing route road is larger than candidate. { - ftypes::HighwayClass const highwayClass = t.m_highwayClass; - // @note The bigger is the road, the lesser is HighwayClass value. - if (static_cast(highwayClass) < static_cast(outgoingRouteRoadClass)) - { - // Any alternative turn to a bigger road keeps the turn direction. + // If outgoing route's road is significantly larger than candidate's service road, the candidate can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) + continue; + + // Any alternative turn to a smaller road keeps the turn direction if it's GoStraight or SlightTurn. + if (IsGoStraightOrSlightTurn(IntermediateDirection(t.m_angle))) return false; - } - else if (highwayClass == outgoingRouteRoadClass) - { - // Can't discard turn if alternative cadidate's angle is not enough sharp compared to the route. - if (abs(t.m_angle) < abs(routeAngle) + kMinAbsAngleDiffSameRoadClass) - return false; - } - else // static_cast(highwayClass) > static_cast(outgoingRouteRoadClass) - { - // Any alternative turn to a smaller road keeps the turn direction if it's GoStraight or SlightTurn. - if (IsGoStraightOrSlightTurn(IntermediateDirection(t.m_angle))) - return false; - } } } return true; @@ -271,14 +251,13 @@ bool KeepRoundaboutTurnByHighwayClass(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, NumMwmIds const & numMwmIds) { Segment firstOutgoingSegment; - bool const validFirstOutgoingSeg = - turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment); + bool const validFirstOutgoingSeg = turnInfo.m_outgoing->m_segmentRange.GetFirstSegment(numMwmIds, firstOutgoingSegment); for (auto const & t : possibleTurns.candidates) { if (!validFirstOutgoingSeg || t.m_segment == firstOutgoingSegment) continue; - if (static_cast(t.m_highwayClass) != static_cast(ftypes::HighwayClass::Service)) + if (static_cast(t.m_highwayClass) != static_cast(HighwayClass::Service)) return true; } return false; @@ -354,18 +333,18 @@ m2::PointD GetPointByIndex(TUnpackedPathSegments const & segments, RoutePointInd return segments[index.m_segmentIndex].m_path[index.m_pathIndex].GetPoint(); } -double CalcEstimatedTimeToPass(double distance, ftypes::HighwayClass highwayClass) +double CalcEstimatedTimeToPass(double distance, HighwayClass highwayClass) { double speed = 0; switch (highwayClass) { - case ftypes::HighwayClass::Trunk: speed = 100000.0/60/60; break; - case ftypes::HighwayClass::Primary: speed = 70000.0/60/60; break; - case ftypes::HighwayClass::Secondary: speed = 70000.0/60/60; break; - case ftypes::HighwayClass::Tertiary: speed = 50000.0/60/60; break; - case ftypes::HighwayClass::LivingStreet: speed = 20000.0/60/60; break; - case ftypes::HighwayClass::Service: speed = 10000.0/60/60; break; - case ftypes::HighwayClass::Pedestrian: speed = 50000.0/60/60; break; + case HighwayClass::Trunk: speed = 100000.0/60/60; break; + case HighwayClass::Primary: speed = 70000.0/60/60; break; + case HighwayClass::Secondary: speed = 70000.0/60/60; break; + case HighwayClass::Tertiary: speed = 50000.0/60/60; break; + case HighwayClass::LivingStreet: speed = 20000.0/60/60; break; + case HighwayClass::Service: speed = 10000.0/60/60; break; + case HighwayClass::Pedestrian: speed = 50000.0/60/60; break; default: speed = 500000.0/60/60; break; } return distance / speed; @@ -447,18 +426,7 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment return nextPoint; } -size_t GetLinkCount(vector const & candidates) -{ - size_t numLinks = 0; - for (auto const & c : candidates) - { - if (c.m_isLink) - ++numLinks; - } - return numLinks; -} - -double GetOneSegmentTurnAngle(TurnInfo const & turnInfo) +double CalcOneSegmentTurnAngle(TurnInfo const & turnInfo) { ASSERT_GREATER_OR_EQUAL(turnInfo.m_ingoing->m_path.size(), 2, ()); ASSERT_GREATER_OR_EQUAL(turnInfo.m_outgoing->m_path.size(), 2, ()); @@ -468,7 +436,7 @@ double GetOneSegmentTurnAngle(TurnInfo const & turnInfo) turnInfo.m_outgoing->m_path[1].GetPoint())); } -double GetPathTurnAngle(LoadedPathSegment const & segment, size_t const pathIndex) +double CalcPathTurnAngle(LoadedPathSegment const & segment, size_t const pathIndex) { ASSERT_GREATER_OR_EQUAL(segment.m_path.size(), 3, ()); ASSERT_GREATER(pathIndex, 0, ()); @@ -513,7 +481,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde TurnInfo const turnInfo(&segments[index.m_segmentIndex], &segments[index.m_segmentIndex + 1]); - double const oneSegmentTurnAngle = GetOneSegmentTurnAngle(turnInfo); + double const oneSegmentTurnAngle = CalcOneSegmentTurnAngle(turnInfo); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); if (smoothOnly && !IsGoStraightOrSlightTurn(oneSegmentDirection)) return false; // Too sharp turn angle. @@ -539,7 +507,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde return true; } - if (DiscardTurnByHighwayClass(possibleTurns, turnInfo, numMwmIds, CarDirection::None)) + if (CanDiscardTurnByHighwayClass(possibleTurns.candidates, turnInfo, numMwmIds)) { // Taking the next point of the next segment. nextIndex = {index.m_segmentIndex + 1, 1 /* m_pathIndex */}; @@ -557,7 +525,7 @@ bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex c auto const & segments = result.GetSegments(); if (smoothOnly && segments[index.m_segmentIndex].m_path.size() >= 3 && index.m_pathIndex < segments[index.m_segmentIndex].m_path.size() - 1) { - double const oneSegmentTurnAngle = GetPathTurnAngle(segments[index.m_segmentIndex], index.m_pathIndex); + double const oneSegmentTurnAngle = CalcPathTurnAngle(segments[index.m_segmentIndex], index.m_pathIndex); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); if (!IsGoStraightOrSlightTurn(oneSegmentDirection)) return false; // Too sharp turn angle. @@ -573,7 +541,7 @@ bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex c * the route) is checked. If the other way is "go straight" or "slight turn", turn.m_turn is set * to |turnToSet|. */ -void GoStraightCorrection(TurnCandidate const & notRouteCandidate, double const routeAngle, CarDirection const & turnToSet, +void CorrectGoStraight(TurnCandidate const & notRouteCandidate, double const routeAngle, CarDirection const & turnToSet, TurnItem & turn) { if (turn.m_turn != CarDirection::GoStraight) @@ -680,8 +648,7 @@ RouterResultCode MakeTurnAnnotation(IRoutingResult const & result, NumMwmIds con CHECK_GREATER(outgoingSegmentDist, 0, ()); auto const outgoingSegmentIndex = static_cast(outgoingSegmentDist); - skipTurnSegments = - CheckUTurnOnRoute(result, outgoingSegmentIndex, numMwmIds, vehicleSettings, turnItem); + skipTurnSegments = CheckUTurnOnRoute(result, outgoingSegmentIndex, numMwmIds, vehicleSettings, turnItem); if (turnItem.m_turn == CarDirection::None) GetTurnDirection(result, outgoingSegmentIndex, numMwmIds, vehicleSettings, turnItem); @@ -933,16 +900,6 @@ void SelectRecommendedLanes(Route::TTurns & turnsDir) } } -bool CheckRoundaboutEntrance(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout) -{ - return !isIngoingEdgeRoundabout && isOutgoingEdgeRoundabout; -} - -bool CheckRoundaboutExit(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout) -{ - return isIngoingEdgeRoundabout && !isOutgoingEdgeRoundabout; -} - CarDirection GetRoundaboutDirection(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout, bool isMultiTurnJunction, bool keepTurnByHighwayClass) { @@ -953,16 +910,23 @@ CarDirection GetRoundaboutDirection(bool isIngoingEdgeRoundabout, bool isOutgoin return CarDirection::None; } - if (CheckRoundaboutEntrance(isIngoingEdgeRoundabout, isOutgoingEdgeRoundabout)) + if (!isIngoingEdgeRoundabout && isOutgoingEdgeRoundabout) return CarDirection::EnterRoundAbout; - if (CheckRoundaboutExit(isIngoingEdgeRoundabout, isOutgoingEdgeRoundabout)) + if (isIngoingEdgeRoundabout && !isOutgoingEdgeRoundabout) return CarDirection::LeaveRoundAbout; ASSERT(false, ()); return CarDirection::None; } +CarDirection GetRoundaboutDirection(TurnInfo const turnInfo, TurnCandidates const & nodes, NumMwmIds const & numMwmIds) +{ + bool const keepTurnByHighwayClass = KeepRoundaboutTurnByHighwayClass(nodes, turnInfo, numMwmIds); + return GetRoundaboutDirection(turnInfo.m_ingoing->m_onRoundabout, turnInfo.m_outgoing->m_onRoundabout, + nodes.candidates.size() > 1, keepTurnByHighwayClass); +} + CarDirection InvertDirection(CarDirection dir) { switch (dir) @@ -1069,20 +1033,17 @@ double CalcTurnAngle(IRoutingResult const & result, size_t const outgoingSegmentIndex, m2::PointD const & junctionPoint, NumMwmIds const & numMwmIds, - size_t maxIngoingPointsCount, - double const maxIngoingDistMeters, - size_t maxOutgoingPointsCount, - double const maxOutgoingDistMeters) + RoutingSettings const & vehicleSettings) { bool const isStartFakeLoop = PathIsFakeLoop(result.GetSegments()[outgoingSegmentIndex - 1].m_path); size_t const segmentIndexForIngoingPoint = isStartFakeLoop ? outgoingSegmentIndex - 1 : outgoingSegmentIndex; m2::PointD const ingoingPoint = GetPointForTurn( - result, segmentIndexForIngoingPoint, numMwmIds, maxIngoingPointsCount, - maxIngoingDistMeters, false /* forward */); + result, segmentIndexForIngoingPoint, numMwmIds, vehicleSettings.m_maxIngoingPointsCount, + vehicleSettings.m_minIngoingDistMeters, false /* forward */); m2::PointD const outgoingPoint = GetPointForTurn( - result, outgoingSegmentIndex, numMwmIds, maxOutgoingPointsCount, - maxOutgoingDistMeters, true /* forward */); + result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, + vehicleSettings.m_minOutgoingDistMeters, true /* forward */); double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); return turnAngle; @@ -1092,17 +1053,14 @@ double CalcTurnAngle(IRoutingResult const & result, // It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are // from different mwms. // Let's identify it turnCandidates by angle and update according turnCandidate. -void CorrectCandidatesSegmentByOutgoing(IRoutingResult const & result, - size_t const outgoingSegmentIndex, - m2::PointD const & junctionPoint, - NumMwmIds const & numMwmIds, - Segment const & firstOutgoingSeg, - std::vector & candidates) +void CorrectCandidatesSegmentByOutgoing(TurnInfo const & turnInfo, + Segment const & firstOutgoingSeg, + std::vector & candidates) { auto IsFirstOutgoingSeg = [&firstOutgoingSeg](TurnCandidate const & turnCandidate) { return turnCandidate.m_segment == firstOutgoingSeg; }; if (find_if(candidates.begin(), candidates.end(), IsFirstOutgoingSeg) == candidates.end()) { - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, 1, 0.0, 1, 0.0); + double const turnAngle = CalcOneSegmentTurnAngle(turnInfo); auto DoesAngleMatch = [&turnAngle](TurnCandidate const & turnCandidate) { return base::AlmostEqualAbs(turnCandidate.m_angle, turnAngle, 0.001); }; auto it = find_if(candidates.begin(), candidates.end(), DoesAngleMatch); if (it != candidates.end()) @@ -1179,7 +1137,7 @@ void CorrectRightmostAndLeftmost(std::vector const & turnCandidat // The route goes along the leftmost candidate. turn.m_turn = LeftmostDirection(turnAngle); // Compare with the next candidate to the right. - GoStraightCorrection(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightLeft, turn); + CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightLeft, turn); break; } // Ignoring too sharp alternative candidates. @@ -1193,7 +1151,7 @@ void CorrectRightmostAndLeftmost(std::vector const & turnCandidat // The route goes along the rightmost candidate. turn.m_turn = RightmostDirection(turnAngle); // Compare with the next candidate to the left. - GoStraightCorrection(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightRight, turn); + CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightRight, turn); break; } // Ignoring too sharp alternative candidates. @@ -1234,8 +1192,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen return; // No turns are needed on transported road. - if (turnInfo.m_ingoing->m_highwayClass == ftypes::HighwayClass::Transported && - turnInfo.m_outgoing->m_highwayClass == ftypes::HighwayClass::Transported) + if (turnInfo.m_ingoing->m_highwayClass == HighwayClass::Transported && turnInfo.m_outgoing->m_highwayClass == HighwayClass::Transported) return; Segment firstOutgoingSeg; @@ -1243,7 +1200,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (!isFirstOutgoingSegValid) return; - CorrectCandidatesSegmentByOutgoing(result, outgoingSegmentIndex, junctionPoint, numMwmIds, firstOutgoingSeg, nodes.candidates); + CorrectCandidatesSegmentByOutgoing(turnInfo, firstOutgoingSeg, nodes.candidates); RemoveUTurnCandidate(turnInfo, numMwmIds, nodes.candidates); auto const & turnCandidates = nodes.candidates; @@ -1251,50 +1208,33 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // Check for enter or exit to/from roundabout. if (turnInfo.m_ingoing->m_onRoundabout || turnInfo.m_outgoing->m_onRoundabout) { - bool const keepTurnByHighwayClass = KeepRoundaboutTurnByHighwayClass(nodes, turnInfo, numMwmIds); - turn.m_turn = GetRoundaboutDirection(turnInfo.m_ingoing->m_onRoundabout, - turnInfo.m_outgoing->m_onRoundabout, - turnCandidates.size() > 1, - keepTurnByHighwayClass); + turn.m_turn = GetRoundaboutDirection(turnInfo, nodes, numMwmIds); return; } - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, - vehicleSettings.m_maxIngoingPointsCount, - vehicleSettings.m_minIngoingDistMeters, - vehicleSettings.m_maxOutgoingPointsCount, - vehicleSettings.m_minOutgoingDistMeters); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, vehicleSettings); + CarDirection const intermediateDirection = IntermediateDirection(turnAngle); // Checking for exits from highways. - if (IsExit(nodes, turnInfo, firstOutgoingSeg, intermediateDirection, turn.m_turn)) - { + turn.m_turn = TryToGetExitDirection(nodes, turnInfo, firstOutgoingSeg, intermediateDirection); + if (turn.m_turn != CarDirection::None) return; + + if (turnCandidates.size() == 1) + { + ASSERT(turnCandidates.front().m_segment == firstOutgoingSeg, ()); + // IngoingCount includes ingoing segment and reversed outgoing (if it is not one-way). + // Both of them should be isnored. If any other one is present, turn is kept to prevent user from going to oneway alternative. + if (ingoingCount <= 1 + size_t(!turnInfo.m_outgoing->m_isOneWay)) + return; } turn.m_turn = intermediateDirection; - // Note 1. If the road significantly changes its direction this turn shall be kept here. - // Note 2. Keeping a turn at this point means that the decision to keep this turn or not - // will be made after. - if (!turn.m_keepAnyway && IsGoStraightOrSlightTurn(turn.m_turn)) - { - if (DiscardTurnByHighwayClass(nodes, turnInfo, numMwmIds, turn.m_turn) || - DiscardTurnByNoAlignedAlternatives(turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) - { - turn.m_turn = CarDirection::None; - return; - } - } - if (IsGoStraightOrSlightTurn(turn.m_turn)) { - // Removing a slight turn if ingoing and outgoing edges are not links and all other - // possible ways out (except of uturn) are links. - /// @todo Add condition of links m_highwayClass to be higher. - if (!turnInfo.m_ingoing->m_isLink && !turnInfo.m_outgoing->m_isLink && - turnInfo.m_ingoing->m_highwayClass == turnInfo.m_outgoing->m_highwayClass && - GetLinkCount(turnCandidates) + 1 == turnCandidates.size()) + if (!turn.m_keepAnyway && CanDiscardTurnByHighwayClassOrAngles(turn.m_turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) { turn.m_turn = CarDirection::None; return; @@ -1302,21 +1242,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen } if (turnCandidates.size() >= 2) - { CorrectRightmostAndLeftmost(turnCandidates, firstOutgoingSeg, turnAngle, turn); - } - else // turnCandidates.size() == 1 - { - ASSERT(turnCandidates.front().m_segment == firstOutgoingSeg, ()); - ASSERT(!IsGoStraightOrSlightTurn(turn.m_turn), ("GoStraightOrSlightTurn are discarded before if turnCandidates.size() == 1")); - // IngoingCount includes ingoing segment and reversed outgoing (if it is not one-way). - // Both of them should be isnored. If any other one is present, turn is kept to prevent user from going to oneway alternative. - if (ingoingCount <= 1 + size_t(!turnInfo.m_outgoing->m_isOneWay)) - { - turn.m_turn = CarDirection::None; - return; - } - } } void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSegmentIndex, @@ -1329,11 +1255,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe return; m2::PointD const junctionPoint = turnInfo.m_ingoing->m_path.back().GetPoint(); - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, - vehicleSettings.m_maxIngoingPointsCount, - vehicleSettings.m_minIngoingDistMeters, - vehicleSettings.m_maxOutgoingPointsCount, - vehicleSettings.m_minOutgoingDistMeters); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, vehicleSettings); turn.m_sourceName = turnInfo.m_ingoing->m_name; turn.m_targetName = turnInfo.m_outgoing->m_name; @@ -1366,7 +1288,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe // If there is no fork on the road we don't need to generate any turn. It is pointless because // there is no possibility of leaving the route. - if (nodes.candidates.size() <= 1 || (std::fabs(GetOneSegmentTurnAngle(turnInfo)) < kMaxForwardAngleActual && HasSingleForwardTurn(nodes))) + if (nodes.candidates.size() <= 1 || (std::fabs(CalcOneSegmentTurnAngle(turnInfo)) < kMaxForwardAngleActual && HasSingleForwardTurn(nodes))) turn.m_pedestrianTurn = PedestrianDirection::None; } diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index 8fa5fae2a7..3af9c1edff 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -143,18 +143,6 @@ CarDirection LeftmostDirection(double angle); */ CarDirection IntermediateDirection(double angle); -/*! - * \return Returns true if the route enters a roundabout. - * That means isIngoingEdgeRoundabout is false and isOutgoingEdgeRoundabout is true. - */ -bool CheckRoundaboutEntrance(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout); - -/*! - * \return Returns true if the route leaves a roundabout. - * That means isIngoingEdgeRoundabout is true and isOutgoingEdgeRoundabout is false. - */ -bool CheckRoundaboutExit(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout); - /*! * \brief Calculates a turn instruction if the ingoing edge or (and) the outgoing edge belongs to a * roundabout. -- 2.45.3 From 52be2c90a5a4d486b764045ac871e24a272cfa66 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 3 May 2022 10:15:43 +0300 Subject: [PATCH 19/28] [Routing] Refactoring Removed TurnItem::m_keepAnyway. Signed-off-by: Anton Makouski --- routing/turns.cpp | 1 - routing/turns.hpp | 12 +++--------- routing/turns_generator.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/routing/turns.cpp b/routing/turns.cpp index 36629c8506..9d846250a2 100644 --- a/routing/turns.cpp +++ b/routing/turns.cpp @@ -173,7 +173,6 @@ string DebugPrint(TurnItem const & turnItem) << ", m_lanes = " << ::DebugPrint(turnItem.m_lanes) << ", m_exitNum = " << turnItem.m_exitNum << ", m_sourceName = " << turnItem.m_sourceName << ", m_targetName = " << turnItem.m_targetName - << ", m_keepAnyway = " << turnItem.m_keepAnyway << ", m_pedestrianDir = " << DebugPrint(turnItem.m_pedestrianTurn) << " ]" << endl; return out.str(); diff --git a/routing/turns.hpp b/routing/turns.hpp index b6e234f73a..5d96e5db79 100644 --- a/routing/turns.hpp +++ b/routing/turns.hpp @@ -162,19 +162,18 @@ struct TurnItem : m_index(std::numeric_limits::max()), m_turn(CarDirection::None), m_exitNum(0), - m_keepAnyway(false), m_pedestrianTurn(PedestrianDirection::None) { } TurnItem(uint32_t idx, CarDirection t, uint32_t exitNum = 0) - : m_index(idx), m_turn(t), m_exitNum(exitNum), m_keepAnyway(false) + : m_index(idx), m_turn(t), m_exitNum(exitNum) , m_pedestrianTurn(PedestrianDirection::None) { } TurnItem(uint32_t idx, PedestrianDirection p) - : m_index(idx), m_turn(CarDirection::None), m_exitNum(0), m_keepAnyway(false) + : m_index(idx), m_turn(CarDirection::None), m_exitNum(0) , m_pedestrianTurn(p) { } @@ -183,7 +182,7 @@ struct TurnItem { return m_index == rhs.m_index && m_turn == rhs.m_turn && m_lanes == rhs.m_lanes && m_exitNum == rhs.m_exitNum && m_sourceName == rhs.m_sourceName && - m_targetName == rhs.m_targetName && m_keepAnyway == rhs.m_keepAnyway && + m_targetName == rhs.m_targetName && m_pedestrianTurn == rhs.m_pedestrianTurn; } @@ -204,11 +203,6 @@ struct TurnItem uint32_t m_exitNum; /*!< Number of exit on roundabout. */ std::string m_sourceName; /*!< Name of the street which the ingoing edge belongs to */ std::string m_targetName; /*!< Name of the street which the outgoing edge belongs to */ - /*! - * \brief m_keepAnyway is true if the turn shall not be deleted - * and shall be demonstrated to an end user. - */ - bool m_keepAnyway; /*! * \brief m_pedestrianTurn is type of corresponding direction for a pedestrian, or None * if there is no pedestrian specific direction diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 9c730f710b..6372326ed9 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -169,6 +169,9 @@ bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, return false; } + if (!turnInfo.m_ingoing->m_isLink && turnInfo.m_outgoing->m_isLink) + return false; + // Minimum difference between alternative cadidate's angle and the route to ignore this candidate. double constexpr kMinAbsAngleDiffSameRoadClass = 35.0; // Maximum difference between HighwayClasses of the route segments and @@ -1172,7 +1175,6 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (!validTurnInfo) return; - turn.m_keepAnyway = (!turnInfo.m_ingoing->m_isLink && turnInfo.m_outgoing->m_isLink); turn.m_sourceName = turnInfo.m_ingoing->m_name; turn.m_targetName = turnInfo.m_outgoing->m_name; turn.m_turn = CarDirection::None; @@ -1234,7 +1236,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (IsGoStraightOrSlightTurn(turn.m_turn)) { - if (!turn.m_keepAnyway && CanDiscardTurnByHighwayClassOrAngles(turn.m_turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) + if (CanDiscardTurnByHighwayClassOrAngles(turn.m_turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) { turn.m_turn = CarDirection::None; return; -- 2.45.3 From c8c2cfa4ce6fe94570e8bcb67badcb4872ffe02b Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 3 May 2022 15:15:59 +0300 Subject: [PATCH 20/28] [Routing] Refactoring Signed-off-by: Anton Makouski --- .../routing_tests/turns_generator_test.cpp | 16 +- routing/turns_generator.cpp | 306 ++++++++---------- routing/turns_generator.hpp | 2 +- 3 files changed, 151 insertions(+), 173 deletions(-) diff --git a/routing/routing_tests/turns_generator_test.cpp b/routing/routing_tests/turns_generator_test.cpp index f87164a22f..16e3e360bb 100644 --- a/routing/routing_tests/turns_generator_test.cpp +++ b/routing/routing_tests/turns_generator_test.cpp @@ -416,45 +416,45 @@ UNIT_TEST(GetNextRoutePointIndex) // Forward direction. TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, true, nextIndex), ()); + NumMwmIds(), true /* forward */, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, true, nextIndex), ()); + NumMwmIds(), true /* forward */, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), ()); // Trying to get next item after the last item of the first segment. // False because of too sharp turn angle. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, true, nextIndex), ()); + NumMwmIds(), true /* forward */, nextIndex), ()); // Trying to get point about the end of the route. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({1 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), true /* forward */, true, nextIndex), ()); + NumMwmIds(), true /* forward */, nextIndex), ()); // Backward direction. // Moving in backward direction it's possible to get index of the first item of a segment. TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({1 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, true, nextIndex), ()); + NumMwmIds(), false /* forward */, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({1 /* m_segmentIndex */, 0 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 2 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, true, nextIndex), ()); + NumMwmIds(), false /* forward */, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), ()); TEST(GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 1 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, true, nextIndex), ()); + NumMwmIds(), false /* forward */, nextIndex), ()); TEST_EQUAL(nextIndex, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), ()); // Trying to get point before the beginning. TEST(!GetNextRoutePointIndex(resultTest, RoutePointIndex({0 /* m_segmentIndex */, 0 /* m_pathIndex */}), - NumMwmIds(), false /* forward */, true, nextIndex), ()); + NumMwmIds(), false /* forward */, nextIndex), ()); } } // namespace diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 6372326ed9..c273e89325 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -23,7 +23,26 @@ namespace // Angles in degrees for finding route segments with no actual forks. double constexpr kMaxForwardAngleCandidates = 95.0; double constexpr kMaxForwardAngleActual = 60.0; -double constexpr kMinAngleDiffToNotConfuseStraightAndAlternative = 25.0; + +// Min difference of route and alternative turn abs angles in degrees +// to ignore alternative alternative turn (when route direction is GoStraight). +double constexpr kMinAbsAngleDiffForStraightRoute = 25.0; + +// Min difference of route and alternative turn abs angles in degrees +// to ignore this alternative candidate (when alternative road is the same or smaller). +double constexpr kMinAbsAngleDiffForSameOrSmallerRoad = 35.0; + +// Min difference between HighwayClasses of the route segment and alternative turn segment +// to ignore this alternative candidate. +int constexpr kMinHighwayClassDiff = -2; + +// Min difference between HighwayClasses of the route segment and alternative turn segment +// to ignore this alternative candidate (when alternative road is service). +int constexpr kMinHighwayClassDiffForService = -1; + +// Min difference between HighwayClasses of the route segment and alternative turn segment +// to ignore this alternative candidate (when route direction is GoStraight). +int constexpr kMinHighwayClassDiffForStraightRoute = -1; bool IsHighway(HighwayClass hwClass, bool isLink) { @@ -38,7 +57,7 @@ bool IsSmallRoad(HighwayClass hwClass) } /// \brief Return |CarDirection::ExitHighwayToRight| or |CarDirection::ExitHighwayToLeft| -/// or return |CarDirection::None|. +/// or return |CarDirection::None| if no exit is detected. /// \note The function makes a decision about turn based on geometry of the route and turn /// candidates, so it works correctly for both left and right hand traffic. CarDirection TryToGetExitDirection(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, @@ -47,12 +66,12 @@ CarDirection TryToGetExitDirection(TurnCandidates const & possibleTurns, TurnInf if (!possibleTurns.isCandidatesAngleValid) return CarDirection::None; - if (!IsHighway(turnInfo.m_ingoing->m_highwayClass, turnInfo.m_ingoing->m_isLink) || - !(turnInfo.m_outgoing->m_isLink || (IsSmallRoad(turnInfo.m_outgoing->m_highwayClass) && - IsGoStraightOrSlightTurn(intermediateDirection)))) - { + if (!IsHighway(turnInfo.m_ingoing->m_highwayClass, turnInfo.m_ingoing->m_isLink)) return CarDirection::None; - } + + if (!turnInfo.m_outgoing->m_isLink && !(IsSmallRoad(turnInfo.m_outgoing->m_highwayClass) && IsGoStraightOrSlightTurn(intermediateDirection))) + return CarDirection::None; + // At this point it is known that the route goes form a highway to a link road or to a small road // which has a slight angle with the highway. @@ -92,6 +111,8 @@ CarDirection TryToGetExitDirection(TurnCandidates const & possibleTurns, TurnInf return CarDirection::None; } +/// * \returns difference between highway classes. +/// * It should be considered that bigger roads have smaller road class. int CalcDiffRoadClasses(HighwayClass const left, HighwayClass const right) { return static_cast(left) - static_cast(right); @@ -107,11 +128,6 @@ bool CanDiscardTurnByHighwayClass(std::vector const & turnCandida TurnInfo const & turnInfo, NumMwmIds const & numMwmIds) { - // Maximum difference between HighwayClasses of the route segments and - // the possible way segments to keep the junction as a turn. - int constexpr kMinHighwayClassDiff = -2; - int constexpr kMinHighwayClassDiffForService = -1; - HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; @@ -145,44 +161,32 @@ bool CanDiscardTurnByHighwayClass(std::vector const & turnCandida return true; } -/// * \returns false when -/// * for routes which go straight and don't go to a smaller road: -/// * - any alternative GoStraight or SlightTurn turn -/// * for other routes: -/// * - any alternative turn to a bigger road -/// * - or any alternative turn to a similar road if the turn's angle is less than kMaxAbsAngleSameRoadClass degrees (wider than SlightTurn) -/// * - or any alternative turn to a smaller road if it's GoStraight or SlightTurn. -/// * Returns true otherwise. +/// * \returns true when +/// * - the route leads from one big road to another one; +/// * - and the other possible turns lead to small roads or these turns too sharp. +/// * Returns false otherwise. /// \param routeDirection is route direction /// \param routeAngle is route angle /// \param turnCandidates is all possible ways out from a junction except uturn. /// \param turnInfo is information about ingoing and outgoing segments of the route. bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, - double routeAngle, + double const routeAngle, std::vector const & turnCandidates, TurnInfo const & turnInfo, NumMwmIds const & numMwmIds) { if (!IsGoStraightOrSlightTurn(routeDirection)) - { - ASSERT(false, ("It is supposed that current turn is GoStraight or SlightTurn")); return false; - } + + if (turnCandidates.size() < 2) + return true; + // If route goes from non-link to link then turn can't be discarded. if (!turnInfo.m_ingoing->m_isLink && turnInfo.m_outgoing->m_isLink) return false; - // Minimum difference between alternative cadidate's angle and the route to ignore this candidate. - double constexpr kMinAbsAngleDiffSameRoadClass = 35.0; - // Maximum difference between HighwayClasses of the route segments and - // the possible way segments to keep the junction as a turn. - int constexpr kMinHighwayClassDiff = -2; - int constexpr kMinHighwayClassDiffForGoStraight = -1; - int constexpr kMinHighwayClassDiffForService = -1; - HighwayClass outgoingRouteRoadClass = turnInfo.m_outgoing->m_highwayClass; HighwayClass ingoingRouteRoadClass = turnInfo.m_ingoing->m_highwayClass; -// HighwayClass maxRouteRoadClass = static_cast(max(static_cast(ingoingRouteRoadClass), static_cast(outgoingRouteRoadClass))); // The turn should be kept if there's no any information about feature id of outgoing segment // just to be on the safe side. It may happen in case of outgoing segment is a finish segment. @@ -202,47 +206,38 @@ bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiff) continue; - // If igngoing and outgoing edges are not links and candidate-link can be ignored. - /// @todo Maybe add condition of links m_highwayClass to be higher. + // If outgoing route's road is significantly larger than candidate's service road, the candidate can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) + continue; + + // If igngoing and outgoing edges are not links + // and outgoing route road is the same or large than ingoing + // then candidate-link can be ignored. if (!turnInfo.m_ingoing->m_isLink && !turnInfo.m_outgoing->m_isLink && - turnInfo.m_ingoing->m_highwayClass == turnInfo.m_outgoing->m_highwayClass && + CalcDiffRoadClasses(outgoingRouteRoadClass, ingoingRouteRoadClass) <= 0 && t.m_isLink) continue; + + // If alternative cadidate's road size is the same or smaller + // and it's angle is sharp enough compared to the route it can be ignored. + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= 0 && + abs(t.m_angle) > abs(routeAngle) + kMinAbsAngleDiffForSameOrSmallerRoad) + continue; if (routeDirection == CarDirection::GoStraight) { // If outgoing route road is significantly larger than candidate, the candidate can be ignored. - if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForGoStraight) + if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForStraightRoute) continue; // If outgoing route road is the same or large than ingoing // and candidate's angle is sharp enough compared to the route it can be ignored. if (CalcDiffRoadClasses(outgoingRouteRoadClass, ingoingRouteRoadClass) <= 0 && - abs(t.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) + abs(t.m_angle) > abs(routeAngle) + kMinAbsAngleDiffForStraightRoute) continue; } - // If outgoing route road is smaller than candidate candidate turn cant be discarded. - if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) > 0) - { - return false; - } - else if (outgoingRouteRoadClass == candidateRoadClass) - { - // If alternative cadidate's road size is the same and it's angle is not enough sharp compared to the route turn can't be discarded. - if (abs(t.m_angle) < abs(routeAngle) + kMinAbsAngleDiffSameRoadClass) - return false; - } - else // Outgoing route road is larger than candidate. - { - // If outgoing route's road is significantly larger than candidate's service road, the candidate can be ignored. - if (CalcDiffRoadClasses(outgoingRouteRoadClass, candidateRoadClass) <= kMinHighwayClassDiffForService && candidateRoadClass == HighwayClass::Service) - continue; - - // Any alternative turn to a smaller road keeps the turn direction if it's GoStraight or SlightTurn. - if (IsGoStraightOrSlightTurn(IntermediateDirection(t.m_angle))) - return false; - } + return false; } return true; } @@ -260,7 +255,7 @@ bool KeepRoundaboutTurnByHighwayClass(TurnCandidates const & possibleTurns, { if (!validFirstOutgoingSeg || t.m_segment == firstOutgoingSegment) continue; - if (static_cast(t.m_highwayClass) != static_cast(HighwayClass::Service)) + if (t.m_highwayClass != HighwayClass::Service) return true; } return false; @@ -301,10 +296,7 @@ T FindDirectionByAngle(vector> const & lowerBounds, double angle ASSERT_GREATER_OR_EQUAL(angle, -180., (angle)); ASSERT_LESS_OR_EQUAL(angle, 180., (angle)); ASSERT(!lowerBounds.empty(), ()); - ASSERT(is_sorted(lowerBounds.cbegin(), lowerBounds.cend(), - [](pair const & p1, pair const & p2) { - return p1.first > p2.first; - }), + ASSERT(is_sorted(lowerBounds.rbegin(), lowerBounds.rend(), base::LessBy(&pair::first)), ()); for (auto const & lower : lowerBounds) @@ -336,21 +328,21 @@ m2::PointD GetPointByIndex(TUnpackedPathSegments const & segments, RoutePointInd return segments[index.m_segmentIndex].m_path[index.m_pathIndex].GetPoint(); } -double CalcEstimatedTimeToPass(double distance, HighwayClass highwayClass) +double CalcEstimatedTimeToPass(double distanceMeters, HighwayClass highwayClass) { - double speed = 0; + double speedMetersPerSecond = 0; switch (highwayClass) { - case HighwayClass::Trunk: speed = 100000.0/60/60; break; - case HighwayClass::Primary: speed = 70000.0/60/60; break; - case HighwayClass::Secondary: speed = 70000.0/60/60; break; - case HighwayClass::Tertiary: speed = 50000.0/60/60; break; - case HighwayClass::LivingStreet: speed = 20000.0/60/60; break; - case HighwayClass::Service: speed = 10000.0/60/60; break; - case HighwayClass::Pedestrian: speed = 50000.0/60/60; break; - default: speed = 500000.0/60/60; break; + case HighwayClass::Trunk: speedMetersPerSecond = 100000.0/60/60; break; + case HighwayClass::Primary: speedMetersPerSecond = 70000.0/60/60; break; + case HighwayClass::Secondary: speedMetersPerSecond = 70000.0/60/60; break; + case HighwayClass::Tertiary: speedMetersPerSecond = 50000.0/60/60; break; + case HighwayClass::LivingStreet: speedMetersPerSecond = 20000.0/60/60; break; + case HighwayClass::Service: speedMetersPerSecond = 10000.0/60/60; break; + case HighwayClass::Pedestrian: speedMetersPerSecond = 50000.0/60/60; break; + default: speedMetersPerSecond = 500000.0/60/60; break; } - return distance / speed; + return distanceMeters / speedMetersPerSecond; } /*! @@ -387,26 +379,24 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment ASSERT_LESS(index.m_pathIndex, segments[index.m_segmentIndex].m_path.size(), ()); ASSERT_LESS(index.m_segmentIndex, segments.size(), ()); - - RoutePointIndex nextIndex; - ASSERT(!segments[index.m_segmentIndex].m_path.empty(), ()); + RoutePointIndex nextIndex; + ASSERT(GetNextRoutePointIndex(result, index, numMwmIds, forward, nextIndex), ()); + + // There is no need for looking too far for low-speed roads. + // So additional time limit is applied. + double constexpr kMaxTimeSeconds = 3.0; + m2::PointD point = GetPointByIndex(segments, index); - m2::PointD nextPoint; + size_t count = 0; double curDistanceMeters = 0.0; - double curTime = 0.0; + double curTimeSeconds = 0.0; - // There is no need to go too far for low-speed roads. - // So additional time limit is applied. - double maxTime = 3.0; - - ASSERT(GetNextRoutePointIndex(result, index, numMwmIds, forward, false, nextIndex), ()); - - do + while (GetNextRoutePointIndex(result, index, numMwmIds, forward, nextIndex)) { - nextPoint = GetPointByIndex(segments, nextIndex); + m2::PointD nextPoint = GetPointByIndex(segments, nextIndex); // At start and finish there are two edges with zero length. // This function should not be called for the start (|outgoingSegmentIndex| == 0). @@ -416,17 +406,16 @@ m2::PointD GetPointForTurn(IRoutingResult const & result, size_t outgoingSegment double distanceMeters = mercator::DistanceOnEarth(point, nextPoint); curDistanceMeters += distanceMeters; - curTime += CalcEstimatedTimeToPass(distanceMeters, segments[nextIndex.m_segmentIndex].m_highwayClass); + curTimeSeconds += CalcEstimatedTimeToPass(distanceMeters, segments[nextIndex.m_segmentIndex].m_highwayClass); - if (curTime > maxTime || ++count >= maxPointsCount || curDistanceMeters > maxDistMeters) + if (curTimeSeconds > kMaxTimeSeconds || ++count >= maxPointsCount || curDistanceMeters > maxDistMeters) return nextPoint; point = nextPoint; index = nextIndex; } - while (GetNextRoutePointIndex(result, index, numMwmIds, forward, true, nextIndex)); - return nextPoint; + return point; } double CalcOneSegmentTurnAngle(TurnInfo const & turnInfo) @@ -463,7 +452,7 @@ double CalcPathTurnAngle(LoadedPathSegment const & segment, size_t const pathInd * if |index| points at the first or intermediate point in turn segment returns the next one. * \returns true if |nextIndex| fills correctly and false otherwise. */ -bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, bool const smoothOnly, +bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, NumMwmIds const & numMwmIds, RoutePointIndex & nextIndex) { auto const & segments = result.GetSegments(); @@ -486,7 +475,7 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde double const oneSegmentTurnAngle = CalcOneSegmentTurnAngle(turnInfo); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); - if (smoothOnly && !IsGoStraightOrSlightTurn(oneSegmentDirection)) + if (!IsGoStraightOrSlightTurn(oneSegmentDirection)) return false; // Too sharp turn angle. size_t ingoingCount = 0; @@ -520,13 +509,13 @@ bool GetNextCrossSegmentRoutePoint(IRoutingResult const & result, RoutePointInde return false; } -bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, bool const smoothOnly, RoutePointIndex & nextIndex) +bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex const & index, RoutePointIndex & nextIndex) { if (index.m_pathIndex == 0) return false; auto const & segments = result.GetSegments(); - if (smoothOnly && segments[index.m_segmentIndex].m_path.size() >= 3 && index.m_pathIndex < segments[index.m_segmentIndex].m_path.size() - 1) + if (segments[index.m_segmentIndex].m_path.size() >= 3 && index.m_pathIndex < segments[index.m_segmentIndex].m_path.size() - 1) { double const oneSegmentTurnAngle = CalcPathTurnAngle(segments[index.m_segmentIndex], index.m_pathIndex); CarDirection const oneSegmentDirection = IntermediateDirection(oneSegmentTurnAngle); @@ -539,10 +528,8 @@ bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex c } /*! - * \brief Corrects |turn.m_turn| if |turn.m_turn == CarDirection::GoStraight| and there're only - * two ways out from this junction. In that case the other way (the way which is not covered by - * the route) is checked. If the other way is "go straight" or "slight turn", turn.m_turn is set - * to |turnToSet|. + * \brief Corrects |turn.m_turn| if |turn.m_turn == CarDirection::GoStraight|. + * If the other way is not sharp enough, turn.m_turn is set to |turnToSet|. */ void CorrectGoStraight(TurnCandidate const & notRouteCandidate, double const routeAngle, CarDirection const & turnToSet, TurnItem & turn) @@ -550,14 +537,9 @@ void CorrectGoStraight(TurnCandidate const & notRouteCandidate, double const rou if (turn.m_turn != CarDirection::GoStraight) return; - // No need to coorect turn if alternative cadidate's angle is sharp enough compared to the route. - if (abs(notRouteCandidate.m_angle) > abs(routeAngle) + kMinAngleDiffToNotConfuseStraightAndAlternative) - return; - - if (!IsGoStraightOrSlightTurn(IntermediateDirection(notRouteCandidate.m_angle))) - return; - - turn.m_turn = turnToSet; + // Correct turn if alternative cadidate's angle is not sharp enough compared to the route. + if (abs(notRouteCandidate.m_angle) < abs(routeAngle) + kMinAbsAngleDiffForStraightRoute) + turn.m_turn = turnToSet; } // Returns distance in meters between |junctions[start]| and |junctions[end]|. @@ -595,17 +577,17 @@ bool TurnInfo::IsSegmentsValid() const } bool GetNextRoutePointIndex(IRoutingResult const & result, RoutePointIndex const & index, - NumMwmIds const & numMwmIds, bool const forward, bool const smoothOnly, + NumMwmIds const & numMwmIds, bool const forward, RoutePointIndex & nextIndex) { if (forward) { - if (!GetNextCrossSegmentRoutePoint(result, index, smoothOnly, numMwmIds, nextIndex)) + if (!GetNextCrossSegmentRoutePoint(result, index, numMwmIds, nextIndex)) return false; } else { - if (!GetPrevInSegmentRoutePoint(result, index, smoothOnly, nextIndex)) + if (!GetPrevInSegmentRoutePoint(result, index, nextIndex)) return false; } @@ -1048,14 +1030,12 @@ double CalcTurnAngle(IRoutingResult const & result, result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, vehicleSettings.m_minOutgoingDistMeters, true /* forward */); - double const turnAngle = base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); - return turnAngle; + return base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); } // It's possible that |firstOutgoingSeg| is not contained in |turnCandidates|. -// It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are -// from different mwms. -// Let's identify it turnCandidates by angle and update according turnCandidate. +// It may happened if |firstOutgoingSeg| and candidates in |turnCandidates| are from different mwms. +// Let's identify it in turnCandidates by angle and update according turnCandidate. void CorrectCandidatesSegmentByOutgoing(TurnInfo const & turnInfo, Segment const & firstOutgoingSeg, std::vector & candidates) @@ -1120,50 +1100,49 @@ bool GetTurnInfo(IRoutingResult const & result, size_t const outgoingSegmentInde // If the route goes along the rightmost or the leftmost way among available ones: // 1. RightmostDirection or LeftmostDirection is selected // 2. If the turn direction is |CarDirection::GoStraight| -// and there's another GoStraight or SlightTurn turn +// and there's another not sharp enough turn // GoStraight is corrected to TurnSlightRight/TurnSlightLeft -// to avoid ambiguity: 2 or more almost straight turns and GoStraight direction. - -// turnCandidates are sorted by angle from leftmost to rightmost. -// Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. +// to avoid ambiguity for GoStraight direction: 2 or more almost straight turns. void CorrectRightmostAndLeftmost(std::vector const & turnCandidates, - Segment const & firstOutgoingSeg, - double turnAngle, + Segment const & firstOutgoingSeg, double const turnAngle, TurnItem & turn) { - if (adjacent_find(turnCandidates.begin(), turnCandidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) == turnCandidates.end()) + // turnCandidates are sorted by angle from leftmost to rightmost. + // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. + if (adjacent_find(turnCandidates.begin(), turnCandidates.end(), base::EqualsBy(&TurnCandidate::m_angle)) != turnCandidates.end()) { - for (auto candidate = turnCandidates.begin(); candidate != turnCandidates.end(); ++candidate) + LOG(LWARNING, ("nodes.candidates are not expected to have same m_angle.")); + return; + } + + for (auto candidate = turnCandidates.begin(); candidate != turnCandidates.end(); ++candidate) + { + if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.end()) { - if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.end()) - { - // The route goes along the leftmost candidate. - turn.m_turn = LeftmostDirection(turnAngle); - // Compare with the next candidate to the right. - CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightLeft, turn); - break; - } - // Ignoring too sharp alternative candidates. - if (candidate->m_angle > -90) + // The route goes along the leftmost candidate. + turn.m_turn = LeftmostDirection(turnAngle); + // Compare with the next candidate to the right. + CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightLeft, turn); break; } - for (auto candidate = turnCandidates.rbegin(); candidate != turnCandidates.rend(); ++candidate) - { - if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.rend()) - { - // The route goes along the rightmost candidate. - turn.m_turn = RightmostDirection(turnAngle); - // Compare with the next candidate to the left. - CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightRight, turn); - break; - } - // Ignoring too sharp alternative candidates. - if (candidate->m_angle < 90) - break; - }; + // Ignoring too sharp alternative candidates. + if (candidate->m_angle > -90) + break; } - else - LOG(LWARNING, ("nodes.candidates are not expected to have same m_angle.")); + for (auto candidate = turnCandidates.rbegin(); candidate != turnCandidates.rend(); ++candidate) + { + if (candidate->m_segment == firstOutgoingSeg && candidate + 1 != turnCandidates.rend()) + { + // The route goes along the rightmost candidate. + turn.m_turn = RightmostDirection(turnAngle); + // Compare with the next candidate to the left. + CorrectGoStraight(*(candidate + 1), candidate->m_angle, CarDirection::TurnSlightRight, turn); + break; + } + // Ignoring too sharp alternative candidates. + if (candidate->m_angle < 90) + break; + }; } void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmentIndex, @@ -1226,24 +1205,24 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen if (turnCandidates.size() == 1) { ASSERT(turnCandidates.front().m_segment == firstOutgoingSeg, ()); + + if (IsGoStraightOrSlightTurn(intermediateDirection)) + return; // IngoingCount includes ingoing segment and reversed outgoing (if it is not one-way). - // Both of them should be isnored. If any other one is present, turn is kept to prevent user from going to oneway alternative. + // If any other one is present, turn (not straight or slight) is kept to prevent user from going to oneway alternative. + + /// @todo Min abs angle of ingoing ones should be considered. If it's much bigger than route angle - ignore ingoing. + /// Now this data is not available from IRoutingResult::GetPossibleTurns(). if (ingoingCount <= 1 + size_t(!turnInfo.m_outgoing->m_isOneWay)) return; } + if (CanDiscardTurnByHighwayClassOrAngles(intermediateDirection, turnAngle, turnCandidates, turnInfo, numMwmIds)) + return; + turn.m_turn = intermediateDirection; - if (IsGoStraightOrSlightTurn(turn.m_turn)) - { - if (CanDiscardTurnByHighwayClassOrAngles(turn.m_turn, turnAngle, turnCandidates, turnInfo, numMwmIds)) - { - turn.m_turn = CarDirection::None; - return; - } - } - - if (turnCandidates.size() >= 2) + if (turnCandidates.size() >= 2) CorrectRightmostAndLeftmost(turnCandidates, firstOutgoingSeg, turnAngle, turn); } @@ -1270,8 +1249,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { - ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates.end(), - base::LessBy(&TurnCandidate::m_angle)), + ASSERT(is_sorted(nodes.candidates.begin(), nodes.candidates.end(), base::LessBy(&TurnCandidate::m_angle)), ("Turn candidates should be sorted by its angle field.")); } diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index 3af9c1edff..7a750c1655 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -88,7 +88,7 @@ struct TurnInfo * of a segment. But it's impossible moving in forward direction. */ bool GetNextRoutePointIndex(IRoutingResult const & result, RoutePointIndex const & index, - NumMwmIds const & numMwmIds, bool const forward, bool const smoothOnly, RoutePointIndex & nextIndex); + NumMwmIds const & numMwmIds, bool const forward, RoutePointIndex & nextIndex); /*! * \brief Compute turn and time estimation structs for the abstract route result. -- 2.45.3 From 3e629b8a17357a2e259e29781b46a9fe8ee4254e Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 3 May 2022 16:14:15 +0300 Subject: [PATCH 21/28] [Routing] Refactoring Tests update. Signed-off-by: Anton Makouski --- routing/routing_integration_tests/turn_test.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 7833e6a261..a4c9556753 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -398,7 +398,6 @@ UNIT_TEST(Russia_Moscow_VarshavskoeShosseMKAD_TurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); - /// @todo No potential additioanal turn 0 (one alternative way is link, another is much smaller (service)). integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); } @@ -1096,7 +1095,7 @@ UNIT_TEST(Cyprus_NicosiaSchoolParking_TurnTest) integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); } -/* + UNIT_TEST(Russia_Moscow_OnlyUTurnTest1_TurnTest) { TRouteResult const routeResult = @@ -1108,9 +1107,14 @@ UNIT_TEST(Russia_Moscow_OnlyUTurnTest1_TurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - integration::TestRouteLength(route, 3854.44); + integration::TestTurnCount(route, 5 /* expectedTurnCount */); + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::EnterRoundAbout); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::LeaveRoundAbout); } - +/* UNIT_TEST(Russia_Moscow_OnlyUTurnTest1WithDirection_TurnTest) { auto const startDir = mercator::FromLatLon(55.90423, 37.40176); -- 2.45.3 From b0af1c0afe55a16f6a25b0b54a06cb98c1ce0540 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 3 May 2022 16:29:59 +0300 Subject: [PATCH 22/28] [Routing] Refactoring Tests update. Signed-off-by: Anton Makouski --- .../bicycle_turn_test.cpp | 6 +++--- .../routing_integration_tests/turn_test.cpp | 21 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/routing/routing_integration_tests/bicycle_turn_test.cpp b/routing/routing_integration_tests/bicycle_turn_test.cpp index 0f02cf6c6c..9fbbb2670f 100644 --- a/routing/routing_integration_tests/bicycle_turn_test.cpp +++ b/routing/routing_integration_tests/bicycle_turn_test.cpp @@ -19,12 +19,14 @@ UNIT_TEST(RussiaMoscowSevTushinoParkBicycleWayTurnTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo Turn 3 is discarded since first segment of alternative way is too sharp. integration::TestTurnCount(route, 4 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightRight); integration::GetNthTurn(route, 1).TestValid().TestOneOfDirections( {CarDirection::TurnSlightLeft, CarDirection::TurnLeft}); integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnLeft); + /// @todo Turn 3 is discarded since first segment of alternative way is too sharp. + /// Can be fixed if more segments of alternative way will be considered (just like selected route segments). + /// But now it's unavailable from IRoutingResult::GetPossibleTurns(). integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnSlightRight); integration::TestRouteLength(route, 753.0); @@ -232,8 +234,6 @@ UNIT_TEST(TurnsNearKhladkombinatTest) RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo Seems like a bug here. Redundant first turn now, but also I'd expect one more turn - /// before oneway road (we are on bicycle). Should fix algo and test. integration::TestTurnCount(route, 2 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index a4c9556753..1afec54ef3 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -227,7 +227,7 @@ UNIT_TEST(Russia_Moscow_ParallelResidentalUTurnAvoiding_TurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 2 /* expectedTurnCount */); - // Checking a turn in case going from a not-link to a link + // Checking a turn in case going from a not-link to a link. integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnLeft); } @@ -442,7 +442,7 @@ UNIT_TEST(Russia_Moscow_BolshoyKislovskiyPerBolshayaNikitinskayaUl_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; - /// @todo Problem with outgoingTurns from RoutingEngineResult::GetPossibleTurns at (turn_m_index == 4) + /// @todo Problem with outgoingTurns from RoutingEngineResult::GetPossibleTurns at (turn_m_index == 4). /// For some reason it contains only one possible turn (+90), but it is expected that it will be two of them (-90 and +90). /// This is the reason why the RightTurn is discarded. TEST_EQUAL(result, RouterResultCode::NoError, ()); @@ -635,15 +635,14 @@ UNIT_TEST(Russia_Kubinka_TurnTest) TEST_EQUAL(result, RouterResultCode::NoError, ()); - /// @todo This test is obsolete. - /* Route const & route = *routeResult.first; - integration::TestTurnCount(route, 2); + integration::TestTurnCount(route, 5); - integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::ExitHighwayToRight); - integration::GetNthTurn(route, 1).TestValid().TestOneOfDirections( - {CarDirection::TurnSlightLeft, CarDirection::TurnLeft}); - */ + integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 1).TestValid().TestDirection(CarDirection::TurnSlightLeft); + integration::GetNthTurn(route, 2).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 3).TestValid().TestDirection(CarDirection::TurnLeft); + integration::GetNthTurn(route, 4).TestValid().TestDirection(CarDirection::TurnLeft); } // Test on absence of unnecessary turn. @@ -1073,7 +1072,7 @@ UNIT_TEST(Cyprus_NicosiaPresidentialPark_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; - // Issue #2438 + // Issue #2438. TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnRight); @@ -1089,7 +1088,7 @@ UNIT_TEST(Cyprus_NicosiaSchoolParking_TurnTest) Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; - // Issue #2438 + // Issue #2438. TEST_EQUAL(result, RouterResultCode::NoError, ()); integration::TestTurnCount(route, 1 /* expectedTurnCount */); integration::GetNthTurn(route, 0).TestValid().TestDirection(CarDirection::TurnSlightLeft); -- 2.45.3 From e910ffb68651c318ad0613e3d192d39692c1079d Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Tue, 3 May 2022 16:36:53 +0300 Subject: [PATCH 23/28] [Routing] Refactoring Comments. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index c273e89325..81f8cb1fd1 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -165,8 +165,8 @@ bool CanDiscardTurnByHighwayClass(std::vector const & turnCandida /// * - the route leads from one big road to another one; /// * - and the other possible turns lead to small roads or these turns too sharp. /// * Returns false otherwise. -/// \param routeDirection is route direction -/// \param routeAngle is route angle +/// \param routeDirection is route direction. +/// \param routeAngle is route angle. /// \param turnCandidates is all possible ways out from a junction except uturn. /// \param turnInfo is information about ingoing and outgoing segments of the route. bool CanDiscardTurnByHighwayClassOrAngles(CarDirection const routeDirection, @@ -1211,7 +1211,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen // IngoingCount includes ingoing segment and reversed outgoing (if it is not one-way). // If any other one is present, turn (not straight or slight) is kept to prevent user from going to oneway alternative. - /// @todo Min abs angle of ingoing ones should be considered. If it's much bigger than route angle - ignore ingoing. + /// @todo Min abs angle of ingoing ones should be considered. If it's much bigger than route angle - ignore ingoing ones. /// Now this data is not available from IRoutingResult::GetPossibleTurns(). if (ingoingCount <= 1 + size_t(!turnInfo.m_outgoing->m_isOneWay)) return; -- 2.45.3 From 7816be3ad2cb48af949d5444927bdcffa15c8e53 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Wed, 4 May 2022 18:53:36 +0300 Subject: [PATCH 24/28] [Routing] Refactoring Test update. Route changed after map update. TestRouteLength added. Signed-off-by: Anton Makouski --- routing/routing_integration_tests/turn_test.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routing/routing_integration_tests/turn_test.cpp b/routing/routing_integration_tests/turn_test.cpp index 1afec54ef3..7ce93d1bb4 100644 --- a/routing/routing_integration_tests/turn_test.cpp +++ b/routing/routing_integration_tests/turn_test.cpp @@ -1035,13 +1035,15 @@ UNIT_TEST(Cyprus_Nicosia_TurnTest) TRouteResult const routeResult = integration::CalculateRoute(integration::GetVehicleComponents(VehicleType::Car), mercator::FromLatLon(35.12459, 33.34449), {0., 0.}, - mercator::FromLatLon(35.14353, 33.34387)); + mercator::FromLatLon(35.13832, 33.34741)); Route const & route = *routeResult.first; RouterResultCode const result = routeResult.second; // No SlightTurns at not straight junctions. Issue #2262. TEST_EQUAL(result, RouterResultCode::NoError, ()); + integration::TestRouteLength(route, 1941.); + integration::TestTurnCount(route, 0 /* expectedTurnCount */); } -- 2.45.3 From 8e6270db91b1dde735183befee1853bb48082374 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Thu, 5 May 2022 13:34:04 +0300 Subject: [PATCH 25/28] [Routing] Refactoring Code style. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 81f8cb1fd1..4433aaf6aa 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1,6 +1,7 @@ #include "routing/turns_generator.hpp" #include "routing/router.hpp" +#include "platform/measurement_utils.hpp" #include "indexer/ftypes_matcher.hpp" @@ -13,19 +14,19 @@ #include #include -using namespace routing; -using namespace routing::turns; +namespace routing +{ +namespace turns +{ using namespace std; using namespace ftypes; -namespace -{ // Angles in degrees for finding route segments with no actual forks. double constexpr kMaxForwardAngleCandidates = 95.0; double constexpr kMaxForwardAngleActual = 60.0; // Min difference of route and alternative turn abs angles in degrees -// to ignore alternative alternative turn (when route direction is GoStraight). +// to ignore alternative turn (when route direction is GoStraight). double constexpr kMinAbsAngleDiffForStraightRoute = 25.0; // Min difference of route and alternative turn abs angles in degrees @@ -61,7 +62,7 @@ bool IsSmallRoad(HighwayClass hwClass) /// \note The function makes a decision about turn based on geometry of the route and turn /// candidates, so it works correctly for both left and right hand traffic. CarDirection TryToGetExitDirection(TurnCandidates const & possibleTurns, TurnInfo const & turnInfo, - Segment const & firstOutgoingSeg, CarDirection const intermediateDirection) + Segment const & firstOutgoingSeg, CarDirection const intermediateDirection) { if (!possibleTurns.isCandidatesAngleValid) return CarDirection::None; @@ -330,19 +331,19 @@ m2::PointD GetPointByIndex(TUnpackedPathSegments const & segments, RoutePointInd double CalcEstimatedTimeToPass(double distanceMeters, HighwayClass highwayClass) { - double speedMetersPerSecond = 0; + double speedKmph = 0; switch (highwayClass) { - case HighwayClass::Trunk: speedMetersPerSecond = 100000.0/60/60; break; - case HighwayClass::Primary: speedMetersPerSecond = 70000.0/60/60; break; - case HighwayClass::Secondary: speedMetersPerSecond = 70000.0/60/60; break; - case HighwayClass::Tertiary: speedMetersPerSecond = 50000.0/60/60; break; - case HighwayClass::LivingStreet: speedMetersPerSecond = 20000.0/60/60; break; - case HighwayClass::Service: speedMetersPerSecond = 10000.0/60/60; break; - case HighwayClass::Pedestrian: speedMetersPerSecond = 50000.0/60/60; break; - default: speedMetersPerSecond = 500000.0/60/60; break; + case HighwayClass::Trunk: speedKmph = 100.0; break; + case HighwayClass::Primary: speedKmph = 70.0; break; + case HighwayClass::Secondary: speedKmph = 70.0; break; + case HighwayClass::Tertiary: speedKmph = 50.0; break; + case HighwayClass::LivingStreet: speedKmph = 20.0; break; + case HighwayClass::Service: speedKmph = 10.0; break; + case HighwayClass::Pedestrian: speedKmph = 5.0; break; + default: speedKmph = 50.0; break; } - return distanceMeters / speedMetersPerSecond; + return distanceMeters / measurement_utils::KmphToMps(speedKmph); } /*! @@ -532,7 +533,7 @@ bool GetPrevInSegmentRoutePoint(IRoutingResult const & result, RoutePointIndex c * If the other way is not sharp enough, turn.m_turn is set to |turnToSet|. */ void CorrectGoStraight(TurnCandidate const & notRouteCandidate, double const routeAngle, CarDirection const & turnToSet, - TurnItem & turn) + TurnItem & turn) { if (turn.m_turn != CarDirection::GoStraight) return; @@ -553,12 +554,7 @@ double CalcRouteDistanceM(vector const & junctions, return res; } -} // namespace -namespace routing -{ -namespace turns -{ // RoutePointIndex --------------------------------------------------------------------------------- bool RoutePointIndex::operator==(RoutePointIndex const & index) const { @@ -886,7 +882,7 @@ void SelectRecommendedLanes(Route::TTurns & turnsDir) } CarDirection GetRoundaboutDirection(bool isIngoingEdgeRoundabout, bool isOutgoingEdgeRoundabout, - bool isMultiTurnJunction, bool keepTurnByHighwayClass) + bool isMultiTurnJunction, bool keepTurnByHighwayClass) { if (isIngoingEdgeRoundabout && isOutgoingEdgeRoundabout) { @@ -905,7 +901,7 @@ CarDirection GetRoundaboutDirection(bool isIngoingEdgeRoundabout, bool isOutgoin return CarDirection::None; } -CarDirection GetRoundaboutDirection(TurnInfo const turnInfo, TurnCandidates const & nodes, NumMwmIds const & numMwmIds) +CarDirection GetRoundaboutDirection(TurnInfo const & turnInfo, TurnCandidates const & nodes, NumMwmIds const & numMwmIds) { bool const keepTurnByHighwayClass = KeepRoundaboutTurnByHighwayClass(nodes, turnInfo, numMwmIds); return GetRoundaboutDirection(turnInfo.m_ingoing->m_onRoundabout, turnInfo.m_outgoing->m_onRoundabout, @@ -1104,8 +1100,8 @@ bool GetTurnInfo(IRoutingResult const & result, size_t const outgoingSegmentInde // GoStraight is corrected to TurnSlightRight/TurnSlightLeft // to avoid ambiguity for GoStraight direction: 2 or more almost straight turns. void CorrectRightmostAndLeftmost(std::vector const & turnCandidates, - Segment const & firstOutgoingSeg, double const turnAngle, - TurnItem & turn) + Segment const & firstOutgoingSeg, double const turnAngle, + TurnItem & turn) { // turnCandidates are sorted by angle from leftmost to rightmost. // Normally no duplicates should be found. But if they are present we can't identify the leftmost/rightmost by order. @@ -1226,7 +1222,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen CorrectRightmostAndLeftmost(turnCandidates, firstOutgoingSeg, turnAngle, turn); } -void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSegmentIndex, +void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t const outgoingSegmentIndex, NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, TurnItem & turn) { @@ -1272,7 +1268,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t outgoingSe turn.m_pedestrianTurn = PedestrianDirection::None; } -size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t outgoingSegmentIndex, +size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t const outgoingSegmentIndex, NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings, TurnItem & turn) { -- 2.45.3 From 486b6273da65c88b40a2cacecf5ea0bcec7ff870 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Thu, 5 May 2022 14:31:30 +0300 Subject: [PATCH 26/28] [Routing] Refactoring No need to pass junctionPoint. Can be easily calculated. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 4433aaf6aa..88fb10d069 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1011,8 +1011,7 @@ bool PathIsFakeLoop(std::vector const & path) } double CalcTurnAngle(IRoutingResult const & result, - size_t const outgoingSegmentIndex, - m2::PointD const & junctionPoint, + size_t const outgoingSegmentIndex, NumMwmIds const & numMwmIds, RoutingSettings const & vehicleSettings) { @@ -1026,6 +1025,7 @@ double CalcTurnAngle(IRoutingResult const & result, result, outgoingSegmentIndex, numMwmIds, vehicleSettings.m_maxOutgoingPointsCount, vehicleSettings.m_minOutgoingDistMeters, true /* forward */); + m2::PointD const junctionPoint = result.GetSegments()[outgoingSegmentIndex].m_path.front().GetPoint(); return base::RadToDeg(PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); } @@ -1189,7 +1189,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen return; } - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, vehicleSettings); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, numMwmIds, vehicleSettings); CarDirection const intermediateDirection = IntermediateDirection(turnAngle); @@ -1231,8 +1231,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t const outg if (!validTurnInfo) return; - m2::PointD const junctionPoint = turnInfo.m_ingoing->m_path.back().GetPoint(); - double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, junctionPoint, numMwmIds, vehicleSettings); + double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, numMwmIds, vehicleSettings); turn.m_sourceName = turnInfo.m_ingoing->m_name; turn.m_targetName = turnInfo.m_outgoing->m_name; @@ -1242,6 +1241,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t const outg TurnCandidates nodes; size_t ingoingCount = 0; + m2::PointD const junctionPoint = turnInfo.m_ingoing->m_path.back().GetPoint(); result.GetPossibleTurns(turnInfo.m_ingoing->m_segmentRange, junctionPoint, ingoingCount, nodes); if (nodes.isCandidatesAngleValid) { -- 2.45.3 From a261323bdd28253104d4b3f136443d5054a8ef11 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Thu, 5 May 2022 18:31:04 +0300 Subject: [PATCH 27/28] [Routing] Refactoring Code style. Signed-off-by: Anton Makouski --- routing/turns_generator.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 88fb10d069..5c90f99966 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -1146,8 +1146,7 @@ void GetTurnDirection(IRoutingResult const & result, size_t const outgoingSegmen TurnItem & turn) { TurnInfo turnInfo; - bool validTurnInfo = GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo); - if (!validTurnInfo) + if (!GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo)) return; turn.m_sourceName = turnInfo.m_ingoing->m_name; @@ -1227,8 +1226,7 @@ void GetTurnDirectionPedestrian(IRoutingResult const & result, size_t const outg RoutingSettings const & vehicleSettings, TurnItem & turn) { TurnInfo turnInfo; - bool validTurnInfo = GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo); - if (!validTurnInfo) + if (!GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo)) return; double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, numMwmIds, vehicleSettings); -- 2.45.3 From a37cca0bfcd57dbf8ee7fd0659a52e473f60b7d0 Mon Sep 17 00:00:00 2001 From: Anton Makouski Date: Thu, 5 May 2022 19:08:27 +0300 Subject: [PATCH 28/28] [Routing] Refactoring Removed unused CalculateMercatorDistanceAlongPath(). Signed-off-by: Anton Makouski --- routing/routing_tests/turns_generator_test.cpp | 11 ----------- routing/turns_generator.cpp | 13 ------------- routing/turns_generator.hpp | 5 ----- 3 files changed, 29 deletions(-) diff --git a/routing/routing_tests/turns_generator_test.cpp b/routing/routing_tests/turns_generator_test.cpp index 16e3e360bb..abdf8bc64a 100644 --- a/routing/routing_tests/turns_generator_test.cpp +++ b/routing/routing_tests/turns_generator_test.cpp @@ -337,17 +337,6 @@ UNIT_TEST(TestIntermediateDirection) TEST_EQUAL(IntermediateDirection(-170.), CarDirection::TurnSharpLeft, ()); } -UNIT_TEST(TestCalculateMercatorDistanceAlongRoute) -{ - vector const points = {{0., 0.}, {0., 1.}, {0., 1.}, {1., 1.}}; - - uint32_t const lastPointIdx = static_cast(points.size() - 1); - TEST_EQUAL(CalculateMercatorDistanceAlongPath(0, lastPointIdx, points), 2., ()); - TEST_EQUAL(CalculateMercatorDistanceAlongPath(1, 1, points), 0., ()); - TEST_EQUAL(CalculateMercatorDistanceAlongPath(1, 2, points), 0., ()); - TEST_EQUAL(CalculateMercatorDistanceAlongPath(0, 1, points), 1., ()); -} - UNIT_TEST(TestCheckUTurnOnRoute) { TUnpackedPathSegments pathSegments(4, LoadedPathSegment()); diff --git a/routing/turns_generator.cpp b/routing/turns_generator.cpp index 5c90f99966..61fcab089c 100644 --- a/routing/turns_generator.cpp +++ b/routing/turns_generator.cpp @@ -767,19 +767,6 @@ RouterResultCode MakeTurnAnnotationPedestrian( return RouterResultCode::NoError; } -double CalculateMercatorDistanceAlongPath(uint32_t startPointIndex, uint32_t endPointIndex, - vector const & points) -{ - ASSERT_LESS(endPointIndex, points.size(), ()); - ASSERT_LESS_OR_EQUAL(startPointIndex, endPointIndex, ()); - - double mercatorDistanceBetweenTurns = 0; - for (uint32_t i = startPointIndex; i != endPointIndex; ++i) - mercatorDistanceBetweenTurns += points[i].Length(points[i + 1]); - - return mercatorDistanceBetweenTurns; -} - void FixupTurns(vector const & junctions, Route::TTurns & turnsDir) { double const kMergeDistMeters = 15.0; diff --git a/routing/turns_generator.hpp b/routing/turns_generator.hpp index 7a750c1655..5485e7970d 100644 --- a/routing/turns_generator.hpp +++ b/routing/turns_generator.hpp @@ -112,11 +112,6 @@ RouterResultCode MakeTurnAnnotationPedestrian( base::Cancellable const & cancellable, std::vector & points, Route::TTurns & turnsDir, Route::TStreets & streets, std::vector & segments); -// Returns the distance in mercator units for the path of points for the range [startPointIndex, -// endPointIndex]. -double CalculateMercatorDistanceAlongPath(uint32_t startPointIndex, uint32_t endPointIndex, - std::vector const & points); - /*! * \brief Selects lanes which are recommended for an end user. */ -- 2.45.3