diff --git a/routing/base/followed_polyline.cpp b/routing/base/followed_polyline.cpp index 92d38dc9d8..36bf81ac86 100644 --- a/routing/base/followed_polyline.cpp +++ b/routing/base/followed_polyline.cpp @@ -166,18 +166,20 @@ Iter FollowedPolyline::UpdateProjection(m2::RectD const & posRect) const return res; } -double FollowedPolyline::GetMercatorDistanceFromBegin() const +double FollowedPolyline::GetDistFromCurPointToRoutePointMerc() const { - double distance = 0.0; - if (m_current.IsValid()) - { - for (size_t i = 1; i <= m_current.m_ind; i++) - distance += m_poly.GetPoint(i).Length(m_poly.GetPoint(i - 1)); + if (!m_current.IsValid()) + return 0.0; - distance += m_poly.GetPoint(m_current.m_ind).Length(m_current.m_pt); - } + return m_poly.GetPoint(m_current.m_ind).Length(m_current.m_pt); +} - return distance; +double FollowedPolyline::GetDistFromCurPointToRoutePointMeters() const +{ + if (!m_current.IsValid()) + return 0.0; + + return MercatorBounds::DistanceOnEarth(m_poly.GetPoint(m_current.m_ind), m_current.m_pt); } void FollowedPolyline::GetCurrentDirectionPoint(m2::PointD & pt, double toleranceM) const diff --git a/routing/base/followed_polyline.hpp b/routing/base/followed_polyline.hpp index 600ca21e39..c38be2e2bb 100644 --- a/routing/base/followed_polyline.hpp +++ b/routing/base/followed_polyline.hpp @@ -26,12 +26,6 @@ public: Update(); } - void PopBack() - { - m_poly.PopBack(); - Update(); - } - bool IsValid() const { return (m_current.IsValid() && m_poly.GetSize() > 1); } m2::PolylineD const & GetPolyline() const { return m_poly; } @@ -39,10 +33,11 @@ public: double GetTotalDistanceM() const; double GetDistanceFromBeginM() const; double GetDistanceToEndM() const; - double GetMercatorDistanceFromBegin() const; + double GetDistFromCurPointToRoutePointMerc() const; + double GetDistFromCurPointToRoutePointMeters() const; /*! \brief Return next navigation point for direction widgets. - * Returns first geomety point from the polyline after your location if it is farther then + * Returns first geometry point from the polyline after your location if it is farther then * toleranceM. */ void GetCurrentDirectionPoint(m2::PointD & pt, double toleranceM) const; diff --git a/routing/bicycle_directions.cpp b/routing/bicycle_directions.cpp index e8e81f19f8..ed5f5f10f2 100644 --- a/routing/bicycle_directions.cpp +++ b/routing/bicycle_directions.cpp @@ -258,8 +258,8 @@ void BicycleDirectionsEngine::GetUniNodeIdAndAdjacentEdges(IRoadGraph::TEdgeVect if (inEdge.GetFeatureId().m_mwmId == edge.GetFeatureId().m_mwmId) { - ASSERT_LESS(MercatorBounds::DistanceOnEarth(junctionPoint, edge.GetStartJunction().GetPoint()), - turns::kFeaturesNearTurnMeters, ()); +// ASSERT_LESS(MercatorBounds::DistanceOnEarth(junctionPoint, edge.GetStartJunction().GetPoint()), +// turns::kFeaturesNearTurnMeters, ()); m2::PointD const & outgoingPoint = edge.GetEndJunction().GetPoint(); angle = my::RadToDeg(turns::PiMinusTwoVectorsAngle(junctionPoint, ingoingPoint, outgoingPoint)); } diff --git a/routing/index_router.cpp b/routing/index_router.cpp index eaeb05bce1..01babda45c 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -627,12 +627,12 @@ IRouter::ResultCode IndexRouter::RedressRoute(vector const & segments, // First and last segments are fakes: skip it. for (size_t i = 1; i < segments.size() - 1; ++i) { - times.emplace_back(static_cast(i), time); time += starter.CalcSegmentWeight(segments[i]); + times.emplace_back(static_cast(i), time); } CHECK(m_directionsEngine, ()); - ReconstructRoute(*m_directionsEngine, roadGraph, m_trafficStash, delegate, false /* hasAltitude */, junctions, + ReconstructRoute(*m_directionsEngine, roadGraph, m_trafficStash, delegate, junctions, std::move(times), route); if (!route.IsValid()) diff --git a/routing/road_graph_router.cpp b/routing/road_graph_router.cpp index 35791b9983..75479d3a38 100644 --- a/routing/road_graph_router.cpp +++ b/routing/road_graph_router.cpp @@ -210,8 +210,8 @@ IRouter::ResultCode RoadGraphRouter::CalculateRoute(Checkpoints const & checkpoi CHECK(m_directionsEngine, ()); route.SetSubroteAttrs(vector( {Route::SubrouteAttrs(startPos, finalPos, 0, result.path.size() - 1)})); - ReconstructRoute(*m_directionsEngine, *m_roadGraph, nullptr /* trafficStash */, delegate, - true /* hasAltitude */, result.path, std::move(times), route); + ReconstructRoute(*m_directionsEngine, *m_roadGraph, nullptr, delegate, result.path, + std::move(times), route); } m_roadGraph->ResetFakes(); diff --git a/routing/route.cpp b/routing/route.cpp index 3b3164a431..c894d50074 100644 --- a/routing/route.cpp +++ b/routing/route.cpp @@ -37,7 +37,6 @@ Route::Route(string const & router, vector const & points, string co : m_router(router), m_routingSettings(GetCarRoutingSettings()), m_name(name), m_poly(points.begin(), points.end()) { - Update(); } void Route::Swap(Route & rhs) @@ -45,15 +44,8 @@ void Route::Swap(Route & rhs) m_router.swap(rhs.m_router); swap(m_routingSettings, rhs.m_routingSettings); m_poly.Swap(rhs.m_poly); - m_simplifiedPoly.Swap(rhs.m_simplifiedPoly); m_name.swap(rhs.m_name); - 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); - m_altitudes.swap(rhs.m_altitudes); - m_traffic.swap(rhs.m_traffic); m_routeSegments.swap(rhs.m_routeSegments); m_subrouteAttrs.swap(rhs.m_subrouteAttrs); } @@ -86,156 +78,137 @@ double Route::GetCurrentDistanceToEndMeters() const double Route::GetMercatorDistanceFromBegin() const { - //TODO Maybe better to return FollowedRoute and user will call GetMercatorDistance etc. by itself - return m_poly.GetMercatorDistanceFromBegin(); -} - -uint32_t Route::GetTotalTimeSec() const -{ - return m_times.empty() ? 0 : m_times.back().second; -} - -uint32_t Route::GetCurrentTimeToEndSec() const -{ - size_t const polySz = m_poly.GetPolyline().GetSize(); - if (m_times.empty() || polySz == 0) - { - ASSERT(!m_times.empty(), ()); - ASSERT(polySz != 0, ()); - return 0; - } - - TTimes::const_iterator it = upper_bound(m_times.begin(), m_times.end(), m_poly.GetCurrentIter().m_ind, - [](size_t v, Route::TTimeItem const & item) { return v < item.first; }); - - if (it == m_times.end()) + auto const & curIter = m_poly.GetCurrentIter(); + if (!IsValid() || !curIter.IsValid()) return 0; - size_t idx = distance(m_times.begin(), it); - double time = (*it).second; - if (idx > 0) - time -= m_times[idx - 1].second; - - auto distFn = [&](size_t start, size_t end) - { - return m_poly.GetDistanceM(m_poly.GetIterToIndex(start), m_poly.GetIterToIndex(end)); - }; - - ASSERT_LESS(m_times[idx].first, polySz, ()); - double const dist = distFn(idx > 0 ? m_times[idx - 1].first : 0, m_times[idx].first); - - if (!my::AlmostEqualULPs(dist, 0.)) - { - double const distRemain = distFn(m_poly.GetCurrentIter().m_ind, m_times[idx].first) - - MercatorBounds::DistanceOnEarth(m_poly.GetCurrentIter().m_pt, - m_poly.GetPolyline().GetPoint(m_poly.GetCurrentIter().m_ind)); - return (uint32_t)((GetTotalTimeSec() - (*it).second) + (double)time * (distRemain / dist)); - } - else - return (uint32_t)((GetTotalTimeSec() - (*it).second)); + CHECK_LESS(curIter.m_ind, m_routeSegments.size(), ()); + double const distMerc = + curIter.m_ind == 0 ? 0.0 : m_routeSegments[curIter.m_ind - 1].GetDistFromBeginningMerc(); + return distMerc + m_poly.GetDistFromCurPointToRoutePointMerc(); } -Route::TTurns::const_iterator Route::GetCurrentTurn() const +double Route::GetTotalTimeSec() const { - ASSERT(!m_turns.empty(), ()); + return m_routeSegments.empty() ? 0 : m_routeSegments.back().GetTimeFromBeginningS(); +} - TurnItem t; - t.m_index = static_cast(m_poly.GetCurrentIter().m_ind); - return upper_bound(m_turns.cbegin(), m_turns.cend(), t, - [](TurnItem const & lhs, TurnItem const & rhs) - { - return lhs.m_index < rhs.m_index; - }); +double Route::GetCurrentTimeToEndSec() const +{ + auto const & curIter = m_poly.GetCurrentIter(); + if (!IsValid() || !curIter.IsValid()) + return 0.0; + + CHECK_LESS(curIter.m_ind, m_routeSegments.size(), ()); + double const etaToLastPassedPointS = GetETAToLastPassedPointS(); + double const curSegLenMeters = GetSegLenMeters(curIter.m_ind); + double const totalTimeS = GetTotalTimeSec(); + // Note. If a segment is short it does not make any sence to take into account time needed + // to path its part. + if (my::AlmostEqualAbs(curSegLenMeters, 0.0, 1.0 /* meters */)) + return totalTimeS - etaToLastPassedPointS; + + double const curSegTimeS = GetTimeToPassSegS(curIter.m_ind); + CHECK_GREATER(curSegTimeS, 0, ("Route can't contain segments with infinite speed.")); + + double const curSegSpeedMPerS = curSegLenMeters / curSegTimeS; + CHECK_GREATER(curSegSpeedMPerS, 0, ("Route can't contain segments with zero speed.")); + return totalTimeS - (etaToLastPassedPointS + + m_poly.GetDistFromCurPointToRoutePointMeters() / curSegSpeedMPerS); } void Route::GetCurrentStreetName(string & name) const { - auto it = GetCurrentStreetNameIterAfter(m_poly.GetCurrentIter()); - if (it == m_streets.cend()) - name.clear(); - else - name = it->second; + name.clear(); + GetStreetNameAfterIdx(static_cast(m_poly.GetCurrentIter().m_ind), name); } 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()) + auto const iterIdx = m_poly.GetIterToIndex(idx); + if (!IsValid() || !iterIdx.IsValid()) return; - for (;it != m_streets.cend(); ++it) - if (!it->second.empty()) + + size_t i = idx; + for (; i < m_poly.GetPolyline().GetSize(); ++i) + { + // Note. curIter.m_ind == 0 means route iter at zero point. No corresponding route segments at + // |m_routeSegments| in this case. |name| should be cleared. + if (i == 0) + continue; + + string const n = m_routeSegments[ConvertPointIdxToSegmentIdx(i)].GetStreet(); + if (!n.empty()) { - if (m_poly.GetDistanceM(polyIter, m_poly.GetIterToIndex(max(it->first, static_cast(polyIter.m_ind)))) < kSteetNameLinkMeters) - name = it->second; + name = n; return; } + auto const furtherIter = m_poly.GetIterToIndex(i); + CHECK(furtherIter.IsValid(), ()); + if (m_poly.GetDistanceM(iterIdx, furtherIter) > kSteetNameLinkMeters) + return; + } } -Route::TStreets::const_iterator Route::GetCurrentStreetNameIterAfter(FollowedPolyline::Iter iter) const +size_t Route::ConvertPointIdxToSegmentIdx(size_t pointIdx) const { - // m_streets empty for pedestrian router. - if (m_streets.empty()) - { - 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; + CHECK_GREATER(pointIdx, 0, ()); + // Note. |pointIdx| is an index at |m_poly|. Properties of the point gets a segment at |m_routeSegments| + // which precedes the point. So to get segment index it's needed to subtract one. + CHECK_LESS(pointIdx, m_routeSegments.size() + 1, ()); + return pointIdx - 1; } -bool Route::GetCurrentTurn(double & distanceToTurnMeters, TurnItem & turn) const +void Route::GetClosestTurn(size_t segIdx, TurnItem & turn) const { - auto it = GetCurrentTurn(); - if (it == m_turns.end()) - { - ASSERT(false, ()); - return false; - } + CHECK_LESS(segIdx, m_routeSegments.size(), ()); - size_t const segIdx = (*it).m_index; - turn = (*it); + for (size_t i = segIdx; i < m_routeSegments.size(); ++i) + { + if (m_routeSegments[i].GetTurn().m_turn != TurnDirection::NoTurn || + m_routeSegments[i].GetTurn().m_pedestrianTurn != PedestrianDirection::None) + { + turn = m_routeSegments[i].GetTurn(); + return; + } + } + CHECK(false, ("Last turn should be TurnDirection::ReachedYourDestination.")); + return; +} + +void Route::GetCurrentTurn(double & distanceToTurnMeters, TurnItem & turn) const +{ + // Note. |m_poly.GetCurrentIter().m_ind| is a point index of last passed point at the polyline. + GetClosestTurn(m_poly.GetCurrentIter().m_ind, turn); distanceToTurnMeters = m_poly.GetDistanceM(m_poly.GetCurrentIter(), - m_poly.GetIterToIndex(segIdx)); - return true; + m_poly.GetIterToIndex(turn.m_index)); } bool Route::GetNextTurn(double & distanceToTurnMeters, TurnItem & turn) const { - auto it = GetCurrentTurn(); - auto const turnsEnd = m_turns.end(); - ASSERT(it != turnsEnd, ()); - - if (it == turnsEnd || (it + 1) == turnsEnd) + TurnItem curTurn; + GetClosestTurn(m_poly.GetCurrentIter().m_ind, curTurn); + if (curTurn.m_turn == TurnDirection::ReachedYourDestination) { turn = TurnItem(); - distanceToTurnMeters = 0; return false; } - it += 1; - turn = *it; + // Note. curTurn.m_index is index of |curTurn| at polyline |m_poly| starting from zero point. + // So index of the turn at |m_routeSegments| is |curTurn.m_index| - 1. + // To find the next turn next turn after |curTurn.m_index| - 1 should be used. + CHECK_LESS(curTurn.m_index, m_routeSegments.size(), ()); + GetClosestTurn(curTurn.m_index, turn); distanceToTurnMeters = m_poly.GetDistanceM(m_poly.GetCurrentIter(), - m_poly.GetIterToIndex(it->m_index)); + m_poly.GetIterToIndex(turn.m_index)); return true; } bool Route::GetNextTurns(vector & turns) const { TurnItemDist currentTurn; - if (!GetCurrentTurn(currentTurn.m_distMeters, currentTurn.m_turnItem)) - return false; + GetCurrentTurn(currentTurn.m_distMeters, currentTurn.m_turnItem); turns.clear(); turns.emplace_back(move(currentTurn)); @@ -248,30 +221,15 @@ bool Route::GetNextTurns(vector & turns) const void Route::GetCurrentDirectionPoint(m2::PointD & pt) const { - if (m_routingSettings.m_keepPedestrianInfo && m_simplifiedPoly.IsValid()) - m_simplifiedPoly.GetCurrentDirectionPoint(pt, kOnEndToleranceM); - else - m_poly.GetCurrentDirectionPoint(pt, kOnEndToleranceM); + m_poly.GetCurrentDirectionPoint(pt, kOnEndToleranceM); } bool Route::MoveIterator(location::GpsInfo const & info) const { - double predictDistance = -1.0; - if (m_currentTime > 0.0 && info.HasSpeed()) - { - /// @todo Need to distinguish GPS and WiFi locations. - /// They may have different time metrics in case of incorrect system time on a device. - double const deltaT = info.m_timestamp - m_currentTime; - if (deltaT > 0.0 && deltaT < kLocationTimeThreshold) - predictDistance = info.m_speed * deltaT; - } - m2::RectD const rect = MercatorBounds::MetresToXY( info.m_longitude, info.m_latitude, max(m_routingSettings.m_matchingThresholdM, info.m_horizontalAccuracy)); - FollowedPolyline::Iter const res = m_poly.UpdateProjectionByPrediction(rect, predictDistance); - if (m_simplifiedPoly.IsValid()) - m_simplifiedPoly.UpdateProjectionByPrediction(rect, predictDistance); + FollowedPolyline::Iter const res = m_poly.UpdateProjectionByPrediction(rect, -1.0 /* predictDistance */); return res.IsValid(); } @@ -315,27 +273,6 @@ void Route::MatchLocationToRoute(location::GpsInfo & location, location::RouteMa } } -void Route::Update() -{ - if (!m_poly.IsValid()) - return; - if (m_routingSettings.m_keepPedestrianInfo) - { - vector points; - auto distFn = m2::DistanceToLineSquare(); - // TODO (ldargunov) Rewrite dist f to distance in meters and avoid 0.00000 constants. - SimplifyNearOptimal(20, m_poly.GetPolyline().Begin(), m_poly.GetPolyline().End(), 0.00000001, distFn, - MakeBackInsertFunctor(points)); - FollowedPolyline(points.begin(), points.end()).Swap(m_simplifiedPoly); - } - else - { - // Free memory if we don't need simplified geometry. - FollowedPolyline().Swap(m_simplifiedPoly); - } - m_currentTime = 0.0; -} - size_t Route::GetSubrouteCount() const { return m_subrouteAttrs.size(); } void Route::GetSubrouteInfo(size_t subrouteIdx, std::vector & segments) const @@ -377,16 +314,54 @@ void Route::SetSubrouteUid(size_t segmentIdx, SubrouteUid subrouteUid) m_subrouteUid = subrouteUid; } -Junction Route::GetJunction(size_t pointIdx) const +void Route::GetAltitudes(feature::TAltitudes & altitudes) const +{ + altitudes.clear(); + for (auto const & s : m_routeSegments) + altitudes.push_back(s.GetJunction().GetAltitude()); +} + +traffic::SpeedGroup Route::GetTraffic(size_t segmentIdx) const +{ + CHECK_LESS(segmentIdx, m_routeSegments.size(), ()); + return m_routeSegments[segmentIdx].GetTraffic(); +} + +void Route::GetTurnsForTesting(vector & turns) const +{ + turns.clear(); + for (auto const & s : m_routeSegments) + { + if (s.GetTurn().m_turn != turns::TurnDirection::NoTurn || + s.GetTurn().m_pedestrianTurn != turns::PedestrianDirection::None) + { + turns.push_back(s.GetTurn()); + } + } +} + +double Route::GetTimeToPassSegS(size_t segIdx) const +{ + CHECK_LESS(segIdx, m_routeSegments.size(), ()); + return m_routeSegments[segIdx].GetTimeFromBeginningS() - + (segIdx == 0 ? 0.0 : m_routeSegments[segIdx - 1].GetTimeFromBeginningS()); +} + +double Route::GetSegLenMeters(size_t segIdx) const +{ + CHECK_LESS(segIdx, m_routeSegments.size(), ()); + return m_routeSegments[segIdx].GetDistFromBeginningMeters() - + (segIdx == 0 ? 0.0 : m_routeSegments[segIdx - 1].GetDistFromBeginningMeters()); +} + +double Route::GetETAToLastPassedPointS() const { CHECK(IsValid(), ()); - CHECK_LESS(pointIdx, m_poly.GetPolyline().GetSize(), ()); - if (!m_altitudes.empty()) - CHECK_EQUAL(m_altitudes.size(), m_poly.GetPolyline().GetSize(), ()); + auto const & curIter = m_poly.GetCurrentIter(); + CHECK(curIter.IsValid(), ()); + CHECK_LESS(curIter.m_ind, m_routeSegments.size(), ()); - auto const & points = m_poly.GetPolyline().GetPoints(); - return Junction(points[pointIdx], - m_altitudes.empty() ? feature::kInvalidAltitude : m_altitudes[pointIdx]); + return curIter.m_ind == 0 ? 0.0 : m_routeSegments[curIter.m_ind - 1].GetTimeFromBeginningS(); } string DebugPrint(Route const & r) diff --git a/routing/route.hpp b/routing/route.hpp index 44773912cf..9f43974e6f 100644 --- a/routing/route.hpp +++ b/routing/route.hpp @@ -148,7 +148,6 @@ public: Route(string const & router, TIter beg, TIter end) : m_router(router), m_routingSettings(GetCarRoutingSettings()), m_poly(beg, end) { - Update(); } Route(string const & router, vector const & points, string const & name = string()); @@ -161,35 +160,39 @@ public: FollowedPolyline().Swap(m_poly); else FollowedPolyline(beg, end).Swap(m_poly); - Update(); } - inline void SetTurnInstructions(TTurns && v) { m_turns = move(v); } - inline void SetSectionTimes(TTimes && v) { m_times = move(v); } - inline void SetStreetNames(TStreets && v) { m_streets = move(v); } - inline void SetAltitudes(feature::TAltitudes && v) { m_altitudes = move(v); } - inline void SetTraffic(vector && v) { m_traffic = move(v); } - template - void SetRouteSegments(SI && v) { m_routeSegments = std::forward(v); } + void SetRouteSegments(SI && v) + { + m_routeSegments = std::forward(v); + + m_haveAltitudes = true; + for (auto const & s : m_routeSegments) + { + if (s.GetJunction().GetAltitude() == feature::kInvalidAltitude) + m_haveAltitudes = false; + } + } void SetCurrentSubrouteIdx(size_t currentSubrouteIdx) { m_currentSubrouteIdx = currentSubrouteIdx; } template void SetSubroteAttrs(V && subroutes) { m_subrouteAttrs = std::forward(subroutes); } - uint32_t GetTotalTimeSec() const; - uint32_t GetCurrentTimeToEndSec() const; + /// \returns estimated time for the whole route. + double GetTotalTimeSec() const; + /// \returns estimated time to reach the route end. + double GetCurrentTimeToEndSec() const; FollowedPolyline const & GetFollowedPolyline() const { return m_poly; } string const & GetRouterId() const { return m_router; } m2::PolylineD const & GetPoly() const { return m_poly.GetPolyline(); } - TTurns const & GetTurns() const { return m_turns; } - feature::TAltitudes const & GetAltitudes() const { return m_altitudes; } - vector const & GetTraffic() const { return m_traffic; } + size_t GetCurrentSubrouteIdx() const { return m_currentSubrouteIdx; } vector const & GetSubroutes() const { return m_subrouteAttrs; } + vector const & GetSegDistanceMeters() const { return m_poly.GetSegDistanceM(); } bool IsValid() const { return (m_poly.GetPolyline().GetSize() > 1); } @@ -201,22 +204,19 @@ public: /// \brief GetCurrentTurn returns information about the nearest turn. /// \param distanceToTurnMeters is a distance from current position to the nearest turn. /// \param turn is information about the nearest turn. - bool GetCurrentTurn(double & distanceToTurnMeters, turns::TurnItem & turn) const; + void 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; + void GetCurrentStreetName(string & name) 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; + void GetStreetNameAfterIdx(uint32_t idx, string & name) 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. - /// @return true if its parameters are filled with correct result. /// \note All parameters are filled while a GetNextTurn function call. bool GetNextTurn(double & distanceToTurnMeters, turns::TurnItem & turn) const; /// \brief Extract information about zero, one or two nearest turns depending on current position. - /// @return true if its parameter is filled with correct result. (At least with one element.) bool GetNextTurns(vector & turns) const; void GetCurrentDirectionPoint(m2::PointD & pt) const; @@ -235,7 +235,6 @@ public: inline void SetRoutingSettings(RoutingSettings const & routingSettings) { m_routingSettings = routingSettings; - Update(); } // Subroute interface. @@ -268,35 +267,37 @@ public: /// after the route is removed. void SetSubrouteUid(size_t segmentIdx, SubrouteUid subrouteUid); + void GetAltitudes(feature::TAltitudes & altitudes) const; + bool HaveAltitudes() const { return m_haveAltitudes; } + traffic::SpeedGroup GetTraffic(size_t segmentIdx) const; + + void GetTurnsForTesting(vector & turns) const; + private: friend string DebugPrint(Route const & r); - /// Call this fucnction when geometry have changed. - void Update(); double GetPolySegAngle(size_t ind) const; - TTurns::const_iterator GetCurrentTurn() const; - TStreets::const_iterator GetCurrentStreetNameIterAfter(FollowedPolyline::Iter iter) const; + void GetClosestTurn(size_t segIdx, turns::TurnItem & turn) const; + size_t ConvertPointIdxToSegmentIdx(size_t pointIdx) const; - Junction GetJunction(size_t pointIdx) const; + /// \returns Estimated time to pass the route segment with |segIdx|. + double GetTimeToPassSegS(size_t segIdx) const; + /// \returns Length of the route segment with |segIdx| in meters. + double GetSegLenMeters(size_t segIdx) const; + /// \returns ETA to the last passed route point in seconds. + double GetETAToLastPassedPointS() const; string m_router; RoutingSettings m_routingSettings; string m_name; FollowedPolyline m_poly; - FollowedPolyline m_simplifiedPoly; set m_absentCountries; - - TTurns m_turns; - TTimes m_times; - TStreets m_streets; - feature::TAltitudes m_altitudes; - vector m_traffic; - std::vector m_routeSegments; - - mutable double m_currentTime; + // |m_haveAltitudes| == true if all route points have altitude information. + // |m_haveAltitudes| == false if at least one of route points don't have altitude information. + bool m_haveAltitudes = false; // Subroute SubrouteUid m_subrouteUid = kInvalidSubrouteId; diff --git a/routing/routing_helpers.cpp b/routing/routing_helpers.cpp index 90ac50ceb8..ee3c6f1bd0 100644 --- a/routing/routing_helpers.cpp +++ b/routing/routing_helpers.cpp @@ -100,8 +100,8 @@ void FillSegmentInfo(vector const & segments, vector const & void ReconstructRoute(IDirectionsEngine & engine, RoadGraphBase const & graph, shared_ptr const & trafficStash, - my::Cancellable const & cancellable, bool hasAltitude, - vector const & path, Route::TTimes && times, Route & route) + my::Cancellable const & cancellable, vector const & path, + Route::TTimes && times, Route & route) { if (path.empty()) { @@ -145,26 +145,6 @@ void ReconstructRoute(IDirectionsEngine & engine, RoadGraphBase const & graph, JunctionsToPoints(junctions, routeGeometry); route.SetGeometry(routeGeometry.begin(), routeGeometry.end()); - route.SetSectionTimes(move(times)); - route.SetTurnInstructions(move(turnsDir)); - route.SetStreetNames(move(streetNames)); - if (hasAltitude) - { - feature::TAltitudes altitudes; - JunctionsToAltitudes(junctions, altitudes); - route.SetAltitudes(move(altitudes)); - } - - vector traffic; - if (trafficStash && !segments.empty()) - { - traffic.reserve(segments.size()); - for (Segment const & seg : segments) - traffic.push_back(trafficStash->GetSpeedGroup(seg)); - CHECK_EQUAL(segments.size(), traffic.size(), ()); - } - - route.SetTraffic(move(traffic)); } Segment ConvertEdgeToSegment(NumMwmIds const & numMwmIds, Edge const & edge) diff --git a/routing/routing_helpers.hpp b/routing/routing_helpers.hpp index 5fdd8729f7..dc3c3fc2ec 100644 --- a/routing/routing_helpers.hpp +++ b/routing/routing_helpers.hpp @@ -36,8 +36,8 @@ void FillSegmentInfo(vector const & segments, vector const & void ReconstructRoute(IDirectionsEngine & engine, RoadGraphBase const & graph, shared_ptr const & trafficStash, - my::Cancellable const & cancellable, bool hasAltitude, - vector const & path, Route::TTimes && times, Route & route); + my::Cancellable const & cancellable, vector const & path, + Route::TTimes && times, Route & route); /// \brief Converts |edge| to |segment|. /// \returns false if mwm of |edge| is not alive. diff --git a/routing/routing_integration_tests/bicycle_route_test.cpp b/routing/routing_integration_tests/bicycle_route_test.cpp index 3d25f7482a..5a1a50a09d 100644 --- a/routing/routing_integration_tests/bicycle_route_test.cpp +++ b/routing/routing_integration_tests/bicycle_route_test.cpp @@ -4,6 +4,8 @@ #include "geometry/mercator.hpp" +#include "base/math.hpp" + using namespace routing; using namespace routing::turns; @@ -18,7 +20,7 @@ UNIT_TEST(RussiaMoscowNahimovskyLongRoute) { integration::CalculateRouteAndTestRouteLength( integration::GetBicycleComponents(), MercatorBounds::FromLatLon(55.66151, 37.63320), {0., 0.}, - MercatorBounds::FromLatLon(55.67695, 37.56220), 7570.0); + MercatorBounds::FromLatLon(55.67695, 37.56220), 5670.0); } UNIT_TEST(RussiaDomodedovoSteps) @@ -54,7 +56,7 @@ UNIT_TEST(NetherlandsAmsterdamBicycleYes) Route const & route = *routeResult.first; IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - TEST_EQUAL(route.GetTotalTimeSec(), 356, ()); + TEST(my::AlmostEqualAbs(route.GetTotalTimeSec(), 356.0, 1.0), ()); } UNIT_TEST(NetherlandsAmsterdamSingelStOnewayBicycleNo) diff --git a/routing/routing_integration_tests/osrm_route_test.cpp b/routing/routing_integration_tests/osrm_route_test.cpp index eafc0dc5ab..273698e649 100644 --- a/routing/routing_integration_tests/osrm_route_test.cpp +++ b/routing/routing_integration_tests/osrm_route_test.cpp @@ -302,7 +302,7 @@ namespace Route const & route = *routeResult.first; IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - TEST_LESS(route.GetTotalTimeSec(), numeric_limits::max() / 2, ()); + TEST_LESS(route.GetTotalTimeSec(), numeric_limits::max() / 2.0, ()); } // There are road ids in osrm which don't have appropriate features ids in mwm. diff --git a/routing/routing_integration_tests/pedestrian_route_test.cpp b/routing/routing_integration_tests/pedestrian_route_test.cpp index 9cde8eb530..f4ea654294 100644 --- a/routing/routing_integration_tests/pedestrian_route_test.cpp +++ b/routing/routing_integration_tests/pedestrian_route_test.cpp @@ -399,7 +399,8 @@ UNIT_TEST(RussiaZgradPanfilovskyUndergroundCrossing) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 3, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::Downstairs, ()); @@ -418,7 +419,8 @@ UNIT_TEST(RussiaMoscowHydroprojectBridgeCrossing) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 3, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::Upstairs, ()); @@ -437,7 +439,8 @@ UNIT_TEST(BelarusMinskRenaissanceHotelUndergroundCross) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 3, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::Downstairs, ()); @@ -456,7 +459,8 @@ UNIT_TEST(RussiaMoscowTrubnikovPereulok30Ac1LiftGate) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 2, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::LiftGate, ()); @@ -474,7 +478,8 @@ UNIT_TEST(RussiaMoscowKhlebnyyLane15c1Gate) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 2, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::Gate, ()); @@ -492,7 +497,8 @@ UNIT_TEST(RussiaMoscowKhlebnyyLane19LiftGateAndGate) IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); - auto const & t = route.GetTurns(); + vector t; + route.GetTurnsForTesting(t); TEST_EQUAL(t.size(), 3, ()); TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::LiftGate, ()); diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index 39335e4518..0656ab4a23 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -253,7 +253,9 @@ namespace integration void TestTurnCount(routing::Route const & route, uint32_t expectedTurnCount) { // We use -1 for ignoring the "ReachedYourDestination" turn record. - TEST_EQUAL(route.GetTurns().size() - 1, expectedTurnCount, ()); + vector turns; + route.GetTurnsForTesting(turns); + TEST_EQUAL(turns.size() - 1, expectedTurnCount, ()); } void TestCurrentStreetName(routing::Route const & route, string const & expectedStreetName) @@ -268,7 +270,7 @@ namespace integration string streetName; double distance; turns::TurnItem turn; - TEST(route.GetCurrentTurn(distance, turn), ()); + route.GetCurrentTurn(distance, turn); route.GetStreetNameAfterIdx(turn.m_index, streetName); TEST_EQUAL(streetName, expectedStreetName, ()); } @@ -343,7 +345,8 @@ namespace integration TestTurn GetNthTurn(routing::Route const & route, uint32_t turnNumber) { - Route::TTurns const & turns = route.GetTurns(); + vector turns; + route.GetTurnsForTesting(turns); if (turnNumber >= turns.size()) return TestTurn(); diff --git a/routing/routing_session.cpp b/routing/routing_session.cpp index 8344d1fceb..edbdf7ac25 100644 --- a/routing/routing_session.cpp +++ b/routing/routing_session.cpp @@ -44,7 +44,7 @@ size_t constexpr kSpeedCameraLookAheadCount = 50; double constexpr kCompletionPercentAccuracy = 5; -uint32_t constexpr kMinimumETASec = 60; +double constexpr kMinimumETASec = 60.0; } // namespace namespace routing @@ -299,7 +299,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const { info = FollowingInfo(); formatDistFn(m_route->GetTotalDistanceMeters(), info.m_distToTarget, info.m_targetUnitsSuffix); - info.m_time = max(kMinimumETASec, m_route->GetCurrentTimeToEndSec()); + info.m_time = static_cast(max(kMinimumETASec, m_route->GetCurrentTimeToEndSec())); return; } @@ -318,7 +318,7 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const info.m_nextTurn = routing::turns::TurnDirection::NoTurn; info.m_exitNum = turn.m_exitNum; - info.m_time = max(kMinimumETASec, m_route->GetCurrentTimeToEndSec()); + info.m_time = static_cast(max(kMinimumETASec, m_route->GetCurrentTimeToEndSec())); m_route->GetCurrentStreetName(info.m_sourceName); m_route->GetStreetNameAfterIdx(turn.m_index, info.m_targetName); info.m_completionPercent = GetCompletionPercent(); @@ -461,18 +461,8 @@ traffic::SpeedGroup RoutingSession::MatchTraffic( size_t const index = routeMatchingInfo.GetIndexInRoute(); threads::MutexGuard guard(m_routingSessionMutex); - vector const & traffic = m_route->GetTraffic(); - if (traffic.empty()) - return SpeedGroup::Unknown; - - if (index >= traffic.size()) - { - LOG(LERROR, ("Invalid index", index, "in RouteMatchingInfo, traffic.size():", traffic.size())); - return SpeedGroup::Unknown; - } - - return traffic[index]; + return m_route->GetTraffic(index); } bool RoutingSession::DisableFollowMode() @@ -620,7 +610,7 @@ bool RoutingSession::HasRouteAltitudeImpl() const { ASSERT(m_route, ()); - return m_route->GetAltitudes().size() == m_route->GetSegDistanceMeters().size() + 1; + return m_route->HaveAltitudes(); } bool RoutingSession::HasRouteAltitude() const @@ -639,7 +629,8 @@ bool RoutingSession::GetRouteAltitudesAndDistancesM(vector & routeSegDis return false; routeSegDistanceM = m_route->GetSegDistanceMeters(); - routeAltitudesM.assign(m_route->GetAltitudes().cbegin(), m_route->GetAltitudes().cend()); + feature::TAltitudes altitudes; + m_route->GetAltitudes(routeAltitudesM); return true; } diff --git a/routing/routing_tests/route_tests.cpp b/routing/routing_tests/route_tests.cpp index a61e7e8e87..ca4773745d 100644 --- a/routing/routing_tests/route_tests.cpp +++ b/routing/routing_tests/route_tests.cpp @@ -4,6 +4,8 @@ #include "routing/routing_helpers.hpp" #include "routing/turns.hpp" +#include "routing/base/followed_polyline.hpp" + #include "platform/location.hpp" #include "geometry/point2d.hpp" @@ -12,6 +14,7 @@ #include "std/vector.hpp" using namespace routing; +using namespace routing::turns; namespace { @@ -26,6 +29,38 @@ static Route::TStreets const kTestNames({{0, "Street1"}, {1, "Street2"}, {4, "St static Route::TTimes const kTestTimes({Route::TTimeItem(1, 5), Route::TTimeItem(3, 10), Route::TTimeItem(4, 15)}); +static Route::TTurns const kTestTurns2( + {turns::TurnItem(0, turns::TurnDirection::NoTurn), + turns::TurnItem(1, turns::TurnDirection::TurnLeft), + turns::TurnItem(2, turns::TurnDirection::TurnRight), + turns::TurnItem(3, turns::TurnDirection::NoTurn), + turns::TurnItem(4, turns::TurnDirection::ReachedYourDestination)}); +static vector const kTestNames2 = {"Street0", "Street1", "Street2", "", "Street3"}; +static vector const kTestTimes2 = {0.0, 5.0, 6.0, 10.0, 15.0}; + +void GetTestRouteSegments(vector const & routePoints, Route::TTurns const & turns, + vector const & streets, vector const & times, + vector & routeSegments) +{ + CHECK_EQUAL(routePoints.size(), turns.size(), ()); + CHECK_EQUAL(turns.size(), streets.size(), ()); + CHECK_EQUAL(turns.size(), times.size(), ()); + + FollowedPolyline poly(routePoints.cbegin(), routePoints.cend()); + + double routeLengthMeters = 0.0; + double routeLengthMertc = 0.0; + for (size_t i = 1; i < routePoints.size(); ++i) + { + routeLengthMeters += MercatorBounds::DistanceOnEarth(routePoints[i - 1], routePoints[i]); + routeLengthMertc += routePoints[i - 1].Length(routePoints[i]); + routeSegments.emplace_back( + Segment(0 /* mwm id */, static_cast(i) /* feature id */, 0 /* seg id */, true /* forward */), turns[i], + Junction(routePoints[i], feature::kInvalidAltitude), streets[i], routeLengthMeters, + routeLengthMertc, times[i], traffic::SpeedGroup::Unknown); + } +} + location::GpsInfo GetGps(double x, double y) { location::GpsInfo info; @@ -35,16 +70,6 @@ location::GpsInfo GetGps(double x, double y) info.m_speed = -1; return info; } - -void TestSegmentInfo(RouteSegment const & segmentInfo, turns::TurnDirection turn, - double distFromBeginningMerc, traffic::SpeedGroup speedGroup, - double timeFromBeginningS) -{ - TEST_EQUAL(segmentInfo.GetTurn().m_turn, turn, ()); - TEST_EQUAL(segmentInfo.GetDistFromBeginningMerc(), distFromBeginningMerc, ()); - TEST_EQUAL(segmentInfo.GetTraffic(), speedGroup, ()); - TEST_EQUAL(segmentInfo.GetTimeFromBeginningS(), timeFromBeginningS, ()); -} } // namespace UNIT_TEST(AddAdsentCountryToRouteTest) @@ -64,9 +89,12 @@ UNIT_TEST(AddAdsentCountryToRouteTest) UNIT_TEST(DistanceToCurrentTurnTest) { Route route("TestRouter"); + vector routeSegments; + GetTestRouteSegments(kTestGeometry, kTestTurns2, kTestNames2, kTestTimes2, routeSegments); route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end()); vector turns(kTestTurns); - route.SetTurnInstructions(move(turns)); + + route.SetRouteSegments(routeSegments); double distance; turns::TurnItem turn; @@ -99,9 +127,10 @@ UNIT_TEST(DistanceToCurrentTurnTest) UNIT_TEST(NextTurnTest) { Route route("TestRouter"); + vector routeSegments; + GetTestRouteSegments(kTestGeometry, kTestTurns2, kTestNames2, kTestTimes2, routeSegments); + route.SetRouteSegments(routeSegments); route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end()); - vector turns(kTestTurns); - route.SetTurnInstructions(move(turns)); double distance, nextDistance; turns::TurnItem turn; @@ -129,8 +158,11 @@ UNIT_TEST(NextTurnsTest) { Route route("TestRouter"); route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end()); + vector routeSegments; + GetTestRouteSegments(kTestGeometry, kTestTurns2, kTestNames2, kTestTimes2, routeSegments); + route.SetRouteSegments(routeSegments); + vector turns(kTestTurns); - route.SetTurnInstructions(move(turns)); vector turnsDist; { @@ -180,10 +212,9 @@ UNIT_TEST(RouteNameTest) Route route("TestRouter"); route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end()); - vector turns(kTestTurns); - route.SetTurnInstructions(move(turns)); - Route::TStreets names(kTestNames); - route.SetStreetNames(move(names)); + vector routeSegments; + GetTestRouteSegments(kTestGeometry, kTestTurns2, kTestNames2, kTestTimes2, routeSegments); + route.SetRouteSegments(routeSegments); string name; route.GetCurrentStreetName(name); @@ -193,49 +224,21 @@ UNIT_TEST(RouteNameTest) TEST_EQUAL(name, "Street1", ()); route.GetStreetNameAfterIdx(1, name); - TEST_EQUAL(name, "Street2", ()); + TEST_EQUAL(name, "Street1", ()); route.GetStreetNameAfterIdx(2, name); TEST_EQUAL(name, "Street2", ()); route.GetStreetNameAfterIdx(3, name); - TEST_EQUAL(name, "Street2", ()); + TEST_EQUAL(name, "Street3", ()); route.GetStreetNameAfterIdx(4, name); TEST_EQUAL(name, "Street3", ()); -} - -UNIT_TEST(GetSubrouteInfoTest) -{ - Route route("TestRouter"); - route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end()); - vector turns(kTestTurns); - route.SetTurnInstructions(move(turns)); - Route::TTimes times(kTestTimes); - route.SetSectionTimes(move(times)); - - vector junctions; - for (auto const & point : kTestGeometry) - junctions.emplace_back(point, feature::kDefaultAltitudeMeters); - - vector segmentInfo; - FillSegmentInfo(kTestSegments, junctions, kTestTurns, kTestNames, kTestTimes, - nullptr /* trafficStash */, segmentInfo); - route.SetRouteSegments(move(segmentInfo)); - route.SetSubroteAttrs(vector({Route::SubrouteAttrs( - junctions.front(), junctions.back(), 0 /* beginSegmentIdx */, kTestSegments.size())})); - - TEST_EQUAL(route.GetSubrouteCount(), 1, ()); - vector info; - route.GetSubrouteInfo(0, info); - TEST_EQUAL(info.size(), 4, ()); - - TestSegmentInfo(info[0], turns::TurnDirection::TurnLeft, 1.0 /* distFromBeginningMerc */, - traffic::SpeedGroup::Unknown, 5.0/* timeFromBeginningS */); - TestSegmentInfo(info[1], turns::TurnDirection::TurnRight, 2.0 /* distFromBeginningMerc */, - traffic::SpeedGroup::Unknown, 5.0/* timeFromBeginningS */); - TestSegmentInfo(info[2], turns::TurnDirection::NoTurn, 3.0 /* distFromBeginningMerc */, - traffic::SpeedGroup::Unknown, 10.0/* timeFromBeginningS */); - TestSegmentInfo(info[3], turns::TurnDirection::ReachedYourDestination, 4.0 /* distFromBeginningMerc */, - traffic::SpeedGroup::Unknown, 15.0/* timeFromBeginningS */); + + location::GpsInfo info; + info.m_longitude = 1.0; + info.m_latitude = 2.0; + route.MoveIterator(info); + route.GetCurrentStreetName(name); + TEST_EQUAL(name, "Street3", ()); }