From b5e9b6538623e00ddcf941b8671a56db993d0712 Mon Sep 17 00:00:00 2001 From: Maksim Andrianov Date: Thu, 23 Aug 2018 17:34:27 +0300 Subject: [PATCH] [generator] Added iso3166-1 codes --- .../region_info_collector_tests.cpp | 110 +++++++++--- generator/generator_tests/regions_tests.cpp | 96 +++++------ generator/region_info_collector.cpp | 160 +++++++++++++++--- generator/region_info_collector.hpp | 112 ++++++++++-- generator/regions.cpp | 49 ++++-- generator/regions.hpp | 6 +- 6 files changed, 400 insertions(+), 133 deletions(-) diff --git a/generator/generator_tests/region_info_collector_tests.cpp b/generator/generator_tests/region_info_collector_tests.cpp index e6c6e4e227..971b2f7a8e 100644 --- a/generator/generator_tests/region_info_collector_tests.cpp +++ b/generator/generator_tests/region_info_collector_tests.cpp @@ -28,56 +28,98 @@ OsmElement MakeOsmElement(uint64_t id, Tags const & tags) auto const kNotExistingId = std::numeric_limits::max(); auto const kOsmElementEmpty = MakeOsmElement(0, {}); -auto const kOsmElementFull = MakeOsmElement(1, {{"place", "city"}, {"admin_level", "6"}}); +auto const kOsmElementCity = MakeOsmElement(1, {{"place", "city"}, + {"admin_level", "6"}}); +auto const kOsmElementCountry = MakeOsmElement(2, {{"admin_level", "2"}, + {"ISO3166-1:alpha2", "RU"}, + {"ISO3166-1:alpha3", "RUS"}, + {"ISO3166-1:numeric", "643"}}); } // namespace UNIT_TEST(RegionInfoCollector_Add) { generator::RegionInfoCollector regionInfoCollector; - regionInfoCollector.Add(kOsmElementFull); + regionInfoCollector.Add(kOsmElementCity); { - auto const regionData = regionInfoCollector.Get(kOsmElementFull.id); - TEST_EQUAL(regionData.m_osmId, kOsmElementFull.id, ()); - TEST_EQUAL(regionData.m_adminLevel, generator::AdminLevel::Six, ()); - TEST_EQUAL(regionData.m_place, generator::PlaceType::City, ()); + auto const regionData = regionInfoCollector.Get(kOsmElementCity.id); + TEST_EQUAL(regionData.GetOsmId(), kOsmElementCity.id, ()); + TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Six, ()); + TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::City, ()); + TEST(!regionData.HasIsoCodeAlpha2(), ()); + TEST(!regionData.HasIsoCodeAlpha3(), ()); + TEST(!regionData.HasIsoCodeAlphaNumeric(), ()); + } + + regionInfoCollector.Add(kOsmElementCountry); + { + auto const regionData = regionInfoCollector.Get(kOsmElementCountry.id); + TEST_EQUAL(regionData.GetOsmId(), kOsmElementCountry.id, ()); + TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Two, ()); + TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::Unknown, ()); + + TEST(regionData.HasIsoCodeAlpha2(), ()); + TEST(regionData.HasIsoCodeAlpha3(), ()); + TEST(regionData.HasIsoCodeAlphaNumeric(), ()); + TEST_EQUAL(regionData.GetIsoCodeAlpha2(), "RU", ()); + TEST_EQUAL(regionData.GetIsoCodeAlpha3(), "RUS", ()); + TEST_EQUAL(regionData.GetIsoCodeAlphaNumeric(), "643", ()); } regionInfoCollector.Add(kOsmElementEmpty); { auto const regionDataEmpty = regionInfoCollector.Get(kOsmElementEmpty.id); - TEST_EQUAL(regionDataEmpty.m_osmId, kOsmElementEmpty.id, ()); - TEST_EQUAL(regionDataEmpty.m_adminLevel, generator::AdminLevel::Unknown, ()); - TEST_EQUAL(regionDataEmpty.m_place, generator::PlaceType::Unknown, ()); + TEST_EQUAL(regionDataEmpty.GetOsmId(), kOsmElementEmpty.id, ()); + TEST_EQUAL(regionDataEmpty.GetAdminLevel(), generator::AdminLevel::Unknown, ()); + TEST_EQUAL(regionDataEmpty.GetPlaceType(), generator::PlaceType::Unknown, ()); + TEST(!regionDataEmpty.HasIsoCodeAlpha2(), ()); + TEST(!regionDataEmpty.HasIsoCodeAlpha3(), ()); + TEST(!regionDataEmpty.HasIsoCodeAlphaNumeric(), ()); } } UNIT_TEST(RegionInfoCollector_Get) { generator::RegionInfoCollector regionInfoCollector; - regionInfoCollector.Add(kOsmElementFull); + regionInfoCollector.Add(kOsmElementCity); - auto const regionData = regionInfoCollector.Get(kOsmElementFull.id); - TEST_EQUAL(regionData.m_osmId, kOsmElementFull.id, ()); - TEST_EQUAL(regionData.m_adminLevel, generator::AdminLevel::Six, ()); - TEST_EQUAL(regionData.m_place, generator::PlaceType::City, ()); - - TEST_THROW(regionInfoCollector.Get(kNotExistingId), std::out_of_range, ()); + auto const regionData = regionInfoCollector.Get(kOsmElementCity.id); + TEST_EQUAL(regionData.GetOsmId(), kOsmElementCity.id, ()); + TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Six, ()); + TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::City, ()); } UNIT_TEST(RegionInfoCollector_Exists) { generator::RegionInfoCollector regionInfoCollector; - regionInfoCollector.Add(kOsmElementFull); + regionInfoCollector.Add(kOsmElementCity); + regionInfoCollector.Add(kOsmElementCountry); - TEST(regionInfoCollector.Exists(kOsmElementFull.id), ()); - TEST(!regionInfoCollector.Exists(kNotExistingId), ()); + TEST(regionInfoCollector.Get(kOsmElementCountry.id).HasAdminLevel(), ()); + TEST(!regionInfoCollector.Get(kOsmElementCountry.id).HasPlaceType(), ()); + TEST(regionInfoCollector.Get(kOsmElementCountry.id).HasIsoCodeAlpha2(), ()); + TEST(regionInfoCollector.Get(kOsmElementCountry.id).HasIsoCodeAlpha3(), ()); + TEST(regionInfoCollector.Get(kOsmElementCountry.id).HasIsoCodeAlphaNumeric(), ()); + + TEST(regionInfoCollector.Get(kOsmElementCity.id).HasAdminLevel(), ()); + TEST(regionInfoCollector.Get(kOsmElementCity.id).HasPlaceType(), ()); + TEST(!regionInfoCollector.Get(kOsmElementCity.id).HasIsoCodeAlpha2(), ()); + TEST(!regionInfoCollector.Get(kOsmElementCity.id).HasIsoCodeAlpha3(), ()); + TEST(!regionInfoCollector.Get(kOsmElementCity.id).HasIsoCodeAlphaNumeric(), ()); + + TEST(!regionInfoCollector.Get(kNotExistingId).HasAdminLevel(), ()); + TEST(!regionInfoCollector.Get(kNotExistingId).HasPlaceType(), ()); + TEST(!regionInfoCollector.Get(kNotExistingId).HasIsoCodeAlpha2(), ()); + TEST(!regionInfoCollector.Get(kNotExistingId).HasIsoCodeAlpha3(), ()); + TEST(!regionInfoCollector.Get(kNotExistingId).HasIsoCodeAlphaNumeric(), ()); } UNIT_TEST(RegionInfoCollector_Save) { generator::RegionInfoCollector regionInfoCollector; - regionInfoCollector.Add(kOsmElementFull); - auto const regionData = regionInfoCollector.Get(kOsmElementFull.id); + regionInfoCollector.Add(kOsmElementCity); + auto const regionCity = regionInfoCollector.Get(kOsmElementCity.id); + regionInfoCollector.Add(kOsmElementCountry); + auto const regionCountry = regionInfoCollector.Get(kOsmElementCountry.id); auto & platform = GetPlatform(); auto const tmpDir = platform.TmpDir(); @@ -86,10 +128,28 @@ UNIT_TEST(RegionInfoCollector_Save) regionInfoCollector.Save(name); { generator::RegionInfoCollector regionInfoCollector(name); - auto const rRegionData = regionInfoCollector.Get(kOsmElementFull.id); + auto const rRegionData = regionInfoCollector.Get(kOsmElementCity.id); - TEST_EQUAL(regionData.m_osmId, rRegionData.m_osmId, ()); - TEST_EQUAL(regionData.m_adminLevel, rRegionData.m_adminLevel, ()); - TEST_EQUAL(regionData.m_place, rRegionData.m_place, ()); + TEST_EQUAL(regionCity.GetOsmId(), rRegionData.GetOsmId(), ()); + TEST_EQUAL(regionCity.GetAdminLevel(), rRegionData.GetAdminLevel(), ()); + TEST_EQUAL(regionCity.GetPlaceType(), rRegionData.GetPlaceType(), ()); + TEST_EQUAL(regionCity.HasIsoCodeAlpha2(), rRegionData.HasIsoCodeAlpha2(), ()); + TEST_EQUAL(regionCity.HasIsoCodeAlpha3(), rRegionData.HasIsoCodeAlpha3(), ()); + TEST_EQUAL(regionCity.HasIsoCodeAlphaNumeric(), rRegionData.HasIsoCodeAlphaNumeric(), ()); + } + + { + generator::RegionInfoCollector regionInfoCollector(name); + auto const rRegionData = regionInfoCollector.Get(kOsmElementCountry.id); + + TEST_EQUAL(regionCountry.GetOsmId(), rRegionData.GetOsmId(), ()); + TEST_EQUAL(regionCountry.GetAdminLevel(), rRegionData.GetAdminLevel(), ()); + TEST_EQUAL(regionCountry.GetPlaceType(), rRegionData.GetPlaceType(), ()); + TEST_EQUAL(regionCountry.HasIsoCodeAlpha2(), rRegionData.HasIsoCodeAlpha2(), ()); + TEST_EQUAL(regionCountry.HasIsoCodeAlpha3(), rRegionData.HasIsoCodeAlpha3(), ()); + TEST_EQUAL(regionCountry.HasIsoCodeAlphaNumeric(), rRegionData.HasIsoCodeAlphaNumeric(), ()); + TEST_EQUAL(regionCountry.GetIsoCodeAlpha2(), rRegionData.GetIsoCodeAlpha2(), ()); + TEST_EQUAL(regionCountry.GetIsoCodeAlpha3(), rRegionData.GetIsoCodeAlpha3(), ()); + TEST_EQUAL(regionCountry.GetIsoCodeAlphaNumeric(), rRegionData.GetIsoCodeAlphaNumeric(), ()); } } diff --git a/generator/generator_tests/regions_tests.cpp b/generator/generator_tests/regions_tests.cpp index 2d7fae66b1..29896bdc09 100644 --- a/generator/generator_tests/regions_tests.cpp +++ b/generator/generator_tests/regions_tests.cpp @@ -22,7 +22,20 @@ using namespace generator::regions; namespace { -RegionsBuilder::Regions MakeTestDataSet1() +using Tags = std::vector>; + +OsmElement MakeOsmElement(uint64_t id, std::string const & adminLevel, + std::string const & place = "") +{ + OsmElement el; + el.id = id; + el.AddTag("place", place); + el.AddTag("admin_level", adminLevel); + + return el; +} + +RegionsBuilder::Regions MakeTestDataSet1(RegionInfoCollector & collector) { RegionsBuilder::Regions regions; { @@ -33,11 +46,9 @@ RegionsBuilder::Regions MakeTestDataSet1() fb1.AddPolygon(poly); fb1.SetAreaAddHoles({{{5, 8}, {7, 10}, {10, 10}, {11, 7}, {10, 4}, {7, 5}, {5, 8}}}); - RegionData data; - data.m_osmId = 1; - data.m_adminLevel = AdminLevel::Two; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 1; + collector.Add(MakeOsmElement(id, "2")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -47,11 +58,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{5, 8}, {7, 10}, {10, 10}, {11, 7}, {10, 4}, {7, 5}, {5, 8}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 2; - data.m_adminLevel = AdminLevel::Two; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 2; + collector.Add(MakeOsmElement(id, "2")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -61,11 +70,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{0, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 2; - data.m_adminLevel = AdminLevel::Two; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 2; + collector.Add(MakeOsmElement(id, "2")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -75,11 +82,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{4, 4}, {7, 5}, {10, 4}, {12, 9}, {15, 7}, {11, 2}, {4, 4}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 3; - data.m_adminLevel = AdminLevel::Four; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 3; + collector.Add(MakeOsmElement(id, "4")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -90,11 +95,9 @@ RegionsBuilder::Regions MakeTestDataSet1() {11, 7}, {10, 10}, {7, 10}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 4; - data.m_adminLevel = AdminLevel::Four; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 4; + collector.Add(MakeOsmElement(id, "4")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -105,11 +108,9 @@ RegionsBuilder::Regions MakeTestDataSet1() {7, 5}, {4, 4}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 5; - data.m_adminLevel = AdminLevel::Four; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 5; + collector.Add(MakeOsmElement(id, "4")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -119,11 +120,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{4, 4}, {2, 8}, {3, 12}, {4, 10}, {5, 10}, {5, 8}, {7, 5}, {4, 4}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 6; - data.m_adminLevel = AdminLevel::Six; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 6; + collector.Add(MakeOsmElement(id, "6")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -133,11 +132,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{3, 12}, {8, 15}, {9, 12}, {7, 10}, {5, 8}, {5, 10}, {4, 10}, {3, 12}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 7; - data.m_adminLevel = AdminLevel::Six; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 7; + collector.Add(MakeOsmElement(id, "6")); + regions.emplace_back(Region(fb1, collector.Get(id))); } { @@ -147,11 +144,9 @@ RegionsBuilder::Regions MakeTestDataSet1() vector poly = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; fb1.AddPolygon(poly); - RegionData data; - data.m_osmId = 8; - data.m_adminLevel = AdminLevel::Four; - - regions.emplace_back(Region(fb1, data)); + uint64_t const id = 8; + collector.Add(MakeOsmElement(id, "4")); + regions.emplace_back(Region(fb1, collector.Get(id))); } return regions; @@ -187,7 +182,8 @@ bool ExistsName(std::vector const & coll, std::string const name) UNIT_TEST(RegionsBuilderTest_GetCountryNames) { - RegionsBuilder builder(MakeTestDataSet1()); + RegionInfoCollector collector; + RegionsBuilder builder(MakeTestDataSet1(collector)); auto const countryNames = builder.GetCountryNames(); TEST_EQUAL(countryNames.size(), 2, ()); TEST(std::count(std::begin(countryNames), std::end(countryNames), "Country_1"), ()); @@ -196,7 +192,8 @@ UNIT_TEST(RegionsBuilderTest_GetCountryNames) UNIT_TEST(RegionsBuilderTest_GetCountries) { - RegionsBuilder builder(MakeTestDataSet1()); + RegionInfoCollector collector; + RegionsBuilder builder(MakeTestDataSet1(collector)); auto const countries = builder.GetCountries(); TEST_EQUAL(countries.size(), 3, ()); TEST_EQUAL(std::count_if(std::begin(countries), std::end(countries), @@ -207,8 +204,9 @@ UNIT_TEST(RegionsBuilderTest_GetCountries) UNIT_TEST(RegionsBuilderTest_GetCountryTrees) { + RegionInfoCollector collector; std::vector bankOfNames; - RegionsBuilder builder(MakeTestDataSet1(), std::make_unique(bankOfNames)); + RegionsBuilder builder(MakeTestDataSet1(collector), std::make_unique(bankOfNames)); auto const countryTrees = builder.GetCountryTrees(); for (auto const & countryName : builder.GetCountryNames()) diff --git a/generator/region_info_collector.cpp b/generator/region_info_collector.cpp index b010925562..e797dc6fe7 100644 --- a/generator/region_info_collector.cpp +++ b/generator/region_info_collector.cpp @@ -5,9 +5,10 @@ #include "coding/file_writer.hpp" #include "coding/reader.hpp" -#include "coding/write_to_sink.hpp" +#include "base/assert.hpp" #include "base/logging.hpp" +#include "base/macros.hpp" #include @@ -37,6 +38,24 @@ PlaceType EncodePlaceType(std::string const & place) return it == m.end() ? PlaceType::Unknown : it->second; } +void RegionInfoCollector::IsoCode::SetAlpha2(std::string const & alpha2) +{ + CHECK_LESS_OR_EQUAL(alpha2.size() + 1, ARRAY_SIZE(m_alpha2), ()); + std::strcpy(m_alpha2, alpha2.data()); +} + +void RegionInfoCollector::IsoCode::SetAlpha3(std::string const & alpha3) +{ + CHECK_LESS_OR_EQUAL(alpha3.size() + 1, ARRAY_SIZE(m_alpha3), ()); + std::strcpy(m_alpha3, alpha3.data()); +} + +void RegionInfoCollector::IsoCode::SetNumeric(std::string const & numeric) +{ + CHECK_LESS_OR_EQUAL(numeric.size() + 1, ARRAY_SIZE(m_numeric), ()); + std::strcpy(m_numeric, numeric.data()); +} + RegionInfoCollector::RegionInfoCollector(std::string const & filename) { ParseFile(filename); @@ -57,14 +76,8 @@ void RegionInfoCollector::ParseFile(std::string const & filename) uint8_t version; ReadPrimitiveFromSource(src, version); CHECK_EQUAL(version, kVersion, ("Versions do not match.")); - uint32_t size; - ReadPrimitiveFromSource(src, size); - RegionData regionData; - for (uint32_t i = 0; i < size; ++i) - { - ReadPrimitiveFromSource(src, regionData); - m_map.emplace(regionData.m_osmId, regionData); - } + ReadMap(src, m_mapRegionData); + ReadMap(src, m_mapIsoCode); } catch (FileReader::Exception const & e) { @@ -75,8 +88,16 @@ void RegionInfoCollector::ParseFile(std::string const & filename) void RegionInfoCollector::Add(OsmElement const & el) { RegionData regionData; - Fill(el, regionData); - m_map.emplace(el.id, regionData); + FillRegionData(el, regionData); + m_mapRegionData.emplace(el.id, regionData); + + // If the region is a country. + if (regionData.m_adminLevel == AdminLevel::Two) + { + IsoCode isoCode; + FillIsoCode(el, isoCode); + m_mapIsoCode.emplace(el.id, isoCode); + } } void RegionInfoCollector::Save(std::string const & filename) @@ -85,10 +106,8 @@ void RegionInfoCollector::Save(std::string const & filename) { FileWriter writer(filename); WriteToSink(writer, kVersion); - uint32_t const size = static_cast(m_map.size()); - WriteToSink(writer, size); - for (auto const & el : m_map) - writer.Write(&el.second, sizeof(el.second)); + WriteMap(writer, m_mapRegionData); + WriteMap(writer, m_mapIsoCode); } catch (FileWriter::Exception const & e) { @@ -96,26 +115,20 @@ void RegionInfoCollector::Save(std::string const & filename) } } -RegionData & RegionInfoCollector::Get(uint64_t osmId) +RegionDataProxy RegionInfoCollector::Get(uint64_t osmId) const { - return m_map.at(osmId); + return RegionDataProxy(*this, osmId); } -RegionData const & RegionInfoCollector::Get(uint64_t osmId) const -{ - return m_map.at(osmId); -} - -bool RegionInfoCollector::Exists(uint64_t osmId) const -{ - return m_map.count(osmId) != 0; -} - -void RegionInfoCollector::Fill(OsmElement const & el, RegionData & rd) +void RegionInfoCollector::FillRegionData(OsmElement const & el, RegionData & rd) { rd.m_osmId = el.id; rd.m_place = EncodePlaceType(el.GetTag("place")); auto const al = el.GetTag("admin_level"); + + if (al.empty()) + return; + try { auto const adminLevel = std::stoi(al); @@ -130,4 +143,95 @@ void RegionInfoCollector::Fill(OsmElement const & el, RegionData & rd) rd.m_adminLevel = AdminLevel::Unknown; } } + +void RegionInfoCollector::FillIsoCode(OsmElement const & el, IsoCode & rd) +{ + rd.m_osmId = el.id; + rd.SetAlpha2(el.GetTag("ISO3166-1:alpha2")); + rd.SetAlpha3(el.GetTag("ISO3166-1:alpha3")); + rd.SetNumeric(el.GetTag("ISO3166-1:numeric")); +} + +RegionDataProxy::RegionDataProxy(RegionInfoCollector const & regionInfoCollector, uint64_t osmId) + : m_regionInfoCollector(regionInfoCollector), + m_osmId(osmId) +{ +} + +RegionInfoCollector const & RegionDataProxy::GetCollector() const +{ + return m_regionInfoCollector; +} + +RegionInfoCollector::MapRegionData const & RegionDataProxy::GetMapRegionData() const +{ + return GetCollector().m_mapRegionData; +} + +RegionInfoCollector::MapIsoCode const & RegionDataProxy::GetMapIsoCode() const +{ + return GetCollector().m_mapIsoCode; +} + +uint64_t RegionDataProxy::GetOsmId() const +{ + return m_osmId; +} + +AdminLevel RegionDataProxy::GetAdminLevel() const +{ + return GetMapRegionData().at(m_osmId).m_adminLevel; +} + +PlaceType RegionDataProxy::GetPlaceType() const +{ + return GetMapRegionData().at(m_osmId).m_place; +} + +bool RegionDataProxy::HasAdminLevel() const +{ + return (GetMapRegionData().count(m_osmId) != 0) && + (GetMapRegionData().at(m_osmId).m_adminLevel != AdminLevel::Unknown); +} + +bool RegionDataProxy::HasPlaceType() const +{ + return (GetMapRegionData().count(m_osmId) != 0) && + (GetMapRegionData().at(m_osmId).m_place != PlaceType::Unknown); +} + +bool RegionDataProxy::HasIsoCode() const +{ + return GetMapIsoCode().count(m_osmId) != 0; +} + +bool RegionDataProxy::HasIsoCodeAlpha2() const +{ + return HasIsoCode() && GetMapIsoCode().at(m_osmId).HasAlpha2(); +} + +bool RegionDataProxy::HasIsoCodeAlpha3() const +{ + return HasIsoCode() && GetMapIsoCode().at(m_osmId).HasAlpha3(); +} + +bool RegionDataProxy::HasIsoCodeAlphaNumeric() const +{ + return HasIsoCode() && GetMapIsoCode().at(m_osmId).HasNumeric(); +} + +std::string RegionDataProxy::GetIsoCodeAlpha2() const +{ + return GetMapIsoCode().at(m_osmId).GetAlpha2(); +} + +std::string RegionDataProxy::GetIsoCodeAlpha3() const +{ + return GetMapIsoCode().at(m_osmId).GetAlpha3(); +} + +std::string RegionDataProxy::GetIsoCodeAlphaNumeric() const +{ + return GetMapIsoCode().at(m_osmId).GetNumeric(); +} } // namespace generator diff --git a/generator/region_info_collector.hpp b/generator/region_info_collector.hpp index 0983c2ab11..6e04c9537d 100644 --- a/generator/region_info_collector.hpp +++ b/generator/region_info_collector.hpp @@ -2,9 +2,13 @@ #include "platform/platform.hpp" +#include "coding/write_to_sink.hpp" + #include +#include #include #include +#include #include struct OsmElement; @@ -48,12 +52,7 @@ enum class PlaceType: uint8_t PlaceType EncodePlaceType(std::string const & place); -struct RegionData -{ - uint64_t m_osmId = 0; - AdminLevel m_adminLevel = AdminLevel::Unknown; - PlaceType m_place = PlaceType::Unknown; -}; +class RegionDataProxy; // This is a class for working a file with additional information about regions. class RegionInfoCollector @@ -68,16 +67,105 @@ public: // It is supposed to be called already on the filtered osm objects that represent regions. void Add(OsmElement const & el); // osmId is osm relation id. - RegionData & Get(uint64_t osmId); - const RegionData & Get(uint64_t osmId) const; - bool Exists(uint64_t osmId) const; + RegionDataProxy Get(uint64_t osmId) const; void Save(std::string const & filename); private: - void ParseFile(std::string const & filename); - void Fill(OsmElement const & el, RegionData & rd); + friend class RegionDataProxy; - std::unordered_map m_map; + // Codes for the names of countries, dependent territories, and special areas of geographical + // interest. + // https://en.wikipedia.org/wiki/ISO_3166-1 + struct IsoCode + { + bool HasAlpha2() const { return m_alpha2[0] != '\0'; } + bool HasAlpha3() const { return m_alpha3[0] != '\0'; } + bool HasNumeric() const { return m_numeric[0] != '\0'; } + + void SetAlpha2(std::string const & alpha2); + void SetAlpha3(std::string const & alpha3); + void SetNumeric(std::string const & numeric); + + std::string GetAlpha2() const { return m_alpha2; } + std::string GetAlpha3() const { return m_alpha3; } + std::string GetNumeric() const { return m_numeric; } + + uint64_t m_osmId = 0; + char m_alpha2[3] = {}; + char m_alpha3[4] = {}; + char m_numeric[4] = {}; + }; + + struct RegionData + { + uint64_t m_osmId = 0; + AdminLevel m_adminLevel = AdminLevel::Unknown; + PlaceType m_place = PlaceType::Unknown; + }; + + using MapRegionData = std::unordered_map; + using MapIsoCode = std::unordered_map; + + template + void ReadMap(Source & src, Map & seq) + { + uint32_t size = 0; + ReadPrimitiveFromSource(src, size); + typename Map::mapped_type data; + for (uint32_t i = 0; i < size; ++i) + { + ReadPrimitiveFromSource(src, data); + seq.emplace(data.m_osmId, std::move(data)); + } + } + + template + void WriteMap(Sink & sink, Map & seq) + { + static_assert(std::is_trivially_copyable::value, ""); + + uint32_t const sizeRegionData = static_cast(seq.size()); + WriteToSink(sink, sizeRegionData); + for (auto const & el : seq) + sink.Write(&el.second, sizeof(el.second)); + } + + void ParseFile(std::string const & filename); + void FillRegionData(OsmElement const & el, RegionData & rd); + void FillIsoCode(OsmElement const & el, IsoCode & rd); + + MapRegionData m_mapRegionData; + MapIsoCode m_mapIsoCode; +}; + +class RegionDataProxy +{ +public: + RegionDataProxy(RegionInfoCollector const & regionInfoCollector, uint64_t osmId); + + uint64_t GetOsmId() const; + AdminLevel GetAdminLevel() const; + PlaceType GetPlaceType() const; + + bool HasAdminLevel() const; + bool HasPlaceType() const; + + bool HasIsoCodeAlpha2() const; + bool HasIsoCodeAlpha3() const; + bool HasIsoCodeAlphaNumeric() const; + + std::string GetIsoCodeAlpha2() const; + std::string GetIsoCodeAlpha3() const; + std::string GetIsoCodeAlphaNumeric() const; + +private: + bool HasIsoCode() const; + RegionInfoCollector const & GetCollector() const; + RegionInfoCollector::MapRegionData const & GetMapRegionData() const; + RegionInfoCollector::MapIsoCode const & GetMapIsoCode() const; + + std::reference_wrapper m_regionInfoCollector; + uint64_t m_osmId; }; inline std::ostream & operator<<(std::ostream & out, AdminLevel const & t) diff --git a/generator/regions.cpp b/generator/regions.cpp index 8a20816503..437c244b12 100644 --- a/generator/regions.cpp +++ b/generator/regions.cpp @@ -46,6 +46,7 @@ public: std::string ToString(Node::PtrList const & nodePtrList) override { auto const & main = nodePtrList.front()->GetData(); + auto const & country = nodePtrList.back()->GetData(); auto geometry = my::NewJSONObject(); ToJSONObject(*geometry, "type", "Point"); @@ -72,6 +73,8 @@ public: ToJSONObject(*properties, "name", main.GetName()); ToJSONObject(*properties, "rank", main.GetRank()); ToJSONObject(*properties, "address", address); + if (country.HasIsoCode()) + ToJSONObject(*properties, "code", country.GetIsoCode()); auto feature = my::NewJSONObject(); ToJSONObject(*feature, "type", "Feature"); @@ -171,7 +174,6 @@ RegionsBuilder::Regions ReadRegionsFromTmpMwm(feature::GenerateInfo const & genI return; auto const id = fb.GetMostGenericOsmId().GetSerialId(); - CHECK(regionsInfoCollector.Exists(id), ()); auto region = Region(fb, regionsInfoCollector.Get(id)); auto const & label = region.GetLabel(); @@ -277,7 +279,7 @@ void NormalizeTree(Node::Ptr tree) } } // namespace -Region::Region(FeatureBuilder1 const & fb, RegionData const & rd) +Region::Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd) : m_name(fb.GetParams().name), m_regionData(rd), m_polygon(std::make_shared()) @@ -320,7 +322,17 @@ void Region::FillPolygon(FeatureBuilder1 const & fb) bool Region::IsCountry() const { static auto const kAdminLevelCountry = AdminLevel::Two; - return m_regionData.m_adminLevel == kAdminLevelCountry; + return m_regionData.GetAdminLevel() == kAdminLevelCountry; +} + +bool Region::HasIsoCode() const +{ + return m_regionData.HasIsoCodeAlpha2(); +} + +std::string Region::GetIsoCode() const +{ + return m_regionData.GetIsoCodeAlpha2(); } bool Region::Contains(Region const & smaller) const @@ -354,35 +366,36 @@ bool Region::ContainsRect(Region const & smaller) const // This is used when calculating the rank. uint8_t Region::GetRank() const { - - switch (m_regionData.m_adminLevel) + auto const adminLevel = m_regionData.GetAdminLevel(); + auto const placeType = m_regionData.GetPlaceType(); + switch (adminLevel) { case AdminLevel::Two: - case AdminLevel::Four: return static_cast(m_regionData.m_adminLevel); + case AdminLevel::Four: return static_cast(adminLevel); default: break; } - switch (m_regionData.m_place) + switch (placeType) { case PlaceType::City: case PlaceType::Town: case PlaceType::Village: - case PlaceType::Hamlet: return static_cast(m_regionData.m_place); + case PlaceType::Hamlet: return static_cast(placeType); default: break; } - switch (m_regionData.m_adminLevel) + switch (adminLevel) { - case AdminLevel::Six: return static_cast(m_regionData.m_adminLevel); + case AdminLevel::Six: return static_cast(adminLevel); default: break; } - switch (m_regionData.m_place) + switch (placeType) { case PlaceType::Suburb: case PlaceType::Neighbourhood: case PlaceType::Locality: - case PlaceType::IsolatedDwelling: return static_cast(m_regionData.m_place); + case PlaceType::IsolatedDwelling: return static_cast(placeType); default: break; } @@ -391,14 +404,16 @@ uint8_t Region::GetRank() const std::string Region::GetLabel() const { - switch (m_regionData.m_adminLevel) + auto const adminLevel = m_regionData.GetAdminLevel(); + auto const placeType = m_regionData.GetPlaceType(); + switch (adminLevel) { case AdminLevel::Two: return "country"; case AdminLevel::Four: return "region"; default: break; } - switch (m_regionData.m_place) + switch (placeType) { case PlaceType::City: case PlaceType::Town: @@ -407,13 +422,13 @@ std::string Region::GetLabel() const default: break; } - switch (m_regionData.m_adminLevel) + switch (adminLevel) { case AdminLevel::Six: return "subregion"; default: break; } - switch (m_regionData.m_place) + switch (placeType) { case PlaceType::Suburb: case PlaceType::Neighbourhood: return "suburb"; @@ -449,7 +464,7 @@ double Region::GetArea() const uint64_t Region::GetId() const { - return m_regionData.m_osmId; + return m_regionData.GetOsmId(); } RegionsBuilder::RegionsBuilder(Regions && regions) diff --git a/generator/regions.hpp b/generator/regions.hpp index d039256ca0..dc8bee2e05 100644 --- a/generator/regions.hpp +++ b/generator/regions.hpp @@ -34,11 +34,13 @@ struct Region using BoostPolygon = boost::geometry::model::polygon; using BoostRect = boost::geometry::model::box; - explicit Region(FeatureBuilder1 const & fb, RegionData const & rd); + explicit Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd); void DeletePolygon(); std::string GetName(int8_t lang = StringUtf8Multilang::kDefaultCode) const; bool IsCountry() const; + bool HasIsoCode() const; + std::string GetIsoCode() const; bool Contains(Region const & smaller) const; bool ContainsRect(Region const & smaller) const; double CalculateOverlapPercentage(Region const & other) const; @@ -56,7 +58,7 @@ private: void FillPolygon(FeatureBuilder1 const & fb); StringUtf8Multilang m_name; - RegionData m_regionData; + RegionDataProxy m_regionData; std::shared_ptr m_polygon; BoostRect m_rect; double m_area;