From a63f77e8b602ecb9e0e35e2f560512adaf376387 Mon Sep 17 00:00:00 2001 From: Yuri Gorshenin Date: Tue, 5 Jan 2016 16:37:03 +0300 Subject: [PATCH] [search] Implemented bottom-up intersection pass. --- search/reverse_geocoder.cpp | 79 ++++++---- search/reverse_geocoder.hpp | 12 +- .../search_query_v2_test.cpp | 22 ++- search/v2/features_layer.cpp | 1 + search/v2/features_layer.hpp | 9 ++ search/v2/features_layer_matcher.cpp | 15 ++ search/v2/features_layer_matcher.hpp | 123 +++++++++++----- search/v2/features_layer_path_finder.cpp | 138 +++++++++++++++++- search/v2/features_layer_path_finder.hpp | 19 ++- search/v2/geocoder.cpp | 5 +- search/v2/house_numbers_matcher.cpp | 7 +- search/v2/street_vicinity_loader.cpp | 3 + search/v2/street_vicinity_loader.hpp | 3 + 13 files changed, 343 insertions(+), 93 deletions(-) diff --git a/search/reverse_geocoder.cpp b/search/reverse_geocoder.cpp index a1d497319f..2f377b7ef8 100644 --- a/search/reverse_geocoder.cpp +++ b/search/reverse_geocoder.cpp @@ -22,11 +22,7 @@ int const kQueryScale = scales::GetUpperScale(); // static double const ReverseGeocoder::kLookupRadiusM = 500.0; -// static -m2::RectD ReverseGeocoder::GetLookupRect(m2::PointD const & center) -{ - return MercatorBounds::RectByCenterXYAndSizeInMeters(center, kLookupRadiusM); -} +ReverseGeocoder::ReverseGeocoder(Index const & index) : m_index(index) {} void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector & streets) { @@ -35,7 +31,7 @@ void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector & streets) { - m2::RectD const rect = GetLookupRect(center); + m2::RectD const rect = GetLookupRect(center, kLookupRadiusM); auto const addStreet = [&](FeatureType const & ft) { @@ -51,38 +47,18 @@ void ReverseGeocoder::GetNearbyStreets(m2::PointD const & center, vector return; ASSERT(!name.empty(), ()); - streets.push_back({ft.GetID(), feature::GetMinDistanceMeters(ft, center), name}); + + double const distanceM = feature::GetMinDistanceMeters(ft, center); + if (distanceM > kLookupRadiusM) + return; + + streets.push_back({ft.GetID(), distanceM, name}); }; m_index.ForEachInRect(addStreet, rect, kQueryScale); sort(streets.begin(), streets.end(), my::CompareBy(&Street::m_distanceMeters)); } -void ReverseGeocoder::GetNearbyBuildings(m2::PointD const & center, vector & buildings) -{ - // Seems like a copy-paste here of the GetNearbyStreets function. - // Trying to factor out common logic will cause many variables logic. - - m2::RectD const rect = GetLookupRect(center); - - auto const addBuilding = [&](FeatureType const & ft) - { - if (!ftypes::IsBuildingChecker::Instance()(ft)) - return; - - // Skip empty house numbers. - string const number = ft.GetHouseNumber(); - if (number.empty()) - return; - - buildings.push_back({ft.GetID(), feature::GetMinDistanceMeters(ft, center), - number, feature::GetCenter(ft)}); - }; - - m_index.ForEachInRect(addBuilding, rect, kQueryScale); - sort(buildings.begin(), buildings.end(), my::CompareBy(&Building::m_distanceMeters)); -} - // static size_t ReverseGeocoder::GetMatchedStreetIndex(string const & keyName, vector const & streets) @@ -151,4 +127,43 @@ void ReverseGeocoder::GetNearbyAddress(m2::PointD const & center, Address & addr } } +void ReverseGeocoder::GetNearbyBuildings(m2::PointD const & center, vector & buildings) +{ + GetNearbyBuildings(center, kLookupRadiusM, buildings); +} + +void ReverseGeocoder::GetNearbyBuildings(m2::PointD const & center, double radiusM, + vector & buildings) +{ + // Seems like a copy-paste here of the GetNearbyStreets function. + // Trying to factor out common logic will cause many variables logic. + + m2::RectD const rect = GetLookupRect(center, radiusM); + + auto const addBuilding = [&](FeatureType const & ft) + { + if (!ftypes::IsBuildingChecker::Instance()(ft)) + return; + + // Skip empty house numbers. + string const number = ft.GetHouseNumber(); + if (number.empty()) + return; + + double const distanceM = feature::GetMinDistanceMeters(ft, center); + if (distanceM > radiusM) + return; + + buildings.push_back({ft.GetID(), distanceM, number, feature::GetCenter(ft)}); + }; + + m_index.ForEachInRect(addBuilding, rect, kQueryScale); + sort(buildings.begin(), buildings.end(), my::CompareBy(&Building::m_distanceMeters)); +} + +// static +m2::RectD ReverseGeocoder::GetLookupRect(m2::PointD const & center, double radiusM) +{ + return MercatorBounds::RectByCenterXYAndSizeInMeters(center, radiusM); +} } // namespace search diff --git a/search/reverse_geocoder.hpp b/search/reverse_geocoder.hpp index c957e1c6bf..f1252f4813 100644 --- a/search/reverse_geocoder.hpp +++ b/search/reverse_geocoder.hpp @@ -34,9 +34,8 @@ class ReverseGeocoder public: static double const kLookupRadiusM; - static m2::RectD GetLookupRect(m2::PointD const & center); - explicit ReverseGeocoder(Index const & index) : m_index(index) {} + explicit ReverseGeocoder(Index const & index); using Street = Object; @@ -69,9 +68,14 @@ public: void GetNearbyAddress(m2::PointD const & center, Address & addr); -private: - void GetNearbyStreets(m2::PointD const & center, vector & streets); void GetNearbyBuildings(m2::PointD const & center, vector & buildings); + + void GetNearbyBuildings(m2::PointD const & center, double radiusM, vector & buildings); + +private: + static m2::RectD GetLookupRect(m2::PointD const & center, double radiusM); + + void GetNearbyStreets(m2::PointD const & center, vector & streets); }; } // namespace search diff --git a/search/search_integration_tests/search_query_v2_test.cpp b/search/search_integration_tests/search_query_v2_test.cpp index db99f13c97..c7455a99f5 100644 --- a/search/search_integration_tests/search_query_v2_test.cpp +++ b/search/search_integration_tests/search_query_v2_test.cpp @@ -83,18 +83,24 @@ UNIT_TEST(SearchQueryV2_Smoke) auto const feynmanStreet = make_shared( vector{m2::PointD(9.999, 9.999), m2::PointD(10, 10), m2::PointD(10.001, 10.001)}, "Feynman street", "en"); - auto const bohrStreet = make_shared( + auto const bohrStreet1 = make_shared( vector{m2::PointD(9.999, 10.001), m2::PointD(10, 10), m2::PointD(10.001, 9.999)}, "Bohr street", "en"); - auto const feynmanHouse = make_shared(m2::PointD(10, 10), "Feynman house", + auto const bohrStreet2 = make_shared( + vector{m2::PointD(10.001, 9.999), m2::PointD(10, 10), m2::PointD(10.002, 9.998)}, + "Bohr street", "en"); + auto const bohrStreet3 = make_shared( + vector{m2::PointD(10.002, 9.998), m2::PointD(10, 10), m2::PointD(10.003, 9.997)}, + "Bohr street", "en"); + auto const feynmanHouse = make_shared(m2::PointD(10, 10), "Feynman house 1 unit 1", "1 unit 1", *feynmanStreet, "en"); - auto const bohrHouse = - make_shared(m2::PointD(10, 10), "Bohr house", "1 unit 1", *bohrStreet, "en"); + auto const bohrHouse = make_shared(m2::PointD(10, 10), "Bohr house 1 unit 1 ", + "1 unit 1", *bohrStreet1, "en"); auto const hilbertHouse = make_shared( vector{ {10.0005, 10.0005}, {10.0006, 10.0005}, {10.0006, 10.0006}, {10.0005, 10.0006}}, - "Hilbert house", "1 unit 2", *bohrStreet, "en"); + "Hilbert house 1 unit 2", "1 unit 2", *bohrStreet1, "en"); auto const lantern1 = make_shared(m2::PointD(10.0005, 10.0005), "lantern 1", "en"); auto const lantern2 = make_shared(m2::PointD(10.0006, 10.0005), "lantern 2", "en"); @@ -107,7 +113,9 @@ UNIT_TEST(SearchQueryV2_Smoke) builder.Add(*quantumTeleport2); builder.Add(*quantumCafe); builder.Add(*feynmanStreet); - builder.Add(*bohrStreet); + builder.Add(*bohrStreet1); + builder.Add(*bohrStreet2); + builder.Add(*bohrStreet3); builder.Add(*feynmanHouse); builder.Add(*bohrHouse); @@ -196,7 +204,7 @@ UNIT_TEST(SearchQueryV2_Smoke) } { - TestSearchRequest request(engine, "bohr street 1 unit 2 lantern ", "en", + TestSearchRequest request(engine, "bohr street 1 lantern ", "en", search::SearchParams::ALL, viewport); request.Wait(); vector> rules = {make_shared(wonderlandId, lantern1), diff --git a/search/v2/features_layer.cpp b/search/v2/features_layer.cpp index bc8914450f..07cae07255 100644 --- a/search/v2/features_layer.cpp +++ b/search/v2/features_layer.cpp @@ -17,6 +17,7 @@ void FeaturesLayer::Clear() m_startToken = 0; m_endToken = 0; m_type = SearchModel::SEARCH_TYPE_COUNT; + m_hasDelayedFeatures = false; } string DebugPrint(FeaturesLayer const & layer) diff --git a/search/v2/features_layer.hpp b/search/v2/features_layer.hpp index fe267a8219..f0a750610f 100644 --- a/search/v2/features_layer.hpp +++ b/search/v2/features_layer.hpp @@ -25,6 +25,15 @@ struct FeaturesLayer size_t m_startToken; size_t m_endToken; SearchModel::SearchType m_type; + + // *NOTE* This field is meaningful only when m_type equals to + // SEARCH_TYPE_BUILDING. + // + // When true, m_sortedFeatures contains only features retrieved from + // search index by m_subQuery, and it's necessary for Geocoder to + // perform additional work to retrieve features matching by house + // number. + bool m_hasDelayedFeatures; }; string DebugPrint(FeaturesLayer const & layer); diff --git a/search/v2/features_layer_matcher.cpp b/search/v2/features_layer_matcher.cpp index 3355518bef..e5f06a294f 100644 --- a/search/v2/features_layer_matcher.cpp +++ b/search/v2/features_layer_matcher.cpp @@ -10,6 +10,9 @@ namespace search { namespace v2 { +// static +double const FeaturesLayerMatcher::kBuildingRadiusMeters = 50; + FeaturesLayerMatcher::FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable) : m_context(context) @@ -65,6 +68,18 @@ vector const & FeaturesLayerMatcher::GetNearbyStreets( return GetNearbyStreetsImpl(featureId, feature); } +vector const & FeaturesLayerMatcher::GetNearbyBuildings( + m2::PointD const & center) +{ + auto const it = m_nearbyBuildingsCache.find(center); + if (it != m_nearbyBuildingsCache.cend()) + return it->second; + + auto & buildings = m_nearbyBuildingsCache[center]; + m_reverseGeocoder.GetNearbyBuildings(center, kBuildingRadiusMeters, buildings); + return buildings; +} + uint32_t FeaturesLayerMatcher::GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature) { auto const & streets = GetNearbyStreets(houseId, houseFeature); diff --git a/search/v2/features_layer_matcher.hpp b/search/v2/features_layer_matcher.hpp index 440defccbd..255ee8902a 100644 --- a/search/v2/features_layer_matcher.hpp +++ b/search/v2/features_layer_matcher.hpp @@ -56,6 +56,7 @@ class FeaturesLayerMatcher { public: static uint32_t const kInvalidId = numeric_limits::max(); + static double const kBuildingRadiusMeters; FeaturesLayerMatcher(Index & index, MwmContext & context, my::Cancellable const & cancellable); @@ -91,34 +92,39 @@ private: template void MatchPOIsWithBuildings(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn) { - static double const kBuildingRadiusMeters = 50; + // Following code initially loads centers of POIs, and, then, for + // each building, tries to find all POIs located at distance less + // than kBuildingRadiusMeters. ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_POI, ()); ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_BUILDING, ()); + auto const & pois = *child.m_sortedFeatures; + auto const & buildings = *parent.m_sortedFeatures; + BailIfCancelled(m_cancellable); - vector poiCenters(child.m_sortedFeatures->size()); + vector poiCenters(pois.size()); - size_t const numPOIs = child.m_sortedFeatures->size(); + size_t const numPOIs = pois.size(); vector isPOIProcessed(numPOIs); size_t processedPOIs = 0; - for (size_t i = 0; i < child.m_sortedFeatures->size(); ++i) + for (size_t i = 0; i < pois.size(); ++i) { FeatureType poiFt; - GetByIndex((*child.m_sortedFeatures)[i], poiFt); + GetByIndex(pois[i], poiFt); poiCenters[i] = feature::GetCenter(poiFt, FeatureType::WORST_GEOMETRY); } - for (size_t i = 0; i < parent.m_sortedFeatures->size() && processedPOIs != numPOIs; ++i) + for (size_t i = 0; i < buildings.size() && processedPOIs != numPOIs; ++i) { BailIfCancelled(m_cancellable); FeatureType buildingFt; - GetByIndex((*parent.m_sortedFeatures)[i], buildingFt); + GetByIndex(buildings[i], buildingFt); - for (size_t j = 0; j < child.m_sortedFeatures->size(); ++j) + for (size_t j = 0; j < pois.size(); ++j) { if (isPOIProcessed[j]) continue; @@ -126,12 +132,33 @@ private: double const distMeters = feature::GetMinDistanceMeters(buildingFt, poiCenters[j]); if (distMeters <= kBuildingRadiusMeters) { - fn((*child.m_sortedFeatures)[j], (*parent.m_sortedFeatures)[i]); + fn(pois[j], buildings[i]); isPOIProcessed[j] = true; ++processedPOIs; } } } + + if (!parent.m_hasDelayedFeatures) + return; + + // |buildings| do not contain buildings matching by house number, so + // following code reads buildings in POIs vicinities and checks + // house numbers. + auto const & mwmId = m_context.m_handle.GetId(); + vector queryTokens; + NormalizeHouseNumber(parent.m_subQuery, queryTokens); + + for (size_t i = 0; i < pois.size(); ++i) + { + for (auto const & building : GetNearbyBuildings(poiCenters[i])) + { + if (building.m_id.m_mwmId != mwmId) + continue; + if (HouseNumbersMatch(building.m_name, queryTokens)) + fn(pois[i], building.m_id.m_index); + } + } } template @@ -140,10 +167,33 @@ private: ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_POI, ()); ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ()); - for (uint32_t streetId : *parent.m_sortedFeatures) + auto const & pois = *child.m_sortedFeatures; + auto const & streets = *parent.m_sortedFeatures; + + // When number of POIs less than number of STREETs, 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) + { + for (auto const & street : GetNearbyStreets(poiId)) + { + if (street.m_id.m_mwmId != mwmId) + continue; + + uint32_t const streetId = street.m_id.m_index; + if (binary_search(streets.begin(), streets.end(), streetId)) + fn(poiId, streetId); + } + } + return; + } + + for (uint32_t streetId : streets) { BailIfCancelled(m_cancellable); - m_loader.ForEachInVicinity(streetId, *child.m_sortedFeatures, bind(fn, _1, streetId)); + m_loader.ForEachInVicinity(streetId, pois, bind(fn, _1, streetId)); } } @@ -151,37 +201,31 @@ private: void MatchBuildingsWithStreets(FeaturesLayer const & child, FeaturesLayer const & parent, TFn && fn) { - // child.m_sortedFeatures contains only buildings matched by name, - // not by house number. So, we need to add to - // child.m_sortedFeatures all buildings match by house number - // here. - ASSERT_EQUAL(child.m_type, SearchModel::SEARCH_TYPE_BUILDING, ()); ASSERT_EQUAL(parent.m_type, SearchModel::SEARCH_TYPE_STREET, ()); - vector queryTokens; - NormalizeHouseNumber(child.m_subQuery, queryTokens); - bool const queryLooksLikeHouseNumber = - !queryTokens.empty() && feature::IsHouseNumber(child.m_subQuery); + auto const & buildings = *child.m_sortedFeatures; + auto const & streets = *parent.m_sortedFeatures; - // When building name does not look like a house number it's - // faster to check nearby streets for each building instead of - // street vicinities loading. - if (!queryLooksLikeHouseNumber && - child.m_sortedFeatures->size() < parent.m_sortedFeatures->size()) + // When all buildings are in |buildings| and number of buildins + // less than number of streets, it's probably faster to check + // nearby streets for each building instead of street vicinities + // loading. + if (!child.m_hasDelayedFeatures && buildings.size() < streets.size()) { + auto const & streets = *parent.m_sortedFeatures; for (uint32_t houseId : *child.m_sortedFeatures) { uint32_t streetId = GetMatchingStreet(houseId); - if (binary_search(parent.m_sortedFeatures->begin(), parent.m_sortedFeatures->end(), - streetId)) - { + if (binary_search(streets.begin(), streets.end(), streetId)) fn(houseId, streetId); - } } return; } + vector queryTokens; + NormalizeHouseNumber(child.m_subQuery, queryTokens); + auto const & checker = ftypes::IsBuildingChecker::Instance(); uint32_t numFilterInvocations = 0; auto houseNumberFilter = [&](uint32_t id, FeatureType & feature, bool & loaded) -> bool @@ -190,7 +234,7 @@ private: if ((numFilterInvocations & 0xFF) == 0) BailIfCancelled(m_cancellable); - if (binary_search(child.m_sortedFeatures->begin(), child.m_sortedFeatures->end(), id)) + if (binary_search(buildings.begin(), buildings.end(), id)) return true; // HouseNumbersMatch() calls are expensive, so following code @@ -204,13 +248,15 @@ private: GetByIndex(id, feature); loaded = true; } + if (!checker(feature)) return false; - string const houseNumber = feature.GetHouseNumber(); - if (!queryLooksLikeHouseNumber || !feature::IsHouseNumber(houseNumber)) + if (!child.m_hasDelayedFeatures) return false; - if (queryTokens[0][0] != houseNumber[0]) + + string const houseNumber = feature.GetHouseNumber(); + if (!feature::IsHouseNumber(houseNumber)) return false; return HouseNumbersMatch(houseNumber, queryTokens); }; @@ -227,7 +273,7 @@ private: }; ProjectionOnStreet proj; - for (uint32_t streetId : *parent.m_sortedFeatures) + for (uint32_t streetId : streets) { BailIfCancelled(m_cancellable); StreetVicinityLoader::Street const & street = m_loader.GetStreet(streetId); @@ -267,6 +313,8 @@ private: vector const & GetNearbyStreets(uint32_t featureId, FeatureType & feature); + vector const & GetNearbyBuildings(m2::PointD const & center); + uint32_t GetMatchingStreetImpl(uint32_t houseId, FeatureType & houseFeature); vector const & GetNearbyStreetsImpl(uint32_t featureId, @@ -282,9 +330,14 @@ private: ReverseGeocoder m_reverseGeocoder; // Cache of streets in a feature's vicinity. All lists in the cache - // are ordered by distance. + // are ordered by distance from feature. unordered_map> m_nearbyStreetsCache; + // Cache of buildings near in a POI's vicinity. All lists in the + // cache are ordered by distance from POI. + unordered_map, m2::PointD::Hash> + m_nearbyBuildingsCache; + // Cache of correct streets for buildings. Current search algorithm // supports only one street for a building, whereas buildings can be // located on multiple streets. diff --git a/search/v2/features_layer_path_finder.cpp b/search/v2/features_layer_path_finder.cpp index efc5906125..14bb73a1fe 100644 --- a/search/v2/features_layer_path_finder.cpp +++ b/search/v2/features_layer_path_finder.cpp @@ -11,6 +11,39 @@ namespace search { namespace v2 { +namespace +{ +// This function tries to estimate amount of work needed to perform an +// intersection pass on a sequence of layers. +template +uint64_t CalcPassCost(TIt begin, TIt end) +{ + uint64_t cost = 0; + + if (begin == end) + return cost; + + uint64_t reachable = max((*begin)->m_sortedFeatures->size(), static_cast(1)); + for (++begin; begin != end; ++begin) + { + uint64_t const layer = max((*begin)->m_sortedFeatures->size(), static_cast(1)); + cost += layer * reachable; + reachable = min(reachable, layer); + } + return cost; +} + +uint64_t CalcTopDownPassCost(vector const & layers) +{ + return CalcPassCost(layers.rbegin(), layers.rend()); +} + +uint64_t CalcBottomUpPassCost(vector const & layers) +{ + return CalcPassCost(layers.begin(), layers.end()); +} +} // namespace + FeaturesLayerPathFinder::FeaturesLayerPathFinder(my::Cancellable const & cancellable) : m_cancellable(cancellable) { @@ -23,10 +56,28 @@ void FeaturesLayerPathFinder::FindReachableVertices(FeaturesLayerMatcher & match if (layers.empty()) return; + uint64_t const topDownCost = CalcTopDownPassCost(layers); + uint64_t const bottomUpCost = CalcBottomUpPassCost(layers); + + if (bottomUpCost < topDownCost) + FindReachableVerticesBottomUp(matcher, layers, reachable); + else + FindReachableVerticesTopDown(matcher, layers, reachable); +} + +void FeaturesLayerPathFinder::FindReachableVerticesTopDown( + FeaturesLayerMatcher & matcher, vector const & layers, + vector & reachable) +{ reachable = *(layers.back()->m_sortedFeatures); vector buffer; + auto addEdge = [&](uint32_t childFeature, uint32_t /* parentFeature */) + { + buffer.push_back(childFeature); + }; + // 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) @@ -36,19 +87,92 @@ void FeaturesLayerPathFinder::FindReachableVertices(FeaturesLayerMatcher & match if (reachable.empty()) break; - buffer.clear(); - auto addEdge = [&](uint32_t childFeature, uint32_t /* parentFeature */) - { - buffer.push_back(childFeature); - }; - FeaturesLayer parent(*layers[i]); parent.m_sortedFeatures = &reachable; - matcher.Match(*layers[i - 1], parent, addEdge); + parent.m_hasDelayedFeatures = false; + FeaturesLayer child(*layers[i - 1]); + child.m_hasDelayedFeatures = false; + if (child.m_type == SearchModel::SEARCH_TYPE_BUILDING) + { + vector tokens; + NormalizeHouseNumber(child.m_subQuery, tokens); + bool const looksLikeHouseNumber = !tokens.empty() && feature::IsHouseNumber(tokens.front()); + child.m_hasDelayedFeatures = looksLikeHouseNumber; + } + + buffer.clear(); + matcher.Match(child, parent, addEdge); reachable.swap(buffer); my::SortUnique(reachable); } } + +void FeaturesLayerPathFinder::FindReachableVerticesBottomUp( + FeaturesLayerMatcher & matcher, vector const & layers, + vector & reachable) +{ + reachable = *(layers.front()->m_sortedFeatures); + + unordered_map parentGraph; + for (uint32_t id : reachable) + parentGraph[id] = id; + + unordered_map nparentGraph; + vector buffer; + + // We're going from low levels to high levels and we need to report + // features from the lowest layer that are reachable from the higher + // layer by some path. Therefore, following code checks and updates + // edges from features in |reachable| to features on the lowest + // layer. + auto addEdge = [&](uint32_t childFeature, uint32_t parentFeature) + { + auto const it = parentGraph.find(childFeature); + if (it == parentGraph.cend()) + return; + nparentGraph[parentFeature] = it->second; + buffer.push_back(parentFeature); + }; + + + for (size_t i = 0; i + 1 != layers.size(); ++i) + { + BailIfCancelled(m_cancellable); + + if (reachable.empty()) + break; + + FeaturesLayer child(*layers[i]); + child.m_sortedFeatures = &reachable; + child.m_hasDelayedFeatures = false; + + FeaturesLayer parent(*layers[i + 1]); + parent.m_hasDelayedFeatures = false; + if (parent.m_type == SearchModel::SEARCH_TYPE_BUILDING) + { + vector tokens; + NormalizeHouseNumber(parent.m_subQuery, tokens); + bool const looksLikeHouseNumber = !tokens.empty() && feature::IsHouseNumber(tokens.front()); + parent.m_hasDelayedFeatures = looksLikeHouseNumber; + } + + buffer.clear(); + nparentGraph.clear(); + matcher.Match(child, parent, addEdge); + parentGraph.swap(nparentGraph); + reachable.swap(buffer); + my::SortUnique(reachable); + } + + buffer.clear(); + for (uint32_t id : reachable) + { + ASSERT_NOT_EQUAL(parentGraph.count(id), 0, ()); + buffer.push_back(parentGraph[id]); + } + reachable.swap(buffer); + my::SortUnique(reachable); +} } // namespace v2 } // namespace search diff --git a/search/v2/features_layer_path_finder.hpp b/search/v2/features_layer_path_finder.hpp index 1670e71b16..b5d160f060 100644 --- a/search/v2/features_layer_path_finder.hpp +++ b/search/v2/features_layer_path_finder.hpp @@ -5,8 +5,8 @@ #include "std/vector.hpp" #if defined(DEBUG) +#include "base/logging.hpp" #include "base/timer.hpp" -#include "std/cstdio.hpp" #endif // defined(DEBUG) class FeaturesVector; @@ -48,9 +48,8 @@ public: // FindReachableVertices() will work fast for most cases // (significantly less than 1 second). #if defined(DEBUG) - fprintf(stderr, "FeaturesLayerPathFinder()\n"); for (auto const * layer : layers) - fprintf(stderr, "Layer: %s\n", DebugPrint(*layer).c_str()); + LOG(LINFO, (DebugPrint(*layer))); my::Timer timer; #endif // defined(DEBUG) @@ -58,7 +57,7 @@ public: FindReachableVertices(matcher, layers, reachable); #if defined(DEBUG) - fprintf(stderr, "Found: %zu, elapsed: %lf seconds\n", reachable.size(), timer.ElapsedSeconds()); + LOG(LINFO, ("Found:", reachable.size(), "elapsed:", timer.ElapsedSeconds(), "seconds")); #endif // defined(DEBUG) for (uint32_t featureId : reachable) @@ -70,6 +69,18 @@ private: vector const & layers, vector & reachable); + // Tries to find all |reachable| features from the lowest layer in a + // high level -> low level pass. + void FindReachableVerticesTopDown(FeaturesLayerMatcher & matcher, + vector const & layers, + vector & reachable); + + // Tries to find all |reachable| features from the lowest layer in a + // low level -> high level pass. + void FindReachableVerticesBottomUp(FeaturesLayerMatcher & matcher, + vector const & layers, + vector & reachable); + my::Cancellable const & m_cancellable; }; } // namespace v2 diff --git a/search/v2/geocoder.cpp b/search/v2/geocoder.cpp index 6152389f42..50b72e6235 100644 --- a/search/v2/geocoder.cpp +++ b/search/v2/geocoder.cpp @@ -32,7 +32,6 @@ #if defined(DEBUG) #include "base/timer.hpp" -#include "std/cstdio.hpp" #endif #if defined(USE_GOOGLE_PROFILER) && defined(OMIM_OS_LINUX) @@ -117,7 +116,7 @@ void Geocoder::Go(vector & results) my::Timer timer; MY_SCOPE_GUARD(printDuration, [&timer]() { - fprintf(stderr, "Total geocoding time: %lf seconds.\n", timer.ElapsedSeconds()); + LOG(LINFO, ("Total geocoding time:", timer.ElapsedSeconds(), "seconds")); }); #endif #if defined(USE_GOOGLE_PROFILER) && defined(OMIM_OS_LINUX) @@ -410,7 +409,7 @@ void Geocoder::DoGeocoding(size_t curToken) FeatureType feature; m_context->m_vector.GetByIndex(featureId, feature); feature.ParseTypes(); - SearchModel::SearchType searchType = m_model.GetSearchType(feature); + SearchModel::SearchType const searchType = m_model.GetSearchType(feature); // All SEARCH_TYPE_CITY features were filtered in DoGeocodingWithLocalities(). if (searchType < SearchModel::SEARCH_TYPE_CITY) diff --git a/search/v2/house_numbers_matcher.cpp b/search/v2/house_numbers_matcher.cpp index 70cd45d5ae..17986cb415 100644 --- a/search/v2/house_numbers_matcher.cpp +++ b/search/v2/house_numbers_matcher.cpp @@ -180,10 +180,15 @@ bool HouseNumbersMatch(string const & houseNumber, string const & query) bool HouseNumbersMatch(string const & houseNumber, vector const & queryTokens) { + if (houseNumber.empty() || queryTokens.empty()) + return false; + if (queryTokens[0][0] != houseNumber[0]) + return false; + vector houseNumberTokens; NormalizeHouseNumber(houseNumber, houseNumberTokens); - if (houseNumberTokens.empty() || queryTokens.empty()) + if (houseNumberTokens.empty()) return false; // Check first tokens (hope, house numbers). diff --git a/search/v2/street_vicinity_loader.cpp b/search/v2/street_vicinity_loader.cpp index dedc174482..dd19ee23f4 100644 --- a/search/v2/street_vicinity_loader.cpp +++ b/search/v2/street_vicinity_loader.cpp @@ -13,6 +13,8 @@ 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) @@ -58,4 +60,5 @@ void StreetVicinityLoader::LoadStreet(uint32_t featureId, Street & street) if (!points.empty()) street.m_calculator = make_unique(move(points), m_offsetMeters); } +} // namespace v2 } // namespace search diff --git a/search/v2/street_vicinity_loader.hpp b/search/v2/street_vicinity_loader.hpp index 731f36d021..c1f6e76369 100644 --- a/search/v2/street_vicinity_loader.hpp +++ b/search/v2/street_vicinity_loader.hpp @@ -19,6 +19,8 @@ class MwmValue; namespace search { +namespace v2 +{ // This class is able to load features in a street's vicinity. // // NOTE: this class *IS NOT* thread-safe. @@ -84,4 +86,5 @@ private: DISALLOW_COPY_AND_MOVE(StreetVicinityLoader); }; +} // namespace v2 } // namespace search