[search] Fixed search in World.

Fixed search for cities and/or countries on World.mwm.
This commit is contained in:
Yuri Gorshenin 2016-01-28 15:25:11 +03:00 committed by Sergey Yershov
parent 3ac96c7f94
commit bfbe02e4d1
6 changed files with 151 additions and 65 deletions

View file

@ -86,7 +86,10 @@ bool Result::HasPoint() const
FeatureID Result::GetFeatureID() const
{
ASSERT_EQUAL(GetResultType(), RESULT_FEATURE, ());
#if defined(DEBUG)
auto const type = GetResultType();
ASSERT(type == RESULT_FEATURE || type == RESULT_SUGGEST_FROM_FEATURE, (type));
#endif
return m_id;
}

View file

@ -276,3 +276,59 @@ UNIT_TEST(SearchQueryV2_Smoke)
TEST(MatchResults(engine, rules, request.Results()), ());
}
}
UNIT_TEST(SearchQueryV2_SearchInWorld)
{
my::ScopedLogLevelChanger const debugLogLevel(LDEBUG);
classificator::Load();
platform::LocalCountryFile testWorld(GetPlatform().WritableDir(),
platform::CountryFile("testWorld"), 0);
auto cleanup = [&]() {Cleanup(testWorld);};
cleanup();
MY_SCOPE_GUARD(cleanupAtExit, cleanup);
vector<storage::CountryDef> countries;
countries.emplace_back("Wonderland", m2::RectD(m2::PointD(-1.0, -1.0), m2::PointD(1.0, 1.0)));
TestSearchEngine engine("en", make_unique<storage::CountryInfoGetterForTesting>(countries),
make_unique<TestSearchQueryFactory>());
auto const wonderland = make_shared<TestCountry>(m2::PointD(0, 0), "Wonderland", "en");
auto const losAlamos =
make_shared<TestCity>(m2::PointD(0, 0), "Los Alamos", "en", 100 /* rank */);
{
TestMwmBuilder builder(testWorld, feature::DataHeader::world);
builder.Add(*wonderland);
builder.Add(*losAlamos);
}
auto const result = engine.RegisterMap(testWorld);
TEST_EQUAL(result.second, MwmSet::RegResult::Success, ());
auto worldId = result.first;
m2::RectD const viewport(m2::PointD(-1.0, -1.0), m2::PointD(-0.5, -0.5));
{
TestSearchRequest request(engine, "Los Alamos", "en", search::SearchParams::ALL, viewport);
request.Wait();
vector<shared_ptr<MatchingRule>> rules = {make_shared<ExactMatch>(worldId, losAlamos)};
TEST(MatchResults(engine, rules, request.Results()), ());
}
{
TestSearchRequest request(engine, "Wonderland", "en", search::SearchParams::ALL, viewport);
request.Wait();
vector<shared_ptr<MatchingRule>> rules = {make_shared<ExactMatch>(worldId, wonderland)};
TEST(MatchResults(engine, rules, request.Results()), ());
}
{
TestSearchRequest request(engine, "Wonderland Los Alamos", "en", search::SearchParams::ALL,
viewport);
request.Wait();
vector<shared_ptr<MatchingRule>> rules = {make_shared<ExactMatch>(worldId, losAlamos)};
TEST(MatchResults(engine, rules, request.Results()), ());
}
}

View file

@ -60,7 +60,7 @@ size_t constexpr kMaxNumLocalities = kMaxNumCities + kMaxNumStates + kMaxNumCoun
// List of countries we're supporting search by state. Elements of the
// list should be valid prefixes of corresponding mwms names.
string const kCountriesWithStates[] = {"USA_", "Canada_"};
string const kCountriesWithStates[] = {"US_", "Canada_"};
double constexpr kComparePoints = MercatorBounds::GetCellID2PointAbsEpsilon();
template <typename T>
@ -193,7 +193,7 @@ void GetEnglishName(FeatureType const & ft, string & name)
continue;
strings::AsciiToLower(name);
if (HasAllSubstrings(name, kUSA))
name = "usa";
name = "us";
else if (HasAllSubstrings(name, kUK))
name = "uk";
else
@ -340,6 +340,7 @@ Geocoder::Geocoder(Index & index, storage::CountryInfoGetter const & infoGetter)
, m_matcher(nullptr)
, m_villages(nullptr)
, m_finder(static_cast<my::Cancellable const &>(*this))
, m_lastMatchedRegion(nullptr)
, m_results(nullptr)
{
}
@ -483,7 +484,7 @@ void Geocoder::GoImpl(vector<shared_ptr<MwmInfo>> & infos, bool inViewport)
m_context = move(context);
MY_SCOPE_GUARD(cleanup, [&]()
{
LOG(LDEBUG, (m_context->GetMwmName(), "processing complete."));
LOG(LDEBUG, (m_context->GetName(), "processing complete."));
m_matcher->OnQueryFinished();
m_matcher = nullptr;
m_context.reset();
@ -529,6 +530,8 @@ void Geocoder::GoImpl(vector<shared_ptr<MwmInfo>> & infos, bool inViewport)
});
m_usedTokens.assign(m_numTokens, false);
m_lastMatchedRegion = nullptr;
MatchRegions(REGION_TYPE_COUNTRY);
if (index < numIntersectingMaps || m_results->empty())
@ -709,6 +712,7 @@ void Geocoder::FillLocalitiesTable()
if (numStates < kMaxNumStates && ft.GetFeatureType() == feature::GEOM_POINT)
{
Region state(l, REGION_TYPE_STATE);
state.m_center = ft.GetCenter();
string name;
GetEnglishName(ft, name);
@ -735,6 +739,8 @@ void Geocoder::FillLocalitiesTable()
if (numCountries < kMaxNumCountries && ft.GetFeatureType() == feature::GEOM_POINT)
{
Region country(l, REGION_TYPE_COUNTRY);
country.m_center = ft.GetCenter();
GetEnglishName(ft, country.m_enName);
m_infoGetter.GetMatchedRegions(country.m_enName, country.m_ids);
@ -794,7 +800,7 @@ void Geocoder::ForEachCountry(vector<shared_ptr<MwmInfo>> const & infos, TFn &&
for (size_t i = 0; i < infos.size(); ++i)
{
auto const & info = infos[i];
if (info->GetType() != MwmInfo::COUNTRY)
if (info->GetType() != MwmInfo::COUNTRY && info->GetType() != MwmInfo::WORLD)
continue;
auto handle = m_index.GetMwmHandleById(MwmSet::MwmId(info));
if (!handle.IsAlive())
@ -827,7 +833,8 @@ void Geocoder::MatchRegions(RegionType type)
auto const & regions = m_regions[type];
auto const & fileName = m_context->GetMwmName();
auto const & fileName = m_context->GetName();
bool const isWorld = m_context->GetInfo()->GetType() == MwmInfo::WORLD;
// Try to match regions.
for (auto const & p : regions)
@ -839,83 +846,95 @@ void Geocoder::MatchRegions(RegionType type)
if (HasUsedTokensInRange(startToken, endToken))
continue;
ScopedMarkTokens mark(m_usedTokens, startToken, endToken);
if (AllTokensUsed())
{
// Region matches to search query, we need to emit it as is.
for (auto const & region : p.second)
m_results->emplace_back(m_worldId, region.m_featureId);
continue;
}
bool matches = false;
for (auto const & region : p.second)
{
if (m_infoGetter.IsBelongToRegions(fileName, region.m_ids))
bool matches = false;
// On the World.mwm we need to check that CITY - STATE - COUNTRY
// form a nested sequence. Otherwise, as mwm borders do not
// intersect state or country boundaries, it's enough to check
// that the currently processing mwm belongs to region.
if (isWorld)
{
matches = m_lastMatchedRegion == nullptr ||
m_infoGetter.IsBelongToRegions(region.m_center, m_lastMatchedRegion->m_ids);
}
else if (m_infoGetter.IsBelongToRegions(fileName, region.m_ids))
{
matches = true;
break;
}
}
if (!matches)
continue;
if (!matches)
continue;
switch (type)
{
case REGION_TYPE_STATE:
MatchCities();
break;
case REGION_TYPE_COUNTRY:
MatchRegions(REGION_TYPE_STATE);
break;
case REGION_TYPE_COUNT:
ASSERT(false, ("Invalid region type."));
break;
ScopedMarkTokens mark(m_usedTokens, startToken, endToken);
if (AllTokensUsed())
{
// Region matches to search query, we need to emit it as is.
m_results->emplace_back(m_worldId, region.m_featureId);
continue;
}
m_lastMatchedRegion = &region;
MY_SCOPE_GUARD(cleanup, [this]() { m_lastMatchedRegion = nullptr; });
switch (type)
{
case REGION_TYPE_STATE:
MatchCities();
break;
case REGION_TYPE_COUNTRY:
MatchRegions(REGION_TYPE_STATE);
break;
case REGION_TYPE_COUNT:
ASSERT(false, ("Invalid region type."));
break;
}
}
}
}
void Geocoder::MatchCities()
{
m2::RectD const countryBounds = m_context->m_value.GetHeader().GetBounds();
// Localities are ordered my (m_startToken, m_endToken) pairs.
for (auto const & p : m_cities)
{
BailIfCancelled();
size_t const startToken = p.first.first;
size_t const endToken = p.first.second;
if (HasUsedTokensInRange(startToken, endToken))
continue;
ScopedMarkTokens mark(m_usedTokens, startToken, endToken);
if (AllTokensUsed())
{
// Localities match to search query.
for (auto const & city : p.second)
m_results->emplace_back(city.m_countryId, city.m_featureId);
continue;
}
// Unites features from all localities and uses the resulting bit
// vector as a filter for features retrieved during geocoding.
CBVPtr allFeatures;
for (auto const & city : p.second)
{
m2::RectD rect = city.m_rect;
if (!rect.Intersect(countryBounds))
BailIfCancelled();
if (m_lastMatchedRegion &&
!m_infoGetter.IsBelongToRegions(city.m_rect.Center(), m_lastMatchedRegion->m_ids))
{
continue;
}
ScopedMarkTokens mark(m_usedTokens, startToken, endToken);
if (AllTokensUsed())
{
// City matches to search query.
m_results->emplace_back(m_worldId, city.m_featureId);
continue;
}
// No need to search features in the World map.
if (m_context->GetInfo()->GetType() == MwmInfo::WORLD)
continue;
allFeatures.Union(RetrieveGeometryFeatures(*m_context, rect, city.m_featureId));
// Unites features from all localities and uses the resulting bit
// vector as a filter for features retrieved during geocoding.
auto const * cityFeatures = RetrieveGeometryFeatures(*m_context, city.m_rect, CITY_ID);
if (coding::CompressedBitVector::IsEmpty(cityFeatures))
continue;
// Filter will be applied for all non-empty bit vectors.
LimitedSearch(cityFeatures, 0 /* filterThreshold */);
}
if (allFeatures.IsEmpty())
continue;
// Filter will be applied for all non-empty bit vectors.
LimitedSearch(allFeatures.Get(), 0);
}
}
@ -937,7 +956,7 @@ void Geocoder::MatchViewportAndPosition()
}
// Filter will be applied only for large bit vectors.
LimitedSearch(allFeatures.Get(), m_params.m_maxNumResults);
LimitedSearch(allFeatures.Get(), m_params.m_maxNumResults /* filterThreshold */);
}
void Geocoder::LimitedSearch(coding::CompressedBitVector const * filter, size_t filterThreshold)

View file

@ -111,10 +111,11 @@ private:
// is used to filter maps before search.
struct Region : public Locality
{
Region(Locality const & l, RegionType type) : Locality(l), m_type(type) {}
Region(Locality const & l, RegionType type) : Locality(l), m_center(0, 0), m_type(type) {}
storage::CountryInfoGetter::IdSet m_ids;
string m_enName;
m2::PointD m_center;
RegionType m_type;
};
@ -133,7 +134,12 @@ private:
template <typename TLocality>
using TLocalitiesCache = map<pair<size_t, size_t>, vector<TLocality>>;
enum { VIEWPORT_ID = -1, POSITION_ID = -2 };
enum
{
VIEWPORT_ID,
POSITION_ID,
CITY_ID
};
SearchQueryParams::TSynonymsVector const & GetTokens(size_t i) const;
@ -278,6 +284,9 @@ private:
// Search query params prepared for retrieval.
SearchQueryParams m_retrievalParams;
// Pointer to the most nested region filled during geocoding.
Region const * m_lastMatchedRegion;
// Stack of layers filled during geocoding.
vector<FeaturesLayer> m_layers;

View file

@ -15,10 +15,8 @@ MwmContext::MwmContext(MwmSet::MwmHandle handle)
{
}
string const & MwmContext::GetMwmName() const
{
return m_id.GetInfo()->GetCountryName();
}
string const & MwmContext::GetName() const { return m_id.GetInfo()->GetCountryName(); }
shared_ptr<MwmInfo> const & MwmContext::GetInfo() const { return m_id.GetInfo(); }
} // namespace v2
} // namespace search

View file

@ -22,7 +22,8 @@ struct MwmContext
FeaturesVector m_vector;
ScaleIndex<ModelReaderPtr> m_index;
string const & GetMwmName() const;
string const & GetName() const;
shared_ptr<MwmInfo> const & GetInfo() const;
DISALLOW_COPY_AND_MOVE(MwmContext);
};