diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 1ad68ee8df..8634a0f859 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -49,6 +49,8 @@ set( collector_interface.hpp collector_tag.cpp collector_tag.hpp + complex_loader.cpp + complex_loader.hpp composite_id.cpp composite_id.hpp cross_mwm_osm_ways_collector.cpp diff --git a/generator/complex_loader.cpp b/generator/complex_loader.cpp new file mode 100644 index 0000000000..72f748365c --- /dev/null +++ b/generator/complex_loader.cpp @@ -0,0 +1,110 @@ +#include "generator/complex_loader.hpp" + +#include "indexer/classificator.hpp" + +#include "coding/csv_reader.hpp" + +#include "base/logging.hpp" +#include "base/stl_helpers.hpp" + +#include +#include +#include +#include +#include + +namespace +{ +size_t const kNumRequiredTypes = 3; + +std::unordered_set GetRequiredComplexTypes() +{ + std::vector> const kRequiredComplexPaths = { + // Sights + {"amenity", "place_of_worship"}, + {"historic", "archaeological_site"}, + {"historic", "boundary_stone"}, + {"historic", "castle"}, + {"historic", "fort"}, + {"historic", "memorial"}, + {"historic", "monument"}, + {"historic", "ruins"}, + {"historic", "ship"}, + {"historic", "tomb"}, + {"historic", "wayside_cross"}, + {"historic", "wayside_shrine"}, + {"tourism", "artwork"}, + {"tourism", "attraction"}, + {"tourism", "theme_park"}, + {"tourism", "viewpoint"}, + {"waterway", "waterfall"}, + + // Museum + {"amenity", "arts_centre"}, + {"tourism", "gallery"}, + {"tourism", "museum"}, + }; + + std::unordered_set requiredComplexTypes; + requiredComplexTypes.reserve(kRequiredComplexPaths.size()); + for (auto const & path : kRequiredComplexPaths) + requiredComplexTypes.emplace(classif().GetTypeByPath(path)); + return requiredComplexTypes; +} +} // namespace + +namespace generator +{ +bool IsComplex(tree_node::types::Ptr const & tree) +{ + static auto const kTypes = GetRequiredComplexTypes(); + return tree_node::CountIf(tree, [&](auto const & e) { return kTypes.count(e.m_type) != 0; }) >= + kNumRequiredTypes; +} + +std::string GetCountry(tree_node::types::Ptr const & tree) +{ + return tree->GetData().m_countryName; +} + +ComplexLoader::ComplexLoader(std::string const & filename) +{ + auto trees = hierarchy::LoadHierachy(filename); + base::EraseIf(trees, [](auto const & e){ return !IsComplex(e); }); + for (auto const & tree : trees) + m_forests[GetCountry(tree)].Append(tree); +} + +tree_node::Forest const & ComplexLoader::GetForest( + std::string const & country) const +{ + static tree_node::Forest const kEmpty; + auto const it = m_forests.find(country); + return it == std::cend(m_forests) ? kEmpty : it->second; +} + +std::unordered_set ComplexLoader::GetIdsSet() const +{ + std::unordered_set set; + ForEach([&](auto const &, auto const & forest) { + forest.ForEachTree([&](auto const & tree) { + tree_node::ForEach(tree, [&](auto const & entry) { set.emplace(entry.m_id); }); + }); + }); + return set; +} + +ComplexLoader const & GetOrCreateComplexLoader(std::string const & filename) +{ + static std::mutex m; + static std::unordered_map complexLoaders; + + std::lock_guard lock(m); + auto const it = complexLoaders.find(filename); + if (it != std::cend(complexLoaders)) + return it->second; + + auto const eIt = complexLoaders.emplace(filename, ComplexLoader(filename)); + return eIt.first->second; +} +} // namespace generator diff --git a/generator/complex_loader.hpp b/generator/complex_loader.hpp new file mode 100644 index 0000000000..e5f134dc8f --- /dev/null +++ b/generator/complex_loader.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "generator/hierarchy_entry.hpp" + +#include "indexer/complex/tree_node.hpp" + +#include +#include +#include +#include +#include + +namespace generator +{ +namespace complex +{ +// Feature ids. +using Ids = std::vector; +} // namespace complex + +// ComplexLoader loads complexes from hierarchy source file and provides easy access. +class ComplexLoader +{ +public: + explicit ComplexLoader(std::string const & filename); + + tree_node::Forest const & GetForest(std::string const & country) const; + + // fn accepts country name and tree. + template + void ForEach(Fn && fn) const + { + for (auto const & pair : m_forests) + fn(pair.first, pair.second); + } + + std::unordered_set GetIdsSet() const; + +private: + std::unordered_map> m_forests; +}; + +// Returns true if hierarchy tree is complex; otherwise returns false. +// Complex is defined at https://confluence.mail.ru/display/MAPSME/Complexes. +bool IsComplex(tree_node::types::Ptr const & tree); + +// Returns country name of tree. +std::string GetCountry(tree_node::types::Ptr const & tree); + +// Returns initilized ComplexLoader by filename. +// It loads only at the time of the first call. +ComplexLoader const & GetOrCreateComplexLoader(std::string const & filename); + +template +tree_node::Forest TraformToIdsForest( + tree_node::Forest const & forest, Fn && fn) +{ + tree_node::Forest res; + forest.ForEachTree([&](auto const & tree) { + res.Append(tree_node::TransformToTree(tree, std::forward(fn))); + }); + return res; +} +} // namespace generator diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index 9c198cedf2..62229f6dae 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -15,6 +15,7 @@ set( collector_city_area_tests.cpp common.cpp common.hpp + complex_loader_tests.cpp cross_mwm_osm_ways_collector_tests.cpp descriptions_section_builder_tests.cpp feature_builder_test.cpp diff --git a/generator/generator_tests/complex_loader_tests.cpp b/generator/generator_tests/complex_loader_tests.cpp new file mode 100644 index 0000000000..589ffda46d --- /dev/null +++ b/generator/generator_tests/complex_loader_tests.cpp @@ -0,0 +1,165 @@ +#include "testing/testing.hpp" + +#include "generator/complex_loader.hpp" +#include "generator/generator_tests_support/test_with_classificator.hpp" +#include "generator/hierarchy_entry.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/classificator_loader.hpp" +#include "indexer/complex/tree_node.hpp" + +#include "coding/csv_reader.hpp" + +#include "base/geo_object_id.hpp" + +#include "platform/platform_tests_support/scoped_file.hpp" + +using generator::tests_support::TestWithClassificator; +using platform::tests_support::ScopedFile; + +namespace +{ +std::string const kCsv1 = + "13835058055284963881 9223372037111861697;" + ";" + "1;" + "37.5303271;" + "67.3684086;" + "amenity-university;" + "Lomonosov Moscow State Univesity;" + "Russia_Moscow\n" + + "9223372036879747192 9223372036879747192;" + "13835058055284963881 9223372037111861697;" + "2;" + "37.5272372;" + "67.3775872;" + "leisure-garden;" + "Ботанический сад МГУ;" + "Russia_Moscow\n" + + "9223372036938640141 9223372036938640141;" + "9223372036879747192 9223372036879747192;" + "3;" + "37.5274156;" + "67.3758813;" + "amenity-university;" + "Отдел флоры;" + "Russia_Moscow\n" + + "9223372036964008573 9223372036964008573;" + "9223372036879747192 9223372036879747192;" + "3;" + "37.5279467;" + "67.3756452;" + "amenity-university;" + "Дендрарий Ботанического сада МГУ;" + "Russia_Moscow\n" + + "4611686019330739245 4611686019330739245;" + "13835058055284963881 9223372037111861697;" + "2;" + "37.5357492;" + "67.3735142;" + "historic-memorial;" + "Александр Иванович Герцен;" + "Russia_Moscow\n" + + "4611686019330739269 4611686019330739269;" + "13835058055284963881 9223372037111861697;" + "2;" + "37.5351269;" + "67.3741606;" + "historic-memorial;" + "Николай Гаврилович Чернышевский;" + "Russia_Moscow\n" + + "4611686019330739276 4611686019330739276;" + "13835058055284963881 9223372037111861697;" + "2;" + "37.5345234;" + "67.3723206;" + "historic-memorial;" + "Николай Егорович Жуковский;" + "Russia_Moscow\n" + + "13835058055283526046 9223372037165115538;" + ";" + "1;" + "37.6112346;" + "67.4426053;" + "place-square;" + "Манежная площадь;" + "Russia_Moscow\n" + + "4611686019087591587 4611686019087591587;" + "13835058055283526046 9223372037165115538;" + "2;" + "37.6112346;" + "67.4426053;" + "historic-memorial;" + "Georgy Zhukov;" + "Russia_Moscow\n" + + "4611686023709502091 4611686023709502091;" + "13835058055283526046 9223372037165115538;" + "2;" + "37.6112346;" + "67.4426053;" + "place-square;" + "Манежная площадь;" + "Russia_Moscow\n" + + "4611686024983153989 4611686024983153989;" + "13835058055283526046 9223372037165115538;" + "2;" + "37.6112346;" + "67.4426053;" + "amenity-cafe;" + "ShakeUp;" + "Russia_Moscow\n"; + +UNIT_CLASS_TEST(TestWithClassificator, Complex_IsComplex) +{ + auto const filename = "test.csv"; + ScopedFile sf(filename, kCsv1); + auto const forest = generator::hierarchy::LoadHierachy(sf.GetFullPath()); + TEST_EQUAL(forest.size(), 2, ()); + auto const & complex = *forest.begin(); + auto const & notComplex = *forest.end(); + TEST(generator::IsComplex(complex), ()); + TEST(!generator::IsComplex(notComplex), ()); +} + +UNIT_CLASS_TEST(TestWithClassificator, Complex_GetCountry) +{ + auto const filename = "test.csv"; + ScopedFile sf(filename, kCsv1); + auto const forest = generator::hierarchy::LoadHierachy(sf.GetFullPath()); + TEST_EQUAL(forest.size(), 2, ()); + auto const & complex = *forest.begin(); + auto const & notComplex = *forest.end(); + TEST_EQUAL(generator::GetCountry(complex), "Russia_Moscow", ()); + TEST_EQUAL(generator::GetCountry(notComplex), "Russia_Moscow", ()); +} + +UNIT_CLASS_TEST(TestWithClassificator, Complex_ComplexLoader) +{ + auto const filename = "test.csv"; + ScopedFile sf(filename, kCsv1); + generator::ComplexLoader const loader(sf.GetFullPath()); + auto const forest = loader.GetForest("Russia_Moscow"); + TEST_EQUAL(forest.Size(), 1, ()); + forest.ForEachTree([](auto const & tree) { TEST_EQUAL(tree_node::Size(tree), 7, ()); }); +} + +UNIT_CLASS_TEST(TestWithClassificator, Complex_GetOrCreateComplexLoader) +{ + auto const filename = "test.csv"; + ScopedFile sf(filename, kCsv1); + auto const & loader = generator::GetOrCreateComplexLoader(sf.GetFullPath()); + auto const forest = loader.GetForest("Russia_Moscow"); + TEST_EQUAL(forest.Size(), 1, ()); + forest.ForEachTree([](auto const & tree) { TEST_EQUAL(tree_node::Size(tree), 7, ()); }); +} +} // namespace diff --git a/indexer/complex/tree_node.hpp b/indexer/complex/tree_node.hpp index 9fb1b624d4..d04157a220 100644 --- a/indexer/complex/tree_node.hpp +++ b/indexer/complex/tree_node.hpp @@ -105,6 +105,14 @@ void PreOrderVisit(types::Ptr const & node, Fn && fn) preOrderVisitDetail(node); } +template +void ForEach(types::Ptr const & node, Fn && fn) +{ + PreOrderVisit(node, [&](auto const & node) { + fn(node->GetData()); + }); +} + template decltype(auto) FindIf(types::Ptr const & node, Fn && fn) {