diff --git a/drape_frontend/selection_shape.cpp b/drape_frontend/selection_shape.cpp index bcbadd2ebb..6fa0bfb7e4 100644 --- a/drape_frontend/selection_shape.cpp +++ b/drape_frontend/selection_shape.cpp @@ -84,6 +84,9 @@ bool SelectionShape::IsVisible(ScreenBase const & screen, m2::PointD & pxPos) co void SelectionShape::Render(ref_ptr context, ref_ptr mng, ScreenBase const & screen, int zoomLevel, FrameValues const & frameValues) { + if (m_selectedObject == OBJECT_TRACK) + return; + ShowHideAnimation::EState state = m_animation.GetState(); if (state != ShowHideAnimation::STATE_VISIBLE && state != ShowHideAnimation::STATE_SHOW_DIRECTION) return; diff --git a/drape_frontend/selection_shape.hpp b/drape_frontend/selection_shape.hpp index d493cdddaf..3866999af1 100644 --- a/drape_frontend/selection_shape.hpp +++ b/drape_frontend/selection_shape.hpp @@ -32,7 +32,8 @@ public: OBJECT_EMPTY, OBJECT_POI, OBJECT_USER_MARK, - OBJECT_MY_POSITION + OBJECT_MY_POSITION, + OBJECT_TRACK }; SelectionShape(ref_ptr context, ref_ptr mng); diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index 75c3bb8d03..f1deacf761 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -123,6 +123,8 @@ set( tips_api_delegate.hpp track.cpp track.hpp + track_mark.cpp + track_mark.hpp traffic_manager.cpp traffic_manager.hpp transit/transit_display.cpp diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index d6a3f36aea..2de92b2aca 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -26,6 +26,7 @@ #include "coding/zip_creator.hpp" #include "coding/zip_reader.hpp" +#include "geometry/rect_intersect.hpp" #include "geometry/transformations.hpp" #include "base/file_name_utils.hpp" @@ -542,6 +543,8 @@ BookmarkManager::BookmarkManager(User & user, Callbacks && callbacks) m_selectionMark = CreateUserMark(m2::PointD{}); m_myPositionMark = CreateUserMark(m2::PointD{}); + m_trackInfoMarkId = CreateUserMark(m2::PointD{})->GetId(); + using namespace std::placeholders; m_bookmarkCloud.SetSynchronizationHandlers( std::bind(&BookmarkManager::OnSynchronizationStarted, this, _1), @@ -727,6 +730,9 @@ void BookmarkManager::DetachTrack(kml::TrackId trackId, kml::MarkGroupId groupId void BookmarkManager::DeleteTrack(kml::TrackId trackId) { CHECK_THREAD_CHECKER(m_threadChecker, ()); + auto const markId = GetTrackSelectionMarkId(trackId); + if (markId != kml::kInvalidMarkId) + DeleteUserMark(markId); auto it = m_tracks.find(trackId); auto const groupId = it->second->GetGroupId(); if (groupId != kml::kInvalidMarkGroupId) @@ -838,15 +844,6 @@ kml::TrackIdSet const & BookmarkManager::GetTrackIds(kml::MarkGroupId groupId) c return GetGroup(groupId)->GetUserLines(); } -ElevationInfo BookmarkManager::MakeElevationInfo(kml::TrackId trackId) const -{ - CHECK_THREAD_CHECKER(m_threadChecker, ()); - auto const track = GetTrack(trackId); - CHECK(track != nullptr, ()); - - return ElevationInfo(*track); -} - bool BookmarkManager::GetLastSortingType(kml::MarkGroupId groupId, SortingType & sortingType) const { CHECK_THREAD_CHECKER(m_threadChecker, ()); @@ -1030,6 +1027,15 @@ bool BookmarkManager::IsGuide(kml::AccessRules accessRules) accessRules == kml::AccessRules::P2P; } +ElevationInfo BookmarkManager::MakeElevationInfo(kml::TrackId trackId) const +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + auto const track = GetTrack(trackId); + CHECK(track != nullptr, ()); + + return ElevationInfo(*track); +} + void BookmarkManager::SetElevationActivePoint(kml::TrackId const & trackId, double targetDistance) { CHECK_THREAD_CHECKER(m_threadChecker, ()); @@ -1037,39 +1043,21 @@ void BookmarkManager::SetElevationActivePoint(kml::TrackId const & trackId, doub auto const track = GetTrack(trackId); CHECK(track != nullptr, ()); - auto const & points = track->GetPointsWithAltitudes(); + m2::PointD pt; + CHECK(track->GetPoint(targetDistance, pt), (trackId, targetDistance)); - if (points.empty()) - return; - - m2::PointD result = m2::PointD::Zero(); - double distance = 0.0; - for (size_t i = 1; i < points.size(); ++i) - { - auto const & ptA = points[i - 1].GetPoint(); - auto const & ptB = points[i].GetPoint(); - double lastDistance = mercator::DistanceOnEarth(ptA, ptB); - distance += lastDistance; - if (distance >= targetDistance) - { - auto const k = (lastDistance - (distance - targetDistance)) / lastDistance; - result = ptA + (ptB - ptA) * k; - - // TODO(darina): propagate |result| and |distance| into track user mark. - - return; - } - } - - // TODO(darina): propagate |points.back()| and |distance| into track user mark. + SelectTrack(TrackSelectionInfo(trackId, pt, targetDistance)); } double BookmarkManager::GetElevationActivePoint(kml::TrackId const & trackId) const { CHECK_THREAD_CHECKER(m_threadChecker, ()); - // TODO(darina): implement receiving of active point from track user mark. - return 0.0; + auto const markId = GetTrackSelectionMarkId(trackId); + CHECK(markId != kml::kInvalidMarkId, ()); + + auto const trackSelectionMark = GetMark(markId); + return trackSelectionMark->GetDistance(); } void BookmarkManager::SetElevationActivePointChangedCallback(ElevationActivePointChangedCallback const & cb) @@ -1079,6 +1067,151 @@ void BookmarkManager::SetElevationActivePointChangedCallback(ElevationActivePoin m_elevationActivePointChanged = cb; } +BookmarkManager::TrackSelectionInfo BookmarkManager::FindNearestTrack(m2::RectD const & touchRect) const +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + TrackSelectionInfo selectionInfo; + + auto minSquaredDist = std::numeric_limits::max(); + for (auto const & pair : m_categories) + { + auto const & category = *pair.second; + if (!category.IsVisible()) + continue; + + for (auto trackId : category.GetUserLines()) + { + auto const track = GetTrack(trackId); + auto const trackRect = track->GetLimitRect(); + + if (trackRect.IsIntersect(touchRect)) + { + auto const & pointsWithAlt = track->GetPointsWithAltitudes(); + for (size_t i = 0; i + 1 < pointsWithAlt.size(); ++i) + { + auto pt1 = pointsWithAlt[i].GetPoint(); + auto pt2 = pointsWithAlt[i + 1].GetPoint(); + if (m2::Intersect(touchRect, pt1, pt2)) + { + m2::ParametrizedSegment seg(pt1, pt2); + auto const closestPoint = seg.ClosestPointTo(touchRect.Center()); + auto const squaredDist = closestPoint.SquaredLength(touchRect.Center()); + if (squaredDist < minSquaredDist) + { + minSquaredDist = squaredDist; + selectionInfo.m_trackId = trackId; + selectionInfo.m_trackPoint = closestPoint; + + auto const segDistInMeters = mercator::DistanceOnEarth(pointsWithAlt[i].GetPoint(), + closestPoint); + selectionInfo.m_distanceInMeters = segDistInMeters; + if (i > 0) + selectionInfo.m_distanceInMeters += track->GetLengthMeters(i - 1); + } + } + } + } + } + } + + return selectionInfo; +} + +BookmarkManager::TrackSelectionInfo BookmarkManager::GetTrackSelectionInfo(kml::TrackId const & trackId) const +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + auto const markId = GetTrackSelectionMarkId(trackId); + if (markId == kml::kInvalidMarkId) + return {}; + + auto const mark = GetMark(markId); + return TrackSelectionInfo(trackId, mark->GetPivot(), mark->GetDistance()); +} + +kml::MarkId BookmarkManager::GetTrackSelectionMarkId(kml::TrackId trackId) const +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + CHECK_NOT_EQUAL(trackId, kml::kInvalidTrackId, ()); + + for (auto markId : GetUserMarkIds(UserMark::Type::TRACK_SELECTION)) + { + auto const * mark = GetMark(markId); + if (mark->GetTrackId() == trackId) + return markId; + } + return kml::kInvalidMarkId; +} + +void BookmarkManager::SelectTrack(TrackSelectionInfo const & trackSelectionInfo) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + CHECK_NOT_EQUAL(trackSelectionInfo.m_trackId, kml::kInvalidTrackId, ()); + + auto const markId = GetTrackSelectionMarkId(trackSelectionInfo.m_trackId); + + auto es = GetEditSession(); + TrackSelectionMark * trackSelectionMark = nullptr; + if (markId == kml::kInvalidMarkId) + { + trackSelectionMark = es.CreateUserMark(trackSelectionInfo.m_trackPoint); + trackSelectionMark->SetTrackId(trackSelectionInfo.m_trackId); + } + else + { + trackSelectionMark = es.GetMarkForEdit(markId); + } + + trackSelectionMark->SetPosition(trackSelectionInfo.m_trackPoint); + trackSelectionMark->SetDistance(trackSelectionInfo.m_distanceInMeters); + + if (m_elevationActivePointChanged != nullptr) + m_elevationActivePointChanged(); +} + +void BookmarkManager::DeselectTrack(kml::TrackId trackId) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + CHECK_NOT_EQUAL(trackId, kml::kInvalidTrackId, ()); + + auto const markId = GetTrackSelectionMarkId(trackId); + if (markId != kml::kInvalidMarkId) + GetEditSession().DeleteUserMark(markId); +} + +void BookmarkManager::ShowDefaultTrackInfo(kml::TrackId trackId) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + auto track = GetTrack(trackId); + CHECK(track != nullptr, ()); + + auto const & points = track->GetPointsWithAltitudes(); + auto const pt = points[points.size() / 2].GetPoint(); + auto const distance = track->GetLengthMeters(points.size() / 2 - 1); + + auto es = GetEditSession(); + auto trackInfoMark = es.GetMarkForEdit(m_trackInfoMarkId); + trackInfoMark->SetPosition(pt); + trackInfoMark->SetIsVisible(true); + trackInfoMark->SetTrackId(trackId); + + SelectTrack(TrackSelectionInfo(trackId, pt, distance)); +} + +void BookmarkManager::HideTrackInfo(kml::TrackId trackId) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + auto es = GetEditSession(); + auto trackInfoMark = es.GetMarkForEdit(m_trackInfoMarkId); + if (trackInfoMark->GetTrackId() != trackId) + return; + + trackInfoMark->SetPosition(m2::PointD::Zero()); + trackInfoMark->SetIsVisible(false); + trackInfoMark->SetTrackId(kml::kInvalidTrackId); +} + void BookmarkManager::PrepareBookmarksAddresses(std::vector & bookmarksForSort, AddressesCollection & newAddresses) { @@ -1657,6 +1790,23 @@ void BookmarkManager::SetDrapeEngine(ref_ptr engine) { m_drapeEngine.Set(engine); m_firstDrapeNotification = true; + + std::vector symbols; + symbols.push_back("bookmark-default-m"); + symbols.push_back("ic_marker_ontrack"); + + m_drapeEngine.SafeCall(&df::DrapeEngine::RequestSymbolsSize, symbols, + [this](std::map && sizes) + { + GetPlatform().RunTask(Platform::Thread::Gui, + [this, sizes = move(sizes)]() mutable + { + auto es = GetEditSession(); + auto infoMark = es.GetMarkForEdit(m_trackInfoMarkId); + infoMark->SetOffset(m2::PointF(0.0, -sizes.at("ic_marker_ontrack").y / 2)); + m_maxBookmarkSymbolSize = sizes.at("bookmark-default-m"); + }); + }); } void BookmarkManager::InitRegionAddressGetter(DataSource const & dataSource, @@ -2402,15 +2552,22 @@ UserMark const * BookmarkManager::FindNearestUserMark(TTouchRectHolder const & h { CHECK_THREAD_CHECKER(m_threadChecker, ()); BestUserMarkFinder finder(holder, findOnlyVisible, this); - auto const hasFound = finder(UserMark::Type::ROUTING) || - finder(UserMark::Type::ROAD_WARNING) || - finder(UserMark::Type::SEARCH) || - finder(UserMark::Type::API); + auto hasFound = finder(UserMark::Type::ROUTING) || + finder(UserMark::Type::ROAD_WARNING) || + finder(UserMark::Type::SEARCH) || + finder(UserMark::Type::API); if (!hasFound) { for (auto const & pair : m_categories) - finder(pair.first); + hasFound = finder(pair.first) || hasFound; } + + if (!hasFound) + { + hasFound = finder(UserMark::Type::TRACK_INFO) || + finder(UserMark::Type::TRACK_SELECTION); + } + return finder.GetFoundMark(); } diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index df917e8755..0aef23ca5e 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -6,6 +6,7 @@ #include "map/cloud.hpp" #include "map/elevation_info.hpp" #include "map/track.hpp" +#include "map/track_mark.hpp" #include "map/user_mark_layer.hpp" #include "drape_frontend/drape_engine_safe_ptr.hpp" @@ -210,8 +211,6 @@ public: kml::MarkIdSet const & GetUserMarkIds(kml::MarkGroupId groupId) const; kml::TrackIdSet const & GetTrackIds(kml::MarkGroupId groupId) const; - ElevationInfo MakeElevationInfo(kml::TrackId trackId) const; - // Do not change the order. enum class SortingType { @@ -452,12 +451,35 @@ public: std::vector GetCategoriesFromCatalog(AccessRulesFilter && filter) const; static bool IsGuide(kml::AccessRules accessRules); + ElevationInfo MakeElevationInfo(kml::TrackId trackId) const; void SetElevationActivePoint(kml::TrackId const & trackId, double distanceInMeters); // Returns distance from start of the track to active point in meters. double GetElevationActivePoint(kml::TrackId const & trackId) const; - void SetElevationActivePointChangedCallback(ElevationActivePointChangedCallback const & cb); + struct TrackSelectionInfo + { + TrackSelectionInfo() = default; + TrackSelectionInfo(kml::TrackId trackId, m2::PointD const & trackPoint, double distanceInMeters) + : m_trackId(trackId) + , m_trackPoint(trackPoint) + , m_distanceInMeters(distanceInMeters) + {} + + kml::TrackId m_trackId = kml::kInvalidTrackId; + m2::PointD m_trackPoint = m2::PointD::Zero(); + double m_distanceInMeters = 0.0; + }; + + TrackSelectionInfo FindNearestTrack(m2::RectD const & touchRect) const; + TrackSelectionInfo GetTrackSelectionInfo(kml::TrackId const & trackId) const; + + void SelectTrack(TrackSelectionInfo const & trackSelectionInfo); + void DeselectTrack(kml::TrackId trackId); + + void ShowDefaultTrackInfo(kml::TrackId trackId); + void HideTrackInfo(kml::TrackId trackId); + private: class MarksChangesTracker : public df::UserMarksProvider { @@ -728,6 +750,8 @@ private: std::vector GetAllPaidCategoriesIds() const; + kml::MarkId GetTrackSelectionMarkId(kml::TrackId trackId) const; + ThreadChecker m_threadChecker; User & m_user; @@ -769,6 +793,9 @@ private: StaticMarkPoint * m_selectionMark = nullptr; MyPositionMarkPoint * m_myPositionMark = nullptr; + kml::MarkId m_trackInfoMarkId = kml::kInvalidMarkId; + m2::PointF m_maxBookmarkSymbolSize; + bool m_asyncLoadingInProgress = false; struct BookmarkLoaderInfo { diff --git a/map/framework.cpp b/map/framework.cpp index 6026fc01a1..441e8ce6a4 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -792,10 +792,12 @@ void Framework::FillBookmarkInfo(Bookmark const & bmk, place_page::Info & info) FillPointInfoForBookmark(bmk, info); } -void Framework::FillTrackInfo(Track const & track, place_page::Info & info) const +void Framework::FillTrackInfo(Track const & track, m2::PointD const & trackPoint, + place_page::Info & info) const { info.SetTrackId(track.GetId()); info.SetBookmarkCategoryId(track.GetGroupId()); + info.SetMercator(trackPoint); } search::ReverseGeocoder::Address Framework::GetAddressAtPoint(m2::PointD const & pt) const @@ -1128,6 +1130,8 @@ void Framework::ShowTrack(kml::TrackId trackId) StopLocationFollow(); ShowRect(rect); + + GetBookmarkManager().ShowDefaultTrackInfo(trackId); } void Framework::ShowBookmarkCategory(kml::MarkGroupId categoryId, bool animation) @@ -2529,6 +2533,16 @@ FeatureID Framework::FindBuildingAtPoint(m2::PointD const & mercator) const return featureId; } +void Framework::BuildTrackPlacePage(BookmarkManager::TrackSelectionInfo const & trackSelectionInfo, + place_page::Info & info) +{ + info.SetSelectedObject(df::SelectionShape::OBJECT_TRACK); + auto const & track = *GetBookmarkManager().GetTrack(trackSelectionInfo.m_trackId); + FillTrackInfo(track, trackSelectionInfo.m_trackPoint, info); + GetBookmarkManager().SelectTrack(trackSelectionInfo); + GetBookmarkManager().HideTrackInfo(trackSelectionInfo.m_trackId); +} + std::optional Framework::BuildPlacePageInfo( place_page::BuildInfo const & buildInfo) { @@ -2564,6 +2578,20 @@ std::optional Framework::BuildPlacePageInfo( case UserMark::Type::ROAD_WARNING: FillRoadTypeMarkInfo(*static_cast(mark), outInfo); break; + case UserMark::Type::TRACK_INFO: + { + auto const & infoMark = *static_cast(mark); + BuildTrackPlacePage(GetBookmarkManager().GetTrackSelectionInfo(infoMark.GetTrackId()), + outInfo); + return outInfo; + } + case UserMark::Type::TRACK_SELECTION: + { + auto const & selMark = *static_cast(mark); + BuildTrackPlacePage(GetBookmarkManager().GetTrackSelectionInfo(selMark.GetTrackId()), + outInfo); + return outInfo; + } default: ASSERT(false, ("FindNearestUserMark returned invalid mark.")); } @@ -2583,6 +2611,16 @@ std::optional Framework::BuildPlacePageInfo( return outInfo; } + if (buildInfo.IsTrackMatchingEnabled()) + { + auto const trackSelectionInfo = FindTrackInTapPosition(buildInfo); + if (trackSelectionInfo.m_trackId != kml::kInvalidTrackId) + { + BuildTrackPlacePage(trackSelectionInfo, outInfo); + return outInfo; + } + } + if (!buildInfo.m_postcode.empty()) { outInfo.SetSelectedObject(df::SelectionShape::OBJECT_POI); @@ -2643,6 +2681,21 @@ void Framework::UpdatePlacePageInfoForCurrentSelection( m_onPlacePageUpdate(); } +BookmarkManager::TrackSelectionInfo Framework::FindTrackInTapPosition( + place_page::BuildInfo const & buildInfo) const +{ + auto const & bm = GetBookmarkManager(); + if (buildInfo.m_trackId != kml::kInvalidTrackId) + { + auto const selection = bm.GetTrackSelectionInfo(buildInfo.m_trackId); + CHECK_NOT_EQUAL(selection.m_trackId, kml::kInvalidTrackId, ()); + return selection; + } + auto const touchRect = df::TapInfo::GetDefaultSearchRect(buildInfo.m_mercator, + m_currentModelView).GetGlobalRect(); + return bm.FindNearestTrack(touchRect); +} + UserMark const * Framework::FindUserMarkInTapPosition(place_page::BuildInfo const & buildInfo) const { if (buildInfo.m_userMarkId != kml::kInvalidMarkId) diff --git a/map/framework.hpp b/map/framework.hpp index 7be07a565c..5066f1cd95 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -437,6 +437,9 @@ private: void OnTapEvent(place_page::BuildInfo const & buildInfo); std::optional BuildPlacePageInfo(place_page::BuildInfo const & buildInfo); + void BuildTrackPlacePage(BookmarkManager::TrackSelectionInfo const & trackSelectionInfo, + place_page::Info & info); + BookmarkManager::TrackSelectionInfo FindTrackInTapPosition(place_page::BuildInfo const & buildInfo) const; UserMark const * FindUserMarkInTapPosition(place_page::BuildInfo const & buildInfo) const; FeatureID FindBuildingAtPoint(m2::PointD const & mercator) const; @@ -685,7 +688,8 @@ private: void FillRoadTypeMarkInfo(RoadWarningMark const & roadTypeMark, place_page::Info & info) const; void FillPointInfoForBookmark(Bookmark const & bmk, place_page::Info & info) const; void FillBookmarkInfo(Bookmark const & bmk, place_page::Info & info) const; - void FillTrackInfo(Track const & trackId, place_page::Info & info) const; + void FillTrackInfo(Track const & track, m2::PointD const & trackPoint, + place_page::Info & info) const; void SetPlacePageLocation(place_page::Info & info); void FillLocalExperts(FeatureType & ft, place_page::Info & info) const; void FillDescription(FeatureType & ft, place_page::Info & info) const; diff --git a/map/place_page_info.hpp b/map/place_page_info.hpp index 37a063b372..d42c18401f 100644 --- a/map/place_page_info.hpp +++ b/map/place_page_info.hpp @@ -90,6 +90,7 @@ struct BuildInfo Everything = 0, FeatureOnly, UserMarkOnly, + TrackOnly, Nothing }; @@ -113,6 +114,11 @@ struct BuildInfo return m_match == Match::Everything || m_match == Match::UserMarkOnly; } + bool IsTrackMatchingEnabled() const + { + return m_match == Match::Everything || m_match == Match::TrackOnly; + } + Source m_source = Source::Other; m2::PointD m_mercator; bool m_isLongTap = false; @@ -120,6 +126,7 @@ struct BuildInfo FeatureID m_featureId; Match m_match = Match::Everything; kml::MarkId m_userMarkId = kml::kInvalidMarkId; + kml::TrackId m_trackId = kml::kInvalidTrackId; bool m_isGeometrySelectionAllowed = false; bool m_needAnimationOnSelection = true; std::string m_postcode; diff --git a/map/track.cpp b/map/track.cpp index 38a84f5cff..9e91db592c 100644 --- a/map/track.cpp +++ b/map/track.cpp @@ -3,7 +3,6 @@ #include "map/bookmark_helpers.hpp" #include "map/user_mark_id_storage.hpp" -#include "geometry/distance_on_sphere.hpp" #include "geometry/mercator.hpp" #include @@ -11,10 +10,24 @@ Track::Track(kml::TrackData && data) : Base(data.m_id == kml::kInvalidTrackId ? UserMarkIdStorage::Instance().GetNextTrackId() : data.m_id) , m_data(std::move(data)) - , m_groupID(0) { m_data.m_id = GetId(); - ASSERT_GREATER(m_data.m_pointsWithAltitudes.size(), 1, ()); + CHECK_GREATER(m_data.m_pointsWithAltitudes.size(), 1, ()); + CacheLengths(); +} + +void Track::CacheLengths() +{ + m_cachedLengths.resize(m_data.m_pointsWithAltitudes.size() - 1); + double length = 0.0; + for (size_t i = 1; i < m_data.m_pointsWithAltitudes.size(); ++i) + { + auto const & pt1 = m_data.m_pointsWithAltitudes[i - 1].GetPoint(); + auto const & pt2 = m_data.m_pointsWithAltitudes[i].GetPoint(); + auto const segmentLength = mercator::DistanceOnEarth(pt1, pt2); + length += segmentLength; + m_cachedLengths[i - 1] = length; + } } std::string Track::GetName() const @@ -32,21 +45,13 @@ m2::RectD Track::GetLimitRect() const double Track::GetLengthMeters() const { - double res = 0.0; + return m_cachedLengths.back(); +} - auto it = m_data.m_pointsWithAltitudes.begin(); - double lat1 = mercator::YToLat(it->GetPoint().y); - double lon1 = mercator::XToLon(it->GetPoint().x); - for (++it; it != m_data.m_pointsWithAltitudes.end(); ++it) - { - double const lat2 = mercator::YToLat(it->GetPoint().y); - double const lon2 = mercator::XToLon(it->GetPoint().x); - res += ms::DistanceOnEarth(lat1, lon1, lat2, lon2); - lat1 = lat2; - lon1 = lon2; - } - - return res; +double Track::GetLengthMeters(size_t segmentIndex) const +{ + CHECK_LESS(segmentIndex, m_cachedLengths.size(), (segmentIndex)); + return m_cachedLengths[segmentIndex]; } df::DepthLayer Track::GetDepthLayer() const @@ -93,11 +98,49 @@ std::vector const & Track::GetPointsWithAltitudes() void Track::Attach(kml::MarkGroupId groupId) { - ASSERT(!m_groupID, ()); + ASSERT_EQUAL(m_groupID, kml::kInvalidMarkGroupId, ()); m_groupID = groupId; } void Track::Detach() { - m_groupID = 0; + m_groupID = kml::kInvalidMarkGroupId; +} + +void Track::SetSelectionMarkId(kml::MarkId markId) +{ + m_selectionMarkId = markId; +} + +bool Track::GetPoint(double distanceInMeters, m2::PointD & pt) const +{ + CHECK_GREATER_OR_EQUAL(distanceInMeters, 0.0, (distanceInMeters)); + + if (distanceInMeters == 0.0) + { + pt = m_data.m_pointsWithAltitudes.front().GetPoint(); + return true; + } + + if (fabs(distanceInMeters - m_cachedLengths.back()) < 1e-2) + { + pt = m_data.m_pointsWithAltitudes.back().GetPoint(); + return true; + } + + auto const it = std::lower_bound(m_cachedLengths.begin(), m_cachedLengths.end(), distanceInMeters); + if (it == m_cachedLengths.end()) + return false; + + auto const segmentIndex = it - m_cachedLengths.begin(); + auto const length = *it; + + auto const segmentLength = length - (segmentIndex == 0 ? 0.0 : m_cachedLengths[segmentIndex - 1]); + auto const k = (segmentLength - (length - distanceInMeters)) / segmentLength; + + auto const & pt1 = m_data.m_pointsWithAltitudes[segmentIndex].GetPoint(); + auto const & pt2 = m_data.m_pointsWithAltitudes[segmentIndex + 1].GetPoint(); + pt = pt1 + (pt2 - pt1) * k; + + return true; } diff --git a/map/track.hpp b/map/track.hpp index 80432c01c0..e93a1484d0 100644 --- a/map/track.hpp +++ b/map/track.hpp @@ -22,6 +22,7 @@ public: std::string GetName() const; m2::RectD GetLimitRect() const; double GetLengthMeters() const; + double GetLengthMeters(size_t segmentIndex) const; int GetMinZoom() const override { return 1; } df::DepthLayer GetDepthLayer() const override; @@ -35,8 +36,16 @@ public: void Attach(kml::MarkGroupId groupId); void Detach(); + kml::MarkId GetSelectionMarkId() const { return m_selectionMarkId; } + void SetSelectionMarkId(kml::MarkId markId); + bool GetPoint(double distanceInMeters, m2::PointD & pt) const; + private: + void CacheLengths(); + kml::TrackData m_data; - kml::MarkGroupId m_groupID; + kml::MarkGroupId m_groupID = kml::kInvalidMarkGroupId; + kml::MarkId m_selectionMarkId = kml::kInvalidMarkId; + std::vector m_cachedLengths; mutable bool m_isDirty = true; }; diff --git a/map/track_mark.cpp b/map/track_mark.cpp new file mode 100644 index 0000000000..f775fca6d6 --- /dev/null +++ b/map/track_mark.cpp @@ -0,0 +1,72 @@ +#include "map/track_mark.hpp" + +#include "indexer/scales.hpp" + +TrackInfoMark::TrackInfoMark(m2::PointD const & ptOrg) + : UserMark(ptOrg, UserMark::TRACK_INFO) +{ +} + +void TrackInfoMark::SetOffset(m2::PointF const & offset) +{ + SetDirty(); + m_offset = offset; +} + +void TrackInfoMark::SetPosition(m2::PointD const & ptOrg) +{ + SetDirty(); + m_ptOrg = ptOrg; +} + +void TrackInfoMark::SetIsVisible(bool isVisible) +{ + SetDirty(); + m_isVisible = isVisible; +} + +void TrackInfoMark::SetTrackId(kml::TrackId trackId) +{ + m_trackId = trackId; +} + +drape_ptr TrackInfoMark::GetSymbolNames() const +{ + auto symbol = make_unique_dp(); + symbol->insert(std::make_pair(1 /* zoomLevel */, "ic_marker_infosign")); + return symbol; +} + +drape_ptr TrackInfoMark::GetSymbolOffsets() const +{ + SymbolOffsets offsets(scales::UPPER_STYLE_SCALE, m_offset); + return make_unique_dp(offsets); +} + +TrackSelectionMark::TrackSelectionMark(m2::PointD const & ptOrg) + : UserMark(ptOrg, UserMark::TRACK_SELECTION) +{ +} + +void TrackSelectionMark::SetPosition(m2::PointD const & ptOrg) +{ + SetDirty(); + m_ptOrg = ptOrg; +} + +void TrackSelectionMark::SetTrackId(kml::TrackId trackId) +{ + m_trackId = trackId; +} + +void TrackSelectionMark::SetDistance(double distance) +{ + m_distance = distance; +} + +drape_ptr TrackSelectionMark::GetSymbolNames() const +{ + auto symbol = make_unique_dp(); + symbol->insert(std::make_pair(1 /* zoomLevel */, "ic_marker_ontrack")); + return symbol; +} diff --git a/map/track_mark.hpp b/map/track_mark.hpp new file mode 100644 index 0000000000..20bfcdd145 --- /dev/null +++ b/map/track_mark.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "map/user_mark.hpp" + +class TrackInfoMark : public UserMark +{ +public: + explicit TrackInfoMark(m2::PointD const & ptOrg); + + void SetOffset(m2::PointF const & offset); + void SetPosition(m2::PointD const & ptOrg); + void SetIsVisible(bool isVisible); + + void SetTrackId(kml::TrackId trackId); + kml::TrackId GetTrackId() const { return m_trackId; } + + drape_ptr GetSymbolNames() const override; + drape_ptr GetSymbolOffsets() const override; + dp::Anchor GetAnchor() const override { return dp::Bottom; } + bool IsVisible() const override { return m_isVisible; } + bool SymbolIsPOI() const override { return true; } + df::SpecialDisplacement GetDisplacement() const override { return df::SpecialDisplacement::SpecialModeUserMark; } + df::DepthLayer GetDepthLayer() const override { return df::DepthLayer::RoutingMarkLayer; } + +private: + m2::PointF m_offset; + bool m_isVisible = false; + kml::TrackId m_trackId = kml::kInvalidTrackId; +}; + +class TrackSelectionMark : public UserMark +{ +public: + explicit TrackSelectionMark(m2::PointD const & ptOrg); + + void SetPosition(m2::PointD const & ptOrg); + + void SetTrackId(kml::TrackId trackId); + kml::TrackId GetTrackId() const { return m_trackId; } + + void SetDistance(double distance); + double GetDistance() const { return m_distance; } + + drape_ptr GetSymbolNames() const override; + +private: + double m_distance = 0.0; + kml::TrackId m_trackId = kml::kInvalidTrackId; +}; diff --git a/map/user_mark.cpp b/map/user_mark.cpp index 2542d64764..23a0fe3275 100644 --- a/map/user_mark.cpp +++ b/map/user_mark.cpp @@ -107,6 +107,8 @@ std::string DebugPrint(UserMark::Type type) case UserMark::Type::SPEED_CAM: return "SPEED_CAM"; case UserMark::Type::LOCAL_ADS: return "LOCAL_ADS"; case UserMark::Type::TRANSIT: return "TRANSIT"; + case UserMark::Type::TRACK_INFO: return "TRACK_INFO"; + case UserMark::Type::TRACK_SELECTION: return "TRACK_SELECTION"; case UserMark::Type::COLORED: return "COLORED"; case UserMark::Type::USER_MARK_TYPES_COUNT: return "USER_MARK_TYPES_COUNT"; case UserMark::Type::USER_MARK_TYPES_COUNT_MAX: return "USER_MARK_TYPES_COUNT_MAX"; diff --git a/map/user_mark.hpp b/map/user_mark.hpp index 1ad3216bcb..d0dba42c4b 100644 --- a/map/user_mark.hpp +++ b/map/user_mark.hpp @@ -47,6 +47,8 @@ public: ROAD_WARNING, TRANSIT, LOCAL_ADS, + TRACK_INFO, + TRACK_SELECTION, DEBUG_MARK, // Plain "DEBUG" results in a name collision. COLORED, USER_MARK_TYPES_COUNT, diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index b4d630dd32..2fa3719973 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -221,6 +221,8 @@ 67F183841BD5049500AB1840 /* libtess2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67F183801BD5049500AB1840 /* libtess2.a */; }; 991CE2D02372BDB1009EB02A /* onboarding.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 991CE2CE2372BDB0009EB02A /* onboarding.hpp */; }; 991CE2D12372BDB1009EB02A /* onboarding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 991CE2CF2372BDB1009EB02A /* onboarding.cpp */; }; + BB1C0196241BF73C0067FD5C /* track_mark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB1C0194241BF73C0067FD5C /* track_mark.hpp */; }; + BB1C0197241BF73C0067FD5C /* track_mark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB1C0195241BF73C0067FD5C /* track_mark.cpp */; }; BB421D6C1E8C0031005BFA4D /* transliteration_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB421D6A1E8C0026005BFA4D /* transliteration_test.cpp */; }; BB4E5F251FCC664A00A77250 /* transit_display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB4E5F211FCC664A00A77250 /* transit_display.cpp */; }; BB4E5F261FCC664A00A77250 /* transit_display.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB4E5F221FCC664A00A77250 /* transit_display.hpp */; }; @@ -519,6 +521,8 @@ 67F183871BD5050900AB1840 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 991CE2CE2372BDB0009EB02A /* onboarding.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = onboarding.hpp; sourceTree = ""; }; 991CE2CF2372BDB1009EB02A /* onboarding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = onboarding.cpp; sourceTree = ""; }; + BB1C0194241BF73C0067FD5C /* track_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = track_mark.hpp; sourceTree = ""; }; + BB1C0195241BF73C0067FD5C /* track_mark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = track_mark.cpp; sourceTree = ""; }; BB421D6A1E8C0026005BFA4D /* transliteration_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transliteration_test.cpp; sourceTree = ""; }; BB4E5F211FCC664A00A77250 /* transit_display.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = transit_display.cpp; path = transit/transit_display.cpp; sourceTree = ""; }; BB4E5F221FCC664A00A77250 /* transit_display.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = transit_display.hpp; path = transit/transit_display.hpp; sourceTree = ""; }; @@ -888,6 +892,8 @@ 675345BD1A4054AD00A0A8C3 /* map */ = { isa = PBXGroup; children = ( + BB1C0195241BF73C0067FD5C /* track_mark.cpp */, + BB1C0194241BF73C0067FD5C /* track_mark.hpp */, 3DBD7B90240D523300ED9FE8 /* elevation_info.cpp */, 3DBD7B91240D523300ED9FE8 /* elevation_info.hpp */, BBB7060923E45F3300A7F29A /* isolines_manager.cpp */, @@ -1081,6 +1087,7 @@ 3DD692B3220AD240001C3C62 /* caching_address_getter.hpp in Headers */, 3D62CBDA20FF6C8B00E7BB6E /* discovery_search.hpp in Headers */, 3DD1166C21888AAD007A2ED4 /* notification_queue_serdes.hpp in Headers */, + BB1C0196241BF73C0067FD5C /* track_mark.hpp in Headers */, 3D0D2F7423D854AA00945C8D /* tips_api_delegate.hpp in Headers */, 675346491A4054E800A0A8C3 /* bookmark_manager.hpp in Headers */, 3DA5714320B5CC80007BDE27 /* booking_filter_processor.hpp in Headers */, @@ -1295,6 +1302,7 @@ 675346641A4054E800A0A8C3 /* framework.cpp in Sources */, 40ACC79623191C2600238E21 /* countries_names_tests.cpp in Sources */, 3DBD7B92240D523400ED9FE8 /* elevation_info.cpp in Sources */, + BB1C0197241BF73C0067FD5C /* track_mark.cpp in Sources */, 40ACC79823191C2600238E21 /* notification_tests.cpp in Sources */, 3DEE1AE021EE03B400054A91 /* power_manager.cpp in Sources */, BB4E5F281FCC664A00A77250 /* transit_reader.cpp in Sources */,