diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index 180ff71402..204afad342 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -58,6 +58,8 @@ set( routing_manager.hpp routing_mark.cpp routing_mark.hpp + search_api.cpp + search_api.hpp search_mark.cpp search_mark.hpp taxi_delegate.cpp diff --git a/map/address_finder.cpp b/map/address_finder.cpp index 825dce4609..49e7f76e1e 100644 --- a/map/address_finder.cpp +++ b/map/address_finder.cpp @@ -511,8 +511,6 @@ search::AddressInfo Framework::GetFeatureAddressInfo(FeatureType & ft) const vector Framework::GetPrintableFeatureTypes(FeatureType const & ft) const { - ASSERT(m_searchEngine, ()); - vector results; int8_t const locale = CategoriesHolder::MapLocaleToInteger(languages::GetCurrentOrig()); diff --git a/map/framework.cpp b/map/framework.cpp index 68efffc43d..91a73559e6 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -139,7 +139,6 @@ char const kTranslitMode[] = "TransliterationMode"; char const kICUDataFile[] = "icudt57l.dat"; #endif -double const kDistEqualQueryMeters = 100.0; double const kLargeFontsScaleFactor = 1.6; size_t constexpr kMaxTrafficCacheSizeBytes = 64 /* Mb */ * 1024 * 1024; @@ -147,30 +146,23 @@ size_t constexpr kMaxTrafficCacheSizeBytes = 64 /* Mb */ * 1024 * 1024; // To adjust GpsTrackFilter was added secret command "?gpstrackaccuracy:xxx;" // where xxx is a new value for horizontal accuracy. // This is temporary solution while we don't have a good filter. -void ParseSetGpsTrackMinAccuracyCommand(string const & query) +bool ParseSetGpsTrackMinAccuracyCommand(string const & query) { const char kGpsAccuracy[] = "?gpstrackaccuracy:"; - if (strings::StartsWith(query, kGpsAccuracy)) - { - size_t const end = query.find(';', sizeof(kGpsAccuracy) - 1); - if (end != string::npos) - { - string s(query.begin() + sizeof(kGpsAccuracy) - 1, query.begin() + end); - double value; - if (strings::to_double(s, value)) - { - GpsTrackFilter::StoreMinHorizontalAccuracy(value); - } - } - } -} + if (!strings::StartsWith(query, kGpsAccuracy)) + return false; -// Cancels search query by |handle|. -void CancelQuery(weak_ptr & handle) -{ - if (auto queryHandle = handle.lock()) - queryHandle->Cancel(); - handle.reset(); + size_t const end = query.find(';', sizeof(kGpsAccuracy) - 1); + if (end == string::npos) + return false; + + string s(query.begin() + sizeof(kGpsAccuracy) - 1, query.begin() + end); + double value; + if (!strings::to_double(s, value)) + return false; + + GpsTrackFilter::StoreMinHorizontalAccuracy(value); + return true; } string MakeSearchBookingUrl(booking::Api const & bookingApi, search::CityFinder & cityFinder, @@ -285,24 +277,7 @@ void Framework::OnViewportChanged(ScreenBase const & screen) { m_currentModelView = screen; - auto const forceSearchInViewport = !m_isViewportInitialized; - - if (!m_isViewportInitialized) - { - m_isViewportInitialized = true; - for (size_t i = 0; i < static_cast(search::Mode::Count); i++) - { - auto & intent = m_searchIntents[i]; - // Viewport search will be triggered below, in PokeSearchInViewport(). - if (!intent.m_isDelayed || static_cast(i) == search::Mode::Viewport) - continue; - SetViewportIfPossible(intent.m_params); - SetCurrentPositionIfPossible(intent.m_params); - Search(intent); - } - } - - PokeSearchInViewport(forceSearchInViewport); + GetSearchAPI().OnViewportChanged(GetCurrentViewport()); m_trafficManager.UpdateViewport(m_currentModelView); m_localAdsManager.UpdateViewport(m_currentModelView); @@ -355,7 +330,7 @@ void Framework::Migrate(bool keepDownloaded) OnDestroyGLContext(); } m_selectedFeature = FeatureID(); - m_searchEngine.reset(); + m_searchAPI.reset(); m_infoGetter.reset(); m_taxiEngine.reset(); m_cityFinder.reset(); @@ -367,7 +342,7 @@ void Framework::Migrate(bool keepDownloaded) GetStorage().Migrate(keepDownloaded ? existedCountries : TCountriesVec()); InitCountryInfoGetter(); InitUGC(); - InitSearchEngine(); + InitSearchAPI(); InitCityFinder(); InitTaxiEngine(); RegisterAllMaps(); @@ -450,8 +425,8 @@ Framework::Framework(FrameworkParams const & params) InitUGC(); LOG(LDEBUG, ("UGC initialized")); - InitSearchEngine(); - LOG(LDEBUG, ("Search engine initialized")); + InitSearchAPI(); + LOG(LDEBUG, ("Search API initialized")); InitCityFinder(); InitTaxiEngine(); @@ -462,7 +437,7 @@ Framework::Framework(FrameworkParams const & params) LOG(LDEBUG, ("Maps initialized")); // Need to reload cities boundaries because maps in indexer were updated. - m_searchEngine->LoadCitiesBoundaries(); + GetSearchAPI().LoadCitiesBoundaries(); // Init storage with needed callback. m_storage.Init( @@ -591,7 +566,7 @@ void Framework::OnCountryFileDownloaded(storage::TCountryId const & countryId, s m_trafficManager.Invalidate(); m_localAdsManager.OnDownloadCountry(countryId); InvalidateRect(rect); - m_searchEngine->ClearCaches(); + GetSearchAPI().ClearCaches(); } bool Framework::OnCountryFileDelete(storage::TCountryId const & countryId, storage::TLocalFilePtr const localFile) @@ -616,7 +591,7 @@ bool Framework::OnCountryFileDelete(storage::TCountryId const & countryId, stora } InvalidateRect(rect); - m_searchEngine->ClearCaches(); + GetSearchAPI().ClearCaches(); return deferredDelete; } @@ -970,9 +945,9 @@ void Framework::FillSearchResultInfo(SearchMarkPoint const & smp, place_page::In void Framework::FillMyPositionInfo(place_page::Info & info, df::TapInfo const & tapInfo) const { - double lat, lon; - VERIFY(GetCurrentPosition(lat, lon), ()); - info.SetMercator(MercatorBounds::FromLatLon(lat, lon)); + auto const position = GetCurrentPosition(); + VERIFY(position, ()); + info.SetMercator(*position); info.SetIsMyPosition(); info.SetCustomName(m_stringsBundle.GetString("my_position")); @@ -1301,7 +1276,7 @@ void Framework::ClearAllCaches() { m_model.ClearCaches(); m_infoGetter->ClearCaches(); - m_searchEngine->ClearCaches(); + GetSearchAPI().ClearCaches(); } void Framework::OnUpdateCurrentCountry(m2::PointF const & pt, int zoomLevel) @@ -1330,120 +1305,32 @@ void Framework::SetCurrentCountryChangedListener(TCurrentCountryChanged const & void Framework::PokeSearchInViewport(bool forceSearch) { - if (!m_isViewportInitialized || !IsViewportSearchActive()) - return; - - // Copy is intentional here, to skip possible duplicating requests. - auto params = m_searchIntents[static_cast(search::Mode::Viewport)].m_params; - SetViewportIfPossible(params); - SetCurrentPositionIfPossible(params); - Search(params, forceSearch); + return GetSearchAPI().PokeSearchInViewport(forceSearch); } bool Framework::SearchEverywhere(search::EverywhereSearchParams const & params) { - search::SearchParams p; - p.m_query = params.m_query; - p.m_inputLocale = params.m_inputLocale; - p.m_mode = search::Mode::Everywhere; - SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. - p.m_maxNumResults = search::SearchParams::kDefaultNumResultsEverywhere; - p.m_suggestsEnabled = true; - p.m_needAddress = true; - p.m_needHighlighting = true; - p.m_hotelsFilter = params.m_hotelsFilter; - p.m_cianMode = IsCianMode(params.m_query); - - p.m_onResults = search::EverywhereSearchCallback( - static_cast(*this), - [params](search::Results const & results, vector const & isLocalAdsCustomer) { - if (params.m_onResults) - GetPlatform().RunOnGuiThread([params, results, isLocalAdsCustomer]() { - params.m_onResults(results, isLocalAdsCustomer); - }); - }); - SetCurrentPositionIfPossible(p); - return Search(p, true /* forceSearch */); + return GetSearchAPI().SearchEverywhere(params); } bool Framework::SearchInViewport(search::ViewportSearchParams const & params) { - // TODO: delete me after Cian project is finished. - m_cianSearchMode = IsCianMode(params.m_query); - - search::SearchParams p; - p.m_query = params.m_query; - p.m_inputLocale = params.m_inputLocale; - SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. - p.m_maxNumResults = search::SearchParams::kDefaultNumResultsInViewport; - p.m_mode = search::Mode::Viewport; - p.m_suggestsEnabled = false; - p.m_needAddress = false; - p.m_needHighlighting = false; - p.m_hotelsFilter = params.m_hotelsFilter; - p.m_cianMode = m_cianSearchMode; - - p.m_onStarted = [params]() { - if (params.m_onStarted) - GetPlatform().RunOnGuiThread([params]() { params.m_onStarted(); }); - }; - - p.m_onResults = search::ViewportSearchCallback( - static_cast(*this), - [params](search::Results const & results) { - if (results.IsEndMarker() && params.m_onCompleted) - GetPlatform().RunOnGuiThread([params, results]() { params.m_onCompleted(results); }); - }); - SetCurrentPositionIfPossible(p); - - return Search(p, false /* forceSearch */); + return GetSearchAPI().SearchInViewport(params); } bool Framework::SearchInDownloader(DownloaderSearchParams const & params) { - search::SearchParams p; - p.m_query = params.m_query; - p.m_inputLocale = params.m_inputLocale; - SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. - p.m_maxNumResults = search::SearchParams::kDefaultNumResultsEverywhere; - p.m_mode = search::Mode::Downloader; - p.m_suggestsEnabled = false; - p.m_needAddress = false; - p.m_needHighlighting = false; - p.m_onResults = search::DownloaderSearchCallback( - static_cast(*this), m_model.GetIndex(), - GetCountryInfoGetter(), GetStorage(), params); - return Search(p, true /* forceSearch */); -} - -void Framework::SetViewportIfPossible(search::SearchParams & params) -{ - if (m_isViewportInitialized) - params.m_viewport = GetCurrentViewport(); + return GetSearchAPI().SearchInDownloader(params); } void Framework::CancelSearch(search::Mode mode) { - ASSERT_NOT_EQUAL(mode, search::Mode::Count, ()); - - if (mode == search::Mode::Viewport) - { - // TODO: delete me after Cian project is finished. - m_cianSearchMode = false; - - ClearSearchResultsMarks(); - SetDisplacementMode(DisplacementModeManager::SLOT_INTERACTIVE_SEARCH, false /* show */); - } - - auto & intent = m_searchIntents[static_cast(mode)]; - intent.m_params.Clear(); - CancelQuery(intent.m_handle); + return GetSearchAPI().CancelSearch(mode); } void Framework::CancelAllSearches() { - for (size_t i = 0; i < static_cast(search::Mode::Count); ++i) - CancelSearch(static_cast(i)); + return GetSearchAPI().CancelAllSearches(); } void Framework::MemoryWarning() @@ -1491,20 +1378,6 @@ void Framework::EnterForeground() m_routingManager.SetAllowSendingPoints(true); } -bool Framework::GetCurrentPosition(double & lat, double & lon) const -{ - m2::PointD pos; - MyPositionMarkPoint * myPosMark = UserMarkContainer::UserMarkForMyPostion(); - if (!myPosMark->HasPosition()) - return false; - - pos = myPosMark->GetPivot(); - - lat = MercatorBounds::YToLat(pos.y); - lon = MercatorBounds::XToLon(pos.x); - return true; -} - void Framework::InitCountryInfoGetter() { ASSERT(!m_infoGetter.get(), ("InitCountryInfoGetter() must be called only once.")); @@ -1520,21 +1393,18 @@ void Framework::InitUGC() m_ugcApi = make_unique(m_model.GetIndex()); } -void Framework::InitSearchEngine() +void Framework::InitSearchAPI() { - ASSERT(!m_searchEngine.get(), ("InitSearchEngine() must be called only once.")); + ASSERT(!m_searchAPI.get(), ("InitSearchAPI() must be called only once.")); ASSERT(m_infoGetter.get(), ()); try { - search::Engine::Params params; - params.m_locale = languages::GetCurrentOrig(); - params.m_numThreads = 1; - m_searchEngine = make_unique(const_cast(m_model.GetIndex()), - GetDefaultCategories(), *m_infoGetter, params); + m_searchAPI = make_unique(const_cast(m_model.GetIndex()), m_storage, + *m_infoGetter, static_cast(*this)); } catch (RootException const & e) { - LOG(LCRITICAL, ("Can't load needed resources for search::Engine:", e.Msg())); + LOG(LCRITICAL, ("Can't load needed resources for SearchAPI:", e.Msg())); } } @@ -1602,19 +1472,27 @@ Framework::DoAfterUpdate Framework::ToDoAfterUpdate() const return DoAfterUpdate::AskForUpdateMaps; } +SearchAPI & Framework::GetSearchAPI() +{ + ASSERT(m_searchAPI != nullptr, ("Search API is not initialized.")); + return *m_searchAPI; +} + +SearchAPI const & Framework::GetSearchAPI() const +{ + ASSERT(m_searchAPI != nullptr, ("Search API is not initialized.")); + return *m_searchAPI; +} + search::DisplayedCategories const & Framework::GetDisplayedCategories() { ASSERT(m_displayedCategories, ()); ASSERT(m_cityFinder, ()); - ms::LatLon latlon; string city; - if (GetCurrentPosition(latlon.lat, latlon.lon)) - { - city = m_cityFinder->GetCityName(MercatorBounds::FromLatLon(latlon), - StringUtf8Multilang::kEnglishCode); - } + if (auto const position = GetCurrentPosition()) + city = m_cityFinder->GetCityName(*position, StringUtf8Multilang::kEnglishCode); CianModifier cianModifier(city); m_displayedCategories->Modify(cianModifier); @@ -1622,96 +1500,6 @@ search::DisplayedCategories const & Framework::GetDisplayedCategories() return *m_displayedCategories; } -bool Framework::Search(search::SearchParams const & params, bool forceSearch) -{ - if (ParseDrapeDebugCommand(params.m_query)) - return false; - - auto const mode = params.m_mode; - auto & intent = m_searchIntents[static_cast(mode)]; - -#ifdef FIXED_LOCATION - search::SearchParams rParams(params); - if (params.IsValidPosition()) - { - m_fixedPos.GetLat(rParams.m_lat); - m_fixedPos.GetLon(rParams.m_lon); - } -#else - search::SearchParams const & rParams = params; -#endif - - ParseSetGpsTrackMinAccuracyCommand(params.m_query); - if (ParseEditorDebugCommand(params)) - return true; - - if (!forceSearch && QueryMayBeSkipped(intent, rParams)) - return false; - - intent.m_params = rParams; - // Cancels previous search request (if any) and initiates new search request. - CancelQuery(intent.m_handle); - - double const eps = m_searchMarks.GetMaxDimension(m_currentModelView); - intent.m_params.m_minDistanceOnMapBetweenResults = eps; - - Search(intent); - - return true; -} - -void Framework::Search(SearchIntent & intent) const -{ - if (!m_isViewportInitialized) - { - intent.m_isDelayed = true; - return; - } - - intent.m_handle = m_searchEngine->Search(intent.m_params); - intent.m_isDelayed = false; -} - -void Framework::SetCurrentPositionIfPossible(search::SearchParams & params) -{ - double lat; - double lon; - if (GetCurrentPosition(lat, lon)) - params.m_position = MercatorBounds::FromLatLon(lat, lon); -} - -bool Framework::QueryMayBeSkipped(SearchIntent const & intent, - search::SearchParams const & params) const -{ - auto const & lastParams = intent.m_params; - auto const & lastViewport = lastParams.m_viewport; - - auto const & viewport = params.m_viewport; - - if (!lastParams.IsEqualCommon(params)) - return false; - if (!lastViewport.IsValid() || - !search::IsEqualMercator(lastViewport, viewport, kDistEqualQueryMeters)) - { - return false; - } - - if (lastParams.m_position && params.m_position && - MercatorBounds::DistanceOnEarth(*lastParams.m_position, *params.m_position) > - kDistEqualQueryMeters) - { - return false; - } - - if (static_cast(lastParams.m_position) != static_cast(params.m_position)) - return false; - - if (!search::hotels_filter::Rule::IsIdentical(lastParams.m_hotelsFilter, params.m_hotelsFilter)) - return false; - - return true; -} - void Framework::SelectSearchResult(search::Result const & result, bool animation) { place_page::Info info; @@ -1850,7 +1638,7 @@ void Framework::FillSearchResultsMarks(search::Results::ConstIter begin, } // TODO: delete me after Cian project is finished. - if (m_cianSearchMode) + if (GetSearchAPI().IsCianSearchMode()) { mark->SetMarkType(SearchMarkType::Cian); continue; @@ -2434,13 +2222,17 @@ void Framework::OnTapEvent(TapEvent const & tapEvent) m_lastTapEvent = make_unique(tapEvent); { // Log statistics event. - ms::LatLon const ll = info.GetLatLon(); - double myLat, myLon; + auto const ll = info.GetLatLon(); double metersToTap = -1; if (info.IsMyPosition()) + { metersToTap = 0; - else if (GetCurrentPosition(myLat, myLon)) - metersToTap = ms::DistanceOnEarth(myLat, myLon, ll.lat, ll.lon); + } + else if (auto const position = GetCurrentPosition()) + { + auto const tapPoint = MercatorBounds::FromLatLon(ll); + metersToTap = MercatorBounds::DistanceOnEarth(*position, tapPoint); + } alohalytics::TStringMap kv = {{"longTap", tapInfo.m_isLong ? "1" : "0"}, {"title", info.GetTitle()}, @@ -2449,10 +2241,14 @@ void Framework::OnTapEvent(TapEvent const & tapEvent) if (info.IsFeature()) kv["types"] = DebugPrint(info.GetTypes()); // Older version of statistics used "$GetUserMark" event. - alohalytics::Stats::Instance().LogEvent("$SelectMapObject", kv, alohalytics::Location::FromLatLon(ll.lat, ll.lon)); + alohalytics::Stats::Instance().LogEvent("$SelectMapObject", kv, + alohalytics::Location::FromLatLon(ll.lat, ll.lon)); if (info.GetSponsoredType() == SponsoredType::Booking) - GetPlatform().GetMarketingService().SendMarketingEvent(marketing::kPlacepageHotelBook, {{"provider", "booking.com"}}); + { + GetPlatform().GetMarketingService().SendMarketingEvent(marketing::kPlacepageHotelBook, + {{"provider", "booking.com"}}); + } } SetPlacePageLocation(info); @@ -3329,6 +3125,42 @@ ads::Engine const & Framework::GetAdsEngine() const return *m_adsEngine; } +void Framework::RunUITask(function fn) { GetPlatform().RunOnGuiThread(move(fn)); } + +void Framework::SetSearchDisplacementModeEnabled(bool enabled) +{ + SetDisplacementMode(DisplacementModeManager::SLOT_INTERACTIVE_SEARCH, enabled /* show */); +} + +void Framework::ShowViewportSearchResults(search::Results const & results) +{ + FillSearchResultsMarks(results); +} + +void Framework::ClearViewportSearchResults() { ClearSearchResultsMarks(); } + +boost::optional Framework::GetCurrentPosition() const +{ + m2::PointD position; + MyPositionMarkPoint * myPosMark = UserMarkContainer::UserMarkForMyPostion(); + if (!myPosMark->HasPosition()) + return {}; + + position = myPosMark->GetPivot(); + return position; +} + +bool Framework::ParseMagicSearchQuery(search::SearchParams const & params) +{ + if (ParseDrapeDebugCommand(params.m_query)) + return true; + if (ParseSetGpsTrackMinAccuracyCommand(params.m_query)) + return true; + if (ParseEditorDebugCommand(params)) + return true; + return false; +} + bool Framework::IsLocalAdsCustomer(search::Result const & result) const { if (result.IsSuggest()) @@ -3338,6 +3170,11 @@ bool Framework::IsLocalAdsCustomer(search::Result const & result) const return m_localAdsManager.Contains(result.GetFeatureID()); } +double Framework::GetMinDistanceBetweenResults() const +{ + return m_searchMarks.GetMaxDimension(m_currentModelView); +} + vector Framework::GetMwmsByRect(m2::RectD const & rect, bool rough) const { vector result; diff --git a/map/framework.hpp b/map/framework.hpp index 5546a1012c..eca64cd6fe 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -10,6 +10,7 @@ #include "map/place_page_info.hpp" #include "map/routing_manager.hpp" #include "map/routing_mark.hpp" +#include "map/search_api.hpp" #include "map/search_mark.hpp" #include "map/track.hpp" #include "map/traffic_manager.hpp" @@ -33,13 +34,9 @@ #include "search/city_finder.hpp" #include "search/displayed_categories.hpp" -#include "search/downloader_search_callback.hpp" -#include "search/engine.hpp" -#include "search/everywhere_search_callback.hpp" #include "search/mode.hpp" #include "search/query_saver.hpp" #include "search/result.hpp" -#include "search/viewport_search_callback.hpp" #include "storage/downloading_policy.hpp" #include "storage/storage.hpp" @@ -75,7 +72,8 @@ #include "std/target_os.hpp" #include "std/unique_ptr.hpp" #include "std/vector.hpp" -#include "std/weak_ptr.hpp" + +#include "boost/optional.hpp" namespace osm { @@ -122,10 +120,7 @@ struct FrameworkParams {} }; -class Framework : public search::ViewportSearchCallback::Delegate, - public search::DownloaderSearchCallback::Delegate, - public search::EverywhereSearchCallback::Delegate, - public RoutingManager::Delegate +class Framework : public SearchAPI::Delegate, public RoutingManager::Delegate { DISALLOW_COPY(Framework); @@ -166,7 +161,7 @@ protected: unique_ptr m_ugcApi; - unique_ptr m_searchEngine; + unique_ptr m_searchAPI; search::QuerySaver m_searchQuerySaver; @@ -304,8 +299,8 @@ public: Index const & GetIndex() const { return m_model.GetIndex(); } - search::Engine & GetSearchEngine() { return *m_searchEngine; } - search::Engine const & GetSearchEngine() const { return *m_searchEngine; } + SearchAPI & GetSearchAPI(); + SearchAPI const & GetSearchAPI() const; /// @name Bookmarks, Tracks and other UserMarks //@{ @@ -346,29 +341,16 @@ public: ads::Engine const & GetAdsEngine() const; -protected: - // search::ViewportSearchCallback::Delegate overrides: - void RunUITask(function fn) override { GetPlatform().RunOnGuiThread(move(fn)); } - - void SetHotelDisplacementMode() override - { - SetDisplacementMode(DisplacementModeManager::SLOT_INTERACTIVE_SEARCH, true /* show */); - } - - bool IsViewportSearchActive() const override - { - return !m_searchIntents[static_cast(search::Mode::Viewport)].m_params.m_query.empty(); - } - - void ShowViewportSearchResults(search::Results const & results) override - { - FillSearchResultsMarks(results); - } - - void ClearViewportSearchResults() override { ClearSearchResultsMarks(); } - - // EverywhereSearchCallback::Delegate overrides: +public: + // SearchAPI::Delegate overrides: + void RunUITask(function fn) override; + void SetSearchDisplacementModeEnabled(bool enabled) override; + void ShowViewportSearchResults(search::Results const & results) override; + void ClearViewportSearchResults() override; + boost::optional GetCurrentPosition() const override; + bool ParseMagicSearchQuery(search::SearchParams const & params) override; bool IsLocalAdsCustomer(search::Result const & result) const override; + double GetMinDistanceBetweenResults() const override; private: void ActivateMapSelection(bool needAnimation, @@ -515,40 +497,19 @@ public: void SetDisplacementMode(DisplacementModeManager::Slot slot, bool show); private: - struct SearchIntent - { - search::SearchParams m_params; - weak_ptr m_handle; - bool m_isDelayed = false; - }; - void InitCountryInfoGetter(); void InitUGC(); - void InitSearchEngine(); + void InitSearchAPI(); DisplacementModeManager m_displacementModeManager; bool m_connectToGpsTrack; // need to connect to tracker when Drape is being constructed - void SetCurrentPositionIfPossible(search::SearchParams & params); - void OnUpdateCurrentCountry(m2::PointF const & pt, int zoomLevel); storage::TCountryId m_lastReportedCountry; TCurrentCountryChanged m_currentCountryChanged; - // Descriptions of last search queries for different modes. May be - // used for search requests skipping. This field is not guarded - // because it must be used from the UI thread only. - SearchIntent m_searchIntents[static_cast(search::Mode::Count)]; - - bool Search(search::SearchParams const & params, bool forceSearch); - void Search(SearchIntent & intent) const; - - // Returns true when |params| is almost the same as the latest - // search query params in |intent|. - bool QueryMayBeSkipped(SearchIntent const & intent, search::SearchParams const & params) const; - void OnUpdateGpsTrackPointsCallback(vector> && toAdd, pair const & toRemove); @@ -570,13 +531,9 @@ public: // Search for maps by countries or cities. bool SearchInDownloader(storage::DownloaderSearchParams const & params); - void SetViewportIfPossible(search::SearchParams & params); - void CancelSearch(search::Mode mode); void CancelAllSearches(); - bool GetCurrentPosition(double & lat, double & lon) const; - // Moves viewport to the search result and taps on it. void SelectSearchResult(search::Result const & res, bool animation); @@ -846,9 +803,6 @@ private: // taxi::Engine and, therefore, destroyed after taxi::Engine. unique_ptr m_taxiEngine; - // TODO: delete me after Cian project is finished. - bool m_cianSearchMode = false; - void InitCityFinder(); void InitTaxiEngine(); diff --git a/map/map.pro b/map/map.pro index a5c113ac63..f5f06e90e0 100644 --- a/map/map.pro +++ b/map/map.pro @@ -34,6 +34,7 @@ HEADERS += \ reachable_by_taxi_checker.hpp \ routing_manager.hpp \ routing_mark.hpp \ + search_api.hpp \ search_mark.hpp \ taxi_delegate.hpp \ track.hpp \ @@ -69,6 +70,7 @@ SOURCES += \ reachable_by_taxi_checker.cpp \ routing_manager.cpp \ routing_mark.cpp \ + search_api.cpp \ search_mark.cpp \ taxi_delegate.cpp \ track.cpp \ diff --git a/map/search_api.cpp b/map/search_api.cpp new file mode 100644 index 0000000000..714607c3e0 --- /dev/null +++ b/map/search_api.cpp @@ -0,0 +1,295 @@ +#include "map/search_api.hpp" + +#include "search/everywhere_search_params.hpp" +#include "search/geometry_utils.hpp" +#include "search/hotels_filter.hpp" +#include "search/viewport_search_params.hpp" + +#include "storage/downloader_search_params.hpp" + +#include "platform/preferred_languages.hpp" + +#include "geometry/mercator.hpp" + +#include "base/string_utils.hpp" + +#include + +using namespace search; +using namespace std; + +namespace +{ +double const kDistEqualQueryMeters = 100.0; + +// Cancels search query by |handle|. +void CancelQuery(weak_ptr & handle) +{ + if (auto queryHandle = handle.lock()) + queryHandle->Cancel(); + handle.reset(); +} + +bool IsCianMode(string query) +{ + strings::Trim(query); + strings::AsciiToLower(query); + return query == "cian"; +} +} // namespace + +SearchAPI::SearchAPI(Index & index, storage::Storage const & storage, + storage::CountryInfoGetter const & infoGetter, Delegate & delegate) + : m_index(index) + , m_storage(storage) + , m_infoGetter(infoGetter) + , m_delegate(delegate) + , m_engine(m_index, GetDefaultCategories(), m_infoGetter, + Engine::Params(languages::GetCurrentOrig() /* locale */, 1 /* params.m_numThreads */)) +{ +} + +void SearchAPI::OnViewportChanged(m2::RectD const & viewport) +{ + m_viewport = viewport; + + auto const forceSearchInViewport = !m_isViewportInitialized; + if (!m_isViewportInitialized) + { + m_isViewportInitialized = true; + for (size_t i = 0; i < static_cast(Mode::Count); i++) + { + auto & intent = m_searchIntents[i]; + // Viewport search will be triggered below, in PokeSearchInViewport(). + if (!intent.m_isDelayed || static_cast(i) == Mode::Viewport) + continue; + intent.m_params.m_viewport = m_viewport; + intent.m_params.m_position = m_delegate.GetCurrentPosition(); + Search(intent); + } + } + + PokeSearchInViewport(forceSearchInViewport); +} + +bool SearchAPI::SearchEverywhere(EverywhereSearchParams const & params) +{ + SearchParams p; + p.m_query = params.m_query; + p.m_inputLocale = params.m_inputLocale; + p.m_mode = Mode::Everywhere; + p.m_position = m_delegate.GetCurrentPosition(); + SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. + p.m_maxNumResults = SearchParams::kDefaultNumResultsEverywhere; + p.m_suggestsEnabled = true; + p.m_needAddress = true; + p.m_needHighlighting = true; + p.m_hotelsFilter = params.m_hotelsFilter; + p.m_cianMode = IsCianMode(params.m_query); + + p.m_onResults = EverywhereSearchCallback( + static_cast(*this), + [this, params](Results const & results, vector const & isLocalAdsCustomer) { + if (params.m_onResults) + RunUITask([params, results, isLocalAdsCustomer]() { + params.m_onResults(results, isLocalAdsCustomer); + }); + }); + + return Search(p, true /* forceSearch */); +} + +bool SearchAPI::SearchInViewport(ViewportSearchParams const & params) +{ + // TODO: delete me after Cian project is finished. + m_cianSearchMode = IsCianMode(params.m_query); + + SearchParams p; + p.m_query = params.m_query; + p.m_inputLocale = params.m_inputLocale; + p.m_position = m_delegate.GetCurrentPosition(); + SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. + p.m_maxNumResults = SearchParams::kDefaultNumResultsInViewport; + p.m_mode = Mode::Viewport; + p.m_suggestsEnabled = false; + p.m_needAddress = false; + p.m_needHighlighting = false; + p.m_hotelsFilter = params.m_hotelsFilter; + p.m_cianMode = m_cianSearchMode; + + p.m_onStarted = [this, params]() { + if (params.m_onStarted) + RunUITask([params]() { params.m_onStarted(); }); + }; + + p.m_onResults = ViewportSearchCallback( + static_cast(*this), + [this, params](Results const & results) { + if (results.IsEndMarker() && params.m_onCompleted) + RunUITask([params, results]() { params.m_onCompleted(results); }); + }); + + return Search(p, false /* forceSearch */); +} + +bool SearchAPI::SearchInDownloader(storage::DownloaderSearchParams const & params) +{ + SearchParams p; + p.m_query = params.m_query; + p.m_inputLocale = params.m_inputLocale; + p.m_position = m_delegate.GetCurrentPosition(); + SetViewportIfPossible(p); // Search request will be delayed if viewport is not available. + p.m_maxNumResults = SearchParams::kDefaultNumResultsEverywhere; + p.m_mode = Mode::Downloader; + p.m_suggestsEnabled = false; + p.m_needAddress = false; + p.m_needHighlighting = false; + + p.m_onResults = DownloaderSearchCallback(static_cast(*this), + m_index, m_infoGetter, m_storage, params); + + return Search(p, true /* forceSearch */); +} + +void SearchAPI::PokeSearchInViewport(bool forceSearch) +{ + if (!m_isViewportInitialized || !IsViewportSearchActive()) + return; + + // Copy is intentional here, to skip possible duplicating requests. + auto params = m_searchIntents[static_cast(Mode::Viewport)].m_params; + SetViewportIfPossible(params); + params.m_position = m_delegate.GetCurrentPosition(); + Search(params, forceSearch); +} + +void SearchAPI::CancelSearch(Mode mode) +{ + ASSERT_NOT_EQUAL(mode, Mode::Count, ()); + + if (mode == Mode::Viewport) + { + // TODO: delete me after Cian project is finished. + m_cianSearchMode = false; + + m_delegate.ClearViewportSearchResults(); + m_delegate.SetSearchDisplacementModeEnabled(false /* enabled */); + } + + auto & intent = m_searchIntents[static_cast(mode)]; + intent.m_params.Clear(); + CancelQuery(intent.m_handle); +} + +void SearchAPI::CancelAllSearches() +{ + for (size_t i = 0; i < static_cast(Mode::Count); ++i) + CancelSearch(static_cast(i)); +} + +void SearchAPI::RunUITask(std::function fn) { return m_delegate.RunUITask(fn); } + +void SearchAPI::SetHotelDisplacementMode() +{ + return m_delegate.SetSearchDisplacementModeEnabled(true); +} + +bool SearchAPI::IsViewportSearchActive() const +{ + return !m_searchIntents[static_cast(Mode::Viewport)].m_params.m_query.empty(); +} + +void SearchAPI::ShowViewportSearchResults(Results const & results) +{ + return m_delegate.ShowViewportSearchResults(results); +} + +void SearchAPI::ClearViewportSearchResults() { return m_delegate.ClearViewportSearchResults(); } + +bool SearchAPI::IsLocalAdsCustomer(Result const & result) const +{ + return m_delegate.IsLocalAdsCustomer(result); +} + +bool SearchAPI::Search(SearchParams const & params, bool forceSearch) +{ + if (m_delegate.ParseMagicSearchQuery(params)) + return false; + + auto const mode = params.m_mode; + auto & intent = m_searchIntents[static_cast(mode)]; + +#ifdef FIXED_LOCATION + SearchParams currParams = params; + if (currParams.IsValidPosition()) + { + // TODO (@y): fix this + m_fixedPos.GetLat(currParams.m_lat); + m_fixedPos.GetLon(rParams.m_lon); + } +#else + SearchParams const & currParams = params; +#endif + + if (!forceSearch && QueryMayBeSkipped(intent.m_params, currParams)) + return false; + + intent.m_params = currParams; + // Cancels previous search request (if any) and initiates new search request. + CancelQuery(intent.m_handle); + + intent.m_params.m_minDistanceOnMapBetweenResults = m_delegate.GetMinDistanceBetweenResults(); + + Search(intent); + + return true; +} + +void SearchAPI::Search(SearchIntent & intent) +{ + if (!m_isViewportInitialized) + { + intent.m_isDelayed = true; + return; + } + + intent.m_handle = m_engine.Search(intent.m_params); + intent.m_isDelayed = false; +} + +void SearchAPI::SetViewportIfPossible(SearchParams & params) +{ + if (m_isViewportInitialized) + params.m_viewport = m_viewport; +} + +bool SearchAPI::QueryMayBeSkipped(SearchParams const & prevParams, + SearchParams const & currParams) const +{ + auto const & prevViewport = prevParams.m_viewport; + auto const & currViewport = currParams.m_viewport; + + if (!prevParams.IsEqualCommon(currParams)) + return false; + + if (!prevViewport.IsValid() || + !IsEqualMercator(prevViewport, currViewport, kDistEqualQueryMeters)) + { + return false; + } + + if (prevParams.m_position && currParams.m_position && + MercatorBounds::DistanceOnEarth(*prevParams.m_position, *currParams.m_position) > + kDistEqualQueryMeters) + { + return false; + } + + if (static_cast(prevParams.m_position) != static_cast(currParams.m_position)) + return false; + + if (!hotels_filter::Rule::IsIdentical(prevParams.m_hotelsFilter, currParams.m_hotelsFilter)) + return false; + + return true; +} diff --git a/map/search_api.hpp b/map/search_api.hpp new file mode 100644 index 0000000000..5f438e1bf8 --- /dev/null +++ b/map/search_api.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include "search/downloader_search_callback.hpp" +#include "search/engine.hpp" +#include "search/everywhere_search_callback.hpp" +#include "search/mode.hpp" +#include "search/result.hpp" +#include "search/search_params.hpp" +#include "search/viewport_search_callback.hpp" + +#include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" + +#include +#include +#include +#include + +#include + +namespace search +{ +struct EverywhereSearchParams; +struct ViewportSearchParams; +} + +namespace storage +{ +class CountryInfoGetter; +class Storage; +struct DownloaderSearchParams; +} + +class SearchAPI : public search::DownloaderSearchCallback::Delegate, + public search::EverywhereSearchCallback::Delegate, + public search::ViewportSearchCallback::Delegate +{ +public: + struct Delegate + { + virtual ~Delegate() = default; + + virtual void RunUITask(std::function /* fn */) {} + virtual void SetSearchDisplacementModeEnabled(bool /* enabled */) {} + virtual void ShowViewportSearchResults(search::Results const & /* results */) {} + virtual void ClearViewportSearchResults() {} + + virtual boost::optional GetCurrentPosition() const { return {}; }; + virtual bool ParseMagicSearchQuery(search::SearchParams const & /* params */) { return false; }; + virtual double GetMinDistanceBetweenResults() const { return 0.0; }; + virtual bool IsLocalAdsCustomer(search::Result const & /* result */) const { return false; } + }; + + SearchAPI(Index & index, storage::Storage const & storage, + storage::CountryInfoGetter const & infoGetter, Delegate & delegate); + virtual ~SearchAPI() = default; + + void OnViewportChanged(m2::RectD const & viewport); + + void LoadCitiesBoundaries() { m_engine.LoadCitiesBoundaries(); } + + bool IsCianSearchMode() const { return m_cianSearchMode; } + + // Search everywhere. + bool SearchEverywhere(search::EverywhereSearchParams const & params); + + // Search in the viewport. + bool SearchInViewport(search::ViewportSearchParams const & params); + + // Search for maps by countries or cities. + bool SearchInDownloader(storage::DownloaderSearchParams const & params); + + search::Engine & GetEngine() { return m_engine; } + search::Engine const & GetEngine() const { return m_engine; } + + // When search in viewport is active or delayed, restarts search in + // viewport. When |forceSearch| is false, request is skipped when it + // is similar to the previous request in the current + // search-in-viewport session. + void PokeSearchInViewport(bool forceSearch = true); + + void CancelSearch(search::Mode mode); + void CancelAllSearches(); + void ClearCaches() { return m_engine.ClearCaches(); } + + // *SearchCallback::Delegate overrides: + void RunUITask(std::function fn) override; + void SetHotelDisplacementMode() override; + bool IsViewportSearchActive() const override; + void ShowViewportSearchResults(search::Results const & results) override; + void ClearViewportSearchResults() override; + bool IsLocalAdsCustomer(search::Result const & result) const override; + +private: + struct SearchIntent + { + search::SearchParams m_params; + std::weak_ptr m_handle; + bool m_isDelayed = false; + }; + + bool Search(search::SearchParams const & params, bool forceSearch); + void Search(SearchIntent & intent); + + void SetViewportIfPossible(search::SearchParams & params); + + bool QueryMayBeSkipped(search::SearchParams const & prevParams, + search::SearchParams const & currParams) const; + + Index & m_index; + storage::Storage const & m_storage; + storage::CountryInfoGetter const & m_infoGetter; + Delegate & m_delegate; + + search::Engine m_engine; + + // Descriptions of last search queries for different modes. May be + // used for search requests skipping. This field is not guarded + // because it must be used from the UI thread only. + SearchIntent m_searchIntents[static_cast(search::Mode::Count)]; + + m2::RectD m_viewport; + bool m_isViewportInitialized = false; + + // TODO (@y, @m): delete me after Cian project is finished. + bool m_cianSearchMode = false; +}; diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index b42e848110..5db6bb0329 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -277,11 +277,12 @@ bool DrawWidget::Search(search::EverywhereSearchParams const & params) string DrawWidget::GetDistance(search::Result const & res) const { string dist; - double lat, lon; - if (m_framework.GetCurrentPosition(lat, lon)) + if (auto const position = m_framework.GetCurrentPosition()) { + auto const ll = MercatorBounds::ToLatLon(*position); double dummy; - (void) m_framework.GetDistanceAndAzimut(res.GetFeatureCenter(), lat, lon, -1.0, dist, dummy); + (void)m_framework.GetDistanceAndAzimut(res.GetFeatureCenter(), ll.lat, ll.lon, -1.0, dist, + dummy); } return dist; } diff --git a/search/search_quality/assessment_tool/main_model.cpp b/search/search_quality/assessment_tool/main_model.cpp index b9c988841e..a4f3b30aa7 100644 --- a/search/search_quality/assessment_tool/main_model.cpp +++ b/search/search_quality/assessment_tool/main_model.cpp @@ -136,7 +136,7 @@ void MainModel::OnSampleSelected(int index) return; } - auto & engine = m_framework.GetSearchEngine(); + auto & engine = m_framework.GetSearchAPI().GetEngine(); { search::SearchParams params; sample.FillSearchParams(params);