#include "indexer/data_source.hpp" #include "base/logging.hpp" #include #include using platform::CountryFile; using platform::LocalCountryFile; using namespace std; namespace { template class ReadMWMFunctor { public: using Fn = function; ReadMWMFunctor(Fn const & fn, FeatureSourceFactory const & factory) : m_fn(fn), m_factory(factory) { } template enable_if_t::value> ProcessElement(FeatureSource & src, uint32_t index) const { if (FeatureStatus::Deleted == src.GetFeatureStatus(index)) return; m_fn(src.GetFeatureId(index)); } template enable_if_t::value> ProcessElement(FeatureSource & src, uint32_t index) const { FeatureType feature; switch (src.GetFeatureStatus(index)) { case FeatureStatus::Created: CHECK(false, ("Created features index should be generated.")); case FeatureStatus::Deleted: case FeatureStatus::Obsolete: return; case FeatureStatus::Modified: { VERIFY(src.GetModifiedFeature(index, feature), ()); break; } case FeatureStatus::Untouched: { src.GetOriginalFeature(index, feature); break; } } m_fn(feature); } // Reads features visible at |scale| covered by |cov| from mwm and applies |m_fn| to it. // Feature reading process consists of two steps: untouched (original) features reading and // touched (created, edited etc.) features reading. void operator()(MwmSet::MwmHandle const & handle, covering::CoveringGetter & cov, int scale) const { auto src = m_factory(handle); MwmValue const * pValue = handle.GetValue(); if (pValue) { // Untouched (original) features reading. Applies covering |cov| to geometry index, gets // feature ids from it, gets untouched features by ids from |src| and applies |m_fn| by // ProcessElement. feature::DataHeader const & header = pValue->GetHeader(); CheckUniqueIndexes checkUnique(header.GetFormat() >= version::Format::v5); // In case of WorldCoasts we should pass correct scale in ForEachInIntervalAndScale. auto const lastScale = header.GetLastScale(); if (scale > lastScale) scale = lastScale; // Use last coding scale for covering (see index_builder.cpp). covering::Intervals const & intervals = cov.Get(lastScale); ScaleIndex index(pValue->m_cont.GetReader(INDEX_FILE_TAG), pValue->m_factory); // iterate through intervals for (auto const & i : intervals) { index.ForEachInIntervalAndScale(i.first, i.second, scale, [&](uint32_t index) { if (!checkUnique(index)) return; ProcessElement(*src, index); }); } } // Check created features container. // Need to do it on a per-mwm basis, because Drape relies on features in a sorted order. // Touched (created, edited) features reading. src->ForEachInRectAndScale(cov.GetRect(), scale, m_fn); } private: Fn m_fn; FeatureSourceFactory const & m_factory; }; } // namespace //------------------------- DataSource::FeaturesLoaderGuard ---------------------- string DataSource::FeaturesLoaderGuard::GetCountryFileName() const { if (!m_handle.IsAlive()) return string(); return m_handle.GetValue()->GetCountryFileName(); } bool DataSource::FeaturesLoaderGuard::IsWorld() const { if (!m_handle.IsAlive()) return false; return m_handle.GetValue()->GetHeader().GetType() == feature::DataHeader::world; } unique_ptr DataSource::FeaturesLoaderGuard::GetOriginalFeatureByIndex( uint32_t index) const { auto feature = make_unique(); if (GetOriginalFeatureByIndex(index, *feature)) return feature; return {}; } unique_ptr DataSource::FeaturesLoaderGuard::GetOriginalOrEditedFeatureByIndex( uint32_t index) const { auto feature = make_unique(); if (!m_handle.IsAlive()) return {}; ASSERT_NOT_EQUAL(m_source->GetFeatureStatus(index), FeatureStatus::Created, ()); if (GetFeatureByIndex(index, *feature)) return feature; return {}; } /// Everyone, except Editor core, should use this method. WARN_UNUSED_RESULT bool DataSource::FeaturesLoaderGuard::GetFeatureByIndex(uint32_t index, FeatureType & ft) const { if (!m_handle.IsAlive()) return false; ASSERT_NOT_EQUAL(FeatureStatus::Deleted, m_source->GetFeatureStatus(index), ("Deleted feature was cached. It should not be here. Please review your code.")); if (m_source->GetModifiedFeature(index, ft)) return true; return GetOriginalFeatureByIndex(index, ft); } /// Editor core only method, to get 'untouched', original version of feature. WARN_UNUSED_RESULT bool DataSource::FeaturesLoaderGuard::GetOriginalFeatureByIndex( uint32_t index, FeatureType & ft) const { return m_handle.IsAlive() ? m_source->GetOriginalFeature(index, ft) : false; } //---------------- DataSource ----------------------------------------------- unique_ptr DataSource::CreateInfo(platform::LocalCountryFile const & localFile) const { MwmValue value(localFile); feature::DataHeader const & h = value.GetHeader(); if (!h.IsMWMSuitable()) return nullptr; auto info = make_unique(); info->m_limitRect = h.GetBounds(); pair const scaleR = h.GetScaleRange(); info->m_minScale = static_cast(scaleR.first); info->m_maxScale = static_cast(scaleR.second); info->m_version = value.GetMwmVersion(); // Copying to drop the const qualifier. feature::RegionData regionData(value.GetRegionData()); info->m_data = regionData; return unique_ptr(move(info)); } unique_ptr DataSource::CreateValue(MwmInfo & info) const { // Create a section with rank table if it does not exist. platform::LocalCountryFile const & localFile = info.GetLocalFile(); unique_ptr p(new MwmValue(localFile)); p->SetTable(dynamic_cast(info)); ASSERT(p->GetHeader().IsMWMSuitable(), ()); return unique_ptr(move(p)); } pair DataSource::RegisterMap(LocalCountryFile const & localFile) { return Register(localFile); } bool DataSource::DeregisterMap(CountryFile const & countryFile) { return Deregister(countryFile); } void DataSource::ForEachInIntervals(ReaderCallback const & fn, covering::CoveringMode mode, m2::RectD const & rect, int scale) const { vector> mwms; GetMwmsInfo(mwms); covering::CoveringGetter cov(rect, mode); MwmId worldID[2]; for (shared_ptr const & info : mwms) { if (info->m_minScale <= scale && scale <= info->m_maxScale && rect.IsIntersect(info->m_limitRect)) { MwmId const mwmId(info); switch (info->GetType()) { case MwmInfo::COUNTRY: fn(GetMwmHandleById(mwmId), cov, scale); break; case MwmInfo::COASTS: worldID[0] = mwmId; break; case MwmInfo::WORLD: worldID[1] = mwmId; break; } } } if (worldID[0].IsAlive()) fn(GetMwmHandleById(worldID[0]), cov, scale); if (worldID[1].IsAlive()) fn(GetMwmHandleById(worldID[1]), cov, scale); } void DataSource::ForEachFeatureIDInRect(FeatureIdCallback const & f, m2::RectD const & rect, int scale) const { ReadMWMFunctor readFunctor(f, *m_factory); ForEachInIntervals(readFunctor, covering::LowLevelsOnly, rect, scale); } void DataSource::ForEachInRect(FeatureCallback const & f, m2::RectD const & rect, int scale) const { ReadMWMFunctor readFunctor(f, *m_factory); ForEachInIntervals(readFunctor, covering::ViewportWithLowLevels, rect, scale); } void DataSource::ForEachInScale(FeatureCallback const & f, int scale) const { ReadMWMFunctor readFunctor(f, *m_factory); ForEachInIntervals(readFunctor, covering::FullCover, m2::RectD::GetInfiniteRect(), scale); } void DataSource::ForEachInRectForMWM(FeatureCallback const & f, m2::RectD const & rect, int scale, MwmId const & id) const { MwmHandle const handle = GetMwmHandleById(id); if (handle.IsAlive()) { covering::CoveringGetter cov(rect, covering::ViewportWithLowLevels); ReadMWMFunctor readFunctor(f, *m_factory); readFunctor(handle, cov, scale); } } void DataSource::ReadFeatures(FeatureConstCallback const & fn, vector const & features) const { ASSERT(is_sorted(features.begin(), features.end()), ()); auto fidIter = features.begin(); auto const endIter = features.end(); while (fidIter != endIter) { MwmId const & id = fidIter->m_mwmId; MwmHandle const handle = GetMwmHandleById(id); if (handle.IsAlive()) { // Prepare features reading. auto src = (*m_factory)(handle); do { auto const fts = src->GetFeatureStatus(fidIter->m_index); ASSERT_NOT_EQUAL( FeatureStatus::Deleted, fts, ("Deleted feature was cached. It should not be here. Please review your code.")); FeatureType featureType; if (fts == FeatureStatus::Modified || fts == FeatureStatus::Created) VERIFY(src->GetModifiedFeature(fidIter->m_index, featureType), ()); else src->GetOriginalFeature(fidIter->m_index, featureType); fn(featureType); } while (++fidIter != endIter && id == fidIter->m_mwmId); } else { // Skip unregistered mwm files. while (++fidIter != endIter && id == fidIter->m_mwmId) ; } } }