diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index da4265e2cf..402ebeface 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -148,6 +148,8 @@ set(SRC region_meta.hpp regions/collector_region_info.cpp regions/collector_region_info.hpp + regions/country_specifier.cpp + regions/country_specifier.hpp regions/level_region.hpp regions/node.cpp regions/node.hpp diff --git a/generator/regions/country_specifier.cpp b/generator/regions/country_specifier.cpp new file mode 100644 index 0000000000..9dff18c843 --- /dev/null +++ b/generator/regions/country_specifier.cpp @@ -0,0 +1,89 @@ +#include "generator/regions/country_specifier.hpp" + +namespace generator +{ +namespace regions +{ +PlaceLevel CountrySpecifier::GetLevel(Region const & region) const +{ + auto const placeLevel = GetLevel(region.GetPlaceType()); + if (placeLevel != PlaceLevel::Unknown) + return placeLevel; + + if (region.GetAdminLevel() == AdminLevel::Two) + return PlaceLevel::Country; + + return PlaceLevel::Unknown; +} + +PlaceLevel CountrySpecifier::GetLevel(PlaceType placeType) const +{ + switch (placeType) + { + case PlaceType::Country: + return PlaceLevel::Country; + case PlaceType::State: + case PlaceType::Province: + return PlaceLevel::Region; + case PlaceType::District: + case PlaceType::County: + case PlaceType::Municipality: + return PlaceLevel::Subregion; + case PlaceType::City: + case PlaceType::Town: + case PlaceType::Village: + case PlaceType::Hamlet: + return PlaceLevel::Locality; + case PlaceType::Suburb: + return PlaceLevel::Suburb; + case PlaceType::Quarter: + case PlaceType::Neighbourhood: + return PlaceLevel::Sublocality; + case PlaceType::IsolatedDwelling: + return PlaceLevel::Sublocality; + case PlaceType::Unknown: + break; + } + + return PlaceLevel::Unknown; +} + +int CountrySpecifier::RelateByWeight(LevelRegion const & l, LevelRegion const & r) const +{ + if (l.GetLevel() != PlaceLevel::Unknown && r.GetLevel() != PlaceLevel::Unknown) + { + if (l.GetLevel() > r.GetLevel()) + return -1; + if (l.GetLevel() < r.GetLevel()) + return 1; + } + + auto const lPlaceType = l.GetPlaceType(); + auto const rPlaceType = r.GetPlaceType(); + if (lPlaceType != PlaceType::Unknown && rPlaceType != PlaceType::Unknown) + { + if (lPlaceType > rPlaceType) + return -1; + if (lPlaceType < rPlaceType) + return 1; + // Check by admin level (administrative city (district + city) > city). + } + + auto const lAdminLevel = l.GetAdminLevel(); + auto const rAdminLevel = r.GetAdminLevel(); + if (lAdminLevel != AdminLevel::Unknown && rAdminLevel != AdminLevel::Unknown) + { + if (lAdminLevel > rAdminLevel) + return -1; + if (lAdminLevel < rAdminLevel) + return 1; + } + if (lAdminLevel != AdminLevel::Unknown) + return 1; + if (rAdminLevel != AdminLevel::Unknown) + return -1; + + return 0; +} +} // namespace regions +} // namespace generator diff --git a/generator/regions/country_specifier.hpp b/generator/regions/country_specifier.hpp new file mode 100644 index 0000000000..d9b719b270 --- /dev/null +++ b/generator/regions/country_specifier.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "generator/regions/collector_region_info.hpp" +#include "generator/regions/level_region.hpp" +#include "generator/regions/region.hpp" + +namespace generator +{ +namespace regions +{ +class CountrySpecifier +{ +public: + virtual ~CountrySpecifier() = default; + + virtual PlaceLevel GetLevel(Region const & region) const; + // Return -1 - |l| is under place of |r|, 0 - undefined relation, 1 - |r| is under place of |l|. + // Non-transitive. + virtual int RelateByWeight(LevelRegion const & l, LevelRegion const & r) const; + +protected: + PlaceLevel GetLevel(PlaceType placeType) const; +}; +} // namespace regions +} // namespace generator diff --git a/generator/regions/regions.cpp b/generator/regions/regions.cpp index 9513240d39..2100678a39 100644 --- a/generator/regions/regions.cpp +++ b/generator/regions/regions.cpp @@ -142,8 +142,7 @@ private: auto region = Region(fb, collector.Get(id)); auto const & name = region.GetName(); - auto const level = RegionsBuilder::GetLevel(region); - if (name.empty() || level == PlaceLevel::Unknown) + if (name.empty()) return; regions.emplace_back(std::move(region)); diff --git a/generator/regions/regions_builder.cpp b/generator/regions/regions_builder.cpp index 6278c6a567..154c13480a 100644 --- a/generator/regions/regions_builder.cpp +++ b/generator/regions/regions_builder.cpp @@ -69,13 +69,16 @@ RegionsBuilder::StringsList RegionsBuilder::GetCountryNames() const } Node::PtrList RegionsBuilder::MakeSelectedRegionsByCountry(Region const & outer, - Regions const & allRegions) + Regions const & allRegions, CountrySpecifier const & countrySpecifier) { std::vector regionsInCountry{{PlaceLevel::Country, outer}}; for (auto const & region : allRegions) { if (outer.ContainsRect(region)) - regionsInCountry.emplace_back(GetLevel(region), region); + { + auto const level = countrySpecifier.GetLevel(region); + regionsInCountry.emplace_back(level, region); + } } auto const comp = [](LevelRegion const & l, LevelRegion const & r) { @@ -94,9 +97,9 @@ Node::PtrList RegionsBuilder::MakeSelectedRegionsByCountry(Region const & outer, } Node::Ptr RegionsBuilder::BuildCountryRegionTree(Region const & outer, - Regions const & allRegions) + Regions const & allRegions, CountrySpecifier const & countrySpecifier) { - auto nodes = MakeSelectedRegionsByCountry(outer, allRegions); + auto nodes = MakeSelectedRegionsByCountry(outer, allRegions, countrySpecifier); while (nodes.size() > 1) { auto itFirstNode = std::rbegin(nodes); @@ -109,7 +112,7 @@ Node::Ptr RegionsBuilder::BuildCountryRegionTree(Region const & outer, if (!currRegion.ContainsRect(firstRegion) && !currRegion.Contains(firstRegion.GetCenter())) continue; - auto const c = Compare(currRegion, firstRegion); + auto const c = Compare(currRegion, firstRegion, countrySpecifier); if (c == 1) { (*itFirstNode)->SetParent(*itCurr); @@ -125,7 +128,8 @@ Node::Ptr RegionsBuilder::BuildCountryRegionTree(Region const & outer, } // static -int RegionsBuilder::Compare(LevelRegion const & l, LevelRegion const & r) +int RegionsBuilder::Compare(LevelRegion const & l, LevelRegion const & r, + CountrySpecifier const & countrySpecifier) { if (IsAreaLess(r, l) && l.Contains(r)) return 1; @@ -150,7 +154,7 @@ int RegionsBuilder::Compare(LevelRegion const & l, LevelRegion const & r) return -1; } - return RelateByWeight(l, r); + return countrySpecifier.RelateByWeight(l, r); } // static @@ -160,60 +164,24 @@ bool RegionsBuilder::IsAreaLess(Region const & l, Region const & r) return lAreaRation * l.GetArea() < r.GetArea(); } -// static -int RegionsBuilder::RelateByWeight(LevelRegion const & l, LevelRegion const & r) -{ - if (l.GetLevel() != PlaceLevel::Unknown && r.GetLevel() != PlaceLevel::Unknown) - { - if (l.GetLevel() > r.GetLevel()) - return -1; - if (l.GetLevel() < r.GetLevel()) - return 1; - } - - auto const lPlaceType = l.GetPlaceType(); - auto const rPlaceType = r.GetPlaceType(); - if (lPlaceType != PlaceType::Unknown && rPlaceType != PlaceType::Unknown) - { - if (lPlaceType > rPlaceType) - return -1; - if (lPlaceType < rPlaceType) - return 1; - // Check by admin level (administrative city (district + city) > city). - } - - auto const lAdminLevel = l.GetAdminLevel(); - auto const rAdminLevel = r.GetAdminLevel(); - if (lAdminLevel != AdminLevel::Unknown && rAdminLevel != AdminLevel::Unknown) - { - if (lAdminLevel > rAdminLevel) - return -1; - if (lAdminLevel < rAdminLevel) - return 1; - } - if (lAdminLevel != AdminLevel::Unknown) - return 1; - if (rAdminLevel != AdminLevel::Unknown) - return -1; - - return 0; -} - void RegionsBuilder::ForEachCountry(CountryFn fn) { for (auto const & countryName : GetCountryNames()) { + auto countrySpecifier = GetCountrySpecifier(countryName); + Regions outers; auto const & countries = GetCountriesOuters(); auto const pred = [&](Region const & country) { return countryName == country.GetName(); }; std::copy_if(std::begin(countries), std::end(countries), std::back_inserter(outers), pred); - auto countryTrees = BuildCountryRegionTrees(outers); + auto countryTrees = BuildCountryRegionTrees(outers, *countrySpecifier); fn(countryName, countryTrees); } } -Node::PtrList RegionsBuilder::BuildCountryRegionTrees(Regions const & outers) +Node::PtrList RegionsBuilder::BuildCountryRegionTrees(Regions const & outers, + CountrySpecifier const & countrySpecifier) { std::vector> buildingTasks; { @@ -221,7 +189,8 @@ Node::PtrList RegionsBuilder::BuildCountryRegionTrees(Regions const & outers) for (auto const & outer : outers) { auto result = threadPool.Submit( - &RegionsBuilder::BuildCountryRegionTree, std::cref(outer), std::cref(m_regionsInAreaOrder)); + &RegionsBuilder::BuildCountryRegionTree, + std::cref(outer), std::cref(m_regionsInAreaOrder), std::cref(countrySpecifier)); buildingTasks.emplace_back(std::move(result)); } } @@ -232,40 +201,9 @@ Node::PtrList RegionsBuilder::BuildCountryRegionTrees(Regions const & outers) return trees; } -// static -PlaceLevel RegionsBuilder::GetLevel(Region const & region) +std::unique_ptr RegionsBuilder::GetCountrySpecifier(std::string const & countryName) { - switch (region.GetPlaceType()) - { - case PlaceType::Country: - return PlaceLevel::Country; - case PlaceType::State: - case PlaceType::Province: - return PlaceLevel::Region; - case PlaceType::District: - case PlaceType::County: - case PlaceType::Municipality: - return PlaceLevel::Subregion; - case PlaceType::City: - case PlaceType::Town: - case PlaceType::Village: - case PlaceType::Hamlet: - return PlaceLevel::Locality; - case PlaceType::Suburb: - return PlaceLevel::Suburb; - case PlaceType::Quarter: - case PlaceType::Neighbourhood: - return PlaceLevel::Sublocality; - case PlaceType::IsolatedDwelling: - return PlaceLevel::Sublocality; - case PlaceType::Unknown: - break; - } - - if (region.GetAdminLevel() == AdminLevel::Two) - return PlaceLevel::Country; - - return PlaceLevel::Unknown; + return std::make_unique(); } } // namespace regions } // namespace generator diff --git a/generator/regions/regions_builder.hpp b/generator/regions/regions_builder.hpp index d945bfeb39..ff88712ac4 100644 --- a/generator/regions/regions_builder.hpp +++ b/generator/regions/regions_builder.hpp @@ -1,5 +1,6 @@ #pragma once +#include "generator/regions/country_specifier.hpp" #include "generator/regions/node.hpp" #include "generator/regions/region.hpp" #include "generator/regions/to_string_policy.hpp" @@ -29,21 +30,22 @@ public: StringsList GetCountryNames() const; void ForEachCountry(CountryFn fn); - static PlaceLevel GetLevel(Region const & region); - private: static constexpr double kAreaRelativeErrorPercent = 0.1; Regions FormRegionsInAreaOrder(Regions && regions); Regions ExtractCountriesOuters(Regions & regions); - Node::PtrList BuildCountryRegionTrees(Regions const & outers); - static Node::Ptr BuildCountryRegionTree(Region const & outer, Regions const & allRegions); + Node::PtrList BuildCountryRegionTrees(Regions const & outers, + CountrySpecifier const & countrySpecifier); + static Node::Ptr BuildCountryRegionTree(Region const & outer, Regions const & allRegions, + CountrySpecifier const & countrySpecifier); static Node::PtrList MakeSelectedRegionsByCountry(Region const & outer, - Regions const & allRegions); + Regions const & allRegions, CountrySpecifier const & countrySpecifier); // Return: 0 - no relation, 1 - |l| contains |r|, -1 - |r| contains |l|. - static int Compare(LevelRegion const & l, LevelRegion const & r); + static int Compare(LevelRegion const & l, LevelRegion const & r, + CountrySpecifier const & countrySpecifier); static bool IsAreaLess(Region const & l, Region const & r); - static int RelateByWeight(LevelRegion const & l, LevelRegion const & r); + std::unique_ptr GetCountrySpecifier(std::string const & countryName); Regions m_countriesOuters; Regions m_regionsInAreaOrder;