diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp index c8dbea352a..5b3119d594 100644 --- a/indexer/ftypes_matcher.cpp +++ b/indexer/ftypes_matcher.cpp @@ -70,6 +70,30 @@ IsStreetChecker const & IsStreetChecker::Instance() return inst; } +IsOneWayChecker::IsOneWayChecker() +{ + Classificator const & c = classif(); + m_types.push_back(c.GetTypeByPath({ "hwtag", "oneway" })); +} + +IsOneWayChecker const & IsOneWayChecker::Instance() +{ + static const IsOneWayChecker inst; + return inst; +} + +IsRoundAboutChecker::IsRoundAboutChecker() +{ + Classificator const & c = classif(); + m_types.push_back(c.GetTypeByPath({ "junction", "roundabout" })); +} + +IsRoundAboutChecker const & IsRoundAboutChecker::Instance() +{ + static const IsRoundAboutChecker inst; + return inst; +} + IsBuildingChecker::IsBuildingChecker() { Classificator const & c = classif(); diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp index b1bbf10363..ecf1aabe26 100644 --- a/indexer/ftypes_matcher.hpp +++ b/indexer/ftypes_matcher.hpp @@ -34,6 +34,22 @@ public: static IsStreetChecker const & Instance(); }; +class IsOneWayChecker : public BaseChecker +{ +public: + IsOneWayChecker(); + + static IsOneWayChecker const & Instance(); +}; + +class IsRoundAboutChecker : public BaseChecker +{ +public: + IsRoundAboutChecker(); + + static IsRoundAboutChecker const & Instance(); +}; + class IsBuildingChecker : public BaseChecker { public: diff --git a/routing/osrm_router.cpp b/routing/osrm_router.cpp index f30879b0a4..8152126759 100644 --- a/routing/osrm_router.cpp +++ b/routing/osrm_router.cpp @@ -1,14 +1,18 @@ #include "osrm_router.hpp" -#include "route.hpp" #include "vehicle_model.hpp" +#include "../base/math.hpp" + +#include "../geometry/angles.hpp" #include "../geometry/distance.hpp" #include "../geometry/distance_on_sphere.hpp" +#include "../indexer/ftypes_matcher.hpp" #include "../indexer/mercator.hpp" #include "../indexer/index.hpp" #include "../indexer/scales.hpp" #include "../indexer/mwm_version.hpp" +#include "../indexer/search_string_utils.hpp" #include "../platform/platform.hpp" @@ -537,6 +541,8 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt ASSERT(segBegin.IsValid(), ()); ASSERT(segEnd.IsValid(), ()); + Route::TurnsT turns; + vector points; for (auto i : osrm::irange(0, rawRoute.unpacked_path_segments.size())) { @@ -544,6 +550,10 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt return Cancelled; // Get all the coordinates for the computed route + m2::PointD p1, p; + feature::TypesHolder fTypePrev; + string namePrev; + size_t const n = rawRoute.unpacked_path_segments[i].size(); for (size_t j = 0; j < n; ++j) { @@ -591,6 +601,34 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt if (j == n - 1 && k == endK - 1) endIdx = (seg.m_pointEnd > seg.m_pointStart) ? segEnd.m_pointEnd : segEnd.m_pointStart; + + if (j > 0 && k == startK) + { + ASSERT_LESS(MercatorBounds::DistanceOnEarth(p, ft.GetPoint(startIdx)), 2, ()); + + m2::PointD const p2 = ft.GetPoint((seg.m_pointEnd > seg.m_pointStart) ? startIdx + 1 : startIdx - 1); + + string name; + ft.GetName(FeatureType::DEFAULT_LANG, name); + + string n1, n2; + search::GetStreetNameAsKey(namePrev, n1); + search::GetStreetNameAsKey(name, n2); + + Route::TurnInstruction const t = GetTurnInstruction(fTypePrev, ft, p1, p, p2, (!n1.empty() && (n1 == n2))); + + if (t != Route::NoTurn) + turns.push_back(Route::TurnItemT(points.size(), t)); + } + + if (k == endK - 1) + { + fTypePrev = feature::TypesHolder(ft); + ft.GetName(FeatureType::DEFAULT_LANG, namePrev); + p1 = ft.GetPoint((seg.m_pointEnd > seg.m_pointStart) ? (endIdx - 1) : (endIdx + 1)); + p = ft.GetPoint(endIdx); + } + if (seg.m_pointEnd > seg.m_pointStart) { for (auto idx = startIdx; idx <= endIdx; ++idx) @@ -612,11 +650,62 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRouteImpl(m2::PointD const & startPt if (points.size() < 2) return RouteNotFound; + // osrm multiple seconds to 10, so we need to divide it back + estimateTime /= 10; + route.SetGeometry(points.begin(), points.end()); + route.SetTurnInstructions(turns); return NoError; } +Route::TurnInstruction OsrmRouter::GetTurnInstruction(feature::TypesHolder const & ft1, feature::TypesHolder const & ft2, + m2::PointD const & p1, m2::PointD const & p, m2::PointD const & p2, + bool isStreetEqual) const +{ + bool const isRound1 = ftypes::IsRoundAboutChecker::Instance()(ft1); + bool const isRound2 = ftypes::IsRoundAboutChecker::Instance()(ft2); + + if (isRound1 && isRound2) + return Route::StayOnRoundAbout; + + if (!isRound1 && isRound2) + return Route::EnterRoundAbout; + + if (isRound1 && !isRound2) + return Route::LeaveRoundAbout; + + if (isStreetEqual) + return Route::NoTurn; + + double a = my::RadToDeg(ang::AngleTo(p, p2) - ang::AngleTo(p, p1)); + while (a < 0) + a += 360; + + if (a >= 23 && a < 67) + return Route::TurnSharpRight; + + if (a >= 67 && a < 113) + return Route::TurnRight; + + if (a >= 113 && a < 158) + return Route::TurnSlightRight; + + if (a >= 158 && a < 202) + return Route::GoStraight; + + if (a >= 202 && a < 248) + return Route::TurnSlightLeft; + + if (a >= 248 && a < 292) + return Route::TurnLeft; + + if (a >= 292 && a < 336) + return Route::TurnSharpLeft; + + return Route::UTurn; +} + IRouter::ResultCode OsrmRouter::FindPhantomNodes(string const & fName, m2::PointD const & startPt, m2::PointD const & finalPt, FeatureGraphNodeVecT & res, size_t maxCount, uint32_t & mwmId) { diff --git a/routing/osrm_router.hpp b/routing/osrm_router.hpp index 7cb1bdc7dc..54a3ac1c63 100644 --- a/routing/osrm_router.hpp +++ b/routing/osrm_router.hpp @@ -1,5 +1,6 @@ #pragma once +#include "route.hpp" #include "router.hpp" #include "osrm2feature_map.hpp" #include "osrm_data_facade.hpp" @@ -11,6 +12,7 @@ #include "../3party/osrm/osrm-backend/DataStructures/QueryEdge.h" +namespace feature { class TypesHolder; } class Index; struct PhantomNode; @@ -49,6 +51,10 @@ protected: void CalculateRouteAsync(ReadyCallback const & callback); ResultCode CalculateRouteImpl(m2::PointD const & startPt, m2::PointD const & finalPt, Route & route); + Route::TurnInstruction GetTurnInstruction(feature::TypesHolder const & ft1, feature::TypesHolder const & ft2, + m2::PointD const & p1, m2::PointD const & p, m2::PointD const & p2, + bool isStreetEqual) const; + private: Index const * m_pIndex; diff --git a/routing/route.cpp b/routing/route.cpp index a07d8dcd84..429c955c17 100644 --- a/routing/route.cpp +++ b/routing/route.cpp @@ -34,6 +34,12 @@ void Route::Swap(Route & rhs) m_segProj.swap(rhs.m_segProj); swap(m_current, rhs.m_current); swap(m_currentTime, rhs.m_currentTime); + swap(m_turns, rhs.m_turns); +} + +void Route::SetTurnInstructions(TurnsT & v) +{ + swap(m_turns, v); } double Route::GetDistance() const @@ -123,12 +129,10 @@ Route::IterT Route::FindProjection(m2::RectD const & posRect, double predictDist double Route::GetDistanceOnPolyline(IterT const & it1, IterT const & it2) const { - size_t const n = m_poly.GetSize(); - ASSERT(it1.IsValid() && it2.IsValid(), ()); ASSERT_LESS_OR_EQUAL(it1.m_ind, it2.m_ind, ()); - ASSERT_LESS(it1.m_ind, n, ()); - ASSERT_LESS(it2.m_ind, n, ()); + ASSERT_LESS(it1.m_ind, m_poly.GetSize(), ()); + ASSERT_LESS(it2.m_ind, m_poly.GetSize(), ()); if (it1.m_ind == it2.m_ind) return MercatorBounds::DistanceOnEarth(it1.m_pt, it2.m_pt); diff --git a/routing/route.hpp b/routing/route.hpp index 876a09b3b7..a371c31e63 100644 --- a/routing/route.hpp +++ b/routing/route.hpp @@ -14,6 +14,32 @@ namespace routing class Route { public: + + enum TurnInstruction + { + NoTurn = 0, + GoStraight, + TurnRight, + TurnSharpRight, + TurnSlightRight, + TurnLeft, + TurnSharpLeft, + TurnSlightLeft, + UTurn, + HeadOn, + EnterRoundAbout, + LeaveRoundAbout, + StayOnRoundAbout, + StartAtEndOfStreet, + ReachedYourDestination, + EnterAgainstAllowedDirection, + LeaveAgainstAllowedDirection + }; + + // first value of piar is an number of point in polyline (number of segment + 1) + typedef pair TurnItemT; + typedef vector TurnsT; + explicit Route(string const & router) : m_router(router) {} template @@ -33,6 +59,8 @@ public: Update(); } + void SetTurnInstructions(TurnsT & v); + string const & GetRouterId() const { return m_router; } m2::PolylineD const & GetPoly() const { return m_poly; } string const & GetName() const { return m_name; } @@ -88,6 +116,8 @@ private: /// Precalculated info for fast projection finding. vector> m_segProj; + TurnsT m_turns; + /// Cached result iterator for last MoveIterator query. mutable IterT m_current; mutable double m_currentTime;