forked from organicmaps/organicmaps
[search] Numerous fixes to Geocoder (search query v2).
This commit is contained in:
parent
aca026daca
commit
32f1aeae5d
13 changed files with 66 additions and 87 deletions
|
@ -13,7 +13,6 @@ namespace search
|
|||
{
|
||||
namespace
|
||||
{
|
||||
double constexpr kLookupRadiusM = 500.0;
|
||||
size_t constexpr kMaxStreetIndex = 16;
|
||||
size_t constexpr kSimilarityThresholdPercent = 10;
|
||||
|
||||
|
@ -34,6 +33,9 @@ double CalculateMinDistance(FeatureType const & ft, m2::PointD const & pt)
|
|||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
double const ReverseGeocoder::kLookupRadiusM = 500.0;
|
||||
|
||||
void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector<Street> & streets)
|
||||
{
|
||||
m2::PointD const & center = feature::GetCenter(addrFt);
|
||||
|
|
|
@ -18,6 +18,8 @@ class ReverseGeocoder
|
|||
Index & m_index;
|
||||
|
||||
public:
|
||||
static double const kLookupRadiusM;
|
||||
|
||||
ReverseGeocoder(Index & index) : m_index(index) {}
|
||||
|
||||
struct Street
|
||||
|
|
|
@ -65,6 +65,7 @@ UNIT_TEST(HouseNumbersMatcher_Smoke)
|
|||
TEST(HouseNumbersMatch("1234abcdef", "1234 abcdef"), ());
|
||||
TEST(HouseNumbersMatch("10/42 корпус 2", "10"), ());
|
||||
|
||||
TEST(!HouseNumbersMatch("39", "39 с 79"), ());
|
||||
TEST(!HouseNumbersMatch("127а корпус 2", "127"), ());
|
||||
TEST(!HouseNumbersMatch("6 корпус 2", "7"), ());
|
||||
TEST(!HouseNumbersMatch("10/42 корпус 2", "42"), ());
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "std/vector.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
namespace search
|
||||
{
|
||||
namespace v2
|
||||
|
@ -14,6 +16,7 @@ namespace v2
|
|||
struct FeaturesLayer
|
||||
{
|
||||
FeaturesLayer();
|
||||
FeaturesLayer(FeaturesLayer && layer) = default;
|
||||
|
||||
void Clear();
|
||||
|
||||
|
@ -24,6 +27,8 @@ struct FeaturesLayer
|
|||
size_t m_startToken;
|
||||
size_t m_endToken;
|
||||
SearchModel::SearchType m_type;
|
||||
|
||||
DISALLOW_COPY(FeaturesLayer);
|
||||
};
|
||||
|
||||
string DebugPrint(FeaturesLayer const & layer);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "search/v2/features_layer_matcher.hpp"
|
||||
|
||||
#include "search/projection_on_street.hpp"
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
|
@ -16,8 +16,7 @@ FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmSet::MwmId const &
|
|||
, m_reverseGeocoder(index)
|
||||
, m_houseToStreetTable(HouseToStreetTable::Load(value))
|
||||
, m_featuresVector(featuresVector)
|
||||
, m_loader(value, featuresVector, scales::GetUpperScale(),
|
||||
ProjectionOnStreetCalculator::kDefaultMaxDistMeters)
|
||||
, m_loader(value, featuresVector, scales::GetUpperScale(), ReverseGeocoder::kLookupRadiusM)
|
||||
{
|
||||
ASSERT(m_houseToStreetTable.get(), ("Can't load HouseToStreetTable"));
|
||||
}
|
||||
|
|
|
@ -38,16 +38,17 @@ public:
|
|||
FeaturesVector const & featuresVector);
|
||||
|
||||
template <typename TFn>
|
||||
void Match(FeaturesLayer & child, FeaturesLayer & parent, TFn && fn)
|
||||
void Match(FeaturesLayer const & child, vector<uint32_t> const & sortedParentFeatures,
|
||||
SearchModel::SearchType parentType, TFn && fn)
|
||||
{
|
||||
if (child.m_type >= parent.m_type)
|
||||
if (child.m_type >= parentType)
|
||||
return;
|
||||
if (parent.m_type == SearchModel::SEARCH_TYPE_STREET)
|
||||
if (parentType == SearchModel::SEARCH_TYPE_STREET)
|
||||
{
|
||||
if (child.m_type == SearchModel::SEARCH_TYPE_POI)
|
||||
MatchPOIsWithStreets(child, parent, forward<TFn>(fn));
|
||||
MatchPOIsWithStreets(child, sortedParentFeatures, parentType, forward<TFn>(fn));
|
||||
else if (child.m_type == SearchModel::SEARCH_TYPE_BUILDING)
|
||||
MatchBuildingsWithStreets(child, parent, forward<TFn>(fn));
|
||||
MatchBuildingsWithStreets(child, sortedParentFeatures, parentType, forward<TFn>(fn));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -60,7 +61,7 @@ public:
|
|||
}
|
||||
|
||||
vector<m2::RectD> parentRects;
|
||||
for (uint32_t featureId : parent.m_sortedFeatures)
|
||||
for (uint32_t featureId : sortedParentFeatures)
|
||||
{
|
||||
FeatureType feature;
|
||||
m_featuresVector.GetByIndex(featureId, feature);
|
||||
|
@ -69,29 +70,33 @@ public:
|
|||
parentRects.push_back(MercatorBounds::RectByCenterXYAndSizeInMeters(center, radius));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < parent.m_sortedFeatures.size(); ++j)
|
||||
for (size_t j = 0; j < sortedParentFeatures.size(); ++j)
|
||||
{
|
||||
for (size_t i = 0; i < child.m_sortedFeatures.size(); ++i)
|
||||
{
|
||||
if (parentRects[j].IsPointInside(childCenters[i]))
|
||||
fn(child.m_sortedFeatures[i], parent.m_sortedFeatures[j]);
|
||||
fn(child.m_sortedFeatures[i], sortedParentFeatures[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename TFn>
|
||||
void MatchPOIsWithStreets(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn)
|
||||
void MatchPOIsWithStreets(FeaturesLayer const & child,
|
||||
vector<uint32_t> const & sortedParentFeatures,
|
||||
SearchModel::SearchType parentType, TFn && fn)
|
||||
{
|
||||
ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_POI, ());
|
||||
ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ());
|
||||
ASSERT_EQUAL(parentType, SearchModel::SEARCH_TYPE_STREET, ());
|
||||
|
||||
for (uint32_t streetId : parent.m_sortedFeatures)
|
||||
for (uint32_t streetId : sortedParentFeatures)
|
||||
m_loader.ForEachInVicinity(streetId, child.m_sortedFeatures, bind(fn, _1, streetId));
|
||||
}
|
||||
|
||||
template <typename TFn>
|
||||
void MatchBuildingsWithStreets(FeaturesLayer & child, FeaturesLayer const & parent, TFn && fn)
|
||||
void MatchBuildingsWithStreets(FeaturesLayer const & child,
|
||||
vector<uint32_t> const & sortedParentFeatures,
|
||||
SearchModel::SearchType parentType, TFn && fn)
|
||||
{
|
||||
// child.m_sortedFeatures contains only buildings matched by name,
|
||||
// not by house number. So, we need to add to
|
||||
|
@ -101,7 +106,7 @@ private:
|
|||
auto const & checker = ftypes::IsBuildingChecker::Instance();
|
||||
|
||||
ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_BUILDING, ());
|
||||
ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ());
|
||||
ASSERT_EQUAL(parentType, SearchModel::SEARCH_TYPE_STREET, ());
|
||||
|
||||
vector<string> queryTokens;
|
||||
NormalizeHouseNumber(child.m_subQuery, queryTokens);
|
||||
|
@ -115,26 +120,21 @@ private:
|
|||
return HouseNumbersMatch(feature.GetHouseNumber(), queryTokens);
|
||||
};
|
||||
|
||||
vector<uint32_t> houseIds;
|
||||
|
||||
auto addEdge = [&](uint32_t houseId, FeatureType & houseFeature, uint32_t streetId)
|
||||
{
|
||||
vector<ReverseGeocoder::Street> streets;
|
||||
m_reverseGeocoder.GetNearbyStreets(houseFeature, streets);
|
||||
uint32_t streetIndex = m_houseToStreetTable->Get(houseId);
|
||||
|
||||
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_index == streetId)
|
||||
if (streetIndex < streets.size() && streets[streetIndex].m_id.m_mwmId == m_mwmId &&
|
||||
streets[streetIndex].m_id.m_index == streetId)
|
||||
{
|
||||
houseIds.push_back(houseId);
|
||||
fn(houseId, streetId);
|
||||
}
|
||||
};
|
||||
|
||||
for (uint32_t streetId : parent.m_sortedFeatures)
|
||||
for (uint32_t streetId : sortedParentFeatures)
|
||||
m_loader.FilterFeaturesInVicinity(streetId, filter, bind(addEdge, _1, _2, streetId));
|
||||
|
||||
my::SortUnique(houseIds);
|
||||
child.m_sortedFeatures.swap(houseIds);
|
||||
}
|
||||
|
||||
MwmSet::MwmId m_mwmId;
|
||||
|
|
|
@ -9,37 +9,32 @@ namespace search
|
|||
namespace v2
|
||||
{
|
||||
void FeaturesLayerPathFinder::BuildGraph(FeaturesLayerMatcher & matcher,
|
||||
vector<FeaturesLayer *> const & layers)
|
||||
vector<FeaturesLayer const *> const & layers,
|
||||
vector<uint32_t> & reachable)
|
||||
{
|
||||
m_graph.clear();
|
||||
|
||||
if (layers.empty())
|
||||
return;
|
||||
|
||||
FeaturesLayer child;
|
||||
|
||||
reachable = layers.back()->m_sortedFeatures;
|
||||
|
||||
vector<uint32_t> tmpBuffer;
|
||||
|
||||
// The order matters here, as we need to intersect BUILDINGs with
|
||||
// STREETs first, and then POIs with BUILDINGs.
|
||||
for (size_t i = layers.size() - 1; i != 0; --i)
|
||||
{
|
||||
auto & child = (*layers[i - 1]);
|
||||
auto & parent = (*layers[i]);
|
||||
auto addEdges = [&](uint32_t from, uint32_t to)
|
||||
tmpBuffer.clear();
|
||||
auto addEdge = [&](uint32_t childFeature, uint32_t /* parentFeature */)
|
||||
{
|
||||
m_graph[to].push_back(from);
|
||||
tmpBuffer.push_back(childFeature);
|
||||
};
|
||||
matcher.Match(child, parent, addEdges);
|
||||
}
|
||||
}
|
||||
|
||||
void FeaturesLayerPathFinder::Dfs(uint32_t u)
|
||||
{
|
||||
m_visited.insert(u);
|
||||
auto const adj = m_graph.find(u);
|
||||
if (adj == m_graph.end())
|
||||
return;
|
||||
for (uint32_t v : adj->second)
|
||||
{
|
||||
if (m_visited.count(v) == 0)
|
||||
Dfs(v);
|
||||
matcher.Match(*layers[i - 1], reachable, layers[i]->m_type, addEdge);
|
||||
|
||||
my::SortUnique(tmpBuffer);
|
||||
reachable.swap(tmpBuffer);
|
||||
}
|
||||
}
|
||||
} // namespace v2
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include "search/v2/features_layer.hpp"
|
||||
|
||||
#include "std/unordered_map.hpp"
|
||||
#include "std/unordered_set.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
class FeaturesVector;
|
||||
|
@ -18,36 +16,23 @@ class FeaturesLayerMatcher;
|
|||
class FeaturesLayerPathFinder
|
||||
{
|
||||
public:
|
||||
using TAdjList = vector<uint32_t>;
|
||||
using TLayerGraph = unordered_map<uint32_t, TAdjList>;
|
||||
|
||||
template <typename TFn>
|
||||
void ForEachReachableVertex(FeaturesLayerMatcher & matcher,
|
||||
vector<FeaturesLayer *> const & layers, TFn && fn)
|
||||
vector<FeaturesLayer const *> const & layers, TFn && fn)
|
||||
{
|
||||
if (layers.empty())
|
||||
return;
|
||||
|
||||
BuildGraph(matcher, layers);
|
||||
vector<uint32_t> reachable;
|
||||
BuildGraph(matcher, layers, reachable);
|
||||
|
||||
m_visited.clear();
|
||||
for (uint32_t featureId : (*layers.back()).m_sortedFeatures)
|
||||
Dfs(featureId);
|
||||
|
||||
for (uint32_t featureId : (*layers.front()).m_sortedFeatures)
|
||||
{
|
||||
if (m_visited.count(featureId) != 0)
|
||||
fn(featureId);
|
||||
}
|
||||
for (uint32_t featureId : reachable)
|
||||
fn(featureId);
|
||||
}
|
||||
|
||||
private:
|
||||
void BuildGraph(FeaturesLayerMatcher & matcher, vector<FeaturesLayer *> const & layers);
|
||||
|
||||
void Dfs(uint32_t u);
|
||||
|
||||
TLayerGraph m_graph;
|
||||
unordered_set<uint32_t> m_visited;
|
||||
void BuildGraph(FeaturesLayerMatcher & matcher, vector<FeaturesLayer const *> const & layers,
|
||||
vector<uint32_t> & reachable);
|
||||
};
|
||||
} // namespace v2
|
||||
} // namespace search
|
||||
|
|
|
@ -260,7 +260,7 @@ void Geocoder::FindPaths()
|
|||
};
|
||||
|
||||
// Layers ordered by a search type.
|
||||
vector<FeaturesLayer *> sortedLayers;
|
||||
vector<FeaturesLayer const *> sortedLayers;
|
||||
sortedLayers.reserve(m_layers.size());
|
||||
for (auto & layer : m_layers)
|
||||
sortedLayers.push_back(&layer);
|
||||
|
|
|
@ -202,7 +202,7 @@ bool HouseNumbersMatch(string const & houseNumber, vector<string> const & queryT
|
|||
++i;
|
||||
++j;
|
||||
}
|
||||
return true;
|
||||
return j == queryTokens.size();
|
||||
}
|
||||
} // namespace v2
|
||||
} // namespace search
|
||||
|
|
|
@ -11,14 +11,6 @@ namespace search
|
|||
{
|
||||
namespace v2
|
||||
{
|
||||
SearchModel::SearchModel()
|
||||
{
|
||||
m_poiCheckers.push_back(&IsPeakChecker::Instance());
|
||||
m_poiCheckers.push_back(&IsATMChecker::Instance());
|
||||
m_poiCheckers.push_back(&IsFuelStationChecker::Instance());
|
||||
m_poiCheckers.push_back(&IsRailwayStationChecker::Instance());
|
||||
}
|
||||
|
||||
// static
|
||||
SearchModel const & SearchModel::Instance()
|
||||
{
|
||||
|
@ -32,12 +24,6 @@ SearchModel::SearchType SearchModel::GetSearchType(FeatureType const & feature)
|
|||
static auto const & streetChecker = IsStreetChecker::Instance();
|
||||
static auto const & localityChecker = IsLocalityChecker::Instance();
|
||||
|
||||
for (auto const * checker : m_poiCheckers)
|
||||
{
|
||||
if ((*checker)(feature))
|
||||
return SEARCH_TYPE_POI;
|
||||
}
|
||||
|
||||
if (buildingChecker(feature))
|
||||
return SEARCH_TYPE_BUILDING;
|
||||
|
||||
|
@ -64,7 +50,7 @@ SearchModel::SearchType SearchModel::GetSearchType(FeatureType const & feature)
|
|||
}
|
||||
}
|
||||
|
||||
return SEARCH_TYPE_COUNT;
|
||||
return SEARCH_TYPE_POI;
|
||||
}
|
||||
|
||||
string DebugPrint(SearchModel::SearchType type)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "std/string.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
class FeatureType;
|
||||
|
||||
namespace ftypes
|
||||
|
@ -31,14 +33,14 @@ public:
|
|||
SEARCH_TYPE_COUNT
|
||||
};
|
||||
|
||||
SearchModel();
|
||||
|
||||
static SearchModel const & Instance();
|
||||
|
||||
SearchType GetSearchType(FeatureType const & feature) const;
|
||||
|
||||
private:
|
||||
vector<ftypes::BaseChecker const *> m_poiCheckers;
|
||||
SearchModel() = default;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(SearchModel);
|
||||
};
|
||||
|
||||
string DebugPrint(SearchModel::SearchType type);
|
||||
|
|
|
@ -50,8 +50,10 @@ void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street)
|
|||
covering::CoveringGetter coveringGetter(street.m_rect, covering::ViewportWithLowLevels);
|
||||
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);
|
||||
}
|
||||
|
||||
if (!points.empty())
|
||||
street.m_calculator = make_unique<ProjectionOnStreetCalculator>(move(points), m_offsetMeters);
|
||||
|
|
Loading…
Add table
Reference in a new issue