diff --git a/generator/final_processor_intermediate_mwm.cpp b/generator/final_processor_intermediate_mwm.cpp index d69f4adc4a..bd96de12a4 100644 --- a/generator/final_processor_intermediate_mwm.cpp +++ b/generator/final_processor_intermediate_mwm.cpp @@ -633,11 +633,14 @@ void ComplexFinalProcessor::Process() // these cases separately. First of all let's remove objects with tag building:part is contained // in relations. We will add them back after data processing. std::unordered_map relationBuildingParts; + + auto fbs = ReadAllDatRawFormat( + base::JoinPath(m_mwmTmpPath, filename)); if (m_buildingToParts) - relationBuildingParts = RemoveRelationBuildingParts(filename); + relationBuildingParts = RemoveRelationBuildingParts(fbs); // This case is second. We build a hierarchy using the geometry of objects and their nesting. - hierarchy::HierarchyBuilder builder(base::JoinPath(m_mwmTmpPath, filename)); + hierarchy::HierarchyBuilder builder(std::move(fbs)); builder.SetGetMainTypeFunction(m_getMainType); builder.SetFilter(m_filter); auto trees = builder.Build(); @@ -697,22 +700,26 @@ void ComplexFinalProcessor::Process() } std::unordered_map -ComplexFinalProcessor::RemoveRelationBuildingParts(std::string const & mwmTmpFilename) +ComplexFinalProcessor::RemoveRelationBuildingParts(std::vector & fbs) { CHECK(m_buildingToParts, ()); std::unordered_map relationBuildingParts; - auto const fullFilename = base::JoinPath(m_mwmTmpPath, mwmTmpFilename); - auto const fbs = ReadAllDatRawFormat(fullFilename); - FeatureBuilderWriter writer(fullFilename); - for (auto const & fb : fbs) + auto last = std::end(fbs); + for (auto it = std::begin(fbs); it != last;) { - if (!m_buildingToParts->HasBuildingPart(fb.GetMostGenericOsmId())) - writer.Write(fb); + if (m_buildingToParts->HasBuildingPart(it->GetMostGenericOsmId())) + { + relationBuildingParts.emplace(it->GetMostGenericOsmId(), *it); + std::iter_swap(it, --last); + } else - relationBuildingParts.emplace(fb.GetMostGenericOsmId(), fb); + { + ++it; + } } + fbs.erase(last, std::end(fbs)); return relationBuildingParts; } diff --git a/generator/final_processor_intermediate_mwm.hpp b/generator/final_processor_intermediate_mwm.hpp index bb538b4f52..30d6538ab3 100644 --- a/generator/final_processor_intermediate_mwm.hpp +++ b/generator/final_processor_intermediate_mwm.hpp @@ -162,7 +162,7 @@ private: std::string const & countryName) const; void WriteLines(std::vector const & lines); std::unordered_map RemoveRelationBuildingParts( - std::string const & mwmTmpFilename); + std::vector & fbs); hierarchy::GetMainTypeFn m_getMainType; hierarchy::PrintFn m_printFunction; diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index 3bf1b29dc4..5f651539c4 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -26,6 +26,7 @@ set( filter_elements_tests.cpp gen_mwm_info_tests.cpp hierarchy_entry_tests.cpp + hierarchy_tests.cpp intermediate_data_test.cpp maxspeeds_tests.cpp merge_collectors_tests.cpp diff --git a/generator/generator_tests/hierarchy_tests.cpp b/generator/generator_tests/hierarchy_tests.cpp new file mode 100644 index 0000000000..bb49076f2c --- /dev/null +++ b/generator/generator_tests/hierarchy_tests.cpp @@ -0,0 +1,142 @@ +#include "testing/testing.hpp" + +#include "generator/feature_builder.hpp" +#include "generator/filter_complex.hpp" +#include "generator/generator_tests_support/test_with_classificator.hpp" +#include "generator/hierarchy.hpp" + +#include "indexer/classificator.hpp" + +#include "base/stl_helpers.hpp" + +#include +#include + +namespace +{ +using namespace generator::tests_support; + +std::vector MakeTestSet1() +{ + std::vector fbs; + { + feature::FeatureBuilder outline; + std::vector polygon = {{0.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}, {10.0, 0.0}}; + outline.AddPolygon(polygon); + outline.AddOsmId(base::MakeOsmWay(1)); + outline.AddType(classif().GetTypeByPath({"building"})); + outline.AddType(classif().GetTypeByPath({"tourism", "attraction"})); + outline.SetArea(); + fbs.emplace_back(outline); + } + { + feature::FeatureBuilder buildingPart; + std::vector polygon = {{0.0, 0.0}, {0.0, 8.0}, {8.0, 8.0}, {8.0, 0.0}}; + buildingPart.AddPolygon(polygon); + buildingPart.AddOsmId(base::MakeOsmWay(2)); + buildingPart.AddType(classif().GetTypeByPath({"building:part"})); + buildingPart.SetArea(); + fbs.emplace_back(buildingPart); + } + { + feature::FeatureBuilder buildingPart; + std::vector polygon = {{0.0, 0.0}, {0.0, 7.0}, {7.0, 7.0}, {7.0, 0.0}}; + buildingPart.AddPolygon(polygon); + buildingPart.AddOsmId(base::MakeOsmWay(3)); + buildingPart.AddType(classif().GetTypeByPath({"building:part"})); + buildingPart.SetArea(); + fbs.emplace_back(buildingPart); + } + { + feature::FeatureBuilder buildingPart; + std::vector polygon = {{0.0, 0.0}, {0.0, 6.0}, {6.0, 6.0}, {6.0, 0.0}}; + buildingPart.AddPolygon(polygon); + buildingPart.AddOsmId(base::MakeOsmWay(4)); + buildingPart.AddType(classif().GetTypeByPath({"building:part"})); + buildingPart.SetArea(); + fbs.emplace_back(buildingPart); + } + + return fbs; +} + +void TestDepthNodeById(generator::hierarchy::HierarchyBuilder::Node::Ptr const & tree, uint64_t id, + size_t depth) +{ + auto osmId = base::MakeOsmWay(id); + TEST_EQUAL(tree_node::GetDepth(tree_node::FindIf( + tree, [&](auto const & data) { return data.GetCompositeId().m_mainId == osmId; })), + depth, ()); +} + +UNIT_CLASS_TEST(TestWithClassificator, ComplexHierarchy_FlattenBuildingParts) +{ + generator::hierarchy::HierarchyBuilder builder(MakeTestSet1()); + builder.SetGetMainTypeFunction(generator::hierarchy::GetMainType); + builder.SetFilter(std::make_shared()); + auto trees = builder.Build(); + + TEST_EQUAL(trees.size(), 1, ()); + TEST_EQUAL(tree_node::Size(trees[0]), 4, ()); + + TestDepthNodeById(trees[0], 2 /* id */, 2 /* depth */); + TestDepthNodeById(trees[0], 3 /* id */, 3 /* depth */); + TestDepthNodeById(trees[0], 4 /* id */, 4 /* depth */); + + generator::hierarchy::FlattenBuildingParts(trees); + + TEST_EQUAL(trees.size(), 1, ()); + TEST_EQUAL(tree_node::Size(trees[0]), 4, ()); + + TestDepthNodeById(trees[0], 2 /* id */, 2 /* depth */); + TestDepthNodeById(trees[0], 3 /* id */, 2 /* depth */); + TestDepthNodeById(trees[0], 4 /* id */, 2 /* depth */); +} + +UNIT_CLASS_TEST(TestWithClassificator, ComplexHierarchy_AddChildrenTo) +{ + generator::hierarchy::HierarchyBuilder builder(MakeTestSet1()); + builder.SetGetMainTypeFunction(generator::hierarchy::GetMainType); + builder.SetFilter(std::make_shared()); + auto trees = builder.Build(); + + TEST_EQUAL(trees.size(), 1, ()); + TEST_EQUAL(tree_node::Size(trees[0]), 4, ()); + + auto osmId = base::MakeOsmWay(3); + generator::hierarchy::AddChildrenTo(trees, [&](auto const & compositeId) { + std::vector fbs; + if (compositeId.m_mainId == osmId) + { + feature::FeatureBuilder buildingPart; + std::vector polygon = {{6.0, 6.0}, {6.0, 7.0}, {7.0, 7.0}, {7.0, 6.0}}; + buildingPart.AddPolygon(polygon); + buildingPart.AddOsmId(base::MakeOsmWay(5)); + buildingPart.AddType(classif().GetTypeByPath({"building:part"})); + buildingPart.SetArea(); + fbs.emplace_back(generator::hierarchy::HierarchyPlace(buildingPart)); + } + return fbs; + }); + + auto node = tree_node::FindIf( + trees[0], [&](auto const & data) { return data.GetCompositeId().m_mainId == osmId; }); + + TEST(node, ()); + + auto const children = node->GetChildren(); + TEST_EQUAL(children.size(), 2, ()); + + auto it = base::FindIf(children, [](auto const & node) { + return node->GetData().GetCompositeId().m_mainId == base::MakeOsmWay(4); + }); + CHECK(it != std::end(children), ()); + CHECK((*it)->HasParent() && (*it)->GetParent()->GetData().GetCompositeId().m_mainId == osmId, ()); + + it = base::FindIf(children, [](auto const & node) { + return node->GetData().GetCompositeId().m_mainId == base::MakeOsmWay(5); + }); + CHECK(it != std::end(children), ()); + CHECK((*it)->HasParent() && (*it)->GetParent()->GetData().GetCompositeId().m_mainId == osmId, ()); +} +} // namespace diff --git a/generator/hierarchy.cpp b/generator/hierarchy.cpp index 1fad76f1c5..43e59c7023 100644 --- a/generator/hierarchy.cpp +++ b/generator/hierarchy.cpp @@ -160,8 +160,13 @@ HierarchyLinker::Node::Ptrs HierarchyLinker::Link() return m_nodes; } -HierarchyBuilder::HierarchyBuilder(std::string const & dataFilename) - : m_dataFullFilename(dataFilename) +HierarchyBuilder::HierarchyBuilder(std::vector && fbs) + : m_fbs(std::move(fbs)) +{ +} + +HierarchyBuilder::HierarchyBuilder(std::vector const & fbs) + : m_fbs(fbs) { } @@ -175,26 +180,14 @@ void HierarchyBuilder::SetFilter(std::shared_ptr const & filter m_filter = filter; } -std::vector HierarchyBuilder::ReadFeatures( - std::string const & dataFilename) -{ - std::vector fbs; - ForEachFromDatRawFormat( - dataFilename, [&](FeatureBuilder const & fb, uint64_t /* currPos */) { - if (m_filter->IsAccepted(fb)) - fbs.emplace_back(fb); - }); - return fbs; -} - HierarchyBuilder::Node::Ptrs HierarchyBuilder::Build() { CHECK(m_getMainType, ()); - auto const fbs = ReadFeatures(m_dataFullFilename); + base::EraseIf(m_fbs, [&](auto const & fb) { return !m_filter->IsAccepted(fb); }); Node::Ptrs places; - places.reserve(fbs.size()); - std::transform(std::cbegin(fbs), std::cend(fbs), std::back_inserter(places), + places.reserve(m_fbs.size()); + std::transform(std::cbegin(m_fbs), std::cend(m_fbs), std::back_inserter(places), [](auto const & fb) { return std::make_shared(HierarchyPlace(fb)); }); auto nodes = HierarchyLinker(std::move(places)).Link(); // We leave only the trees. diff --git a/generator/hierarchy.hpp b/generator/hierarchy.hpp index d4c7661feb..f7005f27ea 100644 --- a/generator/hierarchy.hpp +++ b/generator/hierarchy.hpp @@ -102,7 +102,8 @@ class HierarchyBuilder public: using Node = HierarchyLinker::Node; - explicit HierarchyBuilder(std::string const & dataFilename); + explicit HierarchyBuilder(std::vector && fbs); + explicit HierarchyBuilder(std::vector const & fbs); void SetGetMainTypeFunction(GetMainTypeFn const & getMainType); void SetFilter(std::shared_ptr const & filter); @@ -110,11 +111,9 @@ public: Node::Ptrs Build(); protected: - std::vector ReadFeatures(std::string const & dataFilename); - - std::string m_dataFullFilename; GetMainTypeFn m_getMainType; std::shared_ptr m_filter; + std::vector m_fbs; }; class HierarchyEntryEnricher