#include "search/locality_finder.hpp" #include "search/categories_cache.hpp" #include "search/cbv.hpp" #include "search/dummy_rank_table.hpp" #include "search/mwm_context.hpp" #include "indexer/data_source.hpp" #include "indexer/feature_algo.hpp" #include "indexer/ftypes_matcher.hpp" #include "base/assert.hpp" #include "base/stl_helpers.hpp" #include using namespace std; namespace search { namespace { double const kMaxCityRadiusMeters = 30000.0; double const kMaxVillageRadiusMeters = 2000.0; struct Filter { public: virtual ~Filter() = default; virtual bool IsGood(uint32_t id) const = 0; }; class CityFilter : public Filter { public: CityFilter(RankTable const & ranks) : m_ranks(ranks) {} // Filter overrides: bool IsGood(uint32_t id) const override { return m_ranks.Get(id) != 0; } private: RankTable const & m_ranks; }; class VillageFilter : public Filter { public: VillageFilter(MwmContext const & ctx, VillagesCache & villages) : m_cbv(villages.Get(ctx)) {} // Filter overrides: bool IsGood(uint32_t id) const override { return m_cbv.HasBit(id); } private: CBV m_cbv; }; class LocalitiesLoader { public: LocalitiesLoader(MwmContext const & ctx, CitiesBoundariesTable const & boundaries, Filter const & filter, LocalityFinder::Holder & holder, map> & loadedIds) : m_ctx(ctx) , m_boundaries(boundaries) , m_filter(filter) , m_holder(holder) , m_loadedIds(loadedIds[m_ctx.GetId()]) { } void operator()(uint32_t id) const { if (!m_filter.IsGood(id)) return; if (m_loadedIds.count(id) != 0) return; FeatureType ft; if (!m_ctx.GetFeature(id, ft)) return; if (ft.GetFeatureType() != feature::GEOM_POINT) return; using namespace ftypes; switch (IsLocalityChecker::Instance().GetType(ft)) { case CITY: case TOWN: case VILLAGE: break; default: return; } auto const population = ftypes::GetPopulation(ft); if (population == 0) return; auto const names = ft.GetNames(); auto const center = ft.GetCenter(); CitiesBoundariesTable::Boundaries boundaries; auto const fid = ft.GetID(); m_boundaries.Get(fid, boundaries); m_holder.Add(LocalityItem(names, center, boundaries, population, fid)); m_loadedIds.insert(id); } private: MwmContext const & m_ctx; CitiesBoundariesTable const & m_boundaries; Filter const & m_filter; LocalityFinder::Holder & m_holder; unordered_set & m_loadedIds; }; } // namespace // LocalityItem ------------------------------------------------------------------------------------ LocalityItem::LocalityItem(StringUtf8Multilang const & names, m2::PointD const & center, Boundaries const & boundaries, uint64_t population, FeatureID const & id) : m_names(names), m_center(center), m_boundaries(boundaries), m_population(population), m_id(id) { } string DebugPrint(LocalityItem const & item) { stringstream os; os << "Names = " << DebugPrint(item.m_names) << ", "; os << "Center = " << DebugPrint(item.m_center) << ", "; os << "Population = " << item.m_population << ", "; os << "Boundaries = " << DebugPrint(item.m_boundaries); return os.str(); } // LocalitySelector -------------------------------------------------------------------------------- LocalitySelector::LocalitySelector(m2::PointD const & p) : m_p(p) {} void LocalitySelector::operator()(LocalityItem const & item) { auto const inside = item.m_boundaries.HasPoint(m_p); // TODO (@y, @m): replace this naive score by p-values on // multivariate Gaussian. double const distance = MercatorBounds::DistanceOnEarth(item.m_center, m_p); double const score = ftypes::GetPopulationByRadius(distance) / static_cast(item.m_population); if (!inside && m_inside) return; ASSERT(inside || !m_inside, ()); if ((inside && !m_inside) || (score < m_score)) { m_inside = inside; m_score = score; m_locality = &item; } } // LocalityFinder::Holder -------------------------------------------------------------------------- LocalityFinder::Holder::Holder(double radiusMeters) : m_radiusMeters(radiusMeters) {} bool LocalityFinder::Holder::IsCovered(m2::RectD const & rect) const { bool covered = false; m_coverage.ForEachInRect(rect, [&covered](bool) { covered = true; }); return covered; } void LocalityFinder::Holder::SetCovered(m2::PointD const & p) { m_coverage.Add(true, m2::RectD(p, p)); } void LocalityFinder::Holder::Add(LocalityItem const & item) { m_localities.Add(item, m2::RectD(item.m_center, item.m_center)); } void LocalityFinder::Holder::ForEachInVicinity(m2::RectD const & rect, LocalitySelector & selector) const { m_localities.ForEachInRect(rect, selector); } m2::RectD LocalityFinder::Holder::GetRect(m2::PointD const & p) const { return MercatorBounds::RectByCenterXYAndSizeInMeters(p, m_radiusMeters); } m2::RectD LocalityFinder::Holder::GetDRect(m2::PointD const & p) const { return MercatorBounds::RectByCenterXYAndSizeInMeters(p, 2 * m_radiusMeters); } void LocalityFinder::Holder::Clear() { m_coverage.Clear(); m_localities.Clear(); } // LocalityFinder ---------------------------------------------------------------------------------- LocalityFinder::LocalityFinder(DataSource const & dataSource, CitiesBoundariesTable const & boundariesTable, VillagesCache & villagesCache) : m_dataSource(dataSource) , m_boundariesTable(boundariesTable) , m_villagesCache(villagesCache) , m_cities(kMaxCityRadiusMeters) , m_villages(kMaxVillageRadiusMeters) , m_mapsLoaded(false) { } void LocalityFinder::ClearCache() { m_ranks.reset(); m_cities.Clear(); m_villages.Clear(); m_maps.Clear(); m_worldId.Reset(); m_mapsLoaded = false; m_loadedIds.clear(); } void LocalityFinder::LoadVicinity(m2::PointD const & p, bool loadCities, bool loadVillages) { UpdateMaps(); if (loadCities) { m2::RectD const crect = m_cities.GetDRect(p); auto handle = m_dataSource.GetMwmHandleById(m_worldId); if (handle.IsAlive()) { auto const & value = *handle.GetValue(); if (!m_ranks) m_ranks = RankTable::Load(value.m_cont); if (!m_ranks) m_ranks = make_unique(); MwmContext ctx(move(handle)); ctx.ForEachIndex(crect, LocalitiesLoader(ctx, m_boundariesTable, CityFilter(*m_ranks), m_cities, m_loadedIds)); } m_cities.SetCovered(p); } if (loadVillages) { m2::RectD const vrect = m_villages.GetDRect(p); m_maps.ForEachInRect(m2::RectD(p, p), [&](MwmSet::MwmId const & id) { auto handle = m_dataSource.GetMwmHandleById(id); if (!handle.IsAlive()) return; MwmContext ctx(move(handle)); ctx.ForEachIndex(vrect, LocalitiesLoader(ctx, m_boundariesTable, VillageFilter(ctx, m_villagesCache), m_villages, m_loadedIds)); }); m_villages.SetCovered(p); } } void LocalityFinder::UpdateMaps() { if (m_mapsLoaded) return; vector> mwmsInfo; m_dataSource.GetMwmsInfo(mwmsInfo); for (auto const & info : mwmsInfo) { MwmSet::MwmId id(info); switch (info->GetType()) { case MwmInfo::WORLD: m_worldId = id; break; case MwmInfo::COUNTRY: m_maps.Add(id, info->m_limitRect); break; case MwmInfo::COASTS: break; } } m_mapsLoaded = true; } } // namespace search