Merge pull request from gardster/street-names

Street names
This commit is contained in:
Vladimir Byko-Ianko 2016-04-05 13:56:23 +03:00
commit d5f4e75306
11 changed files with 222 additions and 27 deletions

View file

@ -160,9 +160,10 @@ OsrmRouter::ResultCode OsrmRouter::MakeRouteFromCrossesPath(TCheckedPath const &
RouterDelegate const & delegate,
Route & route)
{
Route::TTurns TurnsDir;
Route::TTimes Times;
vector<m2::PointD> Points;
Route::TTurns turnsDir;
Route::TTimes times;
Route::TStreets streets;
vector<m2::PointD> points;
for (RoutePathCross cross : path)
{
ASSERT_EQUAL(cross.startNode.mwmId, cross.finalNode.mwmId, ());
@ -176,49 +177,62 @@ OsrmRouter::ResultCode OsrmRouter::MakeRouteFromCrossesPath(TCheckedPath const &
if (!FindSingleRoute(cross.startNode, cross.finalNode, mwmMapping->m_dataFacade, routingResult))
return RouteNotFound;
if (!Points.empty())
if (!points.empty())
{
// Remove road end point and turn instruction.
Points.pop_back();
TurnsDir.pop_back();
Times.pop_back();
points.pop_back();
turnsDir.pop_back();
times.pop_back();
// Streets might not point to the last point of the path.
}
// Get annotated route.
Route::TTurns mwmTurnsDir;
Route::TTimes mwmTimes;
Route::TStreets mwmStreets;
vector<m2::PointD> mwmPoints;
if (MakeTurnAnnotation(routingResult, mwmMapping, delegate, mwmPoints, mwmTurnsDir, mwmTimes) != NoError)
if (MakeTurnAnnotation(routingResult, mwmMapping, delegate, mwmPoints, mwmTurnsDir, mwmTimes, mwmStreets) != NoError)
{
LOG(LWARNING, ("Can't load road path data from disk for", mwmMapping->GetCountryName()));
return RouteNotFound;
}
// Connect annotated route.
auto const pSize = static_cast<uint32_t>(Points.size());
auto const pSize = static_cast<uint32_t>(points.size());
for (auto turn : mwmTurnsDir)
{
if (turn.m_index == 0)
continue;
turn.m_index += pSize;
TurnsDir.push_back(turn);
turnsDir.push_back(turn);
}
double const estimationTime = Times.size() ? Times.back().second : 0.0;
if (!mwmStreets.empty() && !streets.empty() && mwmStreets.front().second == streets.back().second)
mwmStreets.erase(mwmStreets.begin());
for (auto street : mwmStreets)
{
if (street.first == 0)
continue;
street.first += pSize;
streets.push_back(street);
}
double const estimationTime = times.size() ? times.back().second : 0.0;
for (auto time : mwmTimes)
{
if (time.first == 0)
continue;
time.first += pSize;
time.second += estimationTime;
Times.push_back(time);
times.push_back(time);
}
Points.insert(Points.end(), mwmPoints.begin(), mwmPoints.end());
points.insert(points.end(), mwmPoints.begin(), mwmPoints.end());
}
route.SetGeometry(Points.begin(), Points.end());
route.SetTurnInstructions(TurnsDir);
route.SetSectionTimes(Times);
route.SetGeometry(points.begin(), points.end());
route.SetTurnInstructions(turnsDir);
route.SetSectionTimes(times);
route.SetStreetNames(streets);
return NoError;
}
@ -343,9 +357,10 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRoute(m2::PointD const & startPoint,
Route::TTurns turnsDir;
Route::TTimes times;
Route::TStreets streets;
vector<m2::PointD> points;
if (MakeTurnAnnotation(routingResult, startMapping, delegate, points, turnsDir, times) != NoError)
if (MakeTurnAnnotation(routingResult, startMapping, delegate, points, turnsDir, times, streets) != NoError)
{
LOG(LWARNING, ("Can't load road path data from disk!"));
return RouteNotFound;
@ -354,6 +369,7 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRoute(m2::PointD const & startPoint,
route.SetGeometry(points.begin(), points.end());
route.SetTurnInstructions(turnsDir);
route.SetSectionTimes(times);
route.SetStreetNames(streets);
return NoError;
}
@ -412,7 +428,7 @@ IRouter::ResultCode OsrmRouter::FindPhantomNodes(m2::PointD const & point,
OsrmRouter::ResultCode OsrmRouter::MakeTurnAnnotation(
RawRoutingResult const & routingResult, TRoutingMappingPtr const & mapping,
RouterDelegate const & delegate, vector<m2::PointD> & points, Route::TTurns & turnsDir,
Route::TTimes & times)
Route::TTimes & times, Route::TStreets & streets)
{
ASSERT(mapping, ());
@ -459,6 +475,10 @@ OsrmRouter::ResultCode OsrmRouter::MakeTurnAnnotation(
// ETA information.
double const nodeTimeSeconds = loadedSegment.m_weight * kOSRMWeightToSecondsMultiplier;
// Street names. I put empty names too, to avoid freezing old street name while riding on
// unnamed street.
streets.emplace_back(max(points.size(), static_cast<size_t>(1)) - 1, loadedSegment.m_name);
// Turns information.
if (segmentIndex > 0 && !points.empty() && skipTurnSegments == 0)
{

View file

@ -81,12 +81,13 @@ protected:
* \param points Storage for unpacked points of the path.
* \param turnsDir output turns annotation storage.
* \param times output times annotation storage.
* \param streets output street names along the path.
* \return routing operation result code.
*/
ResultCode MakeTurnAnnotation(RawRoutingResult const & routingResult,
TRoutingMappingPtr const & mapping,
RouterDelegate const & delegate, vector<m2::PointD> & points,
Route::TTurns & turnsDir, Route::TTimes & times);
TRoutingMappingPtr const & mapping, RouterDelegate const & delegate,
vector<m2::PointD> & points, Route::TTurns & turnsDir,
Route::TTimes & times, Route::TStreets & streets);
private:
/*!

View file

@ -251,12 +251,14 @@ void RoadGraphRouter::ReconstructRoute(vector<Junction> && path, Route & route,
Route::TTimes times;
Route::TTurns turnsDir;
Route::TStreets streetNames;
if (m_directionsEngine)
m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, cancellable);
route.SetGeometry(geometry.begin(), geometry.end());
route.SetSectionTimes(times);
route.SetTurnInstructions(turnsDir);
route.SetStreetNames(streetNames);
}
unique_ptr<IRouter> CreatePedestrianAStarRouter(Index & index, TCountryFileFn const & countryFileFn)

View file

@ -19,7 +19,7 @@ namespace
{
double constexpr kLocationTimeThreshold = 60.0 * 1.0;
double constexpr kOnEndToleranceM = 10.0;
double constexpr kSteetNameLinkMeters = 400.;
} // namespace
Route::Route(string const & router, vector<m2::PointD> const & points, string const & name)
@ -39,6 +39,7 @@ void Route::Swap(Route & rhs)
swap(m_currentTime, rhs.m_currentTime);
swap(m_turns, rhs.m_turns);
swap(m_times, rhs.m_times);
swap(m_streets, rhs.m_streets);
m_absentCountries.swap(rhs.m_absentCountries);
}
@ -150,6 +151,53 @@ Route::TTurns::const_iterator Route::GetCurrentTurn() const
});
}
void Route::GetCurrentStreetName(string & name) const
{
auto it = GetCurrentStreetNameIterAfter(m_poly.GetCurrentIter());
if (it == m_streets.cend())
name.clear();
else
name = it->second;
}
void Route::GetStreetNameAfterIdx(uint32_t idx, string & name) const
{
name.clear();
auto polyIter = m_poly.GetIterToIndex(idx);
auto it = GetCurrentStreetNameIterAfter(polyIter);
if (it == m_streets.cend())
return;
for (;it != m_streets.cend(); ++it)
if (!it->second.empty())
{
if (m_poly.GetDistanceM(polyIter, m_poly.GetIterToIndex(max(it->first, static_cast<uint32_t>(polyIter.m_ind)))) < kSteetNameLinkMeters)
name = it->second;
return;
}
}
Route::TStreets::const_iterator Route::GetCurrentStreetNameIterAfter(FollowedPolyline::Iter iter) const
{
if (m_streets.empty())
{
ASSERT(false, ());
return m_streets.cend();
}
TStreets::const_iterator curIter = m_streets.cbegin();
TStreets::const_iterator prevIter = curIter;
curIter++;
while (curIter->first < iter.m_ind)
{
++prevIter;
++curIter;
if (curIter == m_streets.cend())
return curIter;
}
return curIter->first == iter.m_ind ? curIter : prevIter;
}
bool Route::GetCurrentTurn(double & distanceToTurnMeters, turns::TurnItem & turn) const
{
auto it = GetCurrentTurn();

View file

@ -23,9 +23,11 @@ namespace routing
class Route
{
public:
typedef vector<turns::TurnItem> TTurns;
typedef pair<uint32_t, double> TTimeItem;
typedef vector<TTimeItem> TTimes;
using TTurns = vector<turns::TurnItem>;
using TTimeItem = pair<uint32_t, double>;
using TTimes = vector<TTimeItem>;
using TStreetItem = pair<uint32_t, string>;
using TStreets = vector<TStreetItem>;
explicit Route(string const & router)
: m_router(router), m_routingSettings(GetCarRoutingSettings()) {}
@ -57,6 +59,11 @@ public:
swap(m_times, v);
}
inline void SetStreetNames(TStreets & v)
{
swap(m_streets, v);
}
uint32_t GetTotalTimeSec() const;
uint32_t GetCurrentTimeToEndSec() const;
@ -79,6 +86,12 @@ public:
/// \param turn is information about the nearest turn.
bool GetCurrentTurn(double & distanceToTurnMeters, turns::TurnItem & turn) const;
/// \brief Returns a name of a street where the user rides at this moment.
void GetCurrentStreetName(string &) const;
/// \brief Returns a name of a street next to idx point of the path. Function avoids short unnamed links.
void GetStreetNameAfterIdx(uint32_t idx, string &) const;
/// @return true if GetNextTurn() returns a valid result in parameters, false otherwise.
/// \param distanceToTurnMeters is a distance from current position to the second turn.
/// \param turn is information about the second turn.
@ -115,6 +128,7 @@ private:
void Update();
double GetPolySegAngle(size_t ind) const;
TTurns::const_iterator GetCurrentTurn() const;
TStreets::const_iterator GetCurrentStreetNameIterAfter(FollowedPolyline::Iter iter) const;
private:
friend string DebugPrint(Route const & r);
@ -130,6 +144,7 @@ private:
TTurns m_turns;
TTimes m_times;
TStreets m_streets;
mutable double m_currentTime;
};

View file

@ -0,0 +1,56 @@
#include "testing/testing.hpp"
#include "routing/routing_integration_tests/routing_test_tools.hpp"
#include "routing/route.hpp"
#include "platform/location.hpp"
using namespace routing;
using namespace routing::turns;
void MoveRoute(Route & route, ms::LatLon const & coords)
{
location::GpsInfo info;
info.m_horizontalAccuracy = 0.01;
info.m_verticalAccuracy = 0.01;
info.m_longitude = coords.lon;
info.m_latitude = coords.lat;
route.MoveIterator(info);
}
UNIT_TEST(RussiaTulskayaToPaveletskayaStreetNamesTest)
{
TRouteResult const routeResult = integration::CalculateRoute(
integration::GetOsrmComponents(), MercatorBounds::FromLatLon(55.70839, 37.62145), {0., 0.},
MercatorBounds::FromLatLon(55.73198, 37.63945));
Route & route = *routeResult.first;
IRouter::ResultCode const result = routeResult.second;
TEST_EQUAL(result, IRouter::NoError, ());
integration::TestCurrentStreetName(route, "Большая Тульская улица");
integration::TestNextStreetName(route, "Подольское шоссе");
MoveRoute(route, ms::LatLon(55.71398, 37.62443));
integration::TestCurrentStreetName(route, "Подольское шоссе");
integration::TestNextStreetName(route, "Валовая улица");
MoveRoute(route, ms::LatLon(55.72059, 37.62766));
integration::TestCurrentStreetName(route, "Павловская улица");
integration::TestNextStreetName(route, "Валовая улица");
MoveRoute(route, ms::LatLon(55.72469, 37.62624));
integration::TestCurrentStreetName(route, "Большая Серпуховская улица");
integration::TestNextStreetName(route, "Валовая улица");
MoveRoute(route, ms::LatLon(55.73034, 37.63099));
integration::TestCurrentStreetName(route, "Валовая улица");
integration::TestNextStreetName(route, "");
integration::TestRouteLength(route, 3390.);
}

View file

@ -27,6 +27,7 @@ SOURCES += \
cross_section_tests.cpp \
online_cross_tests.cpp \
osrm_route_test.cpp \
osrm_street_names_test.cpp \
osrm_turn_test.cpp \
pedestrian_route_test.cpp \
routing_test_tools.cpp \

View file

@ -188,6 +188,23 @@ namespace integration
TEST_EQUAL(route.GetTurns().size() - 1, expectedTurnCount, ());
}
void TestCurrentStreetName(routing::Route const & route, string const & expectedStreetName)
{
string streetName;
route.GetCurrentStreetName(streetName);
TEST_EQUAL(streetName, expectedStreetName, ());
}
void TestNextStreetName(routing::Route const & route, string const & expectedStreetName)
{
string streetName;
double distance;
turns::TurnItem turn;
TEST(route.GetCurrentTurn(distance, turn), ());
route.GetStreetNameAfterIdx(turn.m_index, streetName);
TEST_EQUAL(streetName, expectedStreetName, ());
}
void TestRouteLength(Route const & route, double expectedRouteMeters,
double relativeError)
{

View file

@ -144,4 +144,8 @@ unique_ptr<storage::CountryInfoGetter> CreateCountryInfoGetter();
/// Extracting appropriate TestTurn if any. If not TestTurn::isValid() returns false.
/// inaccuracy is set in meters.
TestTurn GetNthTurn(Route const & route, uint32_t turnNumber);
void TestCurrentStreetName(routing::Route const & route, string const & expectedStreetName);
void TestNextStreetName(routing::Route const & route, string const & expectedStreetName);
}

View file

@ -288,8 +288,8 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
info.m_exitNum = turn.m_exitNum;
info.m_time = max(kMinimumETASec, m_route.GetCurrentTimeToEndSec());
info.m_sourceName = turn.m_sourceName;
info.m_targetName = turn.m_targetName;
m_route.GetCurrentStreetName(info.m_sourceName);
m_route.GetStreetNameAfterIdx(turn.m_index, info.m_targetName);
info.m_completionPercent = GetCompletionPercent();
// Lane information.
if (distanceToTurnMeters < kShowLanesDistInMeters)

View file

@ -18,6 +18,7 @@ static vector<m2::PointD> const kTestGeometry({{0, 0}, {0,1}, {1,1}, {1,2}, {1,3
static vector<turns::TurnItem> const kTestTurns({turns::TurnItem(1, turns::TurnDirection::TurnLeft),
turns::TurnItem(2, turns::TurnDirection::TurnRight),
turns::TurnItem(4, turns::TurnDirection::ReachedYourDestination)});
static Route::TStreets const kTestNames({{0, "Street1"}, {1, "Street2"}, {4, "Street3"}});
location::GpsInfo GetGps(double x, double y)
{
@ -157,3 +158,33 @@ UNIT_TEST(NextTurnsTest)
TEST_EQUAL(turnsDist.size(), 1, ());
}
}
UNIT_TEST(RouteNameTest)
{
Route route("TestRouter");
route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end());
vector<turns::TurnItem> turns(kTestTurns);
route.SetTurnInstructions(turns);
Route::TStreets names(kTestNames);
route.SetStreetNames(names);
string name;
route.GetCurrentStreetName(name);
TEST_EQUAL(name, "Street1", ());
route.GetStreetNameAfterIdx(0, name);
TEST_EQUAL(name, "Street1", ());
route.GetStreetNameAfterIdx(1, name);
TEST_EQUAL(name, "Street2", ());
route.GetStreetNameAfterIdx(2, name);
TEST_EQUAL(name, "Street2", ());
route.GetStreetNameAfterIdx(3, name);
TEST_EQUAL(name, "Street2", ());
route.GetStreetNameAfterIdx(4, name);
TEST_EQUAL(name, "Street3", ());
}