diff --git a/iphone/Maps/Classes/SearchVC.mm b/iphone/Maps/Classes/SearchVC.mm index ba6db3eb56..08987f044e 100644 --- a/iphone/Maps/Classes/SearchVC.mm +++ b/iphone/Maps/Classes/SearchVC.mm @@ -174,10 +174,10 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) else [self disableRadarMode]; - // Refresh search results with new mode, but - // do not search if text is not entered + // Refresh search results with new mode. NSString * queryString = m_searchBar.text; - if (queryString.length) + // Search even with empty string. + //if (queryString.length) { search::SearchParams params; [self fillSearchParams:params withText:queryString]; @@ -332,19 +332,20 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) { ++g_queryId; - if (searchText.length) + // Search even with empty string. + //if (searchText.length) { search::SearchParams params; [self fillSearchParams:params withText:searchText]; m_framework->Search(params); } - else - { - [g_lastSearchResults release]; - g_lastSearchResults = nil; - // Clean the table - [m_table reloadData]; - } + //else + //{ + // [g_lastSearchResults release]; + // g_lastSearchResults = nil; + // // Clean the table + // [m_table reloadData]; + //} } - (void)onCloseButton:(id)sender @@ -553,10 +554,10 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) - (void)onGpsUpdate:(location::GpsInfo const &)info { - // Refresh search results with newer location, but - // do not search if text is not entered + // Refresh search results with newer location. NSString * queryString = m_searchBar.text; - if (queryString.length) + // Search even with empty string. + //if (queryString.length) { search::SearchParams params; [self fillSearchParams:params withText:queryString]; diff --git a/qt/search_panel.cpp b/qt/search_panel.cpp index e19725e3fd..858c7ffd39 100644 --- a/qt/search_panel.cpp +++ b/qt/search_panel.cpp @@ -167,7 +167,9 @@ void SearchPanel::OnSearchTextChanged(QString const & str) ++m_queryId; QString const normalized = str.normalized(QString::NormalizationForm_KC); - if (!normalized.isEmpty()) + + // search even with empty query + //if (!normalized.isEmpty()) { m_params.m_query = normalized.toUtf8().constData(); m_params.m_callback = bind(&SearchPanel::SearchResultThreadFunc, this, _1, m_queryId); @@ -181,11 +183,11 @@ void SearchPanel::OnSearchTextChanged(QString const & str) m_pClearButton->setFlat(true); m_pClearButton->setVisible(true); } - else - { - // hide X button - m_pClearButton->setVisible(false); - } + //else + //{ + // // hide X button + // m_pClearButton->setVisible(false); + //} } void SearchPanel::OnSearchPanelItemClicked(int row, int) @@ -209,6 +211,7 @@ void SearchPanel::OnSearchPanelItemClicked(int row, int) void SearchPanel::showEvent(QShowEvent *) { connect(m_pDrawWidget, SIGNAL(ViewportChanged()), this, SLOT(OnViewportChanged())); + OnSearchTextChanged(QString()); } void SearchPanel::hideEvent(QHideEvent *) diff --git a/search/lang_keywords_scorer.hpp b/search/lang_keywords_scorer.hpp index 33fa3e4447..8a70fa3d86 100644 --- a/search/lang_keywords_scorer.hpp +++ b/search/lang_keywords_scorer.hpp @@ -15,9 +15,9 @@ public: enum { MAX_SCORE = KeywordMatcher::MAX_SCORE * (NUM_LANG_PRIORITY_TIERS + 1) * (MAX_LANGS_IN_TIER + 1) }; - explicit LangKeywordsScorer(vector > const & languagePriorities, - strings::UniString const * keywords, size_t keywordCount, - strings::UniString const * pPrefix); + LangKeywordsScorer(vector > const & languagePriorities, + strings::UniString const * keywords, size_t keywordCount, + strings::UniString const * pPrefix); uint32_t Score(int8_t lang, string const & name) const; uint32_t Score(int8_t lang, strings::UniString const & name) const; diff --git a/search/search_engine.cpp b/search/search_engine.cpp index deccc1c70f..39ca85666b 100644 --- a/search/search_engine.cpp +++ b/search/search_engine.cpp @@ -96,17 +96,19 @@ namespace return MercatorBounds::MetresToXY(lon, lat, radius); } + enum { VIEWPORT_RECT = 0, NEARME_RECT = 1 }; + /// Check rects for optimal search (avoid duplicating). void AnalizeRects(m2::RectD arrRects[2]) { - if (arrRects[1].IsRectInside(arrRects[0])) - arrRects[0].MakeEmpty(); + if (arrRects[NEARME_RECT].IsRectInside(arrRects[VIEWPORT_RECT])) + arrRects[VIEWPORT_RECT].MakeEmpty(); else { - if (arrRects[0].IsRectInside(arrRects[1]) && - scales::GetScaleLevel(arrRects[0]) + Query::m_scaleDepthSearch >= scales::GetUpperScale()) + if (arrRects[VIEWPORT_RECT].IsRectInside(arrRects[NEARME_RECT]) && + (scales::GetScaleLevel(arrRects[VIEWPORT_RECT]) + Query::m_scaleDepthSearch >= scales::GetUpperScale())) { - arrRects[1].MakeEmpty(); + arrRects[NEARME_RECT].MakeEmpty(); } } } @@ -164,7 +166,7 @@ void Engine::SearchAsync() { threads::MutexGuard updateGuard(m_updateMutex); params = m_params; - arrRects[0] = m_viewport; + arrRects[VIEWPORT_RECT] = m_viewport; } // Initialize query. @@ -173,16 +175,27 @@ void Engine::SearchAsync() { m_pQuery->SetPosition(GetViewportXY(params.m_lat, params.m_lon)); - arrRects[1] = GetViewportRect(params.m_lat, params.m_lon); - - // Do not search in viewport for "NearMe" mode. - if (params.IsNearMeMode()) + if (!params.m_query.empty()) { - worldSearch = false; - arrRects[0].MakeEmpty(); + arrRects[NEARME_RECT] = GetViewportRect(params.m_lat, params.m_lon); + + // Do not search in viewport for "NearMe" mode. + if (params.IsNearMeMode()) + { + worldSearch = false; + arrRects[VIEWPORT_RECT].MakeEmpty(); + } + else + AnalizeRects(arrRects); } else - AnalizeRects(arrRects); + { + // For empty query search in small viewport near position. + arrRects[NEARME_RECT] = GetViewportRect(params.m_lat, params.m_lon, 2500); + + worldSearch = false; + arrRects[VIEWPORT_RECT].MakeEmpty(); + } } else m_pQuery->NullPosition(); @@ -196,7 +209,13 @@ void Engine::SearchAsync() try { - m_pQuery->Search(params.m_query, res); + if (params.m_query.empty()) + { + if (params.m_validPos) + m_pQuery->SearchAllNearMe(res); + } + else + m_pQuery->Search(params.m_query, res); } catch (Query::CancelException const &) { diff --git a/search/search_query.cpp b/search/search_query.cpp index d05e964f30..429ffb3cac 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -181,45 +181,54 @@ namespace }; } +void Query::InitSearch(string const & query) +{ + m_cancel = false; + m_rawQuery = query; + m_uniQuery = NormalizeAndSimplifyString(m_rawQuery); + m_tokens.clear(); + m_prefix.clear(); +} + +void Query::InitKeywordsScorer() +{ + vector > langPriorities(4); + langPriorities[0].push_back(m_currentLang); + langPriorities[1].push_back(m_inputLang); + langPriorities[2].push_back(StringUtf8Multilang::GetLangIndex("int_name")); + langPriorities[2].push_back(StringUtf8Multilang::GetLangIndex("en")); + langPriorities[3].push_back(StringUtf8Multilang::GetLangIndex("default")); + m_pKeywordsScorer.reset(new LangKeywordsScorer(langPriorities, + m_tokens.data(), m_tokens.size(), &m_prefix)); +} + void Query::Search(string const & query, Results & res, unsigned int resultsNeeded) { // Initialize. + + InitSearch(query); + + search::Delimiters delims; + SplitUniString(m_uniQuery, MakeBackInsertFunctor(m_tokens), delims); + + if (!m_tokens.empty() && !delims(strings::LastUniChar(m_rawQuery))) { - m_cancel = false; - m_rawQuery = query; - m_uniQuery = NormalizeAndSimplifyString(m_rawQuery); - m_tokens.clear(); - m_prefix.clear(); + m_prefix.swap(m_tokens.back()); + m_tokens.pop_back(); + } + if (m_tokens.size() > 31) + m_tokens.resize(31); - search::Delimiters delims; - SplitUniString(m_uniQuery, MakeBackInsertFunctor(m_tokens), delims); + InitKeywordsScorer(); - if (!m_tokens.empty() && !delims(strings::LastUniChar(m_rawQuery))) - { - m_prefix.swap(m_tokens.back()); - m_tokens.pop_back(); - } - if (m_tokens.size() > 31) - m_tokens.resize(31); + // Results queue's initialization. + STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare1) ); + STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare2) ); - vector > langPriorities(4); - langPriorities[0].push_back(m_currentLang); - langPriorities[1].push_back(m_inputLang); - langPriorities[2].push_back(StringUtf8Multilang::GetLangIndex("int_name")); - langPriorities[2].push_back(StringUtf8Multilang::GetLangIndex("en")); - langPriorities[3].push_back(StringUtf8Multilang::GetLangIndex("default")); - m_pKeywordsScorer.reset(new LangKeywordsScorer(langPriorities, - m_tokens.data(), m_tokens.size(), &m_prefix)); - - // Results queue's initialization. - STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare1) ); - STATIC_ASSERT ( m_qCount == ARRAY_SIZE(g_arrCompare2) ); - - for (size_t i = 0; i < m_qCount; ++i) - { - m_results[i] = QueueT(2 * resultsNeeded, QueueCompareT(g_arrCompare1[i])); - m_results[i].reserve(2 * resultsNeeded); - } + for (size_t i = 0; i < m_qCount; ++i) + { + m_results[i] = QueueT(2 * resultsNeeded, QueueCompareT(g_arrCompare1[i])); + m_results[i].reserve(2 * resultsNeeded); } // Match (lat, lon). @@ -279,7 +288,7 @@ namespace shared_ptr m_val; public: - explicit IndexedValue(value_type * v) : m_val(v) + IndexedValue(value_type * v) : m_val(v) { for (size_t i = 0; i < m_ind.size(); ++i) m_ind[i] = numeric_limits::max(); @@ -328,6 +337,29 @@ namespace return (r1.GetID() < r2.GetID()); } }; + + template + void RemoveDuplicatingLinear(vector & indV) + { + sort(indV.begin(), indV.end(), ProxyFunctor2()); + indV.erase(unique(indV.begin(), indV.end(), + ProxyFunctor2()), + indV.end()); + } + + template + void AddPreResult2(impl::PreResult2 * p, vector & indV) + { + if (p) + { + // do not insert duplicating results + if (indV.end() == find_if(indV.begin(), indV.end(), + ProxyFunctor1(p))) + indV.push_back(p); + else + delete p; + } + } } namespace impl @@ -359,34 +391,56 @@ namespace impl scoped_ptr m_pFV; + void LoadFeature(pair const & id, + FeatureType & f, string & name, string & country) + { + if (m_pFV.get() == 0 || m_pFV->GetID() != id.first) + m_pFV.reset(new LockedFeaturesVector(*m_query.m_pIndex, id.first)); + + m_pFV->m_vector.Get(id.second, f); + + uint32_t penalty; + m_query.GetBestMatchName(f, penalty, name); + + country = m_pFV->GetCountry(); + } + public: PreResult2Maker(Query & q) : m_query(q) { } // For the best performance, impl::PreResult1 should be sorted by impl::PreResult1::GetID(). - impl::PreResult2 * operator() (impl::PreResult1 const & r) + impl::PreResult2 * operator() (impl::PreResult1 const & res) { - pair const id = r.GetID(); - if (m_pFV.get() == 0 || m_pFV->GetID() != id.first) - m_pFV.reset(new LockedFeaturesVector(*m_query.m_pIndex, id.first)); - FeatureType feature; - m_pFV->m_vector.Get(id.second, feature); + string name, country; + LoadFeature(res.GetID(), feature, name, country); - uint32_t penalty; - string name; - m_query.GetBestMatchName(feature, penalty, name); + return new impl::PreResult2(feature, res, name, country); + } - return new impl::PreResult2(feature, r, name, m_pFV->GetCountry()); + impl::PreResult2 * operator() (pair const & id) + { + FeatureType feature; + string name, country; + LoadFeature(id, feature, name, country); + + if (!name.empty() && !country.empty()) + { + // this results will be with equal rank + impl::PreResult1 res(0, 0, feature.GetLimitRect(FeatureType::WORST_GEOMETRY).Center(), + 0, m_query.m_position, m_query.GetViewport()); + return new impl::PreResult2(feature, res, name, country); + } + else + return 0; } }; } void Query::FlushResults(Results & res) { - typedef impl::PreResult2 ResultT; - vector indV; { @@ -403,27 +457,14 @@ void Query::FlushResults(Results & res) // make PreResult2 vector impl::PreResult2Maker maker(*this); for (PreResultSetT::const_iterator i = theSet.begin(); i != theSet.end(); ++i) - { - ResultT * res = maker(*i); - if (res == 0) continue; - - // do not insert duplicating results - if (indV.end() == find_if(indV.begin(), indV.end(), ProxyFunctor1(res))) - indV.push_back(IndexedValue(res)); - else - delete res; - } + AddPreResult2(maker(*i), indV); } - // remove duplicating linear objects - sort(indV.begin(), indV.end(), ProxyFunctor2()); - indV.erase(unique(indV.begin(), indV.end(), - ProxyFunctor2()), - indV.end()); + RemoveDuplicatingLinear(indV); for (size_t i = 0; i < m_qCount; ++i) { - CompareT comp(g_arrCompare2[i]); + CompareT comp(g_arrCompare2[i]); // sort by needed criteria sort(indV.begin(), indV.end(), comp); @@ -750,4 +791,47 @@ m2::RectD const & Query::GetViewport() const } } +void Query::SearchAllNearMe(Results & res, unsigned int resultsNeeded) +{ + InitSearch(string()); + InitKeywordsScorer(); + + size_t const ind = 1; + ASSERT ( m_viewport[ind].IsValid(), () ); + + vector indV; + + impl::PreResult2Maker maker(*this); + + // load results + for (size_t i = 0; i < m_offsetsInViewport[ind].size(); ++i) + { + if (m_cancel) break; + + for (size_t j = 0; j < m_offsetsInViewport[ind][i].size(); ++j) + { + if (m_cancel) break; + + AddPreResult2(maker(make_pair(i, m_offsetsInViewport[ind][i][j])), indV); + } + } + + if (!m_cancel) + { + RemoveDuplicatingLinear(indV); + + // sort by distance from m_position + sort(indV.begin(), indV.end(), + CompareT(&impl::PreResult2::LessDistance)); + + indV.resize(resultsNeeded); + + // emit results + for (size_t i = 0; i < indV.size(); ++i) + res.AddResult(MakeResult(*(indV[i]))); + } + + for_each(indV.begin(), indV.end(), DeleteFunctor()); +} + } // namespace search diff --git a/search/search_query.hpp b/search/search_query.hpp index 670577ad5b..26585b9213 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -61,6 +61,7 @@ public: inline void SetInputLanguage(int8_t lang) { m_inputLang = lang; } void Search(string const & query, Results & res, unsigned int resultsNeeded = 10); + void SearchAllNearMe(Results & res, unsigned int resultsNeeded = 30); void ClearCache(); @@ -73,6 +74,9 @@ private: friend class impl::BestNameFinder; friend class impl::PreResult2Maker; + void InitSearch(string const & query); + void InitKeywordsScorer(); + void UpdateViewportOffsets(size_t ind); void ClearCache(size_t ind); @@ -144,9 +148,10 @@ private: { template T const & operator() (T const & t) const { return t; } }; - struct RefSmartPtr + struct RefPointer { template typename T::value_type const & operator() (T const & t) const { return *t; } + template T const & operator() (T const * t) const { return *t; } }; typedef CompareT QueueCompareT;