diff --git a/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo+Core.h b/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo+Core.h index ea22fc34b5..a68ce30cbc 100644 --- a/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo+Core.h +++ b/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo+Core.h @@ -1,14 +1,10 @@ #import "TrackInfo.h" #include -#include "map/gps_track_collection.hpp" -#include "map/elevation_info.hpp" +#include "map/track_statistics.hpp" @interface TrackInfo (Core) -- (instancetype)initWithGpsTrackInfo:(GpsTrackInfo const &)info; -- (instancetype)initWithDistance:(double)distance duration:(double)duration; - -- (void)setElevationInfo:(ElevationInfo const &)elevationInfo; +- (instancetype)initWithTrackStatistics:(TrackStatistics const &)statistics; @end diff --git a/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo.mm b/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo.mm index 592fd73793..a6fa375979 100644 --- a/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo.mm +++ b/iphone/CoreApi/CoreApi/Bookmarks/TrackInfo.mm @@ -3,8 +3,6 @@ #import "DistanceFormatter.h" #import "DurationFormatter.h" -#include "map/elevation_info.hpp" - @implementation TrackInfo - (BOOL)hasElevationInfo { @@ -19,31 +17,16 @@ @implementation TrackInfo (Core) -- (instancetype)initWithGpsTrackInfo:(GpsTrackInfo const &)trackInfo { +- (instancetype)initWithTrackStatistics:(TrackStatistics const &)statistics { if (self = [super init]) { - _distance = trackInfo.m_length; - _duration = trackInfo.m_duration; - _ascent = trackInfo.m_ascent; - _descent = trackInfo.m_descent; - _maxElevation = trackInfo.m_maxElevation; - _minElevation = trackInfo.m_minElevation; + _distance = statistics.m_length; + _duration = statistics.m_duration; + _ascent = statistics.m_ascent; + _descent = statistics.m_descent; + _maxElevation = statistics.m_maxElevation; + _minElevation = statistics.m_minElevation; } return self; } -- (instancetype)initWithDistance:(double)distance duration:(double)duration { - if (self = [super init]) { - _distance = distance; - _duration = duration; - } - return self; -} - -- (void)setElevationInfo:(ElevationInfo const &)elevationInfo { - _ascent = elevationInfo.GetAscent(); - _descent = elevationInfo.GetDescent(); - _maxElevation = elevationInfo.GetMaxAltitude(); - _minElevation = elevationInfo.GetMinAltitude(); -} - @end diff --git a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm index f71be86ac1..d00c60df64 100644 --- a/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm +++ b/iphone/CoreApi/CoreApi/Framework/MWMFrameworkHelper.mm @@ -224,8 +224,8 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore( GetFramework().SetTrackRecordingUpdateHandler(nullptr); return; } - GetFramework().SetTrackRecordingUpdateHandler([trackRecordingDidUpdate](GpsTrackInfo const & gpsTrackInfo) { - TrackInfo * info = [[TrackInfo alloc] initWithGpsTrackInfo:gpsTrackInfo]; + GetFramework().SetTrackRecordingUpdateHandler([trackRecordingDidUpdate](TrackStatistics const & statistics) { + TrackInfo * info = [[TrackInfo alloc] initWithTrackStatistics:statistics]; trackRecordingDidUpdate(info); }); } diff --git a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageTrackData.mm b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageTrackData.mm index c75681e772..ad421d8f16 100644 --- a/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageTrackData.mm +++ b/iphone/CoreApi/CoreApi/PlacePageData/Common/PlacePageTrackData.mm @@ -12,11 +12,9 @@ self = [super init]; if (self) { _trackId = track.GetData().m_id; - _trackInfo = [[TrackInfo alloc] initWithDistance:track.GetLengthMeters() - duration:track.GetDurationInSeconds()]; + _trackInfo = [[TrackInfo alloc] initWithTrackStatistics:track.GetStatistics()]; auto const & elevationInfo = track.GetElevationInfo(); if (track.HasAltitudes() && elevationInfo.has_value()) { - [_trackInfo setElevationInfo:elevationInfo.value()]; auto const & bm = GetFramework().GetBookmarkManager(); _elevationProfileData = [[ElevationProfileData alloc] initWithTrackId:_trackId elevationInfo:elevationInfo.value() diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index 2bd9dc7c6a..856e778d9b 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -57,6 +57,8 @@ set(SRC search_product_info.hpp track.cpp track.hpp + track_statistics.cpp + track_statistics.hpp track_mark.cpp track_mark.hpp traffic_manager.cpp diff --git a/map/elevation_info.cpp b/map/elevation_info.cpp index d057970b89..481d161db4 100644 --- a/map/elevation_info.cpp +++ b/map/elevation_info.cpp @@ -4,54 +4,60 @@ #include "geometry/mercator.hpp" -ElevationInfo::ElevationInfo(kml::MultiGeometry const & geometry) -{ - double distance = 0; - // Concatenate all segments. - for (size_t lineIndex = 0; lineIndex < geometry.m_lines.size(); ++lineIndex) - { - auto const & line = geometry.m_lines[lineIndex]; - if (line.empty()) - { - LOG(LWARNING, ("Empty line in elevation info")); - continue; - } +using namespace geometry; +using namespace mercator; - if (lineIndex == 0) - { - m_minAltitude = line.front().GetAltitude(); - m_maxAltitude = m_minAltitude; - } +ElevationInfo::ElevationInfo(std::vector const & lines) +{ + // Concatenate all segments. + for (size_t lineIndex = 0; lineIndex < lines.size(); ++lineIndex) + { + auto const & line = lines[lineIndex]; + if (line.empty()) + continue; if (lineIndex > 0) - m_segmentsDistances.emplace_back(distance); + m_segmentsDistances.emplace_back(m_points.back().m_distance); - for (size_t pointIndex = 0; pointIndex < line.size(); ++pointIndex) - { - auto const & currentPoint = line[pointIndex]; - auto const & currentPointAltitude = currentPoint.GetAltitude(); - if (currentPointAltitude < m_minAltitude) - m_minAltitude = currentPointAltitude; - if (currentPointAltitude > m_maxAltitude) - m_maxAltitude = currentPointAltitude; - - if (pointIndex == 0) - { - m_points.emplace_back(currentPoint, distance); - continue; - } - - auto const & previousPoint = line[pointIndex - 1]; - distance += mercator::DistanceOnEarth(previousPoint.GetPoint(), currentPoint.GetPoint()); - m_points.emplace_back(currentPoint, distance); - - auto const deltaAltitude = currentPointAltitude - previousPoint.GetAltitude(); - if (deltaAltitude > 0) - m_ascent += deltaAltitude; - else - m_descent -= deltaAltitude; - } + AddPoints(line, true /* new segment */); } /// @todo(KK) Implement difficulty calculation. m_difficulty = Difficulty::Unknown; } + +void ElevationInfo::AddGpsPoints(GpsPoints const & points) +{ + GeometryLine line; + line.reserve(points.size()); + for (auto const & point : points) + line.emplace_back(FromLatLon(point.m_latitude, point.m_longitude), point.m_altitude); + AddPoints(line); +} + +void ElevationInfo::AddPoints(GeometryLine const & line, bool isNewSegment) +{ + if (line.empty()) + return; + + double distance = m_points.empty() ? 0 : m_points.back().m_distance; + for (size_t pointIndex = 0; pointIndex < line.size(); ++pointIndex) + { + auto const & point = line[pointIndex]; + + if (m_points.empty()) + { + m_points.emplace_back(point, distance); + continue; + } + + if (isNewSegment && pointIndex == 0) + { + m_points.emplace_back(point, distance); + continue; + } + + auto const & previousPoint = m_points.back().m_point; + distance += mercator::DistanceOnEarth(previousPoint.GetPoint(), point.GetPoint()); + m_points.emplace_back(point, distance); + } +} diff --git a/map/elevation_info.hpp b/map/elevation_info.hpp index 9291d8ff28..e80fd2fefb 100644 --- a/map/elevation_info.hpp +++ b/map/elevation_info.hpp @@ -5,6 +5,8 @@ #include "geometry/point_with_altitude.hpp" #include "geometry/latlon.hpp" +#include "platform/location.hpp" + #include #include #include @@ -22,6 +24,8 @@ public: }; using Points = std::vector; + using GpsPoints = std::vector; + using GeometryLine = kml::MultiGeometry::LineT; using SegmentsDistances = std::vector; enum Difficulty : uint8_t @@ -33,31 +37,23 @@ public: }; ElevationInfo() = default; - explicit ElevationInfo(kml::MultiGeometry const & geometry); + explicit ElevationInfo(std::vector const & lines); + + void AddGpsPoints(GpsPoints const & points); size_t GetSize() const { return m_points.size(); }; Points const & GetPoints() const { return m_points; }; - uint32_t GetAscent() const { return m_ascent; } - uint32_t GetDescent() const { return m_descent; } - geometry::Altitude GetMinAltitude() const { return m_minAltitude; } - geometry::Altitude GetMaxAltitude() const { return m_maxAltitude; } uint8_t GetDifficulty() const { return m_difficulty; } SegmentsDistances const & GetSegmentsDistances() const { return m_segmentsDistances; }; private: // Points with distance from start of the track and altitude. Points m_points; - // Ascent in meters. - uint32_t m_ascent = 0; - // Descent in meters. - uint32_t m_descent = 0; - // Altitude in meters. - geometry::Altitude m_minAltitude = 0; - // Altitude in meters. - geometry::Altitude m_maxAltitude = 0; // Some digital difficulty level with value in range [0-kMaxDifficulty] // or kInvalidDifficulty when difficulty is not found or incorrect. Difficulty m_difficulty = Difficulty::Unknown; // Distances to the start of each segment. SegmentsDistances m_segmentsDistances; + + void AddPoints(GeometryLine const & line, bool isNewSegment = false); }; diff --git a/map/framework.cpp b/map/framework.cpp index 09ddd25f3e..b20207f3ff 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1736,7 +1736,7 @@ void Framework::SetTrackRecordingUpdateHandler(TrackRecordingUpdateHandler && tr { m_trackRecordingUpdateHandler = std::move(trackRecordingDidUpdate); if (m_trackRecordingUpdateHandler) - m_trackRecordingUpdateHandler(GpsTracker::Instance().GetTrackInfo()); + m_trackRecordingUpdateHandler(GpsTracker::Instance().GetTrackStatistics()); } void Framework::StopTrackRecording() @@ -1768,7 +1768,7 @@ bool Framework::IsTrackRecordingEnabled() const void Framework::OnUpdateGpsTrackPointsCallback(vector> && toAdd, pair const & toRemove, - GpsTrackInfo const & trackInfo) + TrackStatistics const & trackStatistics) { ASSERT(m_drapeEngine.get() != nullptr, ()); @@ -1797,7 +1797,7 @@ void Framework::OnUpdateGpsTrackPointsCallback(vectorUpdateGpsTrackPoints(std::move(pointsAdd), std::move(indicesRemove)); if (m_trackRecordingUpdateHandler) - m_trackRecordingUpdateHandler(trackInfo); + m_trackRecordingUpdateHandler(trackStatistics); } void Framework::MarkMapStyle(MapStyle mapStyle) diff --git a/map/framework.hpp b/map/framework.hpp index da68948ae9..16dd6a0217 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -15,9 +15,9 @@ #include "map/search_api.hpp" #include "map/search_mark.hpp" #include "map/track.hpp" +#include "map/track_statistics.hpp" #include "map/traffic_manager.hpp" #include "map/transit/transit_reader.hpp" -#include "map/gps_track_collection.hpp" #include "drape_frontend/gui/skin.hpp" #include "drape_frontend/drape_api.hpp" @@ -437,7 +437,7 @@ public: void ConnectToGpsTracker(); void DisconnectFromGpsTracker(); - using TrackRecordingUpdateHandler = platform::SafeCallback; + using TrackRecordingUpdateHandler = platform::SafeCallback; void StartTrackRecording(); void SetTrackRecordingUpdateHandler(TrackRecordingUpdateHandler && trackRecordingDidUpdate); void StopTrackRecording(); @@ -468,7 +468,7 @@ private: void OnUpdateGpsTrackPointsCallback(std::vector> && toAdd, std::pair const & toRemove, - GpsTrackInfo const & trackInfo); + TrackStatistics const & trackStatistics); TrackRecordingUpdateHandler m_trackRecordingUpdateHandler; diff --git a/map/gps_track.cpp b/map/gps_track.cpp index c808753cda..9392263d69 100644 --- a/map/gps_track.cpp +++ b/map/gps_track.cpp @@ -79,9 +79,9 @@ void GpsTrack::AddPoints(vector const & points) ScheduleTask(); } -GpsTrackInfo GpsTrack::GetTrackInfo() const +TrackStatistics GpsTrack::GetTrackStatistics() const { - return m_collection ? m_collection->GetTrackInfo() : GpsTrackInfo(); + return m_collection ? m_collection->GetTrackStatistics() : TrackStatistics(); } void GpsTrack::Clear() @@ -303,7 +303,7 @@ void GpsTrack::NotifyCallback(pair const & addedIds, pairGetTrackInfo()); + m_callback(std::move(toAdd), make_pair(kInvalidId, kInvalidId), m_collection->GetTrackStatistics()); } else { @@ -324,6 +324,6 @@ void GpsTrack::NotifyCallback(pair const & addedIds, pairGetTrackInfo()); + m_callback(std::move(toAdd), evictedIds, m_collection->GetTrackStatistics()); } } diff --git a/map/gps_track.hpp b/map/gps_track.hpp index 6ba0ba138f..494ab7c0fa 100644 --- a/map/gps_track.hpp +++ b/map/gps_track.hpp @@ -31,7 +31,7 @@ public: void AddPoints(std::vector const & points); /// Returns track statistics - GpsTrackInfo GetTrackInfo() const; + TrackStatistics GetTrackStatistics() const; /// Clears any previous tracking info /// @note Callback is called with 'toRemove' points, if some points were removed. @@ -47,7 +47,7 @@ public: using TGpsTrackDiffCallback = std::function> && toAdd, std::pair const & toRemove, - GpsTrackInfo const & trackInfo)>; + TrackStatistics const & trackStatistics)>; /// Sets callback on change of gps track. /// @param callback - callback callable object diff --git a/map/gps_track_collection.cpp b/map/gps_track_collection.cpp index f39c2e964c..a141a30b78 100644 --- a/map/gps_track_collection.cpp +++ b/map/gps_track_collection.cpp @@ -2,8 +2,6 @@ #include "base/assert.hpp" -#include "geometry/distance_on_sphere.hpp" - #include namespace @@ -35,7 +33,7 @@ size_t const GpsTrackCollection::kInvalidId = std::numeric_limits::max() GpsTrackCollection::GpsTrackCollection() : m_lastId(0) - , m_trackInfo(GpsTrackInfo()) + , m_statistics(TrackStatistics()) {} std::pair GpsTrackCollection::Add(std::vector const & items) @@ -51,26 +49,7 @@ std::pair GpsTrackCollection::Add(std::vector const & ite if (!m_items.empty() && m_items.back().m_timestamp > item.m_timestamp) continue; - if (m_items.empty()) - { - m_trackInfo.m_maxElevation = item.m_altitude; - m_trackInfo.m_minElevation = item.m_altitude; - } - else - { - auto const & lastItem = m_items.back(); - m_trackInfo.m_length += ms::DistanceOnEarth(lastItem.GetLatLon(), item.GetLatLon()); - m_trackInfo.m_duration = item.m_timestamp - m_items.front().m_timestamp; - - auto const deltaAltitude = item.m_altitude - lastItem.m_altitude; - if (item.m_altitude > lastItem.m_altitude) - m_trackInfo.m_ascent += deltaAltitude; - if (item.m_altitude < lastItem.m_altitude) - m_trackInfo.m_descent -= deltaAltitude; - - m_trackInfo.m_maxElevation = std::max(static_cast(m_trackInfo.m_maxElevation), item.m_altitude); - m_trackInfo.m_minElevation = std::min(static_cast(m_trackInfo.m_minElevation), item.m_altitude); - } + m_statistics.AddGpsInfoPoint(item); m_items.emplace_back(item); ++added; @@ -106,7 +85,7 @@ std::pair GpsTrackCollection::Clear(bool resetIds) m_items.clear(); m_items.shrink_to_fit(); - m_trackInfo = {}; + m_statistics = {}; if (resetIds) m_lastId = 0; diff --git a/map/gps_track_collection.hpp b/map/gps_track_collection.hpp index 140459e729..f1e91dc87f 100644 --- a/map/gps_track_collection.hpp +++ b/map/gps_track_collection.hpp @@ -2,21 +2,14 @@ #include "platform/location.hpp" +#include "map/track_statistics.hpp" +#include "map/elevation_info.hpp" + #include #include #include #include -struct GpsTrackInfo -{ - double m_length; - double m_duration; - uint32_t m_ascent; - uint32_t m_descent; - int16_t m_minElevation; - int16_t m_maxElevation; -}; - class GpsTrackCollection final { public: @@ -46,7 +39,8 @@ public: /// Returns number of items in the collection size_t GetSize() const; - GpsTrackInfo GetTrackInfo() const { return m_trackInfo; } + /// Returns track statistics. + const TrackStatistics GetTrackStatistics() const { return m_statistics; } /// Enumerates items in the collection. /// @param f - callable object, which is called with params - item and item id, @@ -72,5 +66,5 @@ private: std::deque m_items; // asc. sorted by timestamp size_t m_lastId; - GpsTrackInfo m_trackInfo; + TrackStatistics m_statistics; }; diff --git a/map/gps_tracker.cpp b/map/gps_tracker.cpp index 2d33dbcf8d..23080815ed 100644 --- a/map/gps_tracker.cpp +++ b/map/gps_tracker.cpp @@ -75,6 +75,11 @@ size_t GpsTracker::GetTrackSize() const return m_track.GetSize(); } +TrackStatistics GpsTracker::GetTrackStatistics() const +{ + return m_track.GetTrackStatistics(); +} + void GpsTracker::Connect(TGpsTrackDiffCallback const & fn) { m_track.SetCallback(fn); diff --git a/map/gps_tracker.hpp b/map/gps_tracker.hpp index 38ff7915c2..8cce4eab0f 100644 --- a/map/gps_tracker.hpp +++ b/map/gps_tracker.hpp @@ -17,12 +17,12 @@ public: bool IsEmpty() const; size_t GetTrackSize() const; - GpsTrackInfo GetTrackInfo() const { return m_track.GetTrackInfo(); } + TrackStatistics GetTrackStatistics() const; using TGpsTrackDiffCallback = std::function> && toAdd, std::pair const & toRemove, - GpsTrackInfo const & trackInfo)>; + TrackStatistics const & trackStatistics)>; void Connect(TGpsTrackDiffCallback const & fn); void Disconnect(); diff --git a/map/map_tests/CMakeLists.txt b/map/map_tests/CMakeLists.txt index c79a222283..fcfc0b46f3 100644 --- a/map/map_tests/CMakeLists.txt +++ b/map/map_tests/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC transliteration_test.cpp working_time_tests.cpp elevation_info_tests.cpp + track_statistics_tests.cpp ) omim_add_test(${PROJECT_NAME} ${SRC} REQUIRE_QT REQUIRE_SERVER) diff --git a/map/map_tests/elevation_info_tests.cpp b/map/map_tests/elevation_info_tests.cpp index 9188c1065b..fdd138bf6c 100644 --- a/map/map_tests/elevation_info_tests.cpp +++ b/map/map_tests/elevation_info_tests.cpp @@ -7,18 +7,24 @@ #include "kml/types.hpp" -namespace geometry +namespace elevation_info_tests { using namespace geometry; +using namespace location; + +GpsInfo const BuildGpsInfo(double latitude, double longitude, double altitude) +{ + GpsInfo gpsInfo; + gpsInfo.m_latitude = latitude; + gpsInfo.m_longitude = longitude; + gpsInfo.m_altitude = altitude; + return gpsInfo; +} UNIT_TEST(ElevationInfo_EmptyMultiGeometry) { ElevationInfo ei; TEST_EQUAL(0, ei.GetSize(), ()); - TEST_EQUAL(0, ei.GetAscent(), ()); - TEST_EQUAL(0, ei.GetDescent(), ()); - TEST_EQUAL(ei.GetMinAltitude(), kDefaultAltitudeMeters, ()); - TEST_EQUAL(ei.GetMaxAltitude(), kDefaultAltitudeMeters, ()); } UNIT_TEST(ElevationInfo_FromMultiGeometry) @@ -32,13 +38,9 @@ UNIT_TEST(ElevationInfo_FromMultiGeometry) point2, point3 }); - ElevationInfo ei(geometry); + ElevationInfo ei(geometry.m_lines); TEST_EQUAL(3, ei.GetSize(), ()); - TEST_EQUAL(ei.GetMinAltitude(), 50, ()); - TEST_EQUAL(ei.GetMaxAltitude(), 150, ()); - TEST_EQUAL(ei.GetAscent(), 50, ()); // Ascent from 100 -> 150 - TEST_EQUAL(ei.GetDescent(), 100, ()); // Descent from 150 -> 50 double distance = 0; TEST_EQUAL(ei.GetPoints()[0].m_distance, distance, ()); @@ -48,23 +50,6 @@ UNIT_TEST(ElevationInfo_FromMultiGeometry) TEST_EQUAL(ei.GetPoints()[2].m_distance, distance, ()); } -UNIT_TEST(ElevationInfo_NoAltitudePoints) -{ - kml::MultiGeometry geometry; - geometry.AddLine({ - PointWithAltitude({0.0, 0.0}), - PointWithAltitude({1.0, 1.0}), - PointWithAltitude({2.0, 2.0}) - }); - ElevationInfo ei(geometry); - - TEST_EQUAL(3, ei.GetSize(), ()); - TEST_EQUAL(ei.GetMinAltitude(), kDefaultAltitudeMeters, ()); - TEST_EQUAL(ei.GetMaxAltitude(), kDefaultAltitudeMeters, ()); - TEST_EQUAL(ei.GetAscent(), 0, ()); - TEST_EQUAL(ei.GetDescent(), 0, ()); -} - UNIT_TEST(ElevationInfo_MultipleLines) { kml::MultiGeometry geometry; @@ -82,32 +67,28 @@ UNIT_TEST(ElevationInfo_MultipleLines) PointWithAltitude({4.0, 4.0}, 200), PointWithAltitude({5.0, 5.0}, 250) }); - ElevationInfo ei(geometry); + ElevationInfo ei(geometry.m_lines); TEST_EQUAL(8, ei.GetSize(), ()); - TEST_EQUAL(ei.GetMinAltitude(), 50, ()); - TEST_EQUAL(ei.GetMaxAltitude(), 250, ()); - TEST_EQUAL(ei.GetAscent(), 125, ()); // Ascent from 100 -> 150, 50 -> 75, 200 -> 250 - TEST_EQUAL(ei.GetDescent(), 25, ()); // Descent from 150 -> 140, 75 -> 60 } UNIT_TEST(ElevationInfo_SegmentDistances) { kml::MultiGeometry geometry; geometry.AddLine({ - geometry::PointWithAltitude({0.0, 0.0}), - geometry::PointWithAltitude({1.0, 0.0}) + PointWithAltitude({0.0, 0.0}), + PointWithAltitude({1.0, 0.0}) }); geometry.AddLine({ - geometry::PointWithAltitude({2.0, 0.0}), - geometry::PointWithAltitude({3.0, 0.0}) + PointWithAltitude({2.0, 0.0}), + PointWithAltitude({3.0, 0.0}) }); geometry.AddLine({ - geometry::PointWithAltitude({4.0, 0.0}), - geometry::PointWithAltitude({5.0, 0.0}) + PointWithAltitude({4.0, 0.0}), + PointWithAltitude({5.0, 0.0}) }); - ElevationInfo ei(geometry); + ElevationInfo ei(geometry.m_lines); auto const & segmentDistances = ei.GetSegmentsDistances(); auto const points = ei.GetPoints(); @@ -116,21 +97,23 @@ UNIT_TEST(ElevationInfo_SegmentDistances) TEST_EQUAL(segmentDistances[1], ei.GetPoints()[4].m_distance, ()); } -UNIT_TEST(ElevationInfo_PositiveAndNegativeAltitudes) +UNIT_TEST(ElevationInfo_BuildWithGpsPoints) { - kml::MultiGeometry geometry; - geometry.AddLine({ - PointWithAltitude({0.0, 0.0}, -10), - PointWithAltitude({1.0, 1.0}, 20), - PointWithAltitude({2.0, 2.0}, -5), - PointWithAltitude({3.0, 3.0}, 15) + auto ei = ElevationInfo(); + ei.AddGpsPoints({ + BuildGpsInfo(0.0, 0.0, 0), + BuildGpsInfo(1.0, 1.0, 50), + BuildGpsInfo(2.0, 2.0, 100), }); - ElevationInfo ei(geometry); + ei.AddGpsPoints({ + BuildGpsInfo(3.0, 3.0, -50) + }); + ei.AddGpsPoints({ + BuildGpsInfo(4.0, 4.0, 0) + }); + ei.AddGpsPoints({}); - TEST_EQUAL(4, ei.GetSize(), ()); - TEST_EQUAL(ei.GetMinAltitude(), -10, ()); - TEST_EQUAL(ei.GetMaxAltitude(), 20, ()); - TEST_EQUAL(ei.GetAscent(), 50, ()); // Ascent from -10 -> 20 and -5 -> 15 - TEST_EQUAL(ei.GetDescent(), 25, ()); // Descent from 20 -> -5 + TEST_EQUAL(5, ei.GetSize(), ()); + TEST_EQUAL(ei.GetSegmentsDistances().size(), 0, ()); } -} // namespace geometry +} // namespace elevation_info_testa diff --git a/map/map_tests/track_statistics_tests.cpp b/map/map_tests/track_statistics_tests.cpp new file mode 100644 index 0000000000..1e2b3e0666 --- /dev/null +++ b/map/map_tests/track_statistics_tests.cpp @@ -0,0 +1,185 @@ +#include "testing/testing.hpp" + +#include "map/track_statistics.hpp" + +#include "geometry/point_with_altitude.hpp" +#include "geometry/mercator.hpp" + +#include "kml/types.hpp" + +namespace track_statistics_tests +{ +using namespace geometry; +using namespace location; + +GpsInfo const BuildGpsInfo(double latitude, double longitude, double altitude, double timestamp = 0) +{ + GpsInfo gpsInfo; + gpsInfo.m_latitude = latitude; + gpsInfo.m_longitude = longitude; + gpsInfo.m_altitude = altitude; + gpsInfo.m_timestamp = timestamp; + return gpsInfo; +} + +UNIT_TEST(TrackStatistics_EmptyMultiGeometry) +{ + TrackStatistics ts; + TEST_EQUAL(0, ts.m_ascent, ()); + TEST_EQUAL(0, ts.m_descent, ()); + TEST_EQUAL(ts.m_minElevation, kDefaultAltitudeMeters, ()); + TEST_EQUAL(ts.m_maxElevation, kDefaultAltitudeMeters, ()); +} + +UNIT_TEST(TrackStatistics_FromMultiGeometry) +{ + 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({ + 0, + 1, + 2 + }); + auto const ts = TrackStatistics(geometry); + TEST_EQUAL(ts.m_minElevation, 50, ()); + TEST_EQUAL(ts.m_maxElevation, 150, ()); + TEST_EQUAL(ts.m_ascent, 50, ()); // Ascent from 100 -> 150 + TEST_EQUAL(ts.m_descent, 100, ()); // Descent from 150 -> 50 + TEST_EQUAL(ts.m_duration, 2, ()); + + double distance = 0; + distance += mercator::DistanceOnEarth(point1, point2); + distance += mercator::DistanceOnEarth(point2, point3); + TEST_EQUAL(ts.m_length, distance, ()); +} + +UNIT_TEST(TrackStatistics_NoAltitudeAndTimestampPoints) +{ + kml::MultiGeometry geometry; + geometry.AddLine({ + PointWithAltitude({0.0, 0.0}), + PointWithAltitude({1.0, 1.0}), + PointWithAltitude({2.0, 2.0}) + }); + + auto const ts = TrackStatistics(geometry); + + TEST_EQUAL(ts.m_minElevation, kDefaultAltitudeMeters, ()); + TEST_EQUAL(ts.m_maxElevation, kDefaultAltitudeMeters, ()); + TEST_EQUAL(ts.m_ascent, 0, ()); + TEST_EQUAL(ts.m_descent, 0, ()); + TEST_EQUAL(ts.m_duration, 0, ()); +} + +UNIT_TEST(TrackStatistics_MultipleLines) +{ + kml::MultiGeometry geometry; + geometry.AddLine({ + PointWithAltitude({0.0, 0.0}, 100), + PointWithAltitude({1.0, 1.0}, 150), + PointWithAltitude({1.0, 1.0}, 140) + }); + geometry.AddTimestamps({ + 0, + 1, + 2 + }); + geometry.AddLine({ + PointWithAltitude({2.0, 2.0}, 50), + PointWithAltitude({3.0, 3.0}, 75), + PointWithAltitude({3.0, 3.0}, 60) + }); + geometry.AddTimestamps({ + 0, + 0, + 0 + }); + geometry.AddLine({ + PointWithAltitude({4.0, 4.0}, 200), + PointWithAltitude({5.0, 5.0}, 250) + }); + geometry.AddTimestamps({ + 4, + 5 + }); + auto const ts = TrackStatistics(geometry); + + TEST_EQUAL(ts.m_minElevation, 50, ()); + TEST_EQUAL(ts.m_maxElevation, 250, ()); + TEST_EQUAL(ts.m_ascent, 125, ()); // Ascent from 100 -> 150, 50 -> 75, 200 -> 250 + TEST_EQUAL(ts.m_descent, 25, ()); // Descent from 150 -> 140, 75 -> 60 + TEST_EQUAL(ts.m_duration, 3, ()); +} + +UNIT_TEST(TrackStatistics_WithGpsPoints) +{ + const std::vector> pointsData = { + { BuildGpsInfo(0.0, 0.0, 0, 0), + BuildGpsInfo(1.0, 1.0, 50, 1), + BuildGpsInfo(2.0, 2.0, 100, 2) + }, + { + BuildGpsInfo(3.0, 3.0, -50, 5) + }, + { + BuildGpsInfo(4.0, 4.0, 0, 10) + } + }; + TrackStatistics ts; + for (auto const & pointsList : pointsData) + { + for (auto const & point : pointsList) + ts.AddGpsInfoPoint(point); + } + TEST_EQUAL(ts.m_minElevation, -50, ()); + TEST_EQUAL(ts.m_maxElevation, 100, ()); + TEST_EQUAL(ts.m_ascent, 150, ()); // Ascent from 0 -> 50, 50 -> 100, -50 -> 0 + TEST_EQUAL(ts.m_descent, 150, ()); // Descent from 100 -> -50 + TEST_EQUAL(ts.m_duration, 10, ()); +} + +UNIT_TEST(TrackStatistics_PositiveAndNegativeAltitudes) +{ + kml::MultiGeometry geometry; + geometry.AddLine({ + PointWithAltitude({0.0, 0.0}, -10), + PointWithAltitude({1.0, 1.0}, 20), + PointWithAltitude({2.0, 2.0}, -5), + PointWithAltitude({3.0, 3.0}, 15) + }); + auto const ts = TrackStatistics(geometry); + + TEST_EQUAL(ts.m_minElevation, -10, ()); + TEST_EQUAL(ts.m_maxElevation, 20, ()); + TEST_EQUAL(ts.m_ascent, 50, ()); // Ascent from -10 -> 20 and -5 -> 15 + TEST_EQUAL(ts.m_descent, 25, ()); // Descent from 20 -> -5 +} + +UNIT_TEST(TrackStatistics_SmallAltitudeDelta) +{ + const std::vector points = { + BuildGpsInfo(0.0, 0.0, 0), + BuildGpsInfo(1.0, 1.0, 0.2), + BuildGpsInfo(2.0, 2.0, 0.4), + BuildGpsInfo(3.0, 3.0, 0.6), + BuildGpsInfo(4.0, 4.0, 0.8), + BuildGpsInfo(5.0, 5.0, 1.0) + }; + + TrackStatistics ts; + for (auto const & point : points) + ts.AddGpsInfoPoint(point); + + TEST_EQUAL(ts.m_minElevation, 0, ()); + TEST_EQUAL(ts.m_maxElevation, 1.0, ()); + TEST_EQUAL(ts.m_ascent, 1.0, ()); + TEST_EQUAL(ts.m_descent, 0, ()); +} +} // namespace track_statistics_tests diff --git a/map/track.cpp b/map/track.cpp index fcdd167e55..ecae285897 100644 --- a/map/track.cpp +++ b/map/track.cpp @@ -8,24 +8,6 @@ #include -namespace -{ -double GetLengthInMeters(kml::MultiGeometry::LineT const & points, size_t pointIndex) -{ - CHECK_LESS(pointIndex, points.size(), (pointIndex, points.size())); - - double length = 0.0; - for (size_t i = 1; i <= pointIndex; ++i) - { - auto const & pt1 = points[i - 1].GetPoint(); - auto const & pt2 = points[i].GetPoint(); - auto const segmentLength = mercator::DistanceOnEarth(pt1, pt2); - length += segmentLength; - } - return length; -} -} // namespace - Track::Track(kml::TrackData && data) : Base(data.m_id == kml::kInvalidTrackId ? UserMarkIdStorage::Instance().GetNextTrackId() : data.m_id) , m_data(std::move(data)) @@ -120,13 +102,7 @@ m2::RectD Track::GetLimitRect() const double Track::GetLengthMeters() const { - if (m_interactionData) - return m_interactionData->m_lengths.back().back(); - - double len = 0; - for (auto const & line : m_data.m_geometry.m_lines) - len += GetLengthInMeters(line, line.size() - 1); - return len; + return GetStatistics().m_length; } double Track::GetLengthMetersImpl(size_t lineIndex, size_t ptIndex) const @@ -236,25 +212,23 @@ kml::MultiGeometry::LineT Track::GetGeometry() const return geometry; } +TrackStatistics Track::GetStatistics() const +{ + if (!m_trackStatistics.has_value()) + m_trackStatistics = TrackStatistics(m_data.m_geometry); + return m_trackStatistics.value(); +} + std::optional Track::GetElevationInfo() const { if (!HasAltitudes()) return std::nullopt; if (!m_elevationInfo) - m_elevationInfo = ElevationInfo(GetData().m_geometry); + m_elevationInfo = ElevationInfo(GetData().m_geometry.m_lines); return m_elevationInfo; } double Track::GetDurationInSeconds() const { - double duration = 0.0; - if (!m_data.m_geometry.HasTimestamps()) - return duration; - for (size_t i = 0; i < m_data.m_geometry.m_timestamps.size(); ++i) - { - ASSERT(m_data.m_geometry.HasTimestampsFor(i), ()); - auto const & timestamps = m_data.m_geometry.m_timestamps[i]; - duration += timestamps.back() - timestamps.front(); - } - return duration; + return GetStatistics().m_duration; } diff --git a/map/track.hpp b/map/track.hpp index da030c2fdc..53f8da6f1d 100644 --- a/map/track.hpp +++ b/map/track.hpp @@ -3,6 +3,7 @@ #include "kml/types.hpp" #include "map/elevation_info.hpp" +#include "map/track_statistics.hpp" #include "drape_frontend/user_marks_provider.hpp" @@ -31,6 +32,7 @@ public: m2::RectD GetLimitRect() const; double GetLengthMeters() const; double GetDurationInSeconds() const; + TrackStatistics GetStatistics() const; std::optional GetElevationInfo() const; std::pair GetCenterPoint() const; @@ -80,6 +82,7 @@ private: kml::TrackData m_data; kml::MarkGroupId m_groupID = kml::kInvalidMarkGroupId; + mutable std::optional m_trackStatistics; mutable std::optional m_elevationInfo; struct InteractionData diff --git a/map/track_statistics.cpp b/map/track_statistics.cpp new file mode 100644 index 0000000000..ca0791a8fd --- /dev/null +++ b/map/track_statistics.cpp @@ -0,0 +1,108 @@ +#include "map/track_statistics.hpp" + +#include "geometry/mercator.hpp" +#include "base/logging.hpp" + +using namespace geometry; + +double constexpr kInvalidTimestamp = std::numeric_limits::min(); +PointWithAltitude const kInvalidPoint = {m2::PointD::Zero(), kInvalidAltitude}; + +TrackStatistics::TrackStatistics() + : m_length(0), + m_duration(0), + m_ascent(0), + m_descent(0), + m_minElevation(kDefaultAltitudeMeters), + m_maxElevation(kDefaultAltitudeMeters), + m_previousPoint(kInvalidPoint), + m_previousTimestamp(kInvalidTimestamp) +{} + +TrackStatistics::TrackStatistics(kml::MultiGeometry const & geometry) + : TrackStatistics() +{ + for (auto const & line : geometry.m_lines) + AddPoints(line, true); + if (geometry.HasTimestamps()) + { + for (size_t i = 0; i < geometry.m_timestamps.size(); ++i) + { + ASSERT(geometry.HasTimestampsFor(i), ()); + AddTimestamps(geometry.m_timestamps[i], true); + } + } +} + +void TrackStatistics::AddGpsInfoPoint(location::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) + { + InitializeNewSegment(line[0]); + startIndex = 1; + } + + ProcessPoints(line, startIndex); +} + +void TrackStatistics::InitializeNewSegment(PointWithAltitude const & firstPoint) +{ + auto const altitude = firstPoint.GetAltitude(); + + bool const hasNoPoints = HasNoPoints(); + 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) + { + auto const & point = points[i]; + auto const pointAltitude = point.GetAltitude(); + + m_minElevation = std::min(m_minElevation, pointAltitude); + m_maxElevation = std::max(m_maxElevation, pointAltitude); + + auto const deltaAltitude = pointAltitude - m_previousPoint.GetAltitude(); + if (deltaAltitude > 0) + m_ascent += deltaAltitude; + else + m_descent -= deltaAltitude; + m_length += mercator::DistanceOnEarth(m_previousPoint.GetPoint(), point.GetPoint()); + + m_previousPoint = point; + } +} + +void TrackStatistics::AddTimestamps(kml::MultiGeometry::TimeT const & timestamps, bool isNewSegment) +{ + 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_previousTimestamp = timestamps.back(); +} + +bool TrackStatistics::HasNoPoints() const +{ + return m_previousPoint == kInvalidPoint; +} diff --git a/map/track_statistics.hpp b/map/track_statistics.hpp new file mode 100644 index 0000000000..bd512f4d58 --- /dev/null +++ b/map/track_statistics.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "platform/location.hpp" + +#include "kml/types.hpp" + +struct TrackStatistics +{ + TrackStatistics(); + explicit TrackStatistics(kml::MultiGeometry const & geometry); + + double m_length; + double m_duration; + double m_ascent; + double m_descent; + int16_t m_minElevation; + int16_t 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); + bool HasNoPoints() const; + + geometry::PointWithAltitude m_previousPoint; + double m_previousTimestamp; +}; diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index f5810fc704..ef97e0d864 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -92,6 +92,9 @@ BBFC7E3A202D29C000531BE7 /* user_mark_layer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BBFC7E38202D29BF00531BE7 /* user_mark_layer.cpp */; }; BBFC7E3B202D29C000531BE7 /* user_mark_layer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BBFC7E39202D29BF00531BE7 /* user_mark_layer.hpp */; }; ED49D74C2CEF3D69004AF27E /* elevation_info_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ED49D74B2CEF3CE3004AF27E /* elevation_info_tests.cpp */; }; + ED85D1CC2D5F4B5B00D8075D /* track_statistics.hpp in Headers */ = {isa = PBXBuildFile; fileRef = ED85D1CB2D5F4B5B00D8075D /* track_statistics.hpp */; }; + ED85D1CE2D5F4B7200D8075D /* track_statistics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ED85D1CD2D5F4B6600D8075D /* track_statistics.cpp */; }; + ED85D1D02D5F508700D8075D /* track_statistics_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ED85D1CF2D5F508700D8075D /* track_statistics_tests.cpp */; }; F6B283031C1B03320081957A /* gps_track_collection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6B282FB1C1B03320081957A /* gps_track_collection.cpp */; }; F6B283041C1B03320081957A /* gps_track_collection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F6B282FC1C1B03320081957A /* gps_track_collection.hpp */; }; F6B283051C1B03320081957A /* gps_track_filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6B282FD1C1B03320081957A /* gps_track_filter.cpp */; }; @@ -240,6 +243,9 @@ BBFC7E38202D29BF00531BE7 /* user_mark_layer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_mark_layer.cpp; sourceTree = ""; }; BBFC7E39202D29BF00531BE7 /* user_mark_layer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = user_mark_layer.hpp; sourceTree = ""; }; ED49D74B2CEF3CE3004AF27E /* elevation_info_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = elevation_info_tests.cpp; sourceTree = ""; }; + ED85D1CB2D5F4B5B00D8075D /* track_statistics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = track_statistics.hpp; sourceTree = ""; }; + ED85D1CD2D5F4B6600D8075D /* track_statistics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = track_statistics.cpp; sourceTree = ""; }; + ED85D1CF2D5F508700D8075D /* track_statistics_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = track_statistics_tests.cpp; sourceTree = ""; }; F6B282FB1C1B03320081957A /* gps_track_collection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_track_collection.cpp; sourceTree = ""; }; F6B282FC1C1B03320081957A /* gps_track_collection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_track_collection.hpp; sourceTree = ""; }; F6B282FD1C1B03320081957A /* gps_track_filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_track_filter.cpp; sourceTree = ""; }; @@ -355,6 +361,7 @@ BB421D6A1E8C0026005BFA4D /* transliteration_test.cpp */, 674A2A351B27011A001A525C /* working_time_tests.cpp */, ED49D74B2CEF3CE3004AF27E /* elevation_info_tests.cpp */, + ED85D1CF2D5F508700D8075D /* track_statistics_tests.cpp */, ); name = map_tests; path = ../../map/map_tests; @@ -444,6 +451,8 @@ BB1C0194241BF73C0067FD5C /* track_mark.hpp */, 6753462C1A4054E800A0A8C3 /* track.cpp */, 6753462D1A4054E800A0A8C3 /* track.hpp */, + ED85D1CD2D5F4B6600D8075D /* track_statistics.cpp */, + ED85D1CB2D5F4B5B00D8075D /* track_statistics.hpp */, 347B60741DD9926D0050FA24 /* traffic_manager.cpp */, 347B60751DD9926D0050FA24 /* traffic_manager.hpp */, BB4E5F201FCC663700A77250 /* transit */, @@ -513,6 +522,7 @@ 675346651A4054E800A0A8C3 /* framework.hpp in Headers */, BBA014B120754997007402E4 /* user_mark_id_storage.hpp in Headers */, 674A2A381B2715FB001A525C /* osm_opening_hours.hpp in Headers */, + ED85D1CC2D5F4B5B00D8075D /* track_statistics.hpp in Headers */, 3DEE1ADF21EE03B400054A91 /* power_manager.hpp in Headers */, F6D2CE7F1EDEB7F500636DFD /* routing_manager.hpp in Headers */, 670E39411C46C5C700E9C0A6 /* gps_tracker.hpp in Headers */, @@ -653,6 +663,7 @@ 679624B21D1017DB00AE4E3C /* mwm_set_test.cpp in Sources */, 679624AD1D1017DB00AE4E3C /* address_tests.cpp in Sources */, 67F183791BD5045700AB1840 /* kmz_unarchive_test.cpp in Sources */, + ED85D1D02D5F508700D8075D /* track_statistics_tests.cpp in Sources */, FAA8387326BB3C09002E54C6 /* check_mwms.cpp in Sources */, 679624AE1D1017DB00AE4E3C /* feature_getters_tests.cpp in Sources */, 67F1837A1BD5045700AB1840 /* mwm_url_tests.cpp in Sources */, @@ -700,6 +711,7 @@ F6B283071C1B03320081957A /* gps_track_storage.cpp in Sources */, 670E39401C46C5C700E9C0A6 /* gps_tracker.cpp in Sources */, BBA014B220754997007402E4 /* user_mark_id_storage.cpp in Sources */, + ED85D1CE2D5F4B7200D8075D /* track_statistics.cpp in Sources */, 6753464A1A4054E800A0A8C3 /* bookmark.cpp in Sources */, 45580ABE1E2CBD5E00CD535D /* benchmark_tools.cpp in Sources */, 3DA5723220C195ED007BDE27 /* everywhere_search_callback.cpp in Sources */,