#pragma once #include "indexer/cell_id.hpp" #include "indexer/feature_covering.hpp" #include "indexer/interval_index.hpp" #include "indexer/locality_object.hpp" #include "indexer/scales.hpp" #include "coding/file_container.hpp" #include "geometry/rect2d.hpp" #include "base/geo_object_id.hpp" #include #include #include #include #include #include #include #include "defines.hpp" namespace indexer { // Geometry index which stores base::GeoObjectId as object identifier. // Used for geocoder server, stores only POIs and buildings which have address information. // Based on IntervalIndex. template class LocalityIndex { public: using ProcessObject = std::function; using ProcessCloseObject = std::function; LocalityIndex() = default; explicit LocalityIndex(Reader const & reader) { m_intervalIndex = std::make_unique>(reader); } void ForEachAtPoint(ProcessObject const & processObject, m2::PointD const & point) const { ForEachInRect(processObject, m2::RectD(point, point)); } void ForEachInRect(ProcessObject const & processObject, m2::RectD const & rect) const { covering::CoveringGetter cov(rect, covering::CoveringMode::ViewportWithLowLevels); covering::Intervals const & intervals = cov.Get(scales::GetUpperScale()); for (auto const & i : intervals) { m_intervalIndex->ForEach( [&processObject](uint64_t /* key */, uint64_t storedId) { processObject(LocalityObject::FromStoredId(storedId)); }, i.first, i.second); } } // Applies |processObject| to the objects located within |radiusM| meters from |center|. // Application to the closest objects and only to them is not guaranteed and the order // of the objects is not specified. // However, the method attempts to process objects that are closer to |center| first // and stop after |sizeHint| objects have been processed. For stability, if an object from // an index cell has been processed, all other objects from this cell will be processed too, // thus probably overflowing the |sizeHint| limit. // |processObject| gets object id in the first argument |objectId| and closeness weight // in the seсond argument |closenessWeight| (closeness weight in the range (0.0, 1.0]). void ForClosestToPoint(ProcessCloseObject const & processObject, m2::PointD const & center, double radiusM, uint32_t sizeHint) const { using Converter = CellIdConverter>; auto const rect = MercatorBounds::RectByCenterXYAndSizeInMeters(center, radiusM); covering::CoveringGetter cov(rect, covering::CoveringMode::Spiral); covering::Intervals const & intervals = cov.Get(scales::GetUpperScale()); CHECK_EQUAL(intervals.begin()->first, intervals.begin()->second - 1, ()); auto cellDepth = covering::GetCodingDepth(scales::GetUpperScale()); auto bestCell = m2::CellId::FromInt64(intervals.begin()->first, cellDepth); std::set bestCells; while (bestCell.Level() > 0) { bestCells.insert(bestCell.ToInt64(cellDepth)); bestCell = bestCell.Parent(); } std::map objectWeights{}; auto const centralCell = Converter::ToCellId(center.x, center.y); auto const centralCellXY = centralCell.XY(); auto chebyshevDistance = [centralCellXY] (auto && cellXY) { auto absDiff = [](auto && a, auto && b) { return a > b ? a - b : b - a; }; auto const distanceX = absDiff(centralCellXY.first, cellXY.first); auto const distanceY = absDiff(centralCellXY.second, cellXY.second); return std::max(distanceX, distanceY); }; auto cellRelativeWeight = [&] (int64_t cellNumber) { if (bestCells.find(cellNumber) != bestCells.end()) return 1.0; auto const cell = m2::CellId::FromInt64(cellNumber, cellDepth); auto const distance = chebyshevDistance(cell.XY()) - cell.Radius(); CHECK_GREATER(distance, 0, ()); return 1.0 / distance; }; auto insertObject = [&] (int64_t cellNumber, uint64_t storedId) { auto const objectId = LocalityObject::FromStoredId(storedId).GetEncodedId(); auto & objectWeight = objectWeights[objectId]; objectWeight = std::max(objectWeight, cellRelativeWeight(cellNumber)); }; auto insertObjectWithinSizeLimit = [&](int64_t cellNumber, uint64_t storedId) { if (objectWeights.size() < sizeHint) insertObject(cellNumber, storedId); }; for (auto const & i : intervals) { if (bestCells.find(i.first) != bestCells.end()) m_intervalIndex->ForEach(insertObject, i.first, i.second); else if (objectWeights.size() < sizeHint) m_intervalIndex->ForEach(insertObjectWithinSizeLimit, i.first, i.second); } std::vector> result(objectWeights.begin(), objectWeights.end()); std::sort(result.begin(), result.end(), [] (auto && l, auto && r) { return l.second > r.second;}); for (auto const & object : result) processObject(base::GeoObjectId(object.first), object.second); } private: std::unique_ptr> m_intervalIndex; }; template using GeoObjectsIndex = LocalityIndex; template using RegionsIndex = LocalityIndex; template struct GeoObjectsIndexBox { static constexpr const char * kFileTag = GEO_OBJECTS_INDEX_FILE_TAG; using ReaderType = Reader; using IndexType = GeoObjectsIndex; }; template struct RegionsIndexBox { static constexpr const char * kFileTag = REGIONS_INDEX_FILE_TAG; using ReaderType = Reader; using IndexType = RegionsIndex; }; template typename IndexBox::IndexType ReadIndex(std::string const & pathIndx) { FilesContainerR cont(pathIndx); auto const offsetSize = cont.GetAbsoluteOffsetAndSize(IndexBox::kFileTag); Reader reader(pathIndx); typename IndexBox::ReaderType subReader(reader.CreateSubReader(offsetSize.first, offsetSize.second)); typename IndexBox::IndexType index(subReader); return index; } } // namespace indexer