Merge pull request #164 from bykoianko/k-th-exit-from-a-roundabout

[turns][sound] Sound notification about an exact number of roundabout exit.
This commit is contained in:
Viktor Govako 2015-10-09 16:48:59 +03:00
commit 85bd6d264a
8 changed files with 166 additions and 5 deletions

View file

@ -202,7 +202,7 @@ UNIT_TEST(RussiaHugeRoundaboutTurnTest)
integration::GetNthTurn(route, 1)
.TestValid()
.TestDirection(TurnDirection::LeaveRoundAbout)
.TestRoundAboutExitNum(0);
.TestRoundAboutExitNum(4);
}
UNIT_TEST(BelarusMiskProspNezavisimostiMKADTurnTest)

View file

@ -138,7 +138,7 @@ UNIT_TEST(TestFixupTurns)
FixupTurns(pointsMerc1, turnsDir1);
Route::TTurns const expectedTurnDir1 = {{0, TurnDirection::EnterRoundAbout, 2},
{2, TurnDirection::LeaveRoundAbout},
{2, TurnDirection::LeaveRoundAbout, 2},
{3, TurnDirection::ReachedYourDestination}};
TEST_EQUAL(turnsDir1, expectedTurnDir1, ());

View file

@ -394,6 +394,114 @@ UNIT_TEST(TurnsSoundComposedTurnTest)
turnSound.GenerateTurnSound(turns6, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification6, ());
}
UNIT_TEST(TurnsSoundRoundaboutTurnTest)
{
TurnsSound turnSound;
turnSound.Enable(true);
turnSound.SetLengthUnits(routing::turns::sound::LengthUnits::Meters);
string const engShortJson =
"\
{\
\"enter_the_roundabout\":\"Enter the roundabout.\",\
\"leave_the_roundabout\":\"Leave the roundabout.\",\
\"take_the_1st_exit\":\"Take the first exit.\",\
\"take_the_2nd_exit\":\"Take the second exit.\",\
\"take_the_4th_exit\":\"Take the fourth exit.\",\
\"in_600_meters\":\"In 600 meters.\",\
\"then\":\"Then.\"\
}";
turnSound.m_getTtsText.ForTestingSetLocaleWithJson(engShortJson);
turnSound.m_settings.ForTestingSetNotificationTimeSecond(30);
turnSound.Reset();
turnSound.SetSpeedMetersPerSecond(20.);
vector<string> turnNotifications;
// Starting nearing the first turn.
// 1000 meters till the first turn.
vector<TurnItemDist> const turns1 = {{{5 /* idx */, TurnDirection::EnterRoundAbout, 2 /* m_exitNum */},
1000. /* m_distMeters */},
{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
2000. /* m_distMeters */}};
turnSound.GenerateTurnSound(turns1, turnNotifications);
TEST(turnNotifications.empty(), ());
// 620 meters till the first turn.
vector<TurnItemDist> const turns2 = {{{5 /* idx */, TurnDirection::EnterRoundAbout, 2 /* m_exitNum */},
620. /* m_distMeters */},
{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
1620. /* m_distMeters */}};
vector<string> const expectedNotification2 = {{"In 600 meters. Enter the roundabout."},
{"Then. Take the second exit."}};
turnSound.GenerateTurnSound(turns2, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification2, ());
// 3 meters till the first turn.
vector<TurnItemDist> const turns3 = {{{5 /* idx */, TurnDirection::EnterRoundAbout, 2 /* m_exitNum */},
3. /* m_distMeters */},
{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
1003. /* m_distMeters */}};
vector<string> const expectedNotification3 = {{"Enter the roundabout."},
{"Then. Take the second exit."}};
turnSound.GenerateTurnSound(turns3, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification3, ());
// 900 meters till the second turn.
vector<TurnItemDist> const turns4 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
900. /* m_distMeters */},
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
1900. /* m_distMeters */}};
turnSound.GenerateTurnSound(turns4, turnNotifications);
TEST(turnNotifications.empty(), ());
// 300 meters till the second turn.
vector<TurnItemDist> const turns5 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
300. /* m_distMeters */},
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
1300. /* m_distMeters */}};
turnSound.GenerateTurnSound(turns5, turnNotifications);
TEST(turnNotifications.empty(), ());
// 3 meters till the second turn.
vector<TurnItemDist> const turns6 = {{{10 /* idx */, TurnDirection::LeaveRoundAbout, 2 /* m_exitNum */},
3. /* m_distMeters */},
{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
1003. /* m_distMeters */}};
vector<string> const expectedNotification6 = {{"Leave the roundabout."}};
turnSound.GenerateTurnSound(turns6, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification6, ());
// 5 meters till the third turn.
vector<TurnItemDist> const turns7 = {{{15 /* idx */, TurnDirection::EnterRoundAbout, 1 /* m_exitNum */},
5. /* m_distMeters */},
{{20 /* idx */, TurnDirection::LeaveRoundAbout, 1 /* m_exitNum */},
1005. /* m_distMeters */}};
vector<string> const expectedNotification7 = {{"Enter the roundabout."},
{"Then. Take the first exit."}};
turnSound.GenerateTurnSound(turns7, turnNotifications); // The first notification fast forwarding.
turnSound.GenerateTurnSound(turns7, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification7, ());
// 900 meters till the 4th turn.
turnSound.Reset();
vector<TurnItemDist> const turns8 = {{{25 /* idx */, TurnDirection::EnterRoundAbout, 4 /* m_exitNum */},
900. /* m_distMeters */},
{{30 /* idx */, TurnDirection::LeaveRoundAbout, 4 /* m_exitNum */},
1200. /* m_distMeters */}};
turnSound.GenerateTurnSound(turns8, turnNotifications);
TEST(turnNotifications.empty(), ());
// 620 meters till the 4th turn.
vector<TurnItemDist> const turns9 = {{{25 /* idx */, TurnDirection::EnterRoundAbout, 4 /* m_exitNum */},
620. /* m_distMeters */},
{{30 /* idx */, TurnDirection::LeaveRoundAbout, 4 /* m_exitNum */},
920. /* m_distMeters */}};
vector<string> const expectedNotification9 = {{"In 600 meters. Enter the roundabout."},
{"Then. Take the fourth exit."}};
turnSound.GenerateTurnSound(turns9, turnNotifications);
TEST_EQUAL(turnNotifications, expectedNotification9, ());
}
} // namespace sound
} // namespace turns
} // namespace routing

View file

@ -579,7 +579,8 @@ void FixupTurns(vector<m2::PointD> const & points, Route::TTurns & turnsDir)
}
else if (roundabout && t.m_turn == TurnDirection::LeaveRoundAbout)
{
roundabout->m_exitNum = exitNum + 1;
roundabout->m_exitNum = exitNum + 1; // For EnterRoundAbout turn.
t.m_exitNum = roundabout->m_exitNum; // For LeaveRoundAbout turn.
roundabout = nullptr;
exitNum = 0;
}

View file

@ -29,6 +29,18 @@ uint32_t CalculateDistBeforeMeters(double m_speedMetersPerSecond)
static_cast<uint32_t>(m_speedMetersPerSecond * kStartBeforeSeconds);
return my::clamp(startBeforeMeters, kMinStartBeforeMeters, kMaxStartBeforeMeters);
}
// Returns true if the closest turn is an entrance to a roundabout and the second is
// an exit form a roundabout.
// Note. There are some cases when another turn (besides an exit from roundabout)
// follows an entrance to a roundabout. It could happend in case of turns inside a roundabout.
// Returns false otherwise.
bool IsClassicEntranceToRoundabout(routing::turns::TurnItemDist const & firstTurn,
routing::turns::TurnItemDist const & secondTurn)
{
return firstTurn.m_turnItem.m_turn == routing::turns::TurnDirection::EnterRoundAbout
&& secondTurn.m_turnItem.m_turn == routing::turns::TurnDirection::LeaveRoundAbout;
}
} // namespace
namespace routing
@ -64,8 +76,11 @@ void TurnsSound::GenerateTurnSound(vector<TurnItemDist> const & turns, vector<st
return;
TurnItemDist const & secondTurn = turns[1];
ASSERT_LESS_OR_EQUAL(firstTurn.m_distMeters, secondTurn.m_distMeters, ());
if (secondTurn.m_distMeters - firstTurn.m_distMeters > kMaxTurnDistM)
if (secondTurn.m_distMeters - firstTurn.m_distMeters > kMaxTurnDistM
&& !IsClassicEntranceToRoundabout(firstTurn, secondTurn))
{
return;
}
string secondNotification = GenerateTurnText(0 /* distanceUnits is not used because of "Then" is used */,
secondTurn.m_turnItem.m_exitNum, true,
secondTurn.m_turnItem.m_turn,

View file

@ -37,6 +37,7 @@ class TurnsSound
friend void UnitTest_TurnsSoundMetersTwoTurnsTest();
friend void UnitTest_TurnsSoundFeetTest();
friend void UnitTest_TurnsSoundComposedTurnTest();
friend void UnitTest_TurnsSoundRoundaboutTurnTest();
/// m_enabled == true when tts is turned on.
/// Important! Clients (iOS/Android) implies that m_enabled is false by default.

View file

@ -1,6 +1,8 @@
#include "routing/turns_sound_settings.hpp"
#include "routing/turns_tts_text.hpp"
#include "base/string_utils.hpp"
#include "std/algorithm.hpp"
#include "std/string.hpp"
#include "std/utility.hpp"
@ -100,6 +102,37 @@ string GetDistanceTextId(Notification const & notification)
return string();
}
string GetRoundaboutTextId(Notification const & notification)
{
if (notification.m_turnDir != TurnDirection::LeaveRoundAbout)
{
ASSERT(false, ());
return string();
}
if (!notification.m_useThenInsteadOfDistance)
return "leave_the_roundabout"; // Notification just before leaving a roundabout.
static const uint8_t kMaxSoundedExit = 11;
if (notification.m_exitNum == 0 || notification.m_exitNum > kMaxSoundedExit)
return "leave_the_roundabout";
if (notification.m_exitNum < 4)
{
switch (notification.m_exitNum)
{
case 1:
return "take_the_1st_exit";
case 2:
return "take_the_2nd_exit";
case 3:
return "take_the_3rd_exit";
}
ASSERT(false, ());
return string();
}
return "take_the_" + strings::to_string(static_cast<int>(notification.m_exitNum)) + "th_exit";
}
string GetDirectionTextId(Notification const & notification)
{
switch (notification.m_turnDir)
@ -123,7 +156,7 @@ string GetDirectionTextId(Notification const & notification)
case TurnDirection::EnterRoundAbout:
return "enter_the_roundabout";
case TurnDirection::LeaveRoundAbout:
return "leave_the_roundabout";
return GetRoundaboutTextId(notification);
case TurnDirection::ReachedYourDestination:
return "you_have_reached_the_destination";
case TurnDirection::StayOnRoundAbout:

View file

@ -36,6 +36,9 @@ private:
/// Generates text message id about the distance of the notification. For example: In 300 meters.
string GetDistanceTextId(Notification const & notification);
/// Generates text message id for roundabouts.
/// For example: leave_the_roundabout or take_the_3rd_exit
string GetRoundaboutTextId(Notification const & notification);
/// Generates text message id about the direction of the notification. For example: Make a right
/// turn.
string GetDirectionTextId(Notification const & notification);