diff options
author | Yuri Gorshenin <y@maps.me> | 2015-10-06 12:54:34 +0300 |
---|---|---|
committer | Yuri Gorshenin <y@maps.me> | 2015-10-06 15:16:29 +0300 |
commit | c47b9d67901618708289e572aecf1867b0a95550 (patch) | |
tree | f7235dc74d3959b69154fb2b9a5f35bead4aa46d /storage | |
parent | ac73294cccf6cf4d64b5b13c01cb2644e9577700 (diff) |
[search] CountryInfoGetter is extracted into individual unit.
Diffstat (limited to 'storage')
-rw-r--r-- | storage/country_decl.hpp | 4 | ||||
-rw-r--r-- | storage/country_info.cpp | 257 | ||||
-rw-r--r-- | storage/country_info.hpp | 84 | ||||
-rw-r--r-- | storage/country_info_getter.cpp | 190 | ||||
-rw-r--r-- | storage/country_info_getter.hpp | 85 | ||||
-rw-r--r-- | storage/storage.pro | 4 | ||||
-rw-r--r-- | storage/storage_tests/country_info_getter_test.cpp (renamed from storage/storage_tests/country_info_test.cpp) | 44 | ||||
-rw-r--r-- | storage/storage_tests/storage_tests.pro | 2 |
8 files changed, 301 insertions, 369 deletions
diff --git a/storage/country_decl.hpp b/storage/country_decl.hpp index 229aaa54a4..732d94d040 100644 --- a/storage/country_decl.hpp +++ b/storage/country_decl.hpp @@ -14,8 +14,8 @@ namespace storage m2::RectD m_rect; CountryDef() {} - CountryDef(string const & name, m2::RectD const & r) - : m_name(name), m_rect(r) + CountryDef(string const & name, m2::RectD const & rect) + : m_name(name), m_rect(rect) { } }; diff --git a/storage/country_info.cpp b/storage/country_info.cpp deleted file mode 100644 index e5d3ac1b72..0000000000 --- a/storage/country_info.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "storage/country_info.hpp" -#include "storage/country_polygon.hpp" -#include "storage/country.hpp" - -#include "indexer/geometry_serialization.hpp" - -#include "geometry/region2d.hpp" - -#include "coding/read_write_utils.hpp" - -#include "base/string_utils.hpp" - - -namespace storage -{ - /* - class LessCountryDef - { - bool CompareStrings(string const & s1, string const & s2) const - { - // Do this stuff because of 'Guinea-Bissau.mwm' goes before 'Guinea.mwm' - // in file system (and in PACKED_POLYGONS_FILE also). - size_t n = min(s1.size(), s2.size()); - return lexicographical_compare(s1.begin(), s1.begin() + n, - s2.begin(), s2.begin() + n); - } - - public: - bool operator() (CountryDef const & r1, string const & r2) const - { - return CompareStrings(r1.m_name, r2); - } - bool operator() (string const & r1, CountryDef const & r2) const - { - return CompareStrings(r1, r2.m_name); - } - bool operator() (CountryDef const & r1, CountryDef const & r2) const - { - return CompareStrings(r1.m_name, r2.m_name); - } - }; - */ - - CountryInfoGetter::CountryInfoGetter(ModelReaderPtr polyR, ModelReaderPtr countryR) - : m_reader(polyR), m_cache(3) - { - ReaderSource<ModelReaderPtr> src(m_reader.GetReader(PACKED_POLYGONS_INFO_TAG)); - rw::Read(src, m_countries); - -/* - // We can't change the order of countries. -#ifdef DEBUG - LessCountryDef check; - for (size_t i = 0; i < m_countries.size() - 1; ++i) - ASSERT ( !check(m_countries[i+1], m_countries[i]), - (m_countries[i].m_name, m_countries[i+1].m_name) ); -#endif -*/ - - string buffer; - countryR.ReadAsString(buffer); - LoadCountryFile2CountryInfo(buffer, m_id2info); - } - - template <class ToDo> - void CountryInfoGetter::ForEachCountry(m2::PointD const & pt, ToDo & toDo) const - { - for (size_t i = 0; i < m_countries.size(); ++i) - if (m_countries[i].m_rect.IsPointInside(pt)) - if (!toDo(i)) - return; - } - - bool CountryInfoGetter::GetByPoint::operator() (size_t id) - { - vector<m2::RegionD> const & rgnV = m_info.GetRegions(id); - - for (size_t i = 0; i < rgnV.size(); ++i) - { - if (rgnV[i].Contains(m_pt)) - { - m_res = id; - return false; - } - } - - return true; - } - - vector<m2::RegionD> const & CountryInfoGetter::GetRegions(size_t id) const - { - bool isFound = false; - vector<m2::RegionD> & rgnV = m_cache.Find(static_cast<uint32_t>(id), isFound); - - if (!isFound) - { - rgnV.clear(); - - // load regions from file - ReaderSource<ModelReaderPtr> src(m_reader.GetReader(strings::to_string(id))); - - uint32_t const count = ReadVarUint<uint32_t>(src); - for (size_t i = 0; i < count; ++i) - { - vector<m2::PointD> points; - serial::LoadOuterPath(src, serial::CodingParams(), points); - rgnV.emplace_back(points.begin(), points.end()); - } - } - - return rgnV; - } - - string CountryInfoGetter::GetRegionFile(m2::PointD const & pt) const - { - GetByPoint doGet(*this, pt); - ForEachCountry(pt, doGet); - - if (doGet.m_res != -1) - return m_countries[doGet.m_res].m_name; - else - return string(); - } - - void CountryInfoGetter::GetRegionInfo(m2::PointD const & pt, CountryInfo & info) const - { - GetByPoint doGet(*this, pt); - ForEachCountry(pt, doGet); - - if (doGet.m_res != -1) - GetRegionInfo(m_countries[doGet.m_res].m_name, info); - } - - void CountryInfoGetter::GetRegionInfo(string const & id, CountryInfo & info) const - { - auto i = m_id2info.find(id); - - // Take into account 'minsk-pass'. - if (i == m_id2info.end()) return; - //ASSERT ( i != m_id2info.end(), () ); - - info = i->second; - - if (info.m_name.empty()) - info.m_name = id; - - CountryInfo::FileName2FullName(info.m_name); - } - - template <class ToDo> void CountryInfoGetter::ForEachCountry(string const & prefix, ToDo toDo) const - { - for (auto const & c : m_countries) - { - if (c.m_name.find(prefix) == 0) - toDo(c); - } - - /// @todo Store sorted list of polygons in PACKED_POLYGONS_FILE. - /* - pair<IterT, IterT> r = equal_range(m_countries.begin(), m_countries.end(), file, LessCountryDef()); - while (r.first != r.second) - { - toDo(r.first->m_name); - ++r.first; - } - */ - } - - namespace - { - class DoCalcUSA - { - m2::RectD * m_rects; - public: - DoCalcUSA(m2::RectD * rects) : m_rects(rects) {} - void operator() (CountryDef const & c) - { - if (c.m_name == "USA_Alaska") - m_rects[1] = c.m_rect; - else if (c.m_name == "USA_Hawaii") - m_rects[2] = c.m_rect; - else - m_rects[0].Add(c.m_rect); - } - }; - - void AddRect(m2::RectD & r, CountryDef const & c) - { - r.Add(c.m_rect); - } - } - - void CountryInfoGetter::CalcUSALimitRect(m2::RectD rects[3]) const - { - ForEachCountry("USA_", DoCalcUSA(rects)); - } - - m2::RectD CountryInfoGetter::CalcLimitRect(string const & prefix) const - { - m2::RectD r; - ForEachCountry(prefix, bind(&AddRect, ref(r), _1)); - return r; - } - - void CountryInfoGetter::GetMatchedRegions(string const & enName, IDSet & regions) const - { - for (size_t i = 0; i < m_countries.size(); ++i) - { - /// Match english name with region file name (they are equal in almost all cases). - /// @todo Do it smarter in future. - string s = m_countries[i].m_name; - strings::AsciiToLower(s); - if (s.find(enName) != string::npos) - regions.push_back(i); - } - } - - bool CountryInfoGetter::IsBelongToRegion(m2::PointD const & pt, IDSet const & regions) const - { - GetByPoint doCheck(*this, pt); - for (size_t i = 0; i < regions.size(); ++i) - if (m_countries[regions[i]].m_rect.IsPointInside(pt) && !doCheck(regions[i])) - return true; - - return false; - } - - bool CountryInfoGetter::IsBelongToRegion(string const & fileName, IDSet const & regions) const - { - for (size_t i = 0; i < regions.size(); ++i) - { - if (m_countries[regions[i]].m_name == fileName) - return true; - } - - return false; - } - -namespace -{ - class DoFreeCacheMemory - { - public: - void operator() (vector<m2::RegionD> & v) const - { - vector<m2::RegionD> emptyV; - emptyV.swap(v); - } - }; -} - - void CountryInfoGetter::ClearCaches() const - { - m_cache.ForEachValue(DoFreeCacheMemory()); - m_cache.Reset(); - } -} diff --git a/storage/country_info.hpp b/storage/country_info.hpp deleted file mode 100644 index a042183d05..0000000000 --- a/storage/country_info.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include "storage/country_decl.hpp" - -#include "geometry/region2d.hpp" - -#include "coding/file_container.hpp" - -#include "base/cache.hpp" - - -namespace storage -{ - class CountryInfoGetter - { - FilesContainerR m_reader; - - vector<CountryDef> m_countries; - - template <class ToDo> void ForEachCountry(string const & prefix, ToDo toDo) const; - - /// ID - is a country file name without an extension. - map<string, CountryInfo> m_id2info; - - mutable my::Cache<uint32_t, vector<m2::RegionD> > m_cache; - - vector<m2::RegionD> const & GetRegions(size_t id) const; - - template <class ToDo> - void ForEachCountry(m2::PointD const & pt, ToDo & toDo) const; - - class GetByPoint - { - CountryInfoGetter const & m_info; - m2::PointD const & m_pt; - - public: - size_t m_res; - - GetByPoint(CountryInfoGetter const & info, m2::PointD const & pt) - : m_info(info), m_pt(pt), m_res(-1) - { - } - - /// @param[in] id Index in m_countries. - /// @return false If point is in country. - bool operator() (size_t id); - }; - - public: - CountryInfoGetter(ModelReaderPtr polyR, ModelReaderPtr countryR); - - string GetRegionFile(m2::PointD const & pt) const; - - /// @param[in] pt Point inside country. - void GetRegionInfo(m2::PointD const & pt, CountryInfo & info) const; - /// @param[in] id Country file name without an extension. - void GetRegionInfo(string const & id, CountryInfo & info) const; - - /// Return limit rects of USA:\n - /// 0 - continental part;\n - /// 1 - Alaska;\n - /// 2 - Hawaii;\n - void CalcUSALimitRect(m2::RectD rects[3]) const; - - m2::RectD CalcLimitRect(string const & prefix) const; - - /// @name Used for checking that objects (by point) are belong to regions. - //@{ - /// ID of region (index in m_countries array). - typedef size_t IDType; - typedef vector<IDType> IDSet; - - /// @param[in] enName English name to match (should be in lower case). - /// Search is case-insensitive. - void GetMatchedRegions(string const & enName, IDSet & regions) const; - bool IsBelongToRegion(m2::PointD const & pt, IDSet const & regions) const; - bool IsBelongToRegion(string const & fileName, IDSet const & regions) const; - //@} - - /// m_cache is mutable. - void ClearCaches() const; - }; -} diff --git a/storage/country_info_getter.cpp b/storage/country_info_getter.cpp new file mode 100644 index 0000000000..ee4e340ae7 --- /dev/null +++ b/storage/country_info_getter.cpp @@ -0,0 +1,190 @@ +#include "storage/country.hpp" +#include "storage/country_info_getter.hpp" +#include "storage/country_polygon.hpp" + +#include "indexer/geometry_serialization.hpp" + +#include "geometry/region2d.hpp" + +#include "coding/read_write_utils.hpp" + +#include "base/string_utils.hpp" + +#include "std/bind.hpp" +#include "std/function.hpp" +#include "std/limits.hpp" + +namespace storage +{ +namespace +{ +size_t const kInvalidId = numeric_limits<size_t>::max(); + +struct DoFreeCacheMemory +{ + void operator()(vector<m2::RegionD> & v) const { vector<m2::RegionD>().swap(v); } +}; + +class DoCalcUSA +{ +public: + DoCalcUSA(m2::RectD * rects) : m_rects(rects) {} + + void operator()(CountryDef const & c) + { + if (c.m_name == "USA_Alaska") + m_rects[1] = c.m_rect; + else if (c.m_name == "USA_Hawaii") + m_rects[2] = c.m_rect; + else + m_rects[0].Add(c.m_rect); + } + +private: + m2::RectD * const m_rects; +}; +} // namespace + +CountryInfoGetter::CountryInfoGetter(ModelReaderPtr polyR, ModelReaderPtr countryR) + : m_reader(polyR), m_cache(3) +{ + ReaderSource<ModelReaderPtr> src(m_reader.GetReader(PACKED_POLYGONS_INFO_TAG)); + rw::Read(src, m_countries); + + string buffer; + countryR.ReadAsString(buffer); + LoadCountryFile2CountryInfo(buffer, m_id2info); +} + +string CountryInfoGetter::GetRegionFile(m2::PointD const & pt) const +{ + IdType const id = FindFirstCountry(pt); + return id != kInvalidId ? m_countries[id].m_name : string(); +} + +void CountryInfoGetter::GetRegionInfo(m2::PointD const & pt, CountryInfo & info) const +{ + IdType const id = FindFirstCountry(pt); + if (id != kInvalidId) + GetRegionInfo(m_countries[id].m_name, info); +} + +void CountryInfoGetter::GetRegionInfo(string const & id, CountryInfo & info) const +{ + auto const it = m_id2info.find(id); + if (it == m_id2info.end()) + return; + + info = it->second; + if (info.m_name.empty()) + info.m_name = id; + + CountryInfo::FileName2FullName(info.m_name); +} + +void CountryInfoGetter::CalcUSALimitRect(m2::RectD rects[3]) const +{ + ForEachCountry("USA_", DoCalcUSA(rects)); +} + +m2::RectD CountryInfoGetter::CalcLimitRect(string const & prefix) const +{ + m2::RectD rect; + ForEachCountry(prefix, [&rect](CountryDef const & c) + { + rect.Add(c.m_rect); + }); + return rect; +} + +void CountryInfoGetter::GetMatchedRegions(string const & enNamePrefix, IdSet & regions) const +{ + for (size_t i = 0; i < m_countries.size(); ++i) + { + // Match english name with region file name (they are equal in almost all cases). + // @todo Do it smarter in future. + string s = m_countries[i].m_name; + strings::AsciiToLower(s); + if (strings::StartsWith(s, enNamePrefix.c_str())) + regions.push_back(i); + } +} + +bool CountryInfoGetter::IsBelongToRegions(m2::PointD const & pt, IdSet const & regions) const +{ + for (auto const & id : regions) + { + if (m_countries[id].m_rect.IsPointInside(pt) && IsBelongToRegion(id, pt)) + return true; + } + return false; +} + +bool CountryInfoGetter::IsBelongToRegions(string const & fileName, IdSet const & regions) const +{ + for (auto const & id : regions) + { + if (m_countries[id].m_name == fileName) + return true; + } + return false; +} + +void CountryInfoGetter::ClearCaches() const +{ + lock_guard<mutex> lock(m_cacheMutex); + + m_cache.ForEachValue(DoFreeCacheMemory()); + m_cache.Reset(); +} + +bool CountryInfoGetter::IsBelongToRegion(size_t id, m2::PointD const & pt) const +{ + lock_guard<mutex> lock(m_cacheMutex); + + bool isFound = false; + vector<m2::RegionD> & rgns = m_cache.Find(static_cast<uint32_t>(id), isFound); + + if (!isFound) + { + rgns.clear(); + // Load regions from file. + ReaderSource<ModelReaderPtr> src(m_reader.GetReader(strings::to_string(id))); + + uint32_t const count = ReadVarUint<uint32_t>(src); + for (size_t i = 0; i < count; ++i) + { + vector<m2::PointD> points; + serial::LoadOuterPath(src, serial::CodingParams(), points); + rgns.emplace_back(move(points)); + } + } + + for (auto const & rgn : rgns) + { + if (rgn.Contains(pt)) + return true; + } + return false; +} + +CountryInfoGetter::IdType CountryInfoGetter::FindFirstCountry(m2::PointD const & pt) const +{ + for (size_t id = 0; id < m_countries.size(); ++id) + { + if (m_countries[id].m_rect.IsPointInside(pt) && IsBelongToRegion(id, pt)) + return id; + } + return kInvalidId; +} + +template <typename ToDo> +void CountryInfoGetter::ForEachCountry(string const & prefix, ToDo && toDo) const +{ + for (auto const & country : m_countries) + { + if (strings::StartsWith(country.m_name, prefix.c_str())) + toDo(country); + } +} +} // namespace storage diff --git a/storage/country_info_getter.hpp b/storage/country_info_getter.hpp new file mode 100644 index 0000000000..9d1d6b3c51 --- /dev/null +++ b/storage/country_info_getter.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "storage/country_decl.hpp" + +#include "geometry/region2d.hpp" + +#include "coding/file_container.hpp" + +#include "base/cache.hpp" + +#include "std/mutex.hpp" + +namespace storage +{ +// This class allows users to get information about country by point +// or by name. +// +// *NOTE* This class is thread-safe. +class CountryInfoGetter +{ +public: + // Identifier of a region (index in m_countries array). + using IdType = size_t; + using IdSet = vector<IdType>; + + CountryInfoGetter(ModelReaderPtr polyR, ModelReaderPtr countryR); + + // Returns country file name without an extension for a country |pt| + // belongs to. If there are no such country, returns an empty + // string. + string GetRegionFile(m2::PointD const & pt) const; + + // Returns info for a region |pt| belongs to. + void GetRegionInfo(m2::PointD const & pt, CountryInfo & info) const; + + // Returns info for a country by file name without an extension. + void GetRegionInfo(string const & id, CountryInfo & info) const; + + // Return limit rects of USA: + // 0 - continental part + // 1 - Alaska + // 2 - Hawaii + void CalcUSALimitRect(m2::RectD rects[3]) const; + + // Calculates limit rect for all countries whose name starts with + // |prefix|. + m2::RectD CalcLimitRect(string const & prefix) const; + + // Returns identifiers for all regions matching to |enNamePrefix|. + void GetMatchedRegions(string const & enNamePrefix, IdSet & regions) const; + + // Returns true when |pt| belongs to at least one of the specified + // |regions|. + bool IsBelongToRegions(m2::PointD const & pt, IdSet const & regions) const; + + // Returns true if there're at least one region with name equals to + // |fileName|. + bool IsBelongToRegions(string const & fileName, IdSet const & regions) const; + + // Clears regions cache. + void ClearCaches() const; + +private: + // Returns true when |pt| belongs to a country identified by |id|. + bool IsBelongToRegion(size_t id, m2::PointD const & pt) const; + + // Returns identifier of a first country containing |pt|. + IdType FindFirstCountry(m2::PointD const & pt) const; + + // Invokes |toDo| on each country whose name starts with |prefix|. + template <typename ToDo> + void ForEachCountry(string const & prefix, ToDo && toDo) const; + + vector<CountryDef> m_countries; + + // Maps country file name without an extension to a country info. + map<string, CountryInfo> m_id2info; + + // Only cache and reader can be modified from different threads, so + // they're guarded by m_cacheMutex. + FilesContainerR m_reader; + mutable my::Cache<uint32_t, vector<m2::RegionD>> m_cache; + mutable mutex m_cacheMutex; +}; +} // namespace storage diff --git a/storage/storage.pro b/storage/storage.pro index 5cd9da9f8a..cee625f092 100644 --- a/storage/storage.pro +++ b/storage/storage.pro @@ -13,7 +13,7 @@ INCLUDEPATH += $$ROOT_DIR/3party/jansson/src HEADERS += \ country.hpp \ country_decl.hpp \ - country_info.hpp \ + country_info_getter.hpp \ country_polygon.hpp \ http_map_files_downloader.hpp \ index.hpp \ @@ -26,7 +26,7 @@ HEADERS += \ SOURCES += \ country.cpp \ country_decl.cpp \ - country_info.cpp \ + country_info_getter.cpp \ http_map_files_downloader.cpp \ index.cpp \ queued_country.cpp \ diff --git a/storage/storage_tests/country_info_test.cpp b/storage/storage_tests/country_info_getter_test.cpp index ba8863ff3e..3862bde2ba 100644 --- a/storage/storage_tests/country_info_test.cpp +++ b/storage/storage_tests/country_info_getter_test.cpp @@ -1,6 +1,6 @@ #include "testing/testing.hpp" -#include "storage/country_info.hpp" +#include "storage/country_info_getter.hpp" #include "storage/country.hpp" #include "indexer/mercator.hpp" @@ -9,23 +9,31 @@ #include "base/logging.hpp" +#include "std/unique_ptr.hpp" + using namespace storage; namespace { - typedef storage::CountryInfoGetter CountryInfoT; - CountryInfoT * GetCountryInfo() - { - Platform & pl = GetPlatform(); - return new CountryInfoT(pl.GetReader(PACKED_POLYGONS_FILE), - pl.GetReader(COUNTRIES_FILE)); - } +unique_ptr<CountryInfoGetter> CreateCountryInfoGetter() +{ + Platform & platform = GetPlatform(); + return make_unique<CountryInfoGetter>(platform.GetReader(PACKED_POLYGONS_FILE), + platform.GetReader(COUNTRIES_FILE)); +} + +bool IsEmptyName(map<string, CountryInfo> const & id2info, string const & id) +{ + auto const it = id2info.find(id); + TEST(it != id2info.end(), ()); + return it->second.m_name.empty(); } +} // namespace -UNIT_TEST(CountryInfo_GetByPoint_Smoke) +UNIT_TEST(CountryInfoGetter_GetByPoint_Smoke) { - unique_ptr<CountryInfoT> const getter(GetCountryInfo()); + auto const getter = CreateCountryInfoGetter(); CountryInfo info; @@ -43,17 +51,7 @@ UNIT_TEST(CountryInfo_GetByPoint_Smoke) TEST_EQUAL(info.m_flag, "jp", ()); } -namespace -{ - bool IsEmptyName(map<string, CountryInfo> const & id2info, string const & id) - { - map<string, CountryInfo>::const_iterator i = id2info.find(id); - TEST(i != id2info.end(), ()); - return i->second.m_name.empty(); - } -} - -UNIT_TEST(CountryInfo_ValidName_Smoke) +UNIT_TEST(CountryInfoGetter_ValidName_Smoke) { string buffer; ReaderPtr<Reader>(GetPlatform().GetReader(COUNTRIES_FILE)).ReadAsString(buffer); @@ -68,9 +66,9 @@ UNIT_TEST(CountryInfo_ValidName_Smoke) TEST(IsEmptyName(id2info, "UK_Northern Ireland"), ()); } -UNIT_TEST(CountryInfo_SomeRects) +UNIT_TEST(CountryInfoGetter_SomeRects) { - unique_ptr<CountryInfoT> const getter(GetCountryInfo()); + auto const getter = CreateCountryInfoGetter(); m2::RectD rects[3]; getter->CalcUSALimitRect(rects); diff --git a/storage/storage_tests/storage_tests.pro b/storage/storage_tests/storage_tests.pro index bd89a738a8..50cb71e9b6 100644 --- a/storage/storage_tests/storage_tests.pro +++ b/storage/storage_tests/storage_tests.pro @@ -21,7 +21,7 @@ HEADERS += \ SOURCES += \ ../../testing/testingmain.cpp \ - country_info_test.cpp \ + country_info_getter_test.cpp \ fake_map_files_downloader.cpp \ queued_country_tests.cpp \ simple_tree_test.cpp \ |