diff --git a/map/gps_track_collection.cpp b/map/gps_track_collection.cpp index a141a30b78..a49d4c3caa 100644 --- a/map/gps_track_collection.cpp +++ b/map/gps_track_collection.cpp @@ -33,7 +33,6 @@ size_t const GpsTrackCollection::kInvalidId = std::numeric_limits::max() GpsTrackCollection::GpsTrackCollection() : m_lastId(0) - , m_statistics(TrackStatistics()) {} std::pair GpsTrackCollection::Add(std::vector const & items) diff --git a/map/map_tests/track_statistics_tests.cpp b/map/map_tests/track_statistics_tests.cpp index 1e2b3e0666..3842a9667d 100644 --- a/map/map_tests/track_statistics_tests.cpp +++ b/map/map_tests/track_statistics_tests.cpp @@ -182,4 +182,39 @@ UNIT_TEST(TrackStatistics_SmallAltitudeDelta) TEST_EQUAL(ts.m_ascent, 1.0, ()); TEST_EQUAL(ts.m_descent, 0, ()); } + +UNIT_TEST(TrackStatistics_MixedMultiGeometryAndGpsPoints) +{ + kml::MultiGeometry geometry; + auto const point1 = PointWithAltitude({0.0, 0.0}, 100); + auto const point2 = PointWithAltitude({1.0, 1.0}, 150); + auto const point3 = PointWithAltitude({2.0, 2.0}, 50); + geometry.AddLine({ + point1, + point2, + point3 + }); + geometry.AddTimestamps({ + 5, + 6, + 7 + }); + + auto ts = TrackStatistics(geometry); + + const std::vector points = { + BuildGpsInfo(3.0, 3.0, 60, 8), + BuildGpsInfo(4.0, 4.0, 160, 10), + BuildGpsInfo(4.0, 4.0, 20, 15) + }; + + for (auto const & point : points) + ts.AddGpsInfoPoint(point); + + TEST_EQUAL(ts.m_minElevation, 20, ()); + TEST_EQUAL(ts.m_maxElevation, 160, ()); + TEST_EQUAL(ts.m_ascent, 160, ()); // 50 + 10 + 100 + TEST_EQUAL(ts.m_descent, 240, ()); // 100 + 140 + TEST_EQUAL(ts.m_duration, 10, ()); // 10 +} } // namespace track_statistics_tests diff --git a/map/track_statistics.cpp b/map/track_statistics.cpp index ca0791a8fd..72a1e2616b 100644 --- a/map/track_statistics.cpp +++ b/map/track_statistics.cpp @@ -4,6 +4,8 @@ #include "base/logging.hpp" using namespace geometry; +using namespace location; +using namespace kml; double constexpr kInvalidTimestamp = std::numeric_limits::min(); PointWithAltitude const kInvalidPoint = {m2::PointD::Zero(), kInvalidAltitude}; @@ -19,58 +21,63 @@ TrackStatistics::TrackStatistics() m_previousTimestamp(kInvalidTimestamp) {} -TrackStatistics::TrackStatistics(kml::MultiGeometry const & geometry) +TrackStatistics::TrackStatistics(MultiGeometry const & geometry) : TrackStatistics() { for (auto const & line : geometry.m_lines) - AddPoints(line, true); + AddPoints(line); if (geometry.HasTimestamps()) { for (size_t i = 0; i < geometry.m_timestamps.size(); ++i) { ASSERT(geometry.HasTimestampsFor(i), ()); - AddTimestamps(geometry.m_timestamps[i], true); + AddTimestamps(geometry.m_timestamps[i]); } } } -void TrackStatistics::AddGpsInfoPoint(location::GpsInfo const & point) +void TrackStatistics::AddGpsInfoPoint(GpsInfo const & point) { auto const pointWithAltitude = geometry::PointWithAltitude(mercator::FromLatLon(point.m_latitude, point.m_longitude), point.m_altitude); - - AddPoints({pointWithAltitude}, false); - AddTimestamps({point.m_timestamp}, false); -} - -void TrackStatistics::AddPoints(kml::MultiGeometry::LineT const & line, bool isNewSegment) -{ - if (line.empty()) - return; - - size_t startIndex = 0; - if (HasNoPoints() || isNewSegment) + auto const altitude = Altitude(point.m_altitude); + if (HasNoPoints()) { - InitializeNewSegment(line[0]); - startIndex = 1; + m_minElevation = altitude; + m_maxElevation = altitude; + m_previousPoint = pointWithAltitude; + m_previousTimestamp = point.m_timestamp; + return; } - ProcessPoints(line, startIndex); + m_minElevation = std::min(m_minElevation, altitude); + m_maxElevation = std::max(m_maxElevation, altitude); + + auto const deltaAltitude = altitude - m_previousPoint.GetAltitude(); + if (deltaAltitude > 0) + m_ascent += deltaAltitude; + else + m_descent -= deltaAltitude; + m_length += mercator::DistanceOnEarth(m_previousPoint.GetPoint(), pointWithAltitude.GetPoint()); + m_duration += point.m_timestamp - m_previousTimestamp; + + m_previousPoint = pointWithAltitude; + m_previousTimestamp = point.m_timestamp; } -void TrackStatistics::InitializeNewSegment(PointWithAltitude const & firstPoint) +void TrackStatistics::AddPoints(Points const & points) { - auto const altitude = firstPoint.GetAltitude(); + if (points.empty()) + return; bool const hasNoPoints = HasNoPoints(); + auto const & firstPoint = points[0]; + auto const altitude = firstPoint.GetAltitude(); + m_minElevation = hasNoPoints ? altitude : std::min(m_minElevation, altitude); m_maxElevation = hasNoPoints ? altitude : std::max(m_maxElevation, altitude); - m_previousPoint = firstPoint; -} -void TrackStatistics::ProcessPoints(kml::MultiGeometry::LineT const & points, size_t startIndex) -{ - for (size_t i = startIndex; i < points.size(); ++i) + for (size_t i = 1; i < points.size(); ++i) { auto const & point = points[i]; auto const pointAltitude = point.GetAltitude(); @@ -89,16 +96,11 @@ void TrackStatistics::ProcessPoints(kml::MultiGeometry::LineT const & points, si } } -void TrackStatistics::AddTimestamps(kml::MultiGeometry::TimeT const & timestamps, bool isNewSegment) +void TrackStatistics::AddTimestamps(Timestamps const & timestamps) { if (timestamps.empty()) return; - - if (m_previousTimestamp == kInvalidTimestamp) - m_previousTimestamp = timestamps.front(); - - auto const baseTimestamp = isNewSegment ? timestamps.front() : m_previousTimestamp; - m_duration += timestamps.back() - baseTimestamp; + m_duration += timestamps.back() - timestamps.front(); m_previousTimestamp = timestamps.back(); } diff --git a/map/track_statistics.hpp b/map/track_statistics.hpp index bd512f4d58..006a4e217f 100644 --- a/map/track_statistics.hpp +++ b/map/track_statistics.hpp @@ -6,6 +6,9 @@ struct TrackStatistics { + using Points = kml::MultiGeometry::LineT; + using Timestamps = kml::MultiGeometry::TimeT; + TrackStatistics(); explicit TrackStatistics(kml::MultiGeometry const & geometry); @@ -13,15 +16,13 @@ struct TrackStatistics double m_duration; double m_ascent; double m_descent; - int16_t m_minElevation; - int16_t m_maxElevation; + geometry::Altitude m_minElevation; + geometry::Altitude m_maxElevation; void AddGpsInfoPoint(location::GpsInfo const & point); private: - void AddPoints(kml::MultiGeometry::LineT const & line, bool isNewSegment); - void AddTimestamps(kml::MultiGeometry::TimeT const & timestamps, bool isNewSegment); - void InitializeNewSegment(geometry::PointWithAltitude const & firstPoint); - void ProcessPoints(kml::MultiGeometry::LineT const & points, size_t startIndex); + void AddPoints(Points const & points); + void AddTimestamps(Timestamps const & timestamps); bool HasNoPoints() const; geometry::PointWithAltitude m_previousPoint;