Compare commits

...
Sign in to create a new pull request.

18 commits

Author SHA1 Message Date
Viktor Govako
36d68cfaea [search] Added 'IsCarInfra' POI category.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
7cc24e9290 [search] Added lower bound threshold for the viewport search.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
0a84a0c6e4 [categories] Removed controversial keywords for cafe and restaurant.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
e6c31d964c [search] Using cities' popularity rank.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
24030c0a1e [generator] Build popularity.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
0e696f3d08 [search] Better "subway" query ranking :)
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
167d2e3e62 Treat leisure and healthcare as POIs.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
fb76df3727 [search] Updated 'IsPublicTransportStopChecker'.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
5055e8adaf [search] Updated 'IsShopOrAmenity'.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
4bb9a58ffa [search] Updated alt_old name rank.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
909eb4ebb1 [search] Increased 'errors' and distance ranking penalty.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
4f188b8978 [search] Updated nearby POIs name rank.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:04 -03:00
Viktor Govako
0ef1887ffe [search] Minor fixes.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:03 -03:00
Viktor Govako
d03c0e5fd0 [search] Added _major_ street's synonyms.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:03 -03:00
Viktor Govako
dc7b1d999e [search] Fixed "attraction" ranking.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:03 -03:00
Viktor Govako
2283befb0d [search] Reduced same streets distance threshold.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:03 -03:00
Viktor Govako
b8f418bd3c [search] Fixed categorial rank.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 23:54:03 -03:00
Viktor Govako
bc46f337d7 [planet] New data from 240124.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2024-01-31 11:38:17 -03:00
37 changed files with 3069 additions and 2673 deletions

View file

@ -202,6 +202,13 @@ std::underlying_type_t<T> constexpr Underlying(T value)
return static_cast<std::underlying_type_t<T>>(value);
}
// Short alias like Enum to Integral.
template <typename T>
std::underlying_type_t<T> constexpr E2I(T value)
{
return Underlying(value);
}
// Use this if you want to make a functor whose first
// argument is ignored and the rest are forwarded to |fn|.
template <typename Fn>

Binary file not shown.

Binary file not shown.

View file

@ -899,16 +899,16 @@ sw:Baa|vinywaji
fa:میکده|کاباره
amenity-cafe|@eat
en:3Cafe|3restaurant|U+2615|U+1F356|U+1F357|U+1F35A|U+1F35B|U+1F35C|U+1F35D|U+1F363|U+1F366|U+1F367|U+1F368|U+1F369|U+1F370|U+1F372|U+1F374|U+1F377|U+1F60B|U+1F375|bistro|lunch|coffee shop|coffee bar|coffee|canteen|cafeteria|dining room|place to eat|buffet
ru:3Кафе|3ресторан|кофейня|столовая|кофе
bg:3Кафе|3ресторант|кафене|столова|бюфет|заведение|ядене|храна
en:3Cafe|3restaurant|U+2615|U+1F356|U+1F357|U+1F35A|U+1F35B|U+1F35C|U+1F35D|U+1F363|U+1F366|U+1F367|U+1F368|U+1F369|U+1F370|U+1F372|U+1F374|U+1F377|U+1F60B|U+1F375|coffee|cafeteria
ru:3Кафе|3ресторан|кофейня|кофе
bg:3Кафе|3ресторант|кафене
ar:مقهى|مطعم|طعام|قهوة|كافتيريا
cs:3Kavárna|restaurace|hospoda
da:3Café|restaurant
nl:3Tearoom|3brasserie|3café|3restaurant
fi:2Kahvila
fr:3Café|3restaurant|3resto
de:3Café|3Restaurant|4Kaffee|6Kaffeehaus|Kaffeebar|4Bistro|Mittagessen|Kantine|Cafeteria|Esszimmer|Speiselokal|Buffet
de:3Café|3Restaurant|4Kaffee|6Kaffeehaus|Kaffeebar|Cafeteria
hi:3कॉफ़ीख़ाना
hu:3Kávézó|3étterem|3élelmiszer
id:3Kafe|3restoran
@ -926,8 +926,8 @@ et:3Kohvik|3restoran
eu:3Kafetegia|3Jatetxea
sv:3Café|3restaurang
th:3 คาเฟ่|ร้านอาหาร่
tr:4Kafeterya|kafe|restoran|2Cafe|Bistro
uk:3Кафе|3ресторан|2їжа|2їсти
tr:4Kafeterya|kafe|restoran|2Cafe
uk:3Кафе|3ресторан
vi:Cà phê|quán ăn
zh-Hans:1咖啡馆่|餐厅|食物
zh-Hant:2咖啡廳|咖啡|3cafe|餐廳|飲食
@ -974,16 +974,16 @@ sk:2Rýchle občerstvenie|4fastfood|3kaviareň|reštaurácia
fa:فست فود|غذای حاضری
amenity-restaurant|@eat
en:3Restaurant|3cafe|U+1F356|U+1F357|U+1F35A|U+1F35B|U+1F35C|U+1F35D|U+1F363|U+1F366|U+1F367|U+1F368|U+1F369|U+1F370|U+1F372|U+1F374|U+1F377|U+1F60B|diner|place to eat|meals|lunch|dinner|eating house|buffet
ru:3Ресторан|3кафе|кухня
bg:3ресторант|3кафе|храна|кухня|обяд|вечеря|заведение|бюфет
en:3Restaurant|3cafe|U+1F356|U+1F357|U+1F35A|U+1F35B|U+1F35C|U+1F35D|U+1F363|U+1F366|U+1F367|U+1F368|U+1F369|U+1F370|U+1F372|U+1F374|U+1F377|U+1F60B
ru:3Ресторан|3кафе
bg:3ресторант|3кафе
ar:2مطعم|مقهى|طعام
cs:3Restaurace|hospoda|kavárna
da:3Restaurant|café
nl:3Restaurant|3café
fi:3Ravintola
fr:3Restaurant|3café|3resto
de:3Restaurant|3Café|4Gasthaus|Gaststube|Imbiss|6Speiselokal|Mahlzeiten|Mittagessen|Abendessen|Gastwirtschaft|Buffet
de:3Restaurant|3Café|4Gasthaus|Gaststube|6Speiselokal|Gastwirtschaft
hi:3रेस्तरां
hu:3Étterem|3kávézó|3élelmiszer
id:3Restoran|3kafe
@ -2660,7 +2660,9 @@ vi:Đường sắt leo núi
zh-Hans:缆车
zh-Hant:纜車
railway-subway_entrance|railway-station-subway|@transport
# Do not add subway_entrance here (pollute a map especially in NY).
# ToDo: make enhanced subway station highlight with entrances.
railway-station-subway|@transport
en:3Subway|3tube|3metro|3underground|U+1F687
ru:3Метро
bg:3Метро|подземие

File diff suppressed because it is too large Load diff

View file

@ -167,6 +167,7 @@ void TestPlace::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
fb.AddType(m_type);
fb.GetParams().rank = m_rank;
}
string TestPlace::ToDebugString() const
@ -178,8 +179,9 @@ string TestPlace::ToDebugString() const
}
TestCountry::TestCountry(m2::PointD const & center, std::string const & name, std::string const & lang)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "country"}))
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "country"}), 170 /* rank */)
{
// Default rank for Country with population ~ 1.0E7
}
TestState::TestState(m2::PointD const & center, string const & name, string const & lang)

View file

@ -560,7 +560,8 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
}
}
if (!FLAGS_wikipedia_pages.empty())
// Check !generate_popular_places to avoid mixing, generate_popular_places stage uses the same wiki flags.
if (!FLAGS_generate_popular_places && !FLAGS_wikipedia_pages.empty())
{
// FLAGS_idToWikidata maybe empty.
DescriptionsSectionBuilder::CollectAndBuild(FLAGS_wikipedia_pages, dataFile, FLAGS_idToWikidata);
@ -572,11 +573,13 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
if (FLAGS_generate_popular_places)
{
if (!BuildPopularPlacesMwmSection(genInfo.m_popularPlacesFilename, dataFile,
osmToFeatureFilename))
{
bool isOk = false;
if (!FLAGS_wikipedia_pages.empty())
isOk = BuildPopularPlacesFromWikiDump(dataFile, FLAGS_wikipedia_pages, FLAGS_idToWikidata);
else
isOk = BuildPopularPlacesFromDescriptions(dataFile);
if (!isOk)
LOG(LCRITICAL, ("Error generating popular places mwm section."));
}
}
if (FLAGS_generate_traffic_keys)

View file

@ -1,17 +1,16 @@
#include "generator/popular_places_section_builder.hpp"
#include "generator/gen_mwm_info.hpp"
#include "generator/descriptions_section_builder.hpp"
#include "generator/utils.hpp"
#include "descriptions/serdes.hpp"
#include "indexer/feature_data.hpp"
#include "indexer/feature_processor.hpp"
#include "indexer/ftraits.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "indexer/rank_table.hpp"
#include "base/string_utils.hpp"
#include "coding/csv_reader.hpp"
#include <cstdint>
#include <limits>
#include <mutex>
#include <utility>
#include <vector>
@ -59,47 +58,96 @@ void LoadPopularPlaces(std::string const & srcFilename, PopularPlaces & places)
}
}
bool BuildPopularPlacesMwmSection(std::string const & srcFilename, std::string const & mwmFile,
std::string const & osmToFeatureFilename)
namespace
{
template <class RankGetterT> void BuildPopularPlacesImpl(std::string const & mwmFile, RankGetterT && getter)
{
LOG(LINFO, ("Build Popular Places section"));
std::unordered_map<uint32_t, base::GeoObjectId> featureIdToOsmId;
if (!ParseFeatureIdToOsmIdMapping(osmToFeatureFilename, featureIdToOsmId))
return false;
PopularPlaces places;
LoadPopularPlaces(srcFilename, places);
bool popularPlaceFound = false;
auto const & placeChecker = ftypes::IsPlaceChecker::Instance();
auto const & poiChecker = ftypes::IsPoiChecker::Instance();
std::vector<PopularityIndex> content;
feature::ForEachFeature(mwmFile, [&](FeatureType const & f, uint32_t featureId)
feature::ForEachFeature(mwmFile, [&](FeatureType & ft, uint32_t featureId)
{
ASSERT_EQUAL(content.size(), featureId, ());
PopularityIndex rank = 0;
auto const it = featureIdToOsmId.find(featureId);
// Non-OSM features (coastlines, sponsored objects) are not used.
if (it != featureIdToOsmId.cend())
{
auto const placesIt = places.find(it->second);
if (placesIt != places.cend())
{
if (placeChecker(ft) || poiChecker(ft))
{
rank = getter(ft, featureId);
if (rank > 0)
popularPlaceFound = true;
rank = placesIt->second;
}
}
content.emplace_back(rank);
});
if (!popularPlaceFound)
return true;
FilesContainerW cont(mwmFile, FileWriter::OP_WRITE_EXISTING);
search::RankTableBuilder::Create(content, cont, POPULARITY_RANKS_FILE_TAG);
if (popularPlaceFound)
{
FilesContainerW cont(mwmFile, FileWriter::OP_WRITE_EXISTING);
search::RankTableBuilder::Create(content, cont, POPULARITY_RANKS_FILE_TAG);
}
}
PopularityIndex CalcRank(std::string const & str)
{
auto const charsNum = strings::MakeUniString(str).size();
return std::min(1.0, charsNum / 100000.0) * std::numeric_limits<PopularityIndex>::max();
}
} // namespace
bool BuildPopularPlacesFromDescriptions(std::string const & mwmFile)
{
LOG(LINFO, ("Build Popular Places section"));
/// @todo This routine looks ugly, should make better API.
SingleMwmDataSource dataSource(mwmFile);
auto handle = dataSource.GetHandle();
auto const * value = handle.GetValue();
if (!value->m_cont.IsExist(DESCRIPTIONS_FILE_TAG))
return false;
auto readerPtr = value->m_cont.GetReader(DESCRIPTIONS_FILE_TAG);
descriptions::Deserializer deserializer;
std::vector<int8_t> langPriority;
langPriority.push_back(StringUtf8Multilang::kEnglishCode);
BuildPopularPlacesImpl(mwmFile, [&](FeatureType &, uint32_t featureId)
{
auto const str = deserializer.Deserialize(*readerPtr.GetPtr(), featureId, langPriority);
return str.empty() ? 0 : CalcRank(str);
});
return true;
}
bool BuildPopularPlacesFromWikiDump(std::string const & mwmFile,
std::string const & wikipediaDir, std::string const & idToWikidataPath)
{
LOG(LINFO, ("Build Popular Places section"));
DescriptionsCollector collector(wikipediaDir, mwmFile, idToWikidataPath);
BuildPopularPlacesImpl(mwmFile, [&](FeatureType & ft, uint32_t featureId) -> PopularityIndex
{
collector(ft.GetMetadata().GetWikiURL(), featureId);
if (collector.m_collection.m_features.empty())
return 0;
// If was added
auto & data = collector.m_collection.m_features.back();
if (data.m_ftIndex == featureId)
{
for (auto const & [lang, idx] : data.m_strIndices)
if (lang == StringUtf8Multilang::kEnglishCode)
return CalcRank(collector.m_collection.m_strings[idx]);
}
return 0;
});
return true;
}

View file

@ -12,8 +12,9 @@ using PopularPlaces = std::unordered_map<base::GeoObjectId, PopularityIndex>;
void LoadPopularPlaces(std::string const & srcFilename, PopularPlaces & places);
bool BuildPopularPlacesMwmSection(std::string const & srcFilename, std::string const & mwmFile,
std::string const & osmToFeatureFilename);
bool BuildPopularPlacesFromDescriptions(std::string const & mwmFile);
bool BuildPopularPlacesFromWikiDump(std::string const & mwmFile,
std::string const & wikipediaDir, std::string const & idToWikidataPath);
PopularPlaces const & GetOrLoadPopularPlaces(std::string const & filename);
} // namespace generator

View file

@ -46,6 +46,7 @@ public:
DataSource & GetDataSource() { return m_dataSource; }
platform::LocalCountryFile const & GetLocalCountryFile() const { return m_countryFile; }
MwmSet::MwmId const & GetMwmId() const { return m_mwmId; }
MwmSet::MwmHandle GetHandle() { return GetDataSource().GetMwmHandleById(GetMwmId()); }
private:
FrozenDataSource m_dataSource;

View file

@ -474,7 +474,8 @@ IsPoiChecker::IsPoiChecker() : BaseChecker(1 /* level */)
"historic",
"railway",
"highway",
"aeroway"
"aeroway",
"healthcare",
};
for (auto const & type : poiTypes)
@ -771,11 +772,6 @@ IsToiletsChecker::IsToiletsChecker() : BaseChecker(2 /* level */)
m_types.push_back(c.GetTypeByPath({"toilets", "yes"}));
}
IsCityChecker::IsCityChecker()
{
m_types.push_back(classif().GetTypeByPath({"place", "city"}));
}
IsCapitalChecker::IsCapitalChecker() : BaseChecker(3 /* level */)
{
m_types.push_back(classif().GetTypeByPath({"place", "city", "capital"}));
@ -783,23 +779,25 @@ IsCapitalChecker::IsCapitalChecker() : BaseChecker(3 /* level */)
IsPublicTransportStopChecker::IsPublicTransportStopChecker()
{
m_types.push_back(classif().GetTypeByPath({"highway", "bus_stop"}));
m_types.push_back(classif().GetTypeByPath({"railway", "tram_stop"}));
Classificator const & c = classif();
/// @todo Add bus station into _major_ class (like IsRailwayStationChecker)?
m_types.push_back(c.GetTypeByPath({"amenity", "bus_station"}));
m_types.push_back(c.GetTypeByPath({"amenity", "ferry_terminal"}));
m_types.push_back(c.GetTypeByPath({"highway", "bus_stop"}));
m_types.push_back(c.GetTypeByPath({"railway", "halt"}));
m_types.push_back(c.GetTypeByPath({"railway", "tram_stop"}));
}
IsMotorwayJunctionChecker::IsMotorwayJunctionChecker()
{
Classificator const & c = classif();
m_types.push_back(c.GetTypeByPath({"highway", "motorway_junction"}));
m_types.push_back(classif().GetTypeByPath({"highway", "motorway_junction"}));
}
IsWayWithDurationChecker::IsWayWithDurationChecker()
{
base::StringIL const types[] = {{"route", "ferry"},
{"route", "shuttle_train"}};
Classificator const & c = classif();
for (auto const & e : types)
m_types.push_back(c.GetTypeByPath(e));
m_types.push_back(c.GetTypeByPath({"route", "ferry"}));
m_types.push_back(c.GetTypeByPath({"route", "shuttle_train"}));
}
LocalityType LocalityFromString(std::string_view place)

View file

@ -320,6 +320,7 @@ public:
DECLARE_CHECKER_INSTANCE(IsPisteChecker);
};
/// @todo Should be merged/replaced with search::IsPoiChecker in model.cpp ?
class IsPoiChecker : public BaseChecker
{
IsPoiChecker();
@ -474,7 +475,7 @@ class IsFeeTypeChecker : public BaseChecker
public:
DECLARE_CHECKER_INSTANCE(IsFeeTypeChecker);
};
class IsToiletsChecker : public BaseChecker
{
IsToiletsChecker();
@ -483,13 +484,6 @@ public:
DECLARE_CHECKER_INSTANCE(IsToiletsChecker);
};
class IsCityChecker : public BaseChecker
{
IsCityChecker();
public:
DECLARE_CHECKER_INSTANCE(IsCityChecker);
};
class IsCapitalChecker : public BaseChecker
{
IsCapitalChecker();

View file

@ -229,7 +229,7 @@ UNIT_TEST(Street_TokensFilter)
}
{
List expected = {{"набережная", 1}, {"проспект", 2}};
List expected = {{"набережная", 1}};
List actual;
Utf8StreetTokensFilter filter(actual);
@ -241,7 +241,8 @@ UNIT_TEST(Street_TokensFilter)
}
{
List expected = {{"ленинский", 0}, {"пропект", 1}};
List expectedWithMP = {{"ленинский", 0}};
List expectedWithoutMP = {{"ленинский", 0}, {"пропект", 1}};
List actualWithMisprints;
List actualWithoutMisprints;
@ -253,8 +254,8 @@ UNIT_TEST(Street_TokensFilter)
filterWithMisprints.Put("пропект", false /* isPrefix */, 1 /* tag */);
filterWithoutMisprints.Put("пропект", false /* isPrefix */, 1 /* tag */);
TEST_EQUAL(expected, actualWithMisprints, ());
TEST_EQUAL(expected, actualWithoutMisprints, ());
TEST_EQUAL(expectedWithMP, actualWithMisprints, ());
TEST_EQUAL(expectedWithoutMP, actualWithoutMisprints, ());
}
{

View file

@ -74,7 +74,7 @@ public:
}
uint64_t Size() const override { return m_coding.Size(); }
RankTable::Version GetVersion() const override { return V0; }
void Serialize(Writer & writer, bool preserveHostEndianness) override
void Serialize(Writer & writer) override
{
uint8_t const version = GetVersion();
size_t constexpr kVersionSize = sizeof(version);
@ -112,7 +112,7 @@ void SerializeRankTable(RankTable & table, FilesContainerW & wcont, string const
vector<char> buffer;
{
MemWriter<decltype(buffer)> writer(buffer);
table.Serialize(writer, true /* preserveHostEndianness */);
table.Serialize(writer);
}
wcont.Write(buffer, sectionName);

View file

@ -58,11 +58,8 @@ public:
// Returns underlying data format version.
virtual Version GetVersion() const = 0;
// Serializes rank table. When |preserveHostEndianness| is true,
// table is serialized in host endianness, otherwise, opposite
// endianness is used. Please, don't set this parameter to false
// unless you know what you do.
virtual void Serialize(Writer & writer, bool preserveHostEndianness) = 0;
// Serializes rank table.
virtual void Serialize(Writer & writer) = 0;
// Copies whole section corresponding to a rank table and
// deserializes it. Returns nullptr if there're no ranks section or

View file

@ -330,13 +330,13 @@ private:
char const * affics[] =
{
// Russian - Русский
"улица", "ул",
"улица", "ул", "проспект",
// English - English
"street", "st", "road", "rd", "drive", "dr", "lane", "ln", "avenue", "av", "ave",
// Belarusian - Беларуская мова
"вуліца", "вул",
"вуліца", "вул", "праспект",
// Arabic
"شارع",
@ -345,13 +345,13 @@ private:
"փողոց",
// Catalan language (Barcelona, Valencia, ...)
"carrer",
"carrer", "avinguda",
// Croatian - Hrvatski
"ulica", // Also common used transcription from RU
// French - Français
"rue",
"rue", "avenue",
// Georgia
"ქუჩა",
@ -366,7 +366,7 @@ private:
"jalan",
// Italian - Italiano
"via",
"via", "viale", "piazza",
/// @todo Also expect that this synonyms should be in categories.txt list, but we dont support lt, lv langs now.
/// @{
@ -383,13 +383,13 @@ private:
"strada",
// Spanish - Español
"calle", "avenida",
"calle", "avenida", "plaza",
// Turkish - Türkçe
"sokağı", "sokak", "sk",
// Ukrainian - Українська
"вулиця", "вул",
"вулиця", "вул", "проспект",
// Vietnamese - Tiếng Việt
"đường",

View file

@ -1,6 +1,5 @@
#pragma once
#include "map/bookmark.hpp"
#include "map/bookmark_helpers.hpp"
#include "map/everywhere_search_callback.hpp"
#include "map/search_product_info.hpp"
@ -17,14 +16,10 @@
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
class DataSource;
@ -63,7 +58,7 @@ public:
virtual bool ParseSearchQueryCommand(search::SearchParams const & /* params */) { return false; }
virtual m2::PointD GetMinDistanceBetweenResults() const { return {}; }
virtual m2::PointD GetMinDistanceBetweenResults() const { return {0, 0}; }
virtual search::ProductInfo GetProductInfo(search::Result const & result) const { return {}; }
};

View file

@ -18,7 +18,7 @@ RankTable::Version DummyRankTable::GetVersion() const
return RankTable::VERSION_COUNT;
}
void DummyRankTable::Serialize(Writer & /* writer */, bool /* preserveHostEndianness */)
void DummyRankTable::Serialize(Writer &)
{
NOTIMPLEMENTED();
}

View file

@ -16,6 +16,6 @@ public:
uint8_t Get(uint64_t i) const override;
uint64_t Size() const override;
Version GetVersion() const override;
void Serialize(Writer & /* writer */, bool /* preserveHostEndianness */) override;
void Serialize(Writer &) override;
};
} // namespace search

View file

@ -281,7 +281,7 @@ void Engine::PostMessage(Args &&... args)
void Engine::DoSearch(SearchParams params, shared_ptr<ProcessorHandle> handle, Processor & processor)
{
LOG(LINFO, ("Search started:", params.m_mode));
LOG(LINFO, ("Search started:", params.m_mode, params.m_viewport));
base::Timer timer;
SCOPE_GUARD(printDuration, [&timer]()
{

View file

@ -91,8 +91,7 @@ public:
MatchPOIsWithParent(child, parent, fn);
break;
case Model::TYPE_STREET:
ASSERT(Model::IsPoi(child.m_type) || child.m_type == Model::TYPE_BUILDING,
("Invalid child layer type:", child.m_type));
ASSERT(Model::IsPoiOrBuilding(child.m_type), ("Invalid child layer type:", child.m_type));
if (Model::IsPoi(child.m_type))
MatchPOIsWithStreets(child, parent, fn);
else

View file

@ -131,10 +131,10 @@ public:
return m_table->GetVersion();
}
void Serialize(Writer & writer, bool preserveHostEndiannes) override
void Serialize(Writer & writer) override
{
EnsureTableLoaded();
m_table->Serialize(writer, preserveHostEndiannes);
m_table->Serialize(writer);
}
private:
@ -1783,7 +1783,7 @@ void Geocoder::TraceResult(Tracer & tracer, BaseContext const & ctx, MwmSet::Mwm
{
SCOPE_GUARD(emitParse, [&]() { tracer.EmitParse(ctx.m_tokens); });
if (!Model::IsPoi(type) && type != Model::TYPE_BUILDING)
if (!Model::IsPoiOrBuilding(type))
return;
if (mwmId != m_context->GetId())

View file

@ -122,11 +122,10 @@ public:
bool IsStreet() const;
StoredRankingInfo const & GetRankingInfo() const { return m_info; }
void SetRankingInfo(RankingInfo const & info)
void SetRankingInfo(RankingInfo const & info, bool viewportMode)
{
m_finalRank = info.GetLinearModelRank();
m_finalRank = info.GetLinearModelRank(viewportMode);
m_info = info;
m_partialCategory = info.m_pureCats && !info.m_allTokensUsed;
}
FeatureID const & GetID() const { return m_id; }
@ -139,7 +138,6 @@ public:
double GetDistanceToPivot() const { return m_info.m_distanceToPivot; }
double GetLinearModelRank() const { return m_finalRank; }
bool IsPartialCategory() const { return m_partialCategory; }
bool GetCountryId(storage::CountryInfoGetter const & infoGetter, uint32_t ftype,
storage::CountryId & countryId) const;
@ -181,7 +179,6 @@ private:
StoredRankingInfo m_info;
std::shared_ptr<RankingInfo> m_dbgInfo; // used in debug logs and tests, nullptr in production
bool m_partialCategory;
FeatureID m_id;
double m_finalRank;

View file

@ -55,13 +55,14 @@ public:
Classificator const & c = classif();
auto paths = {
"amenity", "healthcare", "historic", "office", "railway", "shop", "sport", "tourism", "craft"
"amenity", "craft", "healthcare", "historic", "leisure", "office", "railway", "shop", "sport", "tourism",
};
for (auto const & path : paths)
m_types.push_back(c.GetTypeByPath({path}));
}
};
/// @todo Should be merged/replaced with ftypes::IsPoiChecker?
class IsPoiChecker
{
public:

View file

@ -56,6 +56,7 @@ public:
}
static bool IsPoi(Type const type) { return type == TYPE_SUBPOI || type == TYPE_COMPLEX_POI; }
static bool IsPoiOrBuilding(Type const type) { return IsPoi(type) || type == TYPE_BUILDING; }
Type GetType(FeatureType & feature) const;
};

View file

@ -32,15 +32,30 @@ void SweepNearbyResults(m2::PointD const & eps, unordered_set<FeatureID> const &
for (size_t i = 0; i < results.size(); ++i)
{
auto const & p = results[i].GetInfo().m_center;
uint8_t const rank = results[i].GetInfo().m_rank;
uint8_t const popularity = results[i].GetInfo().m_popularity;
uint8_t const exactMatch = results[i].GetInfo().m_exactMatch ? 1 : 0;
uint8_t const rank = results[i].GetInfo().m_rank > 0 ? 1 : 0;
uint8_t const popularity = results[i].GetInfo().m_popularity > 0 ? 1 : 0;
// Confused with previous priority logic: max{rank, prevCount, popularity, exactMatch}.
// How can we compare rank/popularity and exactMatch, which is 1 only :)
// The new logic aggregates:
// - exactMatch (6) like the main criteria
// - prevCount (3)
// - (1) for rank and popularity
// E.g. 6 > 3 + 1 + 1
/// @todo Can update 'rank' and 'popularity' in future, but still 'exactMatch' should be on top.
/// @todo m_exactMatch flag is not enough here.
/// We can store exact errors number and name's lang (alt or old names have less priority).
/// Example: 'Migrolino' with alt name displaces fuel 'SOCAR Opfikon' when searching "Opfikon":
/// - https://www.openstreetmap.org/way/207760005
/// - https://www.openstreetmap.org/way/207760004
uint8_t const exactMatch = results[i].GetInfo().m_exactMatch ? 6 : 0;
// We prefer result which passed the filter even if it has lower rank / popularity / exactMatch.
//uint8_t const filterPassed = results[i].GetInfo().m_refusedByFilter ? 0 : 2;
// We prefer result from prevEmit over result with better filterPassed because otherwise we have
// lots of blinking results.
uint8_t const prevCount = prevEmit.count(results[i].GetId()) == 0 ? 0 : 3;
uint8_t const priority = max({rank, prevCount, popularity, exactMatch}) + 2 /*filterPassed*/;
uint8_t const priority = exactMatch + prevCount + rank + popularity /* + filterPassed */;
sweeper.Add(p.x, p.y, i, priority);
}

View file

@ -134,6 +134,7 @@ NameScores GetNameScores(FeatureType & ft, Geocoder::Params const & params,
/// 1. Make sure that this conversion also happens for Address and POI results,
/// where street is only one component.
/// 2. Make an optimization: If there are no synonyms or "strasse", skip this step.
/// 3. Short street synonym should be scored like full synonym ("AV.SAENZ 459" -> "Avenida SAENZ 459")
if (type == Model::TYPE_STREET)
{
// Searching for "Santa Fe" should rank "Avenida Santa Fe" like FULL_MATCH or FULL_PREFIX, but not SUBSTRING.
@ -214,7 +215,13 @@ NameScores GetNameScores(FeatureType & ft, Geocoder::Params const & params,
void RemoveDuplicatingLinear(vector<RankerResult> & results)
{
double constexpr kDistSameStreetMeters = 5000.0;
// "Молодечно первомайская ул"; "Тюрли первомайская ул" should remain both :)
double constexpr kDistSameStreetMeters = 3000.0;
/// @todo This kind of filtering doesn't work properly in 2-dimension.
/// - less makes X, Y, Z by GetLinearModelRank
/// - equal doesn't recognize X == Z by PointDistance because of middle Y
/// * Update Result::IsEqualFeature when will be fixed.
auto const lessCmp = [](RankerResult const & r1, RankerResult const & r2)
{
@ -363,7 +370,7 @@ public:
info.m_nameScore = NameScore::SUBSTRING;
}
}
else if (m_params.IsCategorialRequest() && Model::IsPoi(info.m_type))
else if (m_params.IsCategorialRequest() && Model::IsPoiOrBuilding(info.m_type))
{
// Update info.m_classifType.poi with the _best preferred_ type. Important for categorial request,
// when the Feature maybe a restaurant and a toilet simultaneously.
@ -402,7 +409,7 @@ public:
info.m_commonTokensFactor = min(3, std::max(0, count - int(info.m_tokenRanges[info.m_type].Size())));
}
res.SetRankingInfo(info);
res.SetRankingInfo(info, m_isViewportMode);
if (m_params.m_useDebugInfo)
res.m_dbgInfo = std::make_shared<RankingInfo>(std::move(info));
@ -550,6 +557,7 @@ private:
if (scores.m_isAltOrOldName)
isAltOrOldName = true;
matchedLength += scores.m_matchedLength;
return scores.m_nameScore;
};
auto const updateDependScore = [&](Model::Type type, uint32_t dependID)
@ -577,7 +585,15 @@ private:
ASSERT(preInfo.m_tokenRanges[Model::TYPE_VILLAGE].Empty(), ());
}
updateScoreForFeature(*city, type);
auto const cityNameScore = updateScoreForFeature(*city, type);
// Update distance with matched city pivot if we have a _good_ city name score.
// A bit controversial distance score reset, but lets try.
// Other option is to combine old pivot distance and city distance.
// See 'Barcelona_Carrers' test.
// "San Francisco" query should not update rank for "Francisco XXX" street in "San YYY" village.
if (cityNameScore == NameScore::FULL_MATCH)
info.m_distanceToPivot = mercator::DistanceOnEarth(center, city->GetCenter());
}
}
@ -609,7 +625,7 @@ private:
info.m_falseCats = categoriesInfo.IsFalseCategories();
}
uint8_t NormalizeRank(uint8_t rank, Model::Type type, m2::PointD const & center,
uint16_t NormalizeRank(uint16_t rank, Model::Type type, m2::PointD const & center,
string const & country, bool isCapital, bool isRelaxed)
{
// Do not prioritize objects with population < 800. Same as RankToPopulation(rank) < 800, but faster.
@ -624,8 +640,9 @@ private:
case Model::TYPE_VILLAGE: return rank / 2.5;
case Model::TYPE_CITY:
{
/// @todo Tried to reduce more (1.5), but important Famous_Cities_Rank test fails.
if (isCapital || m_ranker.m_params.m_viewport.IsPointInside(center))
return base::Clamp(static_cast<int>(rank) * 2, 0, 0xFF);
return rank * 1.8;
storage::CountryInfo info;
if (country.empty())
@ -633,7 +650,7 @@ private:
else
m_infoGetter.GetRegionInfo(country, info);
if (info.IsNotEmpty() && info.m_name == m_ranker.m_params.m_pivotRegion)
return base::Clamp(static_cast<int>(rank * 1.7), 0, 0xFF);
return rank * 1.7;
// Fallthrough like "STATE" for cities without info.
} [[fallthrough]];
@ -762,17 +779,25 @@ void Ranker::UpdateResults(bool lastUpdate)
if (m_params.m_viewportSearch)
{
// Heuristics to filter partially matched category trash in the viewport.
// https://github.com/organicmaps/organicmaps/issues/5251
auto it = partition(m_tentativeResults.begin(), m_tentativeResults.end(),
[](RankerResult const & r) { return !r.IsPartialCategory(); });
// https://github.com/organicmaps/organicmaps/issues/5566
/// @todo https://github.com/organicmaps/organicmaps/issues/5251 Should review later
// https://github.com/organicmaps/organicmaps/issues/4325
// https://github.com/organicmaps/organicmaps/issues/4190
// https://github.com/organicmaps/organicmaps/issues/3677
size_t const goodCount = distance(m_tentativeResults.begin(), it);
if (goodCount >= 10 || goodCount * 3 >= m_tentativeResults.size())
m_tentativeResults.erase(it, m_tentativeResults.end());
auto & resV = m_tentativeResults;
auto it = std::max_element(resV.begin(), resV.end(), base::LessBy(&RankerResult::GetLinearModelRank));
double const lowestAllowed = it->GetLinearModelRank() - RankingInfo::GetLinearRankViewportThreshold();
sort(m_tentativeResults.begin(), m_tentativeResults.end(),
base::LessBy(&RankerResult::GetDistanceToPivot));
it = std::partition(resV.begin(), resV.end(), [lowestAllowed](RankerResult const & r)
{
return r.GetLinearModelRank() >= lowestAllowed;
});
if (it != resV.end())
{
LOG(LDEBUG, ("Removed", std::distance(it, resV.end()), "viewport results."));
resV.erase(it, resV.end());
}
}
else
{

View file

@ -26,20 +26,30 @@ double constexpr kCategoriesDistanceToPivot = -0.6874177;
double constexpr kCategoriesRank = 1.0000000;
double constexpr kCategoriesFalseCats = -1.0000000;
double constexpr kDistanceToPivot = -0.2123693;
// This constant is very important and checked in Famous_Cities_Rank test.
double constexpr kDistanceToPivot = -0.48;
double constexpr kWalkingDistanceM = 5000.0;
double constexpr AbsPenaltyPerKm()
{
return - kDistanceToPivot * 1000.0 / RankingInfo::kMaxDistMeters;
}
// These constants are very important and checked in Famous_Cities_Rank test.
double constexpr kRank = 0.23;
double constexpr kPopularity = 1.0000000;
double constexpr kPopularity = 0.42;
// Decreased this value:
// - On the one hand, when search for "eat", we'd prefer category types, rather than "eat" name.
// - On the other hand, when search for "subway", do we usually prefer famous fast food or metro?
double constexpr kFalseCats = -0.01;
double constexpr kErrorsMade = -0.15;
double constexpr kErrorsMade = -0.4;
double constexpr kMatchedFraction = 0.1876736;
double constexpr kAllTokensUsed = 0.0478513;
double constexpr kCommonTokens = -0.05;
double constexpr kAltOldName = -0.25; // Some reasonable penalty like kErrorsMade.
// Some thoughts about reasonable diff here:
// - should be a bit less than fabs(kAltOldName) to filter non-obvious matching
// - should be greater than fabs(kErrorsMade) / 2
// - shoulbe be comparable with kRank to keep cities/towns
double constexpr kViewportDiffThreshold = 0.2415;
static_assert(kViewportDiffThreshold < -kAltOldName && kViewportDiffThreshold > -kErrorsMade / 2);
double constexpr kNameScore[] = {
-0.05, // Zero
@ -49,7 +59,7 @@ double constexpr kNameScore[] = {
0.018, // Full Prefix
0.02, // Full Match
};
static_assert(std::size(kNameScore) == static_cast<size_t>(NameScore::COUNT));
static_assert(std::size(kNameScore) == base::E2I(NameScore::COUNT));
// 0-based factors from POIs, Streets, Buildings, since we don't have ratings or popularities now.
double constexpr kType[] = {
@ -58,13 +68,13 @@ double constexpr kType[] = {
0.007, // Building, to compensate max(kStreetType), see Arbat_Address test.
0, // Street
0, // Suburb
-0.02, // Unclassified
-0.025, // Unclassified
0, // Village
0.01, // City
0.0233254, // State
0.1679389, // Country
};
static_assert(std::size(kType) == static_cast<size_t>(Model::TYPE_COUNT));
static_assert(std::size(kType) == Model::TYPE_COUNT);
// 0-based factors from General.
double constexpr kPoiType[] = {
@ -74,10 +84,21 @@ double constexpr kPoiType[] = {
0.01, // Hotel
0.01, // Shop or Amenity
0.01, // Attraction
-0.01, // Service
0.008, // CarInfra
0.005, // PureCategory
0, // General
-0.01, // Service
};
static_assert(std::size(kPoiType) == base::Underlying(PoiType::Count));
static_assert(std::size(kPoiType) == base::E2I(PoiType::Count));
// - When search for "eat", we'd prefer category types, rather than "eat" name.
// - To _equalize_ "subway" search: Metro category should be equal with "Subway" fast food.
// - See NY_Subway test.
double constexpr kFalseCats =
kNameScore[base::E2I(NameScore::FULL_PREFIX)] - kNameScore[base::E2I(NameScore::FULL_MATCH)] +
kPoiType[base::E2I(PoiType::PureCategory)] - kPoiType[base::E2I(PoiType::Eat)]
+ AbsPenaltyPerKm(); // a small 'plus diff' to keep fast food a little bit higher
static_assert(kFalseCats < 0.0);
double constexpr kStreetType[] = {
0, // Default
@ -103,19 +124,18 @@ static_assert(kErrorsMade <= 0, "");
double TransformDistance(double distance)
{
return min(distance, RankingInfo::kMaxDistMeters) / RankingInfo::kMaxDistMeters;
return std::min(distance, RankingInfo::kMaxDistMeters) / RankingInfo::kMaxDistMeters;
}
void PrintParse(ostringstream & oss, array<TokenRange, Model::TYPE_COUNT> const & ranges,
size_t numTokens)
void PrintParse(ostringstream & oss, array<TokenRange, Model::TYPE_COUNT> const & ranges, size_t numTokens)
{
vector<Model::Type> types(numTokens, Model::Type::TYPE_COUNT);
std::vector<Model::Type> types(numTokens, Model::TYPE_COUNT);
for (size_t i = 0; i < ranges.size(); ++i)
{
for (size_t pos : ranges[i])
{
CHECK_LESS(pos, numTokens, ());
CHECK_EQUAL(types[pos], Model::Type::TYPE_COUNT, ());
CHECK_EQUAL(types[pos], Model::TYPE_COUNT, ());
types[pos] = static_cast<Model::Type>(i);
}
}
@ -142,8 +162,19 @@ public:
}
};
class IsAttraction : public BaseTypesChecker
class IsAttraction
{
std::vector<uint32_t> m_types;
public:
bool operator() (feature::TypesHolder const & th) const
{
// Strict check (unlike in BaseTypesChecker) to avoid matching:
// - historic-memorial-plaque
// - leisure-garden-residential
return base::AnyOf(m_types, [&th](uint32_t t) { return th.Has(t); });
}
public:
IsAttraction()
{
@ -181,17 +212,19 @@ public:
// Amenity types are very fragmented, so take only most _interesting_ here.
{"amenity", "bank"},
{"amenity", "brothel"},
{"amenity", "car_rental"},
{"amenity", "casino"},
{"amenity", "cinema"},
{"amenity", "clinic"},
{"amenity", "hospital"},
{"amenity", "ice_cream"},
{"amenity", "library"},
{"amenity", "marketplace"},
{"amenity", "nightclub"},
{"amenity", "pharmacy"},
{"amenity", "police"},
{"amenity", "post_office"},
{"amenity", "stripclub"},
{"amenity", "taxi"},
{"amenity", "theatre"},
};
@ -201,6 +234,29 @@ public:
}
};
class IsCarInfra : public BaseTypesChecker
{
public:
IsCarInfra()
{
base::StringIL const types[] = {
{"amenity", "car_rental"},
{"amenity", "car_sharing"},
{"amenity", "car_wash"},
{"amenity", "charging_station"},
{"amenity", "fuel"},
{"amenity", "parking"},
{"highway", "rest_area"},
{"highway", "services"},
};
Classificator const & c = classif();
for (auto const & e : types)
m_types.push_back(c.GetTypeByPath(e));
}
};
class IsServiceTypeChecker : public BaseTypesChecker
{
public:
@ -299,12 +355,13 @@ void RankingInfo::ToCSV(ostream & os) const
os << (m_hasName ? 1 : 0);
}
double RankingInfo::GetLinearModelRank() const
double RankingInfo::GetLinearRankViewportThreshold()
{
return kViewportDiffThreshold;
}
double RankingInfo::GetLinearModelRank(bool viewportMode /* = false */) const
{
// NOTE: this code must be consistent with scoring_model.py. Keep
// this in mind when you're going to change scoring_model.py or this
// code. We're working on automatic rank calculation code generator
// integrated in the build system.
double const distanceToPivot = TransformDistance(m_distanceToPivot);
double const rank = static_cast<double>(m_rank) / numeric_limits<uint8_t>::max();
double const popularity = static_cast<double>(m_popularity) / numeric_limits<uint8_t>::max();
@ -312,40 +369,55 @@ double RankingInfo::GetLinearModelRank() const
double result = 0.0;
if (!m_categorialRequest)
{
result += kDistanceToPivot * distanceToPivot;
if (!viewportMode)
result += kDistanceToPivot * distanceToPivot;
result += kRank * rank;
result += kPopularity * popularity;
result += kFalseCats * (m_falseCats ? 1 : 0);
if (m_falseCats)
result += kFalseCats;
/// @todo Make also for POIs?
if (Model::IsLocalityType(m_type))
result += kPopularity * popularity;
ASSERT(m_type < Model::TYPE_COUNT, ());
result += kType[GetTypeScore()];
if (Model::IsPoi(m_type))
{
CHECK_LESS(m_classifType.poi, PoiType::Count, ());
ASSERT_LESS(m_classifType.poi, PoiType::Count, ());
result += kPoiType[base::Underlying(GetPoiTypeScore())];
}
else if (m_type == Model::TYPE_STREET)
{
CHECK_LESS(m_classifType.street, StreetType::Count, ());
ASSERT_LESS(m_classifType.street, StreetType::Count, ());
result += kStreetType[base::Underlying(m_classifType.street)];
}
result += (m_allTokensUsed ? 1 : 0) * kAllTokensUsed;
if (m_allTokensUsed)
result += kAllTokensUsed;
auto const nameRank = kNameScore[static_cast<size_t>(GetNameScore())] +
kErrorsMade * GetErrorsMadePerToken() +
kMatchedFraction * m_matchedFraction;
result += (m_isAltOrOldName ? 0.7 : 1.0) * nameRank;
result += nameRank;
result += kCommonTokens * m_commonTokensFactor;
if (m_isAltOrOldName)
result += kAltOldName;
}
else
{
result += kCategoriesDistanceToPivot * distanceToPivot;
if (!viewportMode)
result += kCategoriesDistanceToPivot * distanceToPivot;
result += kCategoriesRank * rank;
result += kCategoriesPopularity * popularity;
result += kCategoriesFalseCats * (m_falseCats ? 1 : 0);
result += m_hasName * kHasName;
if (m_falseCats)
result += kCategoriesFalseCats;
if (m_hasName)
result += kHasName;
}
return result;
@ -367,15 +439,27 @@ double RankingInfo::GetErrorsMadePerToken() const
NameScore RankingInfo::GetNameScore() const
{
if (!m_pureCats && m_type == Model::TYPE_SUBPOI && m_nameScore == NameScore::FULL_PREFIX)
if (!m_pureCats && Model::IsPoi(m_type) && m_classifType.poi <= PoiType::Attraction)
{
// It's better for ranking when POIs would be equal by name score. Some examples:
// query="rewe", pois=["REWE", "REWE City", "REWE to Go"]
// query="carrefour", pois=["Carrefour", "Carrefour Mini", "Carrefour Gurme"]
// It's better for ranking when POIs would be equal by name score in the next cases:
// The reason behind that is that user usually does search for _any_ shop within some commercial network.
// But cities or streets geocoding should distinguish this cases.
return NameScore::FULL_MATCH;
if (m_nameScore == NameScore::FULL_PREFIX)
{
// query = "rewe", pois = ["REWE", "REWE City", "REWE to Go"]
// query = "carrefour", pois = ["Carrefour", "Carrefour Mini", "Carrefour Gurme"]
// The reason behind that is that user usually does search for _any_ shop within some commercial network.
// But cities or streets geocoding should distinguish this cases.
return NameScore::FULL_MATCH;
}
if (m_nameScore != NameScore::ZERO && m_distanceToPivot < kWalkingDistanceM)
{
// query = "rimi", pois = ["Rimi", "Mini Rimi"]
// Equal name score for POIs within some reasonable walking distance.
return NameScore::FULL_MATCH;
}
}
return m_nameScore;
@ -388,8 +472,8 @@ Model::Type RankingInfo::GetTypeScore() const
PoiType RankingInfo::GetPoiTypeScore() const
{
// Do not increment score for category-only-matched results or subways will be _always_ on top otherwise.
return (m_pureCats ? PoiType::General : m_classifType.poi);
// Equalize all *pure category* results to not distinguish different toilets (see NY_Subway, ToiletAirport test).
return (m_pureCats ? PoiType::PureCategory : m_classifType.poi);
}
PoiType GetPoiType(feature::TypesHolder const & th)
@ -418,6 +502,10 @@ PoiType GetPoiType(feature::TypesHolder const & th)
if (shopOrAmenityCheck(th))
return PoiType::ShopOrAmenity;
static IsCarInfra const carInfra;
if (carInfra(th))
return PoiType::CarInfra;
static IsServiceTypeChecker const serviceCheck;
if (serviceCheck(th))
return PoiType::Service;
@ -435,8 +523,10 @@ string DebugPrint(PoiType type)
case PoiType::Hotel: return "Hotel";
case PoiType::ShopOrAmenity: return "ShopOrAmenity";
case PoiType::Attraction: return "Attraction";
case PoiType::Service: return "Service";
case PoiType::CarInfra: return "CarInfra";
case PoiType::PureCategory: return "PureCategory";
case PoiType::General: return "General";
case PoiType::Service: return "Service";
case PoiType::Count: return "Count";
}
UNREACHABLE();

View file

@ -14,6 +14,7 @@ namespace feature { class TypesHolder; }
namespace search
{
/// @note The order is important here (less is better)
enum class PoiType : uint8_t
{
// Railway/subway stations, airports.
@ -28,10 +29,17 @@ enum class PoiType : uint8_t
ShopOrAmenity,
// Attractions.
Attraction,
// Service types: power lines and substations, barrier-fence, etc.
Service,
// Car Infra
CarInfra,
// Factor for *pure category* matched result.
PureCategory,
// All other POIs.
General,
// Service types: power lines and substations, barrier-fence, etc.
Service,
Count
};
@ -74,8 +82,11 @@ struct RankingInfo : public StoredRankingInfo
void ToCSV(std::ostream & os) const;
// Returns rank calculated by a linear model, bigger is better.
double GetLinearModelRank() const;
/// @param[in] viewportMode True, and distance is not included into the final rank.
/// @return Rank calculated by a linear model, bigger is better.
double GetLinearModelRank(bool viewportMode = false) const;
static double GetLinearRankViewportThreshold();
double GetErrorsMadePerToken() const;
@ -100,8 +111,8 @@ struct RankingInfo : public StoredRankingInfo
// Number of misprints.
ErrorsMade m_errorsMade;
// Rank of the feature.
uint8_t m_rank = 0;
// Rank of the feature. Store uint16_t because of possible 'normalization'.
uint16_t m_rank = 0;
// Popularity rank of the feature.
uint8_t m_popularity = 0;

View file

@ -134,6 +134,16 @@ struct NameScores
if (newNameScoreIsBetter && m_nameScore == NameScore::FULL_PREFIX && m_errorsMade.IsBetterThan(rhs.m_errorsMade))
newNameScoreIsBetter = false;
// FULL_PREFIX with !alt_old_name) is better than FULL_MATCH with alt_old_name.
if (!m_isAltOrOldName && rhs.m_isAltOrOldName &&
!rhs.m_errorsMade.IsBetterThan(m_errorsMade) &&
(int)rhs.m_nameScore - (int)m_nameScore < 2)
{
newNameScoreIsBetter = false;
}
/// @todo What should we do with alt_old_name and errors==0 vs !alt_old_name and errors==1?
auto const nameScoresAreEqual = m_nameScore == rhs.m_nameScore;
auto const newLanguageIsBetter = m_isAltOrOldName && !rhs.m_isAltOrOldName;
auto const languagesAreEqual = m_isAltOrOldName == rhs.m_isAltOrOldName;

View file

@ -121,6 +121,7 @@ bool Result::IsEqualFeature(Result const & r) const
if (ftypes::IsPublicTransportStopChecker::Instance()(m_featureType))
return PointDistance(m_center, r.m_center) < 150.0;
/// @todo Keep this check until RemoveDuplicatingLinear will be fixed.
// Filter same streets (with 'same logical street distance' threshold).
if (ftypes::IsWayChecker::Instance().GetSearchRank(m_featureType) != ftypes::IsWayChecker::Default)
return PointDistance(m_center, r.m_center) < 2000.0;

View file

@ -17,7 +17,6 @@
#include "editor/editable_data_source.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_impl.hpp"
#include "geometry/mercator.hpp"
@ -370,7 +369,7 @@ UNIT_CLASS_TEST(ProcessorTest, Smoke)
// Here we expect to find bohrHouse (building next to Bohr street with housenumber '1 unit 1')
// but not lantern1 (building next to Bohr street with name 'lantern 1') because '1' looks like
// housenumber.
Rules rules = { ExactMatch(wonderlandId, bohrHouse),ExactMatch(wonderlandId, hilbertHouse) };
Rules rules = { ExactMatch(wonderlandId, bohrHouse), ExactMatch(wonderlandId, hilbertHouse) };
TEST(ResultsMatch("bohr street 1 ", rules), ());
}
{
@ -1220,7 +1219,7 @@ UNIT_CLASS_TEST(ProcessorTest, FuzzyMatch)
bar.SetTypes({{"amenity", "pub"}});
TestPOI metro({5.0, 5.0}, "Liceu", "es");
metro.SetTypes({{"railway", "subway_entrance"}});
metro.SetTypes({{"railway", "station", "subway"}});
BuildWorld([&](TestMwmBuilder & builder)
{
@ -2060,14 +2059,14 @@ UNIT_CLASS_TEST(ProcessorTest, RemoveDuplicatingStreets)
{
string const streetName = "Октябрьский проспект";
// Distance between centers should be less than 5km.
// Distance between centers should be less than 3km (0.027).
TestStreet street1({{0.0, 0.0}, {0.0, 0.01}}, streetName, "ru");
street1.SetType({ "highway", "primary" });
TestStreet street2({{0.0, 0.01}, {0.0, 0.02}}, streetName, "ru");
TestStreet street2({{0.0, 0.01}, {0.0, 0.015}}, streetName, "ru");
street2.SetType({ "highway", "secondary" });
TestStreet street3({{0.0, 0.02}, {0.0, 0.03}}, streetName, "ru");
TestStreet street3({{0.0, 0.015}, {0.0, 0.02}}, streetName, "ru");
street3.SetType({ "highway", "footway" });
TestStreet street4({{0.0, 0.03}, {0.0, 0.04}}, streetName, "ru");
TestStreet street4({{0.0, 0.02}, {0.0, 0.03}}, streetName, "ru");
street4.SetType({ "highway", "tertiary_link", "tunnel" });
auto wonderlandId = BuildCountry("Wonderland", [&](TestMwmBuilder & builder)
@ -2725,6 +2724,23 @@ UNIT_CLASS_TEST(ProcessorTest, Suburbs)
}
}
UNIT_CLASS_TEST(ProcessorTest, Suburbs1)
{
// The same with neighborhood
TestSuburb suburb({0, 0}, "les Planes", "default");
auto countryId = BuildCountry("Wonderland", [&](TestMwmBuilder & builder)
{
builder.Add(suburb);
});
SetViewport(m2::RectD(-1.0, -1.0, 1.0, 1.0));
/// @todo Should debug this case. Actually, bad matching because of "carrer".
Rules rules = {ExactMatch(countryId, suburb)};
TEST(ResultsMatch("carrer de les planes", rules), ());
}
UNIT_CLASS_TEST(ProcessorTest, ViewportFilter)
{
TestStreet street23({{0.5, -1.0}, {0.5, 1.0}}, "23rd February street", "en");

View file

@ -6,7 +6,6 @@
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include <utility>
#include <vector>
namespace ranker_test
@ -98,8 +97,9 @@ UNIT_CLASS_TEST(RankerTest, UniteSameResults)
UNIT_CLASS_TEST(RankerTest, PreferCountry)
{
std::string const name = "Wonderland";
TestCountry wonderland(m2::PointD(10.0, 10.0), name, "en");
TestCountry wonderland(m2::PointD(9.0, 9.0), name, "en"); // ~1400 km from (0, 0)
TestPOI cafe(m2::PointD(0.0, 0.0), name, "en");
cafe.SetTypes({{"amenity", "cafe"}});
auto const worldID = BuildWorld([&](TestMwmBuilder & builder)
{

View file

@ -43,7 +43,10 @@ public:
TEST_LESS(beg, end, ());
TEST_GREATER_OR_EQUAL(v.size(), end, ());
}
explicit Range(ResultsT const & v) : Range(v, 0, kTopPoiResultsCount) {}
explicit Range(ResultsT const & v, bool all = false)
: Range(v, 0, all ? v.size() : kTopPoiResultsCount)
{
}
size_t size() const { return m_end - m_beg; }
auto begin() const { return m_v.begin() + m_beg; }
@ -51,20 +54,20 @@ public:
auto const & operator[](size_t i) const { return *(begin() + i); }
};
/// @return First (minimal) distance in meters.
static double SortedByDistance(Range const & results, ms::LatLon const & center)
/// @return [First (min), Last (max)] distance in range (meters).
static std::pair<double, double> SortedByDistance(Range const & results, ms::LatLon const & center)
{
double const firstDist = ms::DistanceOnEarth(center, mercator::ToLatLon(results[0].GetFeatureCenter()));
double const firstDist = GetDistanceM(results[0], center);
double prevDist = firstDist;
for (size_t i = 1; i < results.size(); ++i)
{
double const dist = ms::DistanceOnEarth(center, mercator::ToLatLon(results[i].GetFeatureCenter()));
double const dist = GetDistanceM(results[i], center);
TEST_LESS(prevDist, dist + kDistanceEpsilon, (results[i-1], results[i]));
prevDist = dist;
}
return firstDist;
return {firstDist, prevDist};
}
static std::vector<uint32_t> GetClassifTypes(std::vector<base::StringIL> const & arr)
@ -152,81 +155,76 @@ public:
TEST(found, ());
}
double GetDistanceM(search::Result const & r, ms::LatLon const & ll) const
static double GetDistanceM(search::Result const & r, ms::LatLon const & ll)
{
return ms::DistanceOnEarth(ll, mercator::ToLatLon(r.GetFeatureCenter()));
}
};
// https://github.com/organicmaps/organicmaps/issues/3026
UNIT_CLASS_TEST(MwmTestsFixture, Berlin_Rewe)
UNIT_CLASS_TEST(MwmTestsFixture, TopPOIs_Smoke)
{
// Berlin
ms::LatLon const center(52.5170365, 13.3888599);
SetViewportAndLoadMaps(center);
// https://github.com/organicmaps/organicmaps/issues/3026
{
// Berlin
ms::LatLon const center(52.5170365, 13.3888599);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("rewe");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
auto request = MakeRequest("rewe");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
Range const range(results, 0, kPopularPoiResultsCount);
EqualClassifType(range, GetClassifTypes({{"shop"}, {"amenity", "fast_food"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 1000, ());
}
Range const range(results, 0, kPopularPoiResultsCount);
EqualClassifType(range, GetClassifTypes({{"shop"}, {"amenity", "fast_food"}}));
TEST_LESS(SortedByDistance(range, center).first, 500, ());
}
// https://github.com/organicmaps/organicmaps/issues/1376
UNIT_CLASS_TEST(MwmTestsFixture, Madrid_Carrefour)
{
// Madrid
ms::LatLon const center(40.41048, -3.69773);
SetViewportAndLoadMaps(center);
// https://github.com/organicmaps/organicmaps/issues/1376
{
// Madrid
ms::LatLon const center(40.41048, -3.69773);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("carrefour");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
auto request = MakeRequest("carrefour");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
/// @todo 'Carrefour' city in Haiti :)
TEST_EQUAL(results[0].GetFeatureType(), classif().GetTypeByPath({"place", "city", "capital", "3"}), ());
Range const range(results, 0, kPopularPoiResultsCount);
EqualClassifType(range, GetClassifTypes({{"shop"}}));
TEST_LESS(SortedByDistance(range, center).first, 200, ());
}
Range const range(results, 1, kPopularPoiResultsCount);
EqualClassifType(range, GetClassifTypes({{"shop"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 500, ());
}
// https://github.com/organicmaps/organicmaps/issues/2530
{
// Nicosia
ms::LatLon const center(35.16915, 33.36141);
SetViewportAndLoadMaps(center);
// https://github.com/organicmaps/organicmaps/issues/2530
UNIT_CLASS_TEST(MwmTestsFixture, Nicosia_Jumbo)
{
// Nicosia
ms::LatLon const center(35.16915, 33.36141);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("jumb");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
auto request = MakeRequest("jumb");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
Range const range(results, 0, 4);
EqualClassifType(range, GetClassifTypes({{"shop"}}));
TEST_LESS(SortedByDistance(range, center).first, 5000, ());
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop"}, {"amenity", "parking"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 5000, ());
}
// parking (< 6km) should be on top.
EqualClassifType(Range(results, 4, 6), GetClassifTypes({{"leisure", "playground"}, {"amenity", "parking"}}));
}
// https://github.com/organicmaps/organicmaps/issues/2470
UNIT_CLASS_TEST(MwmTestsFixture, Aarhus_Netto)
{
// Aarhus
ms::LatLon const center(56.14958, 10.20394);
SetViewportAndLoadMaps(center);
// https://github.com/organicmaps/organicmaps/issues/2470
{
// Aarhus
ms::LatLon const center(56.14958, 10.20394);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("netto");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
auto request = MakeRequest("netto");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 500, ());
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop"}}));
TEST_LESS(SortedByDistance(range, center).first, 300, ());
}
}
// https://github.com/organicmaps/organicmaps/issues/2133
@ -236,20 +234,15 @@ UNIT_CLASS_TEST(MwmTestsFixture, NY_Subway)
ms::LatLon const center(40.7355019, -73.9948155);
SetViewportAndLoadMaps(center);
// Interesting case, because Manhattan has high density of:
// - "Subway" fast food
// - railway-subway category;
// - bus stops with name ".. subway ..";
// + Some noname cities LIKE("Subway", 1 error) in the World.
auto request = MakeRequest("subway");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
// Fast food "Subways" should be the first despite of some "metro" subways are a bit closer ..
Range const range(results, 0, 2);
EqualClassifType(range, GetClassifTypes({{"amenity", "fast_food"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 1000, ());
/// @todo First result is building-train_station with 'subway' in name;
// - 5 nearby 'subway' fast-foods;
// - 3 railway-station-subway;
EqualClassifType(Range(results, 1, 6), GetClassifTypes({{"amenity", "fast_food"}}));
EqualClassifType(Range(results, 6, 9), GetClassifTypes({{"railway", "station", "subway"}}));
}
// https://github.com/organicmaps/organicmaps/issues/3249
@ -268,8 +261,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, London_Asda)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop"}, {"amenity"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 2000, ());
TEST_LESS(SortedByDistance(range, center).first, 2000, ());
}
}
@ -284,10 +276,10 @@ UNIT_CLASS_TEST(MwmTestsFixture, Lyon_Aldi)
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
Range const range(results);
/// @todo First result is "L'ancre aldine", but maybe it is ok?
Range const range(results, 1, kPopularPoiResultsCount);
EqualClassifType(range, GetClassifTypes({{"shop", "supermarket"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 4000, ());
TEST_LESS(SortedByDistance(range, center).first, 2000, ());
}
// https://github.com/organicmaps/organicmaps/issues/1262
@ -303,8 +295,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, NY_BarnesNoble)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop", "books"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 2000, ());
TEST_LESS(SortedByDistance(range, center).first, 1000, ());
}
// https://github.com/organicmaps/organicmaps/issues/2470
@ -324,15 +315,16 @@ UNIT_CLASS_TEST(MwmTestsFixture, Hamburg_Park)
{"tourism", "theme_park"},
{"amenity", "fast_food"},
{"shop", "gift"},
/// @todo Add _near street_ penalty
// {"amenity", "pharmacy"}, // "Heide-Apotheke" near the "Parkstraße" street
}));
NameStartsWith(range, {"Heide Park", "Heide-Park"});
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 100000, ());
TEST_LESS(SortedByDistance(range, center).first, 100000, ());
EqualClassifType(Range(results, 4, 6), GetClassifTypes({
EqualClassifType(Range(results, 4, 7), GetClassifTypes({
{"highway", "service"},
{"railway", "halt"},
{"highway", "bus_stop"},
}));
}
@ -357,6 +349,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Barcelona_Carrers)
CenterInRect(range, {2.1651583, 41.3899995, 2.1863021, 41.4060494});
}
// In case of a city, distance rank should be from it's origin.
{
auto request = MakeRequest("carrer de les planes sabadell");
auto const & results = request->Results();
@ -366,6 +359,22 @@ UNIT_CLASS_TEST(MwmTestsFixture, Barcelona_Carrers)
EqualClassifType(range, GetClassifTypes({{"highway"}}));
CenterInRect(range, {2.1078314, 41.5437515, 2.1106129, 41.5438819});
}
{
// Прилукская Слобода, Минск
ms::LatLon const center(53.8197647, 27.4701662);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("минск малая ул", "ru");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
EqualClassifType(Range(results, 0, 1), GetClassifTypes({{"highway"}}));
CenterInRect(Range(results, 0, 1), {27.5134786, 53.8717921, 27.5210173, 53.875768});
/// @todo Second result is expected to be the nearby street in "Прилукская Слобода", but not always ..
//TEST_GREATER(GetDistanceM(results[0], center), GetDistanceM(results[1], center), ());
}
}
// https://github.com/organicmaps/organicmaps/issues/3307
@ -397,12 +406,12 @@ UNIT_CLASS_TEST(MwmTestsFixture, IceCream)
auto request = MakeRequest("Gelato");
auto const & results = request->Results();
size_t constexpr kResultsCount = 10;
size_t constexpr kResultsCount = kPopularPoiResultsCount;
TEST_GREATER(results.size(), kResultsCount, ());
Range const range(results, 0, kResultsCount);
EqualClassifType(range, GetClassifTypes({{"amenity", "ice_cream"}, {"cuisine", "ice_cream"}}));
TEST_LESS(SortedByDistance(range, center), 2000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 2000.0, ());
auto request2 = MakeRequest("Ice cream");
auto const & results2 = request2->Results();
@ -484,7 +493,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, French_StopWord_Category)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"emergency", "fire_hydrant"}}));
TEST_LESS(SortedByDistance(range, center), 1000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 1000.0, ());
}
UNIT_CLASS_TEST(MwmTestsFixture, Street_BusStop)
@ -497,12 +506,12 @@ UNIT_CLASS_TEST(MwmTestsFixture, Street_BusStop)
{
auto request = MakeRequest("Juncal", "en");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
// Top results are Hotel, Shop and Street.
// Top results are Amenities, Hotels, Shops and Streets.
// Full Match street (20 km) is better than Full Prefix bus stop (1 km).
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"tourism", "hotel"}, {"shop"}, {"highway", "residential"}}));
EqualClassifType(range, GetClassifTypes({{"tourism", "hotel"}, {"shop"}, {"amenity"}, {"highway", "residential"}}));
}
{
@ -513,7 +522,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Street_BusStop)
// Top results are Streets (maybe in different cities).
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"highway"}}));
TEST_LESS(SortedByDistance(range, center), 5000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 5000.0, ());
}
{
@ -524,7 +533,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Street_BusStop)
// Top results are bus stops.
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"highway", "bus_stop"}}));
TEST_LESS(SortedByDistance(range, center), 5000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 5000.0, ());
}
/// @todo Actually, we have very fancy matching here, starting from 3rd result and below.
@ -534,12 +543,15 @@ UNIT_CLASS_TEST(MwmTestsFixture, Street_BusStop)
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
// First result is a "Juncal" supermarket near the train station, 24km :)
// Second result is a train station in other MWM, >200km away.
Range const range(results, 0, 2);
EqualClassifType(range, GetClassifTypes({{"shop", "supermarket"}, {"railway", "station"}}));
TEST_LESS(!SortedByDistance(range, center), 2.0E5, ());
TEST_LESS(SortedByDistance(range, center), 3.0E5, ());
// -1 "Juncal" supermarket near the train station, 24km :)
// -2 Train station building in "Juncal" village, other MWM, >200km
// -3 Train station building near "Juncal" street, 28km
// -4 Railway station, same as (3)
// -5 Railway station, same as (2)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop", "supermarket"},
{"railway", "station"}, {"building", "train_station"}}));
TEST_LESS(SortedByDistance(Range(results, 0, 2), center).first, 23.0E3, ());
}
}
@ -550,7 +562,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Generic_Buildings_Rank)
SetViewportAndLoadMaps(center);
{
size_t constexpr kResultsCount = 10;
size_t constexpr kResultsCount = kPopularPoiResultsCount;
auto request = MakeRequest("cell tower", "en");
auto const & results = request->Results();
TEST_GREATER(results.size(), kResultsCount, ());
@ -558,7 +570,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Generic_Buildings_Rank)
// results[0] is a named POI ~9km (https://www.openstreetmap.org/node/9730886727)
Range const range(results, 1, kResultsCount);
EqualClassifType(range, GetClassifTypes({{"man_made", "tower", "communication"}}));
TEST_LESS(SortedByDistance(range, center), 5000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 3000.0, ());
}
{
@ -568,7 +580,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Generic_Buildings_Rank)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"shop", "supermarket"}}));
TEST_LESS(SortedByDistance(range, center), 1000.0, ());
TEST_LESS(SortedByDistance(range, center).second, 1000.0, ());
}
}
@ -614,7 +626,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Milan_Streets)
TEST_GREATER(results.size(), kResultsCount, ());
Range const range(results, 0, kResultsCount);
TEST_LESS(SortedByDistance(range, center), 20000.0, ());
TEST_LESS(SortedByDistance(range, center).second, 20000.0, ());
}
// https://github.com/organicmaps/organicmaps/issues/5150
@ -631,7 +643,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, London_RedLion)
// Top first results "The Red Lion" in 5 km.
Range const range(results);
TEST_LESS(SortedByDistance(range, center), 5000.0, ());
TEST_LESS(SortedByDistance(range, center).first, 5000.0, ());
}
UNIT_CLASS_TEST(MwmTestsFixture, AddrInterpolation_Rank)
@ -646,10 +658,10 @@ UNIT_CLASS_TEST(MwmTestsFixture, AddrInterpolation_Rank)
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
// Top first address results in 20 km.
// Top first address results in 15 km.
Range const range(results, 0, 2);
EqualClassifType(range, GetClassifTypes({{"addr:interpolation"}}));
TEST_LESS(SortedByDistance(range, center), 20000.0, ());
TEST_LESS(SortedByDistance(range, center).second, 15000.0, ());
// - 3(4) place: Exact address in Montevideo, Uruguay (~200km)
// - 4+ places: addr:interpolation in Argentina
@ -665,14 +677,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, AddrInterpolation_Rank)
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
// Top first address results.
Range const range(results, 0, 6 /* count */);
EqualClassifType(range, GetClassifTypes({{"building", "address"}}));
// Results are not sorted because one match is not exact (address near street).
//TEST_LESS(SortedByDistance(range, center), 300000.0, ());
// Interesting results goes after, streets on distance ~250km in Pergamino.
// Seems like because of matching 2700 postcode.
EqualClassifType(Range(results, 0, 4), GetClassifTypes({{"building", "address"}}));
}
}
@ -685,14 +690,14 @@ UNIT_CLASS_TEST(MwmTestsFixture, Famous_Cities_Rank)
{
auto const & cl = classif();
uint32_t const capitalType = cl.GetTypeByPath({"place", "city", "capital"});
uint32_t const cityType = cl.GetTypeByPath({"place", "city"});
std::string arrCities[] = {
"Buenos Aires",
"Rio de Janeiro",
"New York",
/// @todo After popularity.
//"San Francisco",
//"Las Vegas",
"San Francisco", // not a capital
"Las Vegas", // not a capital
"Los Angeles",
"Toronto",
"Lisboa",
@ -700,7 +705,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Famous_Cities_Rank)
"Barcelona",
"London",
"Paris",
//"Zurich",
"Zurich", // not a capital
"Rome",
"Milan",
"Venezia",
@ -720,11 +725,14 @@ UNIT_CLASS_TEST(MwmTestsFixture, Famous_Cities_Rank)
};
size_t const count = std::size(arrCities);
std::vector<ms::LatLon> arrCenters;
arrCenters.resize(count);
std::set<std::string> const notCapital = { "San Francisco", "Las Vegas", "Zurich" };
std::vector<ms::LatLon> arrCenters(count);
// Buenos Aires like starting point :)
arrCenters[0] = {-34.60649, -58.43540};
size_t errorsNum = 0;
// For DEBUG.
// bool isGoGo = false;
for (size_t i = 0; i < count; ++i)
@ -734,9 +742,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, Famous_Cities_Rank)
// if (i > 0 && !isGoGo)
// continue;
/// @todo Temporary, USA has a lot of similar close cities.
if (arrCities[i] == "New York")
continue;
size_t constexpr kTopNumber = 3;
LOG(LINFO, ("=== Processing:", arrCities[i]));
SetViewportAndLoadMaps(arrCenters[i]);
@ -745,26 +751,41 @@ UNIT_CLASS_TEST(MwmTestsFixture, Famous_Cities_Rank)
{
auto request = MakeRequest(arrCities[j] + " ", "en");
auto const & results = request->Results();
TEST_GREATER(results.size(), 0, (arrCities[i], arrCities[j]));
TEST(!results.empty(), (arrCities[i], arrCities[j]));
uint32_t type = results[0].GetFeatureType();
ftype::TruncValue(type, 3);
if (type != capitalType)
// Check that needed city is in top.
auto const iEnd = results.begin() + std::min(kTopNumber, results.size());
auto const it = std::find_if(results.begin(), iEnd, [&](search::Result const & r)
{
// Buenos Aires should always work.
TEST(i != 0, ());
// Should make this complex check to ensure that we got a needed city.
uint32_t type = r.GetFeatureType();
if (notCapital.count(arrCities[j]) > 0)
{
ftype::TruncValue(type, 2);
return (type == cityType);
}
else
{
ftype::TruncValue(type, 3);
return (type == capitalType);
}
});
TEST_GREATER(results.size(), 1, (arrCities[i], arrCities[j]));
type = results[1].GetFeatureType();
ftype::TruncValue(type, 3);
TEST(type == capitalType, (cl.GetReadableObjectName(type), arrCities[i], arrCities[j]));
if (it == iEnd)
{
LOG(LWARNING, ("Not matched:", arrCities[i], ":", arrCities[j]));
++errorsNum;
}
else
{
// Fill centers table while processing first city.
if (!arrCenters[j].IsValid())
arrCenters[j] = mercator::ToLatLon(it->GetFeatureCenter());
}
if (i == 0 && i != j)
arrCenters[j] = mercator::ToLatLon(results[0].GetFeatureCenter());
}
}
TEST_LESS(errorsNum, 10, ());
}
UNIT_CLASS_TEST(MwmTestsFixture, Conscription_HN)
@ -800,9 +821,8 @@ UNIT_CLASS_TEST(MwmTestsFixture, ToiletAirport)
TEST_EQUAL(results.size(), kResultsCount, ());
Range const range(results, 0, kResultsCount);
EqualClassifType(range, GetClassifTypes({{"amenity", "toilets"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 1000, ());
EqualClassifType(range, GetClassifTypes({{"amenity", "toilets"}, {"toilets", "yes"}}));
TEST_LESS(SortedByDistance(range, center).second, 1000, ());
}
}
@ -823,8 +843,7 @@ UNIT_CLASS_TEST(MwmTestsFixture, WaterTap)
Range const range(results);
EqualClassifType(range, GetClassifTypes({{"amenity", "drinking_water"}, {"man_made", "water_tap"}}));
double const dist = SortedByDistance(range, center);
TEST_LESS(dist, 3500, ());
TEST_LESS(SortedByDistance(range, center).second, 3500, ());
}
}
@ -888,34 +907,36 @@ UNIT_CLASS_TEST(MwmTestsFixture, Full_Address)
ms::LatLon const center(49.0195332, 12.0974856);
SetViewportAndLoadMaps(center);
/// @todo There is a tricky neighborhood here so ranking gets dumb (and we treat 'A' as a stopword).
/// Anyway, needed addresses are in top 3 among:
/// -1 "Gewerbepark A", "A 1"
/// -2 "Gewerbepark B", "1"
/// -3 "Gewerbepark C", "1"
{
auto request = MakeRequest("Wörth an der Donau Gewerbepark A 1 93086 Germany");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
HasAddress(Range(results, 0, 1), "Gewerbepark A", "A 1", {"shop", "car"});
HasAddress(Range(results, 0, 3), "Gewerbepark A", "A 1", {"shop", "car"});
}
{
auto request = MakeRequest("Wörth an der Donau Gewerbepark C 1 93086 Germany");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
/// @todo There is a tricky neighborhood here, so ranking gets dumb :)
/// 1: "Gewerbepark A", "A 1" near "Gewerbepark C" st
/// 2: "Gewerbepark B", "1" near "Gewerbepark C" st
/// 3: "Gewerbepark C", "1"
HasAddress(Range(results, 0, 3), "Gewerbepark C", "1");
}
}
}
UNIT_CLASS_TEST(MwmTestsFixture, BA_RelaxedStreets)
UNIT_CLASS_TEST(MwmTestsFixture, RelaxedStreets)
{
// Buenos Aires (Palermo)
ms::LatLon const center(-34.5802699, -58.4124979);
SetViewportAndLoadMaps(center);
{
// Buenos Aires (Palermo)
ms::LatLon const center(-34.5802699, -58.4124979);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("French 1700");
auto const & results = request->Results();
TEST_GREATER(results.size(), 20, ());
@ -933,8 +954,24 @@ UNIT_CLASS_TEST(MwmTestsFixture, BA_RelaxedStreets)
}
++count;
}
// "Buenos Aires" neighbour regions should present to get >5 addresses.
TEST_GREATER(count, 5, ());
}
{
// Molodechno
ms::LatLon const center(54.3021037, 26.8366091);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("Молодечно первомайская", "ru");
auto const & results = request->Results();
TEST_GREATER(results.size(), 20, ());
Range const range(results, 0, 3);
EqualClassifType(range, GetClassifTypes({{"highway", "residential"}}));
TEST_LESS(SortedByDistance(range, center).first, 3000, ());
}
}
// https://github.com/organicmaps/organicmaps/issues/6127
@ -968,10 +1005,12 @@ UNIT_CLASS_TEST(MwmTestsFixture, Streets_Rank)
TEST(found, ());
};
/// @todo Street should be highwer than 11.
processRequest("Santa Fe ", 11);
/// @todo Prefix search" gives POIs (Starbucks) near "Avenida Santa Fe".
processRequest("Santa Fe st ", 2);
/// @todo Streets should be highwer. Now POIs additional rank is greater than Streets rank.
processRequest("Santa Fe ", 19);
/// @todo Prefix search gives POIs (Starbucks) near "Avenida Santa Fe". Some WTFs for street's ranking here:
/// - gives 'Full Prefix' name's rank
/// - gives m_matchedFraction: 0.777778, while 'st' should be counted for streets definitely
processRequest("Santa Fe st ", 12);
}
{
@ -990,9 +1029,24 @@ UNIT_CLASS_TEST(MwmTestsFixture, Streets_Rank)
// - "Béke utca"
EqualClassifType(Range(results, 0, kResultsCount), GetClassifTypes({{"highway"}, {"amenity", "fast_food"}}));
}
{
// Minsk
ms::LatLon const center(53.91058, 27.54519);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("победител", "ru");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
// First result is a viewpoint (attraction).
EqualClassifType(Range(results, 0, 1), GetClassifTypes({{"tourism", "viewpoint"}}));
// And next are the streets.
Range const range(results, 1, 4);
EqualClassifType(range, GetClassifTypes({{"highway"}}));
TEST_LESS(SortedByDistance(range, center).first, 500, ());
}
}
// https://github.com/organicmaps/organicmaps/issues/5756
UNIT_CLASS_TEST(MwmTestsFixture, Pois_Rank)
{
{
@ -1000,11 +1054,23 @@ UNIT_CLASS_TEST(MwmTestsFixture, Pois_Rank)
ms::LatLon const center(-34.58524, -58.42516);
SetViewportAndLoadMaps(center);
// mix of amenity=pharmacy + shop=chemistry.
auto request = MakeRequest("farmacity");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
TEST_LESS(SortedByDistance(Range(results, 0, kPopularPoiResultsCount), center), 3000, ());
{
// mix of amenity=pharmacy + shop=chemistry.
auto request = MakeRequest("farmacity");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
TEST_LESS(SortedByDistance(Range(results, 0, kPopularPoiResultsCount), center).second, 2000, ());
}
{
auto request = MakeRequest("Malabia");
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
// alt_name="Malabia"
// name="Malabia XXX" - should take this name's rank
EqualClassifType(Range(results, 0, 1), GetClassifTypes({{"railway", "station", "subway"}}));
}
}
{
@ -1015,16 +1081,37 @@ UNIT_CLASS_TEST(MwmTestsFixture, Pois_Rank)
auto request = MakeRequest("depot");
auto const & results = request->Results();
/// @todo Probably, we should decrease Names matching rank/penalty.
// - nearest post office "Mail Depot" (500m)
// - railway station "XXX Depot"
// - a bunch of streets and POIs "Depot XXX" (up to 100km)
// - nearest post office "Mail Depot" (500m)
size_t constexpr kResultsCount = 20;
TEST_GREATER(results.size(), kResultsCount, ());
TEST_EQUAL(CountClassifType(Range(results, 0, kResultsCount),
classif().GetTypeByPath({"amenity", "post_office"})), 1, ());
Range range(results, 0, 2);
EqualClassifType(range, GetClassifTypes({{"amenity", "post_office"}, {"railway", "station"}}));
TEST_LESS(SortedByDistance(range, center).first, 1000, ());
}
// https://github.com/organicmaps/organicmaps/issues/1017
{
// Adazi, Latvia
ms::LatLon const center(57.0819824, 24.3243274);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("rimi");
auto const & results = request->Results();
size_t constexpr kResultsCount = 20;
TEST_GREATER(results.size(), kResultsCount, ());
// result[0] - 'Rimini' city in Italy
// First 2 results - nearest supermarkets.
Range range(results, 1, 3);
EqualClassifType(range, GetClassifTypes({{"shop", "supermarket"}}));
TEST_LESS(SortedByDistance(range, center).second, 1500, ());
}
// https://github.com/organicmaps/organicmaps/issues/5756
{
// Istanbul
ms::LatLon const center(40.95058, 29.17255);
@ -1035,6 +1122,81 @@ UNIT_CLASS_TEST(MwmTestsFixture, Pois_Rank)
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
EqualClassifType(Range(results, 0, 1), GetClassifTypes({{"leisure", "park"}}));
}
// https://github.com/organicmaps/organicmaps/issues/4291
{
// Greenville, SC
ms::LatLon const center(34.8513533, -82.3984875);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("Greenville Hospital");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
// - First is an airport.
// - Bus stop in the middle.
uint32_t const hospitalType = classif().GetTypeByPath({"amenity", "hospital"});
size_t count = 0;
for (size_t i = 0; i < kTopPoiResultsCount; ++i)
{
if (results[i].GetFeatureType() == hospitalType)
{
++count;
TEST(results[i].GetString().find("Greenville") != std::string::npos, ());
}
}
TEST_GREATER(count, 2, ());
}
}
// "San Francisco" is always an interesting query, because in Latin America there are:
// - hundreds of cities with similar names,
// - thousands of streets/POIs
UNIT_CLASS_TEST(MwmTestsFixture, San_Francisco)
{
auto const & cl = classif();
{
// New York
ms::LatLon const center(40.71253, -74.00628);
SetViewportAndLoadMaps(center);
auto request = MakeRequest("San Francisco");
auto const & results = request->Results();
TEST_GREATER(results.size(), kTopPoiResultsCount, ());
TEST_EQUAL(results[0].GetFeatureType(), cl.GetTypeByPath({"shop", "laundry"}), ());
TEST_LESS(GetDistanceM(results[0], center), 1.0E4, ());
TEST_EQUAL(results[1].GetFeatureType(), cl.GetTypeByPath({"place", "city"}), ());
TEST_LESS(GetDistanceM(results[1], center), 4.2E6, ());
}
}
UNIT_CLASS_TEST(MwmTestsFixture, Opfikon_Viewport)
{
auto const & cl = classif();
// Opfikon, Zurich
ms::LatLon const center(47.42404, 8.5667463);
SetViewportAndLoadMaps(center);
auto params = GetDefaultSearchParams("ofikon"); // spelling error
params.m_viewport = { 8.5418196173686862238, 53.987953174041166449, 8.5900051973703153152, 54.022951994627547379 };
params.m_mode = search::Mode::Viewport;
auto request = MakeRequest(params);
auto const & results = request->Results();
TEST_GREATER(results.size(), kPopularPoiResultsCount, ());
Range allRange(results, true /* all */);
TEST_EQUAL(CountClassifType(allRange, cl.GetTypeByPath({"place", "town"})), 1, ());
TEST_EQUAL(CountClassifType(allRange, cl.GetTypeByPath({"shop"})), 1, ());
TEST_EQUAL(CountClassifType(allRange, cl.GetTypeByPath({"barrier"})), 0, ());
TEST_EQUAL(CountClassifType(allRange, cl.GetTypeByPath({"tourism"})), 2, ());
TEST_GREATER(CountClassifType(allRange, cl.GetTypeByPath({"highway"})), 7, ());
TEST_GREATER(CountClassifType(allRange, cl.GetTypeByPath({"leisure"})), 2, ());
TEST_GREATER(CountClassifType(allRange, cl.GetTypeByPath({"amenity"})), 10, ());
}
} // namespace real_mwm_tests

View file

@ -215,6 +215,7 @@ UNIT_TEST(RankingInfo_PreferCountry)
country.m_distanceToPivot = 1e6;
country.m_tokenRanges[Model::TYPE_COUNTRY] = TokenRange(0, 1);
country.m_type = Model::TYPE_COUNTRY;
country.m_rank = 100; // This is rather small rank for a country.
// Country should be preferred even if cafe is much closer to viewport center.
TEST_LESS(cafe.GetLinearModelRank(), country.GetLinearModelRank(), (cafe, country));

View file

@ -191,6 +191,7 @@ class StageMwm(Stage):
WORLD_NAME: [
StageIndex,
StageCitiesIdsWorld,
StagePopularityWorld,
StagePrepareRoutingWorld,
StageRoutingWorld,
StageMwmStatistics,
@ -201,10 +202,11 @@ class StageMwm(Stage):
mwm_stages = [
StageIndex,
StageUgc,
StagePopularity,
StageSrtm,
StageIsolinesInfo,
StageDescriptions,
# call after descriptions
StagePopularity,
StageRouting,
StageRoutingTransit,
StageMwmDiffs,
@ -270,11 +272,15 @@ class StageUgc(Stage):
@country_stage
@production_only
class StagePopularity(Stage):
def apply(self, env: Env, country, **kwargs):
steps.step_popularity(env, country, **kwargs)
@country_stage
class StagePopularityWorld(Stage):
def apply(self, env: Env, country, **kwargs):
steps.step_popularity_world(env, country, **kwargs)
@country_stage
class StageSrtm(Stage):

View file

@ -306,10 +306,22 @@ def step_popularity(env: Env, country: AnyStr, **kwargs):
out=env.get_subprocess_out(country),
err=env.get_subprocess_out(country),
data_path=env.paths.mwm_path,
intermediate_data_path=env.paths.intermediate_data_path,
cache_path=env.paths.cache_path,
user_resource_path=env.paths.user_resource_path,
popular_places_data=env.paths.popularity_path,
generate_popular_places=True,
output=country,
**kwargs,
)
def step_popularity_world(env: Env, country: AnyStr, **kwargs):
run_gen_tool_with_recovery_country(
env,
env.gen_tool,
out=env.get_subprocess_out(country),
err=env.get_subprocess_out(country),
data_path=env.paths.mwm_path,
user_resource_path=env.paths.user_resource_path,
wikipedia_pages=env.paths.descriptions_path,
idToWikidata=env.paths.id_to_wikidata_path,
generate_popular_places=True,
output=country,
**kwargs,