forked from organicmaps/organicmaps
commit
d5f4e75306
11 changed files with 222 additions and 27 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
/*!
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
56
routing/routing_integration_tests/osrm_street_names_test.cpp
Normal file
56
routing/routing_integration_tests/osrm_street_names_test.cpp
Normal 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.);
|
||||
}
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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", ());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue