diff --git a/search/search_query.cpp b/search/search_query.cpp index b29bd6a6e8..479a96b243 100644 --- a/search/search_query.cpp +++ b/search/search_query.cpp @@ -708,8 +708,10 @@ Query::Params::Params(Query const & q, bool isLocalities/* = false*/) FillLanguages(q); } -void Query::Params::EraseTokens(vector const & eraseInds) +void Query::Params::EraseTokens(vector & eraseInds) { + eraseInds.erase(unique(eraseInds.begin(), eraseInds.end()), eraseInds.end()); + // fill temporary vector vector newTokens; @@ -741,6 +743,111 @@ void Query::Params::EraseTokens(vector const & eraseInds) } } +template void Query::Params::ForEachToken(ToDo toDo) +{ + size_t const count = m_tokens.size(); + for (size_t i = 0; i < count; ++i) + { + ASSERT ( !m_tokens[i].empty(), () ); + ASSERT ( !m_tokens[i].front().empty(), () ); + toDo(m_tokens[i].front(), i); + } + + if (!m_prefixTokens.empty()) + { + ASSERT ( !m_prefixTokens.front().empty(), () ); + toDo(m_prefixTokens.front(), count); + } +} + +namespace +{ + bool IsNumber(strings::UniString const & s) + { + for (size_t i = 0; i < s.size(); ++i) + if (!isdigit(s[i])) + return false; + return true; + } + + class DoStoreNumbers + { + vector & m_vec; + public: + DoStoreNumbers(vector & vec) : m_vec(vec) {} + void operator() (Query::Params::StringT const & s, size_t i) + { + /// @todo Do smart filtering of house numbers and zipcodes. + if (IsNumber(s)) + m_vec.push_back(i); + } + }; + + class DoAddStreetSynonyms + { + Query::Params & m_params; + + Query::Params::TokensVectorT & GetTokens(size_t i) + { + size_t const count = m_params.m_tokens.size(); + if (i < count) + return m_params.m_tokens[i]; + else + { + ASSERT_EQUAL ( i, count, () ); + return m_params.m_prefixTokens; + } + } + + void AddSynonym(size_t i, string const & sym) + { + GetTokens(i).push_back(strings::MakeUniString(sym)); + } + + public: + DoAddStreetSynonyms(Query::Params & params) : m_params(params) {} + + void operator() (Query::Params::StringT const & s, size_t i) + { + if (s.size() <= 2) + { + string const ss = strings::ToUtf8(strings::MakeLowerCase(s)); + + // All synonyms should be lowercase! + if (ss == "n") + AddSynonym(i, "north"); + else if (ss == "w") + AddSynonym(i, "west"); + else if (ss == "s") + AddSynonym(i, "south"); + else if (ss == "e") + AddSynonym(i, "east"); + else if (ss == "nw") + AddSynonym(i, "northwest"); + else if (ss == "ne") + AddSynonym(i, "northeast"); + else if (ss == "sw") + AddSynonym(i, "southwest"); + else if (ss == "se") + AddSynonym(i, "southeast"); + } + } + }; +} + +void Query::Params::ProcessAddressTokens() +{ + // 1. Do simple stuff - erase all number tokens. + // Assume that USA street name numbers endswith "st, nd, rd, th" suffixes. + + vector toErase; + ForEachToken(DoStoreNumbers(toErase)); + EraseTokens(toErase); + + // 2. Add synonyms for N, NE, NW, etc. + ForEachToken(DoAddStreetSynonyms(*this)); +} + void Query::Params::FillLanguages(Query const & q) { for (int i = 0; i < LANG_COUNT; ++i) @@ -891,6 +998,8 @@ void Query::SearchAddress() if (!params.IsEmpty()) { + params.ProcessAddressTokens(); + SetViewportByIndex(mwmInfo, scales::GetRectForLevel(ADDRESS_SCALE, loc.m_value.m_pt, 1.0), ADDRESS_RECT_ID); /// @todo Hack - do not search for address in World.mwm; Do it better in future. @@ -1427,6 +1536,7 @@ m2::RectD const & Query::GetViewport(int viewportID/* = -1*/) const return m_viewport[1]; } } + m2::PointD Query::GetPosition(int viewportID/* = -1*/) const { if (viewportID == ADDRESS_RECT_ID) diff --git a/search/search_query.hpp b/search/search_query.hpp index b025238d5b..575fd66567 100644 --- a/search/search_query.hpp +++ b/search/search_query.hpp @@ -86,6 +86,34 @@ public: typedef trie::ValueReader::ValueType TrieValueT; + struct Params + { + typedef strings::UniString StringT; + typedef vector TokensVectorT; + typedef unordered_set LangsSetT; + + vector m_tokens; + TokensVectorT m_prefixTokens; + LangsSetT m_langs; + + /// Initialize search params (tokens, languages). + /// @param[in] isLocalities Use true when search for locality in World. + Params(Query const & q, bool isLocalities = false); + + /// @param[in] eraseInds Sorted vector of token's indexes. + void EraseTokens(vector & eraseInds); + + void ProcessAddressTokens(); + + bool IsEmpty() const { return (m_tokens.empty() && m_prefixTokens.empty()); } + bool IsLangExist(int8_t l) const { return (m_langs.count(l) > 0); } + + private: + template void ForEachToken(ToDo toDo); + + void FillLanguages(Query const & q); + }; + private: friend class impl::FeatureLoader; friend class impl::BestNameFinder; @@ -108,29 +136,6 @@ private: void FlushResults(Results & res, void (Results::*pAddFn)(Result const &)); - struct Params - { - typedef vector TokensVectorT; - typedef unordered_set LangsSetT; - - vector m_tokens; - TokensVectorT m_prefixTokens; - LangsSetT m_langs; - - /// Initialize search params (tokens, languages). - /// @param[in] isLocalities Use true when search for locality in World. - Params(Query const & q, bool isLocalities = false); - - /// @param[in] eraseInds Sorted vector of token's indexes. - void EraseTokens(vector const & eraseInds); - - bool IsEmpty() const { return (m_tokens.empty() && m_prefixTokens.empty()); } - bool IsLangExist(int8_t l) const { return (m_langs.count(l) > 0); } - - private: - void FillLanguages(Query const & q); - }; - void SearchAddress(); bool SearchLocality(MwmValue * pMwm, impl::Locality & res);