[search] Fixed POI-BUILDING intersection.

This commit is contained in:
Yuri Gorshenin 2015-12-29 17:02:25 +03:00 committed by Sergey Yershov
parent 20827eb501
commit b518d1b717
2 changed files with 99 additions and 72 deletions

View file

@ -64,44 +64,63 @@ public:
{
if (child.m_type >= parent.m_type)
return;
if (parent.m_type == SearchModel::SEARCH_TYPE_STREET)
switch (parent.m_type)
{
case SearchModel::SEARCH_TYPE_POI:
case SearchModel::SEARCH_TYPE_CITY:
case SearchModel::SEARCH_TYPE_COUNT:
ASSERT(false, ("Invalid parent layer type:", parent.m_type));
break;
case SearchModel::SEARCH_TYPE_BUILDING:
MatchPOIsWithBuildings(child, parent, forward<TFn>(fn));
break;
case SearchModel::SEARCH_TYPE_STREET:
if (child.m_type == SearchModel::SEARCH_TYPE_POI)
MatchPOIsWithStreets(child, parent, forward<TFn>(fn));
else if (child.m_type == SearchModel::SEARCH_TYPE_BUILDING)
else
MatchBuildingsWithStreets(child, parent, forward<TFn>(fn));
return;
}
vector<m2::PointD> childCenters;
for (uint32_t featureId : *child.m_sortedFeatures)
{
FeatureType ft;
m_context.m_vector.GetByIndex(featureId, ft);
childCenters.push_back(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY));
}
BailIfCancelled(m_cancellable);
for (size_t j = 0; j < parent.m_sortedFeatures->size(); ++j)
{
BailIfCancelled(m_cancellable);
FeatureType ft;
m_context.m_vector.GetByIndex((*parent.m_sortedFeatures)[j], ft);
m2::PointD const center = feature::GetCenter(ft, FeatureType::WORST_GEOMETRY);
double const radius = ftypes::GetRadiusByPopulation(ft.GetPopulation());
m2::RectD const rect = MercatorBounds::RectByCenterXYAndSizeInMeters(center, radius);
for (size_t i = 0; i < child.m_sortedFeatures->size(); ++i)
{
if (rect.IsPointInside(childCenters[i]))
fn((*child.m_sortedFeatures)[i], (*parent.m_sortedFeatures)[j]);
}
break;
}
}
private:
template <typename TFn>
void MatchPOIsWithBuildings(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
{
static const double kBuildingRadiusMeters = 50;
ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_POI, ());
ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_BUILDING, ());
BailIfCancelled(m_cancellable);
vector<m2::PointD> poiCenters(child.m_sortedFeatures->size());
for (size_t i = 0; i < child.m_sortedFeatures->size(); ++i)
{
FeatureType poiFt;
GetByIndex((*child.m_sortedFeatures)[i], poiFt);
poiCenters[i] = feature::GetCenter(poiFt, FeatureType::WORST_GEOMETRY);
}
for (size_t i = 0; i < parent.m_sortedFeatures->size(); ++i)
{
BailIfCancelled(m_cancellable);
FeatureType buildingFt;
GetByIndex((*parent.m_sortedFeatures)[i], buildingFt);
for (size_t j = 0; j < child.m_sortedFeatures->size(); ++j)
{
double const distM = feature::GetMinDistanceMeters(buildingFt, poiCenters[j]);
if (distM <= kBuildingRadiusMeters)
{
fn((*child.m_sortedFeatures)[j], (*parent.m_sortedFeatures)[i]);
break;
}
}
}
}
template <typename TFn>
void MatchPOIsWithStreets(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
{
@ -124,8 +143,6 @@ private:
// child.m_sortedFeatures all buildings match by house number
// here.
auto const & checker = ftypes::IsBuildingChecker::Instance();
ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_BUILDING, ());
ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ());
@ -152,15 +169,14 @@ private:
return;
}
auto const & checker = ftypes::IsBuildingChecker::Instance();
uint32_t numFilterInvocations = 0;
auto filter = [&](uint32_t id, FeatureType & feature) -> bool
auto houseNumberFilter = [&](uint32_t id, FeatureType & feature, bool & loaded) -> bool
{
++numFilterInvocations;
if ((numFilterInvocations & 0xFF) == 0)
BailIfCancelled(m_cancellable);
if (!checker(feature))
return false;
if (binary_search(child.m_sortedFeatures->begin(), child.m_sortedFeatures->end(), id))
return true;
@ -169,24 +185,61 @@ private:
// optimization: as first tokens from the house-number part of
// the query and feature's house numbers must be numbers, their
// first symbols must be the same.
if (!loaded)
{
GetByIndex(id, feature);
loaded = true;
}
if (!checker(feature))
return false;
string const houseNumber = feature.GetHouseNumber();
if (!queryLooksLikeHouseNumber || !feature::IsHouseNumber(houseNumber))
return false;
if (queryTokens[0][0] != houseNumber[0])
return false;
return HouseNumbersMatch(feature.GetHouseNumber(), queryTokens);
return HouseNumbersMatch(houseNumber, queryTokens);
};
auto addEdge = [&](uint32_t houseId, FeatureType & houseFeature, uint32_t streetId)
unordered_map<uint32_t, bool> cache;
auto cachingHouseNumberFilter = [&](uint32_t id, FeatureType & feature, bool & loaded) -> bool
{
if (GetMatchingStreet(houseId, houseFeature) == streetId)
fn(houseId, streetId);
auto const it = cache.find(id);
if (it != cache.cend())
return it->second;
bool const result = houseNumberFilter(id, feature, loaded);
cache[id] = result;
return result;
};
ProjectionOnStreet proj;
for (uint32_t streetId : *parent.m_sortedFeatures)
{
BailIfCancelled(m_cancellable);
m_loader.FilterFeaturesInVicinity(streetId, filter, bind(addEdge, _1, _2, streetId));
StreetVicinityLoader::Street const & street = m_loader.GetStreet(streetId);
if (street.IsEmpty())
continue;
auto const & calculator = *street.m_calculator;
for (uint32_t houseId : street.m_features)
{
FeatureType feature;
bool loaded = false;
if (!cachingHouseNumberFilter(houseId, feature, loaded))
continue;
if (!loaded)
GetByIndex(houseId, feature);
m2::PointD const center = feature::GetCenter(feature, FeatureType::WORST_GEOMETRY);
if (!calculator.GetProjection(center, proj))
continue;
if (GetMatchingStreet(houseId, feature) == streetId)
fn(houseId, streetId);
}
}
}
@ -206,6 +259,11 @@ private:
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);
}
MwmContext & m_context;
ReverseGeocoder m_reverseGeocoder;

View file

@ -68,42 +68,11 @@ public:
}
}
// Calls |fn| on each feature belonging to the street's vicinity.
// Note that the distance from the feature to the street is
// calculated iff |filter| accepts the feature. This is a hacky
// solution and some kind of leaky abstraction. Probably we do not
// need this filter at all, but it allows us to early throw out all
// uninteresting features with complex geometry.
//
// TODO (@y, @m): fix this design.
template <typename TFilter, typename TFn>
void FilterFeaturesInVicinity(uint32_t streetId, TFilter && filter, TFn && fn)
{
Street const & street = GetStreet(streetId);
if (street.IsEmpty())
return;
ProjectionOnStreetCalculator const & calculator = *street.m_calculator;
ProjectionOnStreet proj;
for (uint32_t id : street.m_features)
{
FeatureType ft;
m_featuresVector.GetByIndex(id, ft);
if (!filter(id, ft))
continue;
if (!calculator.GetProjection(feature::GetCenter(ft, FeatureType::WORST_GEOMETRY), proj))
continue;
fn(id, ft);
}
}
Street const & GetStreet(uint32_t featureId);
inline void ClearCache() { m_cache.clear(); }
private:
Street const & GetStreet(uint32_t featureId);
void LoadStreet(uint32_t featureId, Street & street);
ScaleIndex<ModelReaderPtr> m_index;