diff --git a/std/type_traits.hpp b/std/type_traits.hpp index fcbe0f428c..c292cd1df8 100644 --- a/std/type_traits.hpp +++ b/std/type_traits.hpp @@ -24,6 +24,8 @@ using std::make_unsigned; using std::remove_reference; using std::underlying_type; +using std::result_of; + using std::false_type; using std::true_type; diff --git a/storage/country_info_getter.cpp b/storage/country_info_getter.cpp index be5c1fe9c1..2c7e59e62d 100644 --- a/storage/country_info_getter.cpp +++ b/storage/country_info_getter.cpp @@ -58,13 +58,17 @@ TCountryId CountryInfoGetter::GetRegionCountryId(m2::PointD const & pt) const void CountryInfoGetter::GetRegionsCountryId(m2::PointD const & pt, TCountriesVec & closestCoutryIds) { - // @TODO(bykoianko) Now this method fills |closestCoutryIds| with only a country id of mwm - // which covers |pt|. This method should fill |closestCoutryIds| with several mwms - // which cover |pt| and close to |pt|. + double const kLookupRadiusM = 30 /* km */ * 1000; + closestCoutryIds.clear(); - TCountryId countryId = GetRegionCountryId(pt); - if (!countryId.empty()) - closestCoutryIds.emplace_back(countryId); + + m2::RectD const lookupRect = MercatorBounds::RectByCenterXYAndSizeInMeters(pt, kLookupRadiusM); + + for (size_t id = 0; id < m_countries.size(); ++id) + { + if (m_countries[id].m_rect.Intersect(lookupRect) && IsCloseEnough(id, pt, kLookupRadiusM)) + closestCoutryIds.emplace_back(m_countries[id].m_name); + } } void CountryInfoGetter::GetRegionInfo(m2::PointD const & pt, CountryInfo & info) const @@ -193,7 +197,8 @@ void CountryInfoReader::ClearCachesImpl() const m_cache.Reset(); } -bool CountryInfoReader::IsBelongToRegionImpl(size_t id, m2::PointD const & pt) const +template +typename result_of)>::type CountryInfoReader::WithRegion(size_t id, TFn && fn) const { lock_guard lock(m_cacheMutex); @@ -215,12 +220,39 @@ bool CountryInfoReader::IsBelongToRegionImpl(size_t id, m2::PointD const & pt) c } } - for (auto const & rgn : rgns) + return fn(rgns); +} + + +bool CountryInfoReader::IsBelongToRegionImpl(size_t id, m2::PointD const & pt) const +{ + auto contains = [&pt](vector const & regions) { - if (rgn.Contains(pt)) - return true; - } - return false; + for (auto const & region : regions) + { + if (region.Contains(pt)) + return true; + } + return false; + }; + + return WithRegion(id, contains); +} + +bool CountryInfoReader::IsCloseEnough(size_t id, m2::PointD const & pt, double distance) +{ + m2::RectD const lookupRect = MercatorBounds::RectByCenterXYAndSizeInMeters(pt, distance); + auto isCloseEnough = [&](vector const & regions) + { + for (auto const & region : regions) + { + if (region.Contains(pt) || region.AtBorder(pt, lookupRect.SizeX() / 2)) + return true; + } + return false; + }; + + return WithRegion(id, isCloseEnough); } // CountryInfoGetterForTesting --------------------------------------------------------------------- @@ -246,4 +278,19 @@ bool CountryInfoGetterForTesting::IsBelongToRegionImpl(size_t id, CHECK_LESS(id, m_countries.size(), ()); return m_countries[id].m_rect.IsPointInside(pt); } + +bool CountryInfoGetterForTesting::IsCloseEnough(size_t id, m2::PointD const & pt, double distance) +{ + CHECK_LESS(id, m_countries.size(), ()); + + m2::RegionD rgn; + rgn.AddPoint(m_countries[id].m_rect.LeftTop()); + rgn.AddPoint(m_countries[id].m_rect.RightTop()); + rgn.AddPoint(m_countries[id].m_rect.RightBottom()); + rgn.AddPoint(m_countries[id].m_rect.LeftBottom()); + rgn.AddPoint(m_countries[id].m_rect.LeftTop()); + + m2::RectD const lookupRect = MercatorBounds::RectByCenterXYAndSizeInMeters(pt, distance); + return rgn.Contains(pt) || rgn.AtBorder(pt, lookupRect.SizeX() / 2); +} } // namespace storage diff --git a/storage/country_info_getter.hpp b/storage/country_info_getter.hpp index f5016e83a3..fe83a0e221 100644 --- a/storage/country_info_getter.hpp +++ b/storage/country_info_getter.hpp @@ -10,6 +10,7 @@ #include "std/mutex.hpp" #include "std/unordered_map.hpp" +#include "std/type_traits.hpp" namespace storage { @@ -88,6 +89,9 @@ protected: // Returns true when |pt| belongs to a country identified by |id|. virtual bool IsBelongToRegionImpl(size_t id, m2::PointD const & pt) const = 0; + // Returns true when the distance from |pt| to country identified by |id| less then |distance|. + virtual bool IsCloseEnough(size_t id, m2::PointD const & pt, double distance) = 0; + // @TODO(bykoianko): consider to get rid of m_countryIndex. // The possibility should be considered. // List of all known countries. @@ -115,6 +119,10 @@ protected: // CountryInfoGetter overrides: void ClearCachesImpl() const override; bool IsBelongToRegionImpl(size_t id, m2::PointD const & pt) const override; + bool IsCloseEnough(size_t id, m2::PointD const & pt, double distance) override; + + template + typename result_of)>::type WithRegion(size_t id, TFn && fn) const; FilesContainerR m_reader; mutable my::Cache> m_cache; @@ -136,5 +144,6 @@ protected: // CountryInfoGetter overrides: void ClearCachesImpl() const override; bool IsBelongToRegionImpl(size_t id, m2::PointD const & pt) const override; + bool IsCloseEnough(size_t id, m2::PointD const & pt, double distance) override; }; } // namespace storage diff --git a/storage/storage_tests/country_info_getter_test.cpp b/storage/storage_tests/country_info_getter_test.cpp index fdbcfa075d..637c28b3bc 100644 --- a/storage/storage_tests/country_info_getter_test.cpp +++ b/storage/storage_tests/country_info_getter_test.cpp @@ -82,6 +82,36 @@ UNIT_TEST(CountryInfoGetter_SomeRects) LOG(LINFO, ("Canada: ", getter->CalcLimitRect("Canada_"))); } +UNIT_TEST(CountryInfoGetter_HitsInRadius) +{ + auto const getter = CreateCountryInfoGetterMigrate(); + TCountriesVec results; + getter->GetRegionsCountryId(MercatorBounds::FromLatLon(56.1702, 28.1505), results); + TEST_EQUAL(results.size(), 3, ()); + TEST(find(results.begin(), results.end(), "Belarus_Vitebsk Region") != results.end(), ()); + TEST(find(results.begin(), results.end(), "Latvia") != results.end(), ()); + TEST(find(results.begin(), results.end(), "Russia_Pskov Oblast") != results.end(), ()); +} + +UNIT_TEST(CountryInfoGetter_HitsOnLongLine) +{ + auto const getter = CreateCountryInfoGetterMigrate(); + TCountriesVec results; + getter->GetRegionsCountryId(MercatorBounds::FromLatLon(62.2507, -102.0753), results); + TEST_EQUAL(results.size(), 2, ()); + TEST(find(results.begin(), results.end(), "Canada_Northwest Territories_East") != results.end(), ()); + TEST(find(results.begin(), results.end(), "Canada_Nunavut_South") != results.end(), ()); +} + +UNIT_TEST(CountryInfoGetter_HitsInTheMiddleOfNowhere) +{ + auto const getter = CreateCountryInfoGetterMigrate(); + TCountriesVec results; + getter->GetRegionsCountryId(MercatorBounds::FromLatLon(62.2900, -103.9423), results); + TEST_EQUAL(results.size(), 1, ()); + TEST(find(results.begin(), results.end(), "Canada_Northwest Territories_East") != results.end(), ()); +} + UNIT_TEST(CountryInfoGetter_GetLimitRectForLeafSingleMwm) { auto const getter = CreateCountryInfoGetterMigrate();