diff --git a/data/osm_test_data/building_address.osm b/data/osm_test_data/building_address.osm new file mode 100644 index 0000000000..b2741b3eef --- /dev/null +++ b/data/osm_test_data/building_address.osm @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generator/addresses_collector.cpp b/generator/addresses_collector.cpp index 911b9544d4..54ba1cf34d 100644 --- a/generator/addresses_collector.cpp +++ b/generator/addresses_collector.cpp @@ -22,12 +22,8 @@ uint64_t GetOsmID(FeatureBuilder const & fb) AddressesHolder::AddressInfo FromFB(FeatureBuilder const & fb) { - return { - fb.GetParams().house.Get(), - std::string(fb.GetAddressData().Get(AddressData::Type::Street)), - std::string(fb.GetAddressData().Get(AddressData::Type::Postcode)), - {} - }; + auto const & params = fb.GetParams(); + return { params.house.Get(), std::string(params.GetStreet()), std::string(params.GetPostcode()), {} }; } void LogWarning(std::string const & msg, uint64_t id) @@ -114,7 +110,7 @@ bool AddressesHolder::Update(feature::FeatureBuilder & fb) const if (!info.m_street.empty()) LogWarning("Different streets", id); else - params.AddStreet(i->second.m_street); + params.SetStreet(i->second.m_street); } if (!i->second.m_postcode.empty() && i->second.m_postcode != info.m_postcode) @@ -122,7 +118,7 @@ bool AddressesHolder::Update(feature::FeatureBuilder & fb) const if (!info.m_postcode.empty()) LogWarning("Different postcodes", id); else - params.AddPostcode(i->second.m_postcode); + params.SetPostcode(i->second.m_postcode); } return true; diff --git a/generator/boundary_postcodes_enricher.cpp b/generator/boundary_postcodes_enricher.cpp index a5851ad6b2..25d93b542a 100644 --- a/generator/boundary_postcodes_enricher.cpp +++ b/generator/boundary_postcodes_enricher.cpp @@ -33,11 +33,12 @@ BoundaryPostcodesEnricher::BoundaryPostcodesEnricher(std::string const & boundar void BoundaryPostcodesEnricher::Enrich(feature::FeatureBuilder & fb) const { - if (fb.HasPostcode() || !ftypes::IsAddressObjectChecker::Instance()(fb.GetTypes())) + auto & params = fb.GetParams(); + if (!params.GetPostcode().empty() || !ftypes::IsAddressObjectChecker::Instance()(fb.GetTypes())) return; auto const hasName = !fb.GetMultilangName().IsEmpty(); - auto const hasHouseNumber = !fb.GetParams().house.IsEmpty(); + auto const hasHouseNumber = !params.house.IsEmpty(); // We do not save postcodes for unnamed features without house number to reduce amount of data. // For example with this filter we have +100Kb for Turkey_Marmara Region_Istanbul.mwm, without @@ -52,7 +53,7 @@ void BoundaryPostcodesEnricher::Enrich(feature::FeatureBuilder & fb) const if (!m_boundaryPostcodes[i].second.Contains(center)) return false; - fb.SetPostcode(m_boundaryPostcodes[i].first); + params.SetPostcode(m_boundaryPostcodes[i].first); return true; }); } diff --git a/generator/feature_builder.cpp b/generator/feature_builder.cpp index 3d0842ff7c..b23b5961d6 100644 --- a/generator/feature_builder.cpp +++ b/generator/feature_builder.cpp @@ -161,17 +161,6 @@ void FeatureBuilder::ResetGeometry() m_limitRect.MakeEmpty(); } -bool FeatureBuilder::HasPostcode() const -{ - return m_params.GetMetadata().Has(Metadata::FMD_POSTCODE); -} - -void FeatureBuilder::SetPostcode(std::string const & postcode) -{ - m_params.GetMetadata().Set(Metadata::FMD_POSTCODE, postcode); - m_params.AddPostcode(postcode); -} - bool FeatureBuilder::RemoveInvalidTypes() { if (!m_params.FinishAddingTypes()) diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp index 0cef136ae7..3df2005b8d 100644 --- a/generator/feature_builder.hpp +++ b/generator/feature_builder.hpp @@ -157,14 +157,10 @@ public: std::string_view GetName(int8_t lang = StringUtf8Multilang::kDefaultCode) const; StringUtf8Multilang const & GetMultilangName() const { return m_params.name; } uint8_t GetRank() const { return m_params.rank; } - AddressData const & GetAddressData() const { return m_params.GetAddressData(); } Metadata const & GetMetadata() const { return m_params.GetMetadata(); } Metadata & GetMetadata() { return m_params.GetMetadata(); } - bool HasPostcode() const; - void SetPostcode(std::string const & postcode); - // To work with types and names based on drawing. // Check classificator types for their compatibility with feature geometry type. // Need to call when using any classificator types manipulating. diff --git a/generator/feature_sorter.cpp b/generator/feature_sorter.cpp index 2950aae8ff..aea8bae70a 100644 --- a/generator/feature_sorter.cpp +++ b/generator/feature_sorter.cpp @@ -292,7 +292,7 @@ public: m_boundaryPostcodesEnricher.Enrich(fb); // 2. Write address to a file (with possible updated postcode above). - fb.GetAddressData().SerializeForMwmTmp(*m_addrFile); + fb.GetParams().SerializeAddress(*m_addrFile); // 3. Save metadata. if (!fb.GetMetadata().Empty()) diff --git a/generator/generator_tests/osm_type_test.cpp b/generator/generator_tests/osm_type_test.cpp index 308501a785..bed19d7486 100644 --- a/generator/generator_tests/osm_type_test.cpp +++ b/generator/generator_tests/osm_type_test.cpp @@ -196,8 +196,6 @@ UNIT_CLASS_TEST(TestWithClassificator, OsmType_Address) TEST_EQUAL(params.house.Get(), "42", ()); } - using AddrType = feature::AddressData::Type; - { Tags const tags = { { "addr:conscriptionnumber", "223" }, @@ -215,8 +213,8 @@ UNIT_CLASS_TEST(TestWithClassificator, OsmType_Address) TEST(params.IsTypeExist(addrType), ()); TEST_EQUAL(params.house.Get(), "223/5", ()); - TEST_EQUAL(params.GetAddressData().Get(AddrType::Street), "Řetězová", ()); - TEST_EQUAL(params.GetAddressData().Get(AddrType::Postcode), "11000", ()); + TEST_EQUAL(params.GetStreet(), "Řetězová", ()); + TEST_EQUAL(params.GetPostcode(), "11000", ()); } { @@ -240,8 +238,8 @@ UNIT_CLASS_TEST(TestWithClassificator, OsmType_Address) TEST(params.IsTypeExist(GetType({"internet_access", "wlan"})), ()); TEST_EQUAL(params.house.Get(), "41", ()); - TEST_EQUAL(params.GetAddressData().Get(AddrType::Street), "Leutschenbachstrasse", ()); - TEST_EQUAL(params.GetAddressData().Get(AddrType::Postcode), "8050", ()); + TEST_EQUAL(params.GetStreet(), "Leutschenbachstrasse", ()); + TEST_EQUAL(params.GetPostcode(), "8050", ()); } { @@ -261,8 +259,8 @@ UNIT_CLASS_TEST(TestWithClassificator, OsmType_Address) TEST(params.IsTypeExist(addrType), ()); TEST_EQUAL(params.house.Get(), "Rozemnieki", ()); - TEST(params.GetAddressData().Get(AddrType::Street).empty(), ()); - TEST_EQUAL(params.GetAddressData().Get(AddrType::Postcode), "LV-5695", ()); + TEST(params.GetStreet().empty(), ()); + TEST_EQUAL(params.GetPostcode(), "LV-5695", ()); } } diff --git a/generator/generator_tests/raw_generator_test.cpp b/generator/generator_tests/raw_generator_test.cpp index 33831544a2..4039066f34 100644 --- a/generator/generator_tests/raw_generator_test.cpp +++ b/generator/generator_tests/raw_generator_test.cpp @@ -4,6 +4,7 @@ #include "generator/descriptions_section_builder.hpp" #include "search/cities_boundaries_table.hpp" +#include "search/house_to_street_table.hpp" #include "routing/index_graph_loader.hpp" #include "routing/maxspeeds.hpp" @@ -415,6 +416,59 @@ UNIT_CLASS_TEST(TestRawGenerator, Postcode_Relations) TEST_EQUAL(count, 2, ()); } +UNIT_CLASS_TEST(TestRawGenerator, Building_Address) +{ + std::string const mwmName = "Address"; + BuildFB("./data/osm_test_data/building_address.osm", mwmName, false /* makeWorld */); + + size_t count = 0; + ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) + { + if (ftypes::IsBuildingChecker::Instance()(fb.GetTypes())) + { + auto const & params = fb.GetParams(); + TEST_EQUAL(params.GetStreet(), "Airport Boulevard", ()); + TEST_EQUAL(params.GetPostcode(), "819666", ()); + ++count; + } + }); + TEST_EQUAL(count, 1, ()); + + BuildFeatures(mwmName); + BuildSearch(mwmName); + + FrozenDataSource dataSource; + auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); + CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); + + FeaturesLoaderGuard guard(dataSource, res.first); + + count = 0; + size_t const numFeatures = guard.GetNumFeatures(); + for (size_t id = 0; id < numFeatures; ++id) + { + auto ft = guard.GetFeatureByIndex(id); + if (ftypes::IsBuildingChecker::Instance()(*ft)) + { + TEST_EQUAL(ft->GetHouseNumber(), "78", ()); + TEST_EQUAL(GetPostcode(*ft), "819666", ()); + ++count; + + auto value = guard.GetHandle().GetValue(); + if (!value->m_house2street) + value->m_house2street = search::LoadHouseToStreetTable(*value); + + auto res = value->m_house2street->Get(id); + TEST(res, ()); + + auto street = guard.GetFeatureByIndex(res->m_streetId); + TEST_EQUAL(street->GetName(StringUtf8Multilang::kDefaultCode), "Airport Boulevard", ()); + } + } + + TEST_EQUAL(count, 1, ()); +} + // https://github.com/organicmaps/organicmaps/issues/4974 UNIT_TEST(Relation_Wiki) { @@ -896,8 +950,9 @@ UNIT_CLASS_TEST(TestRawGenerator, Addr_Interpolation) if (fb.GetGeomType() == feature::GeomType::Line && fb.HasType(addrType)) { ++count; - TEST_EQUAL(fb.GetParams().ref, "3602:3800", ()); - TEST_EQUAL(fb.GetAddressData().Get(feature::AddressData::Type::Street), "Juncal", ()); + auto const & params = fb.GetParams(); + TEST_EQUAL(params.ref, "3602:3800", ()); + TEST_EQUAL(params.GetStreet(), "Juncal", ()); } }); @@ -919,14 +974,15 @@ UNIT_CLASS_TEST(TestRawGenerator, NamedAddress) TEST_EQUAL(fb.GetGeomType(), feature::GeomType::Point, ()); TEST(fb.HasType(addrType), ()); - TEST(fb.GetParams().house.IsEmpty() != fb.GetName().empty(), ()); - if (fb.GetParams().house.IsEmpty()) + auto const & params = fb.GetParams(); + TEST(params.house.IsEmpty() != fb.GetName().empty(), ()); + if (params.house.IsEmpty()) ++withName; else ++withNumber; - TEST(fb.GetAddressData().Get(feature::AddressData::Type::Street).empty(), ()); - TEST_EQUAL(fb.GetAddressData().Get(feature::AddressData::Type::Postcode), "LV-5695", ()); + TEST(params.GetStreet().empty(), ()); + TEST_EQUAL(params.GetPostcode(), "LV-5695", ()); }); TEST_EQUAL(withName, 3, ()); diff --git a/generator/generator_tests_support/test_feature.cpp b/generator/generator_tests_support/test_feature.cpp index 7e5495a864..085ab50e29 100644 --- a/generator/generator_tests_support/test_feature.cpp +++ b/generator/generator_tests_support/test_feature.cpp @@ -141,7 +141,7 @@ void TestFeature::Serialize(FeatureBuilder & fb) const }); if (!m_postcode.empty()) - fb.SetPostcode(m_postcode); + fb.GetParams().SetPostcode(m_postcode); } // TestPlace ------------------------------------------------------------------------------------- @@ -309,7 +309,7 @@ void TestPOI::Serialize(FeatureBuilder & fb) const params.AddHouseNumber(m_houseNumber); if (!m_streetName.empty()) - params.AddStreet(m_streetName); + params.SetStreet(m_streetName); } string TestPOI::ToDebugString() const @@ -398,7 +398,7 @@ void TestBuilding::Serialize(FeatureBuilder & fb) const if (!m_houseNumber.empty()) params.AddHouseNumber(m_houseNumber); if (!m_streetName.empty()) - params.AddStreet(m_streetName); + params.SetStreet(m_streetName); if (m_type == 0) fb.AddType(classif().GetTypeByPath({"building"})); diff --git a/generator/osm2type.cpp b/generator/osm2type.cpp index 685a2f6494..b6552512fa 100644 --- a/generator/osm2type.cpp +++ b/generator/osm2type.cpp @@ -1069,8 +1069,9 @@ void GetNameAndType(OsmElement * p, FeatureBuilderParams & params, namesExtractor.Finish(); // Stage3: Process base feature tags. - std::string houseName, houseNumber; - TagProcessor(p).ApplyRules({ + std::string houseName, houseNumber, addrPostcode; + TagProcessor(p).ApplyRules( + { {"addr:housenumber", "*", [&houseNumber](string & k, string & v) { houseNumber = std::move(v); @@ -1085,13 +1086,21 @@ void GetNameAndType(OsmElement * p, FeatureBuilderParams & params, }}, {"addr:street", "*", [¶ms](string & k, string & v) { - params.AddStreet(v); + params.SetStreet(std::move(v)); k.clear(); v.clear(); }}, - {"addr:postcode", "*", - [¶ms](string & k, string & v) { - params.AddPostcode(v); + {"addr:postcode", "*", [&addrPostcode](string & k, string & v) + { + addrPostcode = std::move(v); + k.clear(); + v.clear(); + }}, + {"postal_code", "*", [&addrPostcode](string & k, string & v) + { + addrPostcode = std::move(v); + k.clear(); + v.clear(); }}, {"population", "*", [¶ms](string & k, string & v) { @@ -1120,6 +1129,7 @@ void GetNameAndType(OsmElement * p, FeatureBuilderParams & params, }}, }); + params.SetPostcode(addrPostcode); params.SetHouseNumberAndHouseName(std::move(houseNumber), std::move(houseName)); // Stage4: Match tags to classificator feature types via mapcss-mapping.csv. diff --git a/indexer/feature_data.cpp b/indexer/feature_data.cpp index cfa29a6262..3a1d1c73bd 100644 --- a/indexer/feature_data.cpp +++ b/indexer/feature_data.cpp @@ -523,17 +523,31 @@ uint32_t FeatureParams::GetTypeForIndex(uint32_t i) return classif().GetTypeForIndex(i); } -void FeatureBuilderParams::AddStreet(string s) +void FeatureBuilderParams::SetStreet(string s) { + if (s.empty()) + return; + // Replace \n with spaces because we write addresses to txt file. replace(s.begin(), s.end(), '\n', ' '); - m_addrTags.Add(AddressData::Type::Street, s); + m_addrTags.Set(AddressData::Type::Street, s); } -void FeatureBuilderParams::AddPostcode(string const & s) +std::string_view FeatureBuilderParams::GetStreet() const { - m_addrTags.Add(AddressData::Type::Postcode, s); + return m_addrTags.Get(AddressData::Type::Street); +} + +void FeatureBuilderParams::SetPostcode(string const & s) +{ + if (!s.empty()) + m_metadata.Set(Metadata::FMD_POSTCODE, s); +} + +std::string_view FeatureBuilderParams::GetPostcode() const +{ + return m_metadata.Get(Metadata::FMD_POSTCODE); } namespace @@ -607,8 +621,8 @@ string DebugPrint(FeatureBuilderParams const & p) { ostringstream oss; oss << "ReversedGeometry: " << (p.GetReversedGeometry() ? "true" : "false") << "; "; - oss << DebugPrint(p.GetMetadata()) << "; "; - oss << DebugPrint(p.GetAddressData()) << "; "; + oss << DebugPrint(p.m_metadata) << "; "; + oss << DebugPrint(p.m_addrTags) << "; "; oss << DebugPrint(static_cast(p)); return oss.str(); } diff --git a/indexer/feature_data.hpp b/indexer/feature_data.hpp index 8929212850..00199c4a99 100644 --- a/indexer/feature_data.hpp +++ b/indexer/feature_data.hpp @@ -7,15 +7,10 @@ #include "coding/string_utf8_multilang.hpp" #include "coding/value_opt_string.hpp" -#include "geometry/point2d.hpp" - #include #include -#include -#include #include #include -#include #include struct FeatureParamsBase; @@ -301,6 +296,8 @@ public: /// @todo Make protected and update EditableMapObject code. Types m_types; + friend std::string DebugPrint(FeatureParams const & p); + private: using Base = FeatureParamsBase; @@ -326,17 +323,23 @@ public: m_reversedGeometry = rhs.m_reversedGeometry; } - /// Used to store address to temporary TEMP_ADDR_FILE_TAG section. - void AddStreet(std::string s); - void AddPostcode(std::string const & s); + /// @name Used to store address to temporary TEMP_ADDR_FILE_TAG section. + /// @{ + void SetStreet(std::string s); + std::string_view GetStreet() const; + + template void SerializeAddress(TSink & sink) const + { + m_addrTags.SerializeForMwmTmp(sink); + } + /// @} + + void SetPostcode(std::string const & s); + std::string_view GetPostcode() const; - feature::AddressData const & GetAddressData() const { return m_addrTags; } feature::Metadata const & GetMetadata() const { return m_metadata; } feature::Metadata & GetMetadata() { return m_metadata; } - void SetMetadata(feature::Metadata && metadata) { m_metadata = std::move(metadata); } - void ClearMetadata() { SetMetadata({}); } - template void Write(Sink & sink) const { @@ -359,11 +362,10 @@ public: /// @return true If any inconsistency was found here. bool RemoveInconsistentTypes(); + friend std::string DebugPrint(FeatureBuilderParams const & p); + private: bool m_reversedGeometry = false; feature::Metadata m_metadata; feature::AddressData m_addrTags; }; - -std::string DebugPrint(FeatureParams const & p); -std::string DebugPrint(FeatureBuilderParams const & p); diff --git a/indexer/feature_meta.cpp b/indexer/feature_meta.cpp index 01e3fb98a5..5b2017cd25 100644 --- a/indexer/feature_meta.cpp +++ b/indexer/feature_meta.cpp @@ -95,11 +95,9 @@ bool Metadata::TypeFromString(string_view k, Metadata::EType & outType) outType = Metadata::FMD_TURN_LANES_BACKWARD; else if (k == "email" || k == "contact:email") outType = Metadata::FMD_EMAIL; - - // https://wiki.openstreetmap.org/wiki/Key:postal_code - else if (k == "addr:postcode" || k == "postal_code") + // Process only _main_ tag here, needed for editor ser/des. Actual postcode parsing happens in GetNameAndType. + else if (k == "addr:postcode") outType = Metadata::FMD_POSTCODE; - else if (k == "wikipedia") outType = Metadata::FMD_WIKIPEDIA; else if (k == "wikimedia_commons") @@ -259,10 +257,8 @@ string DebugPrint(Metadata const & metadata) return res; } -string DebugPrint(feature::AddressData const & addressData) +string DebugPrint(AddressData const & addressData) { - return std::string("AddressData [") - .append("Street = \"").append(addressData.Get(AddressData::Type::Street)).append("\"; ") - .append("Postcode = \"").append(addressData.Get(AddressData::Type::Postcode)).append("\"]"); + return string("AddressData { Street = \"").append(addressData.Get(AddressData::Type::Street)) + "\" }"; } } // namespace feature diff --git a/indexer/feature_meta.hpp b/indexer/feature_meta.hpp index c83ee0cc77..1cfbba4dac 100644 --- a/indexer/feature_meta.hpp +++ b/indexer/feature_meta.hpp @@ -179,12 +179,11 @@ public: enum class Type : uint8_t { Street, - Postcode }; - void Add(Type type, std::string const & s) + // Store single value only. + void Set(Type type, std::string const & s) { - // Store single value only. MetadataBase::Set(base::Underlying(type), s); } diff --git a/search/search_integration_tests/processor_test.cpp b/search/search_integration_tests/processor_test.cpp index bfdae100b2..bf7f9167b9 100644 --- a/search/search_integration_tests/processor_test.cpp +++ b/search/search_integration_tests/processor_test.cpp @@ -127,7 +127,7 @@ public: TestStreet::Serialize(fb); if (!m_street.empty()) - fb.GetParams().AddStreet(m_street); + fb.GetParams().SetStreet(m_street); } private: