forked from organicmaps/organicmaps
Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
|
36d68cfaea | ||
|
7cc24e9290 | ||
|
0a84a0c6e4 | ||
|
e6c31d964c | ||
|
24030c0a1e | ||
|
0e696f3d08 | ||
|
167d2e3e62 | ||
|
fb76df3727 | ||
|
5055e8adaf | ||
|
4bb9a58ffa | ||
|
909eb4ebb1 | ||
|
4f188b8978 | ||
|
0ef1887ffe | ||
|
d03c0e5fd0 | ||
|
dc7b1d999e | ||
|
2283befb0d | ||
|
b8f418bd3c | ||
|
bc46f337d7 |
37 changed files with 3069 additions and 2673 deletions
|
@ -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>
|
||||
|
|
BIN
data/World.mwm
BIN
data/World.mwm
Binary file not shown.
Binary file not shown.
|
@ -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Метро|подземие
|
||||
|
|
4622
data/countries.txt
4622
data/countries.txt
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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, ());
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {}; }
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue