forked from organicmaps/organicmaps
[search] Added FeaturesLayerMatcher cache to store loaded streets between queries.
This commit is contained in:
parent
1c175aafd9
commit
b7de3bf800
10 changed files with 168 additions and 78 deletions
|
@ -57,6 +57,7 @@ HEADERS += \
|
|||
v2/rank_table_cache.hpp \
|
||||
v2/search_model.hpp \
|
||||
v2/search_query_v2.hpp \
|
||||
v2/stats_cache.hpp \
|
||||
v2/street_vicinity_loader.hpp \
|
||||
|
||||
SOURCES += \
|
||||
|
|
|
@ -13,59 +13,76 @@ namespace v2
|
|||
// static
|
||||
double const FeaturesLayerMatcher::kBuildingRadiusMeters = 50;
|
||||
|
||||
FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmContext & context,
|
||||
my::Cancellable const & cancellable)
|
||||
: m_context(context)
|
||||
FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, my::Cancellable const & cancellable)
|
||||
: m_context(nullptr)
|
||||
, m_reverseGeocoder(index)
|
||||
, m_houseToStreetTable(HouseToStreetTable::Load(m_context.m_value))
|
||||
, m_loader(context.m_value, context.m_vector, scales::GetUpperScale(),
|
||||
ReverseGeocoder::kLookupRadiusM)
|
||||
, m_nearbyStreetsCache("F2NS")
|
||||
, m_matchingStreetsCache("B2S")
|
||||
, m_loader(scales::GetUpperScale(), ReverseGeocoder::kLookupRadiusM)
|
||||
, m_cancellable(cancellable)
|
||||
{
|
||||
ASSERT(m_houseToStreetTable.get(), ("Can't load HouseToStreetTable"));
|
||||
}
|
||||
|
||||
void FeaturesLayerMatcher::InitContext(MwmContext * context)
|
||||
{
|
||||
m_context = context;
|
||||
m_houseToStreetTable = HouseToStreetTable::Load(m_context->m_value);
|
||||
ASSERT(m_houseToStreetTable, ());
|
||||
m_loader.InitContext(context);
|
||||
}
|
||||
|
||||
void FeaturesLayerMatcher::FinishQuery()
|
||||
{
|
||||
m_nearbyStreetsCache.FinishQuery();
|
||||
m_matchingStreetsCache.FinishQuery();
|
||||
m_loader.FinishQuery();
|
||||
}
|
||||
|
||||
uint32_t FeaturesLayerMatcher::GetMatchingStreet(uint32_t houseId)
|
||||
{
|
||||
auto const it = m_matchingStreetsCache.find(houseId);
|
||||
if (it != m_matchingStreetsCache.cend())
|
||||
return it->second;
|
||||
auto r = m_matchingStreetsCache.Get(houseId);
|
||||
if (!r.second)
|
||||
return r.first;
|
||||
|
||||
FeatureType houseFeature;
|
||||
m_context.m_vector.GetByIndex(houseId, houseFeature);
|
||||
GetByIndex(houseId, houseFeature);
|
||||
|
||||
return GetMatchingStreetImpl(houseId, houseFeature);
|
||||
r.first = GetMatchingStreetImpl(houseId, houseFeature);
|
||||
return r.first;
|
||||
}
|
||||
|
||||
uint32_t FeaturesLayerMatcher::GetMatchingStreet(uint32_t houseId, FeatureType & houseFeature)
|
||||
{
|
||||
auto const it = m_matchingStreetsCache.find(houseId);
|
||||
if (it != m_matchingStreetsCache.cend())
|
||||
return it->second;
|
||||
auto r = m_matchingStreetsCache.Get(houseId);
|
||||
if (!r.second)
|
||||
return r.first;
|
||||
|
||||
return GetMatchingStreetImpl(houseId, houseFeature);
|
||||
r.first = GetMatchingStreetImpl(houseId, houseFeature);
|
||||
return r.first;
|
||||
}
|
||||
|
||||
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreets(uint32_t featureId)
|
||||
{
|
||||
auto const it = m_nearbyStreetsCache.find(featureId);
|
||||
if (it != m_nearbyStreetsCache.cend())
|
||||
return it->second;
|
||||
auto r = m_nearbyStreetsCache.Get(featureId);
|
||||
if (!r.second)
|
||||
return r.first;
|
||||
|
||||
FeatureType feature;
|
||||
m_context.m_vector.GetByIndex(featureId, feature);
|
||||
GetByIndex(featureId, feature);
|
||||
|
||||
return GetNearbyStreetsImpl(featureId, feature);
|
||||
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), r.first);
|
||||
return r.first;
|
||||
}
|
||||
|
||||
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreets(
|
||||
uint32_t featureId, FeatureType & feature)
|
||||
{
|
||||
auto const it = m_nearbyStreetsCache.find(featureId);
|
||||
if (it != m_nearbyStreetsCache.cend())
|
||||
return it->second;
|
||||
auto r = m_nearbyStreetsCache.Get(featureId);
|
||||
if (!r.second)
|
||||
return r.first;
|
||||
|
||||
return GetNearbyStreetsImpl(featureId, feature);
|
||||
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), r.first);
|
||||
return r.first;
|
||||
}
|
||||
|
||||
uint32_t FeaturesLayerMatcher::GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature)
|
||||
|
@ -77,18 +94,10 @@ uint32_t FeaturesLayerMatcher::GetMatchingStreetImpl(uint32_t houseId, FeatureTy
|
|||
if (!m_houseToStreetTable->Get(houseId, streetIndex))
|
||||
streetIndex = streets.size();
|
||||
|
||||
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_context.m_id)
|
||||
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_context->m_id)
|
||||
streetId = streets[streetIndex].m_id.m_index;
|
||||
m_matchingStreetsCache[houseId] = streetId;
|
||||
return streetId;
|
||||
}
|
||||
|
||||
vector<ReverseGeocoder::Street> const & FeaturesLayerMatcher::GetNearbyStreetsImpl(
|
||||
uint32_t featureId, FeatureType & feature)
|
||||
{
|
||||
auto & streets = m_nearbyStreetsCache[featureId];
|
||||
m_reverseGeocoder.GetNearbyStreets(feature::GetCenter(feature), streets);
|
||||
return streets;
|
||||
}
|
||||
} // namespace v2
|
||||
} // namespace search
|
||||
|
|
|
@ -58,7 +58,8 @@ public:
|
|||
static uint32_t const kInvalidId = numeric_limits<uint32_t>::max();
|
||||
static double const kBuildingRadiusMeters;
|
||||
|
||||
FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable);
|
||||
FeaturesLayerMatcher(Index & index, my::Cancellable const & cancellable);
|
||||
void InitContext(MwmContext * context);
|
||||
|
||||
template <typename TFn>
|
||||
void Match(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
|
||||
|
@ -90,6 +91,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void FinishQuery();
|
||||
|
||||
private:
|
||||
template <typename TFn>
|
||||
void MatchPOIsWithBuildings(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
|
||||
|
@ -152,7 +155,6 @@ private:
|
|||
if (queryTokens.empty())
|
||||
return;
|
||||
|
||||
auto const & mwmId = m_context.m_handle.GetId();
|
||||
vector<ReverseGeocoder::Building> nearbyBuildings;
|
||||
for (size_t i = 0; i < pois.size(); ++i)
|
||||
{
|
||||
|
@ -163,7 +165,7 @@ private:
|
|||
m_reverseGeocoder.GetNearbyBuildings(poiCenters[i], kBuildingRadiusMeters, nearbyBuildings);
|
||||
for (auto const & building : nearbyBuildings)
|
||||
{
|
||||
if (building.m_id.m_mwmId != mwmId || building.m_distanceMeters > kBuildingRadiusMeters)
|
||||
if (building.m_id.m_mwmId != m_context->m_id || building.m_distanceMeters > kBuildingRadiusMeters)
|
||||
continue;
|
||||
if (HouseNumbersMatch(building.m_name, queryTokens))
|
||||
fn(pois[i], building.m_id.m_index);
|
||||
|
@ -184,7 +186,6 @@ private:
|
|||
// it's faster to check nearby streets for POIs.
|
||||
if (pois.size() < streets.size())
|
||||
{
|
||||
auto const & mwmId = m_context.m_handle.GetId();
|
||||
for (uint32_t poiId : pois)
|
||||
{
|
||||
// TODO (@y, @m, @vng): implement a faster version
|
||||
|
@ -192,7 +193,7 @@ private:
|
|||
// map.
|
||||
for (auto const & street : GetNearbyStreets(poiId))
|
||||
{
|
||||
if (street.m_id.m_mwmId != mwmId ||
|
||||
if (street.m_id.m_mwmId != m_context->m_id ||
|
||||
street.m_distanceMeters > ReverseGeocoder::kLookupRadiusM)
|
||||
{
|
||||
continue;
|
||||
|
@ -330,26 +331,24 @@ private:
|
|||
|
||||
uint32_t GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature);
|
||||
|
||||
vector<ReverseGeocoder::Street> const & GetNearbyStreetsImpl(uint32_t featureId,
|
||||
FeatureType & feature);
|
||||
|
||||
inline void GetByIndex(uint32_t id, FeatureType & ft) const
|
||||
{
|
||||
m_context.m_vector.GetByIndex(id, ft);
|
||||
/// @todo Add StatsCache for feature id -> (point, name / house number).
|
||||
m_context->m_vector.GetByIndex(id, ft);
|
||||
}
|
||||
|
||||
MwmContext & m_context;
|
||||
MwmContext * m_context;
|
||||
|
||||
ReverseGeocoder m_reverseGeocoder;
|
||||
|
||||
// Cache of streets in a feature's vicinity. All lists in the cache
|
||||
// are ordered by distance from the corresponding feature.
|
||||
unordered_map<uint32_t, vector<ReverseGeocoder::Street>> m_nearbyStreetsCache;
|
||||
StatsCache<uint32_t, vector<ReverseGeocoder::Street>> m_nearbyStreetsCache;
|
||||
|
||||
// Cache of correct streets for buildings. Current search algorithm
|
||||
// supports only one street for a building, whereas buildings can be
|
||||
// located on multiple streets.
|
||||
unordered_map<uint32_t, uint32_t> m_matchingStreetsCache;
|
||||
StatsCache<uint32_t, uint32_t> m_matchingStreetsCache;
|
||||
|
||||
unique_ptr<HouseToStreetTable> m_houseToStreetTable;
|
||||
|
||||
|
|
|
@ -421,13 +421,22 @@ void Geocoder::GoImpl(vector<shared_ptr<MwmInfo>> & infos, bool inViewport)
|
|||
m_context = move(context);
|
||||
MY_SCOPE_GUARD(cleanup, [&]()
|
||||
{
|
||||
m_matcher.reset();
|
||||
LOG(LDEBUG, ("Search results for", m_context->GetMwmName()));
|
||||
m_matcher->FinishQuery();
|
||||
m_matcher = nullptr;
|
||||
m_context.reset();
|
||||
m_addressFeatures.clear();
|
||||
m_streets = nullptr;
|
||||
});
|
||||
|
||||
m_matcher.reset(new FeaturesLayerMatcher(m_index, *m_context, *this /* cancellable */));
|
||||
auto iMatcher = m_matchersCache.find(m_context->m_id);
|
||||
if (iMatcher == m_matchersCache.end())
|
||||
{
|
||||
iMatcher = m_matchersCache.insert(make_pair(m_context->m_id, make_unique<FeaturesLayerMatcher>(
|
||||
m_index, *this /* cancellable */))).first;
|
||||
}
|
||||
m_matcher = iMatcher->second.get();
|
||||
m_matcher->InitContext(m_context.get());
|
||||
|
||||
unique_ptr<coding::CompressedBitVector> viewportCBV;
|
||||
if (inViewport)
|
||||
|
@ -475,7 +484,7 @@ void Geocoder::ClearCaches()
|
|||
{
|
||||
m_geometryFeatures.clear();
|
||||
m_addressFeatures.clear();
|
||||
m_matcher.reset();
|
||||
m_matchersCache.clear();
|
||||
m_streetsCache.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -251,7 +251,8 @@ private:
|
|||
FeaturesFilter m_filter;
|
||||
|
||||
// Features matcher for layers intersection.
|
||||
unique_ptr<FeaturesLayerMatcher> m_matcher;
|
||||
map<MwmSet::MwmId, unique_ptr<FeaturesLayerMatcher>> m_matchersCache;
|
||||
FeaturesLayerMatcher * m_matcher;
|
||||
|
||||
// Path finder for interpretations.
|
||||
FeaturesLayerPathFinder m_finder;
|
||||
|
|
|
@ -11,7 +11,14 @@ MwmContext::MwmContext(MwmSet::MwmHandle handle)
|
|||
, m_value(*m_handle.GetValue<MwmValue>())
|
||||
, m_id(m_handle.GetId())
|
||||
, m_vector(m_value.m_cont, m_value.GetHeader(), m_value.m_table)
|
||||
, m_index(m_value.m_cont.GetReader(INDEX_FILE_TAG), m_value.m_factory)
|
||||
{
|
||||
}
|
||||
|
||||
string const & MwmContext::GetMwmName() const
|
||||
{
|
||||
return m_id.GetInfo()->GetCountryName();
|
||||
}
|
||||
|
||||
} // namespace v2
|
||||
} // namespace search
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "indexer/features_vector.hpp"
|
||||
#include "indexer/mwm_set.hpp"
|
||||
#include "indexer/scale_index.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
|
@ -19,6 +20,9 @@ struct MwmContext
|
|||
MwmValue & m_value;
|
||||
MwmSet::MwmId const & m_id;
|
||||
FeaturesVector m_vector;
|
||||
ScaleIndex<ModelReaderPtr> m_index;
|
||||
|
||||
string const & GetMwmName() const;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(MwmContext);
|
||||
};
|
||||
|
|
55
search/v2/stats_cache.hpp
Normal file
55
search/v2/stats_cache.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include "std/unordered_map.hpp"
|
||||
|
||||
|
||||
namespace search
|
||||
{
|
||||
namespace v2
|
||||
{
|
||||
|
||||
template <class TKey, class TValue>
|
||||
class StatsCache
|
||||
{
|
||||
unordered_map<TKey, TValue> m_map;
|
||||
size_t m_count, m_new, m_emptyCount;
|
||||
char const * m_name;
|
||||
|
||||
public:
|
||||
explicit StatsCache(char const * name) : m_count(0), m_new(0), m_emptyCount(0), m_name(name) {}
|
||||
|
||||
pair<TValue &, bool> Get(TKey const & key)
|
||||
{
|
||||
auto r = m_map.insert(make_pair(key, TValue()));
|
||||
|
||||
++m_count;
|
||||
if (r.second)
|
||||
++m_new;
|
||||
|
||||
return pair<TValue &, bool>(r.first->second, r.second);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_map.clear();
|
||||
m_count = m_new = 0;
|
||||
}
|
||||
|
||||
void FinishQuery()
|
||||
{
|
||||
if (m_count != 0)
|
||||
{
|
||||
LOG(LDEBUG, ("Cache", m_name, "Queries =", m_count, "From cache =", m_count - m_new, "Added =", m_new));
|
||||
m_count = m_new = 0;
|
||||
}
|
||||
else if (++m_emptyCount > 5)
|
||||
{
|
||||
Clear();
|
||||
m_emptyCount = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace v2
|
||||
} // namespace search
|
|
@ -15,30 +15,37 @@ namespace search
|
|||
{
|
||||
namespace v2
|
||||
{
|
||||
StreetVicinityLoader::StreetVicinityLoader(MwmValue & value, FeaturesVector const & featuresVector,
|
||||
int scale, double offsetMeters)
|
||||
: m_index(value.m_cont.GetReader(INDEX_FILE_TAG), value.m_factory)
|
||||
, m_featuresVector(featuresVector)
|
||||
, m_offsetMeters(offsetMeters)
|
||||
StreetVicinityLoader::StreetVicinityLoader(int scale, double offsetMeters)
|
||||
: m_context(nullptr), m_scale(scale), m_offsetMeters(offsetMeters), m_cache("Streets")
|
||||
{
|
||||
auto const scaleRange = value.GetHeader().GetScaleRange();
|
||||
m_scale = my::clamp(scale, scaleRange.first, scaleRange.second);
|
||||
}
|
||||
|
||||
void StreetVicinityLoader::InitContext(MwmContext * context)
|
||||
{
|
||||
m_context = context;
|
||||
auto const scaleRange = m_context->m_value.GetHeader().GetScaleRange();
|
||||
m_scale = my::clamp(m_scale, scaleRange.first, scaleRange.second);
|
||||
}
|
||||
|
||||
void StreetVicinityLoader::FinishQuery()
|
||||
{
|
||||
m_cache.FinishQuery();
|
||||
}
|
||||
|
||||
StreetVicinityLoader::Street const & StreetVicinityLoader::GetStreet(uint32_t featureId)
|
||||
{
|
||||
auto it = m_cache.find(featureId);
|
||||
if (it != m_cache.end())
|
||||
return it->second;
|
||||
auto r = m_cache.Get(featureId);
|
||||
if (!r.second)
|
||||
return r.first;
|
||||
|
||||
LoadStreet(featureId, m_cache[featureId]);
|
||||
return m_cache[featureId];
|
||||
LoadStreet(featureId, r.first);
|
||||
return r.first;
|
||||
}
|
||||
|
||||
void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street)
|
||||
{
|
||||
FeatureType feature;
|
||||
m_featuresVector.GetByIndex(featureId, feature);
|
||||
m_context->m_vector.GetByIndex(featureId, feature);
|
||||
|
||||
if (feature.GetFeatureType() != feature::GEOM_LINE)
|
||||
return;
|
||||
|
@ -54,8 +61,8 @@ void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street)
|
|||
auto const & intervals = coveringGetter.Get(m_scale);
|
||||
for (auto const & interval : intervals)
|
||||
{
|
||||
m_index.ForEachInIntervalAndScale(MakeBackInsertFunctor(street.m_features), interval.first,
|
||||
interval.second, m_scale);
|
||||
m_context->m_index.ForEachInIntervalAndScale(MakeBackInsertFunctor(street.m_features),
|
||||
interval.first, interval.second, m_scale);
|
||||
}
|
||||
|
||||
street.m_calculator = make_unique<ProjectionOnStreetCalculator>(points, m_offsetMeters);
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "search/projection_on_street.hpp"
|
||||
#include "search/v2/mwm_context.hpp"
|
||||
#include "search/v2/stats_cache.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/features_vector.hpp"
|
||||
#include "indexer/scale_index.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "geometry/rect2d.hpp"
|
||||
|
||||
|
@ -15,12 +13,13 @@
|
|||
|
||||
#include "std/unordered_map.hpp"
|
||||
|
||||
class MwmValue;
|
||||
|
||||
namespace search
|
||||
{
|
||||
namespace v2
|
||||
{
|
||||
struct MwmContext;
|
||||
|
||||
// This class is able to load features in a street's vicinity.
|
||||
//
|
||||
// NOTE: this class *IS NOT* thread-safe.
|
||||
|
@ -41,8 +40,8 @@ public:
|
|||
DISALLOW_COPY(Street);
|
||||
};
|
||||
|
||||
StreetVicinityLoader(MwmValue & value, FeaturesVector const & featuresVector, int scale,
|
||||
double offsetMeters);
|
||||
StreetVicinityLoader(int scale, double offsetMeters);
|
||||
void InitContext(MwmContext * context);
|
||||
|
||||
// Calls |fn| on each index in |sortedIds| where sortedIds[index]
|
||||
// belongs to the street's vicinity.
|
||||
|
@ -62,7 +61,7 @@ public:
|
|||
continue;
|
||||
|
||||
FeatureType ft;
|
||||
m_featuresVector.GetByIndex(id, ft);
|
||||
m_context->m_vector.GetByIndex(id, ft);
|
||||
if (!calculator.GetProjection(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY), proj))
|
||||
continue;
|
||||
|
||||
|
@ -70,19 +69,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
Street const & GetStreet(uint32_t featureId);
|
||||
void FinishQuery();
|
||||
|
||||
inline void ClearCache() { m_cache.clear(); }
|
||||
Street const & GetStreet(uint32_t featureId);
|
||||
|
||||
private:
|
||||
void LoadStreet(uint32_t featureId, Street & street);
|
||||
|
||||
ScaleIndex<ModelReaderPtr> m_index;
|
||||
FeaturesVector const & m_featuresVector;
|
||||
MwmContext * m_context;
|
||||
int m_scale;
|
||||
double const m_offsetMeters;
|
||||
|
||||
unordered_map<uint32_t, Street> m_cache;
|
||||
StatsCache<uint32_t, Street> m_cache;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(StreetVicinityLoader);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue