diff --git a/data/editor.config b/data/editor.config index 99793c48d1..9c5d1daea3 100644 --- a/data/editor.config +++ b/data/editor.config @@ -50,6 +50,10 @@ + + + + @@ -112,6 +116,7 @@ + @@ -121,6 +126,7 @@ + diff --git a/editor/editor_config.cpp b/editor/editor_config.cpp index 54d112b0d5..39898031c2 100644 --- a/editor/editor_config.cpp +++ b/editor/editor_config.cpp @@ -11,30 +11,31 @@ namespace using EType = feature::Metadata::EType; // TODO(mgsergio): It would be nice to have this map generated from editor.config. -static unordered_map const kNamesToFMD= { - {"cuisine", feature::Metadata::FMD_CUISINE}, - {"opening_hours", feature::Metadata::FMD_OPEN_HOURS}, - {"phone", feature::Metadata::FMD_PHONE_NUMBER}, - {"fax", feature::Metadata::FMD_FAX_NUMBER}, - {"stars", feature::Metadata::FMD_STARS}, - {"operator", feature::Metadata::FMD_OPERATOR}, - // {"", feature::Metadata::FMD_URL}, - {"website", feature::Metadata::FMD_WEBSITE}, - {"internet", feature::Metadata::FMD_INTERNET}, - {"ele", feature::Metadata::FMD_ELE}, - // {"", feature::Metadata::FMD_TURN_LANES}, - // {"", feature::Metadata::FMD_TURN_LANES_FORWARD}, - // {"", feature::Metadata::FMD_TURN_LANES_BACKWARD}, - {"email", feature::Metadata::FMD_EMAIL}, - {"postcode", feature::Metadata::FMD_POSTCODE}, - {"wikipedia", feature::Metadata::FMD_WIKIPEDIA}, - // {"", feature::Metadata::FMD_MAXSPEED}, - {"flats", feature::Metadata::FMD_FLATS}, - {"height", feature::Metadata::FMD_HEIGHT}, - // {"", feature::Metadata::FMD_MIN_HEIGHT}, - {"denomination", feature::Metadata::FMD_DENOMINATION}, - {"building:levels", feature::Metadata::FMD_BUILDING_LEVELS} - // description +static unordered_map const kNamesToFMD = { + {"cuisine", feature::Metadata::FMD_CUISINE}, + {"opening_hours", feature::Metadata::FMD_OPEN_HOURS}, + {"phone", feature::Metadata::FMD_PHONE_NUMBER}, + {"fax", feature::Metadata::FMD_FAX_NUMBER}, + {"stars", feature::Metadata::FMD_STARS}, + {"operator", feature::Metadata::FMD_OPERATOR}, + // {"", feature::Metadata::FMD_URL}, + {"website", feature::Metadata::FMD_WEBSITE}, + {"internet", feature::Metadata::FMD_INTERNET}, + {"ele", feature::Metadata::FMD_ELE}, + // {"", feature::Metadata::FMD_TURN_LANES}, + // {"", feature::Metadata::FMD_TURN_LANES_FORWARD}, + // {"", feature::Metadata::FMD_TURN_LANES_BACKWARD}, + {"email", feature::Metadata::FMD_EMAIL}, + {"postcode", feature::Metadata::FMD_POSTCODE}, + {"wikipedia", feature::Metadata::FMD_WIKIPEDIA}, + // {"", feature::Metadata::FMD_MAXSPEED}, + {"flats", feature::Metadata::FMD_FLATS}, + {"height", feature::Metadata::FMD_HEIGHT}, + // {"", feature::Metadata::FMD_MIN_HEIGHT}, + {"denomination", feature::Metadata::FMD_DENOMINATION}, + {"building:levels", feature::Metadata::FMD_BUILDING_LEVELS}, + {"level", feature::Metadata::FMD_LEVEL} + // description }; unordered_map const kPriorityWeights = {{"high", 0}, {"", 1}, {"low", 2}}; diff --git a/generator/osm2meta.cpp b/generator/osm2meta.cpp index 9b76032a84..85dd448007 100644 --- a/generator/osm2meta.cpp +++ b/generator/osm2meta.cpp @@ -20,6 +20,10 @@ namespace constexpr char const * kOSMMultivalueDelimiter = ";"; +// https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_the_world +auto constexpr kMaxBuildingLevelsInTheWorld = 167; +auto constexpr kMinBuildingLevel = -6; + template void RemoveDuplicatesAndKeepOrder(vector & vec) { @@ -179,8 +183,6 @@ string MetadataTagProcessorImpl::ValidateAndFormat_height(string const & v) cons string MetadataTagProcessorImpl::ValidateAndFormat_building_levels(string v) const { - // https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_the_world - auto constexpr kMaxBuildingLevelsInTheWorld = 167; // Some mappers use full width unicode digits. We can handle that. strings::NormalizeDigits(v); char * stop; @@ -192,6 +194,22 @@ string MetadataTagProcessorImpl::ValidateAndFormat_building_levels(string v) con return {}; } +string MetadataTagProcessorImpl::ValidateAndFormat_level(string v) const +{ + // Some mappers use full width unicode digits. We can handle that. + strings::NormalizeDigits(v); + char * stop; + char const * s = v.c_str(); + double const levels = strtod(s, &stop); + if (s != stop && isfinite(levels) && levels >= kMinBuildingLevel && + levels <= kMaxBuildingLevelsInTheWorld) + { + return strings::to_string(levels); + } + + return {}; +} + string MetadataTagProcessorImpl::ValidateAndFormat_sponsored_id(string const & v) const { uint64_t id; diff --git a/generator/osm2meta.hpp b/generator/osm2meta.hpp index 1d14aa0e7e..9b8f6d82ac 100644 --- a/generator/osm2meta.hpp +++ b/generator/osm2meta.hpp @@ -30,6 +30,7 @@ struct MetadataTagProcessorImpl std::string ValidateAndFormat_internet(std::string v) const; std::string ValidateAndFormat_height(std::string const & v) const; std::string ValidateAndFormat_building_levels(std::string v) const; + std::string ValidateAndFormat_level(std::string v) const; std::string ValidateAndFormat_denomination(std::string const & v) const; std::string ValidateAndFormat_wikipedia(std::string v) const; std::string ValidateAndFormat_price_rate(std::string const & v) const; @@ -98,6 +99,7 @@ public: case Metadata::FMD_PRICE_RATE: valid = ValidateAndFormat_price_rate(v); break; case Metadata::FMD_RATING: valid = ValidateAndFormat_rating(v); break; case Metadata::FMD_BANNER_URL: valid = ValidateAndFormat_url(v); break; + case Metadata::FMD_LEVEL: valid = ValidateAndFormat_level(v); break; case Metadata::FMD_TEST_ID: case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type.")); diff --git a/indexer/editable_map_object.cpp b/indexer/editable_map_object.cpp index 0f03d30793..30982a103b 100644 --- a/indexer/editable_map_object.cpp +++ b/indexer/editable_map_object.cpp @@ -513,6 +513,11 @@ void EditableMapObject::SetBuildingLevels(string const & buildingLevels) m_metadata.Set(feature::Metadata::FMD_BUILDING_LEVELS, buildingLevels); } +void EditableMapObject::SetLevel(string const & level) +{ + m_metadata.Set(feature::Metadata::FMD_LEVEL, level); +} + LocalizedStreet const & EditableMapObject::GetStreet() const { return m_street; } void EditableMapObject::SetCuisines(vector const & cuisine) @@ -708,4 +713,27 @@ bool EditableMapObject::ValidateEmail(string const & email) return true; } + +// static +bool EditableMapObject::ValidateLevel(string const & level) +{ + if (level.empty()) + return true; + + if (level.size() > 4 /* 10.5, for example */) + return false; + + // Allowing only half-levels. + if (level.find('.') != string::npos && !strings::EndsWith(level, ".5")) + return false; + + // Forbid "04" and "0.". + if ('0' == level.front() && level.size() == 2) + return false; + + auto constexpr kMinBuildingLevel = -9; + double result; + return strings::to_double(level, result) && result > kMinBuildingLevel && + result <= kMaximumLevelsEditableByUsers; +} } // namespace osm diff --git a/indexer/editable_map_object.hpp b/indexer/editable_map_object.hpp index b27caf08ad..b75bc78a1d 100644 --- a/indexer/editable_map_object.hpp +++ b/indexer/editable_map_object.hpp @@ -142,6 +142,7 @@ public: void SetFlats(string const & flats); void SetBuildingLevels(string const & buildingLevels); + void SetLevel(string const & level); /// @param[in] cuisine is a vector of osm cuisine ids. void SetCuisines(vector const & cuisine); void SetOpeningHours(string const & openingHours); @@ -163,6 +164,7 @@ public: static bool ValidatePhone(string const & phone); static bool ValidateWebsite(string const & site); static bool ValidateEmail(string const & email); + static bool ValidateLevel(string const & level); /// Check whether langCode can be used as default name. static bool CanUseAsDefaultName(int8_t const langCode, vector const & nativeMwmLanguages); diff --git a/indexer/feature_meta.cpp b/indexer/feature_meta.cpp index e1362f671b..6b521895ac 100644 --- a/indexer/feature_meta.cpp +++ b/indexer/feature_meta.cpp @@ -96,6 +96,8 @@ bool Metadata::TypeFromString(string const & k, Metadata::EType & outType) outType = Metadata::FMD_RATING; else if (k == "banner_url") outType = Metadata::FMD_BANNER_URL; + else if (k == "level") + outType = Metadata::FMD_LEVEL; else return false; @@ -194,6 +196,7 @@ string DebugPrint(feature::Metadata::EType type) case Metadata::FMD_PRICE_RATE: return "price_rate"; case Metadata::FMD_RATING: return "rating:sponsored"; case Metadata::FMD_BANNER_URL: return "banner_url"; + case Metadata::FMD_LEVEL: return "level"; case Metadata::FMD_TEST_ID: return "test_id"; case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type.")); }; diff --git a/indexer/feature_meta.hpp b/indexer/feature_meta.hpp index c66334f0f2..443a0f4704 100644 --- a/indexer/feature_meta.hpp +++ b/indexer/feature_meta.hpp @@ -127,6 +127,7 @@ public: FMD_PRICE_RATE = 25, FMD_RATING = 26, FMD_BANNER_URL = 27, + FMD_LEVEL = 28, FMD_COUNT }; diff --git a/indexer/map_object.cpp b/indexer/map_object.cpp index 34dac74c6b..8a77c09973 100644 --- a/indexer/map_object.cpp +++ b/indexer/map_object.cpp @@ -61,6 +61,7 @@ string DebugPrint(Props props) case osm::Props::Wikipedia: k = "wikipedia"; break; case osm::Props::Flats: k = "addr:flats"; break; case osm::Props::BuildingLevels: k = "building:levels"; break; + case osm::Props::Level: k = "level"; break; } return k; } @@ -193,6 +194,8 @@ string MapObject::GetWikipediaLink() const { return m_metadata.GetWikiURL(); } string MapObject::GetFlats() const { return m_metadata.Get(feature::Metadata::FMD_FLATS); } +string MapObject::GetLevel() const { return m_metadata.Get(feature::Metadata::FMD_LEVEL); } + string MapObject::GetBuildingLevels() const { return m_metadata.Get(feature::Metadata::FMD_BUILDING_LEVELS); diff --git a/indexer/map_object.hpp b/indexer/map_object.hpp index b3b969c38c..5aedca14fa 100644 --- a/indexer/map_object.hpp +++ b/indexer/map_object.hpp @@ -46,7 +46,8 @@ enum class Props Internet, Wikipedia, Flats, - BuildingLevels + BuildingLevels, + Level }; string DebugPrint(Props props); @@ -89,6 +90,7 @@ public: string GetWikipediaLink() const; string GetFlats() const; string GetBuildingLevels() const; + string GetLevel() const; ftraits::WheelchairAvailability GetWheelchairType() const; // TODO(Vlad, yunikkk): Use Props enum + getters instead of direct metadata access. @@ -136,6 +138,7 @@ vector MetadataToProps(vector const & metadata) case Metadata::FMD_WIKIPEDIA: res.push_back(Props::Wikipedia); break; case Metadata::FMD_FLATS: res.push_back(Props::Flats); break; case Metadata::FMD_BUILDING_LEVELS: res.push_back(Props::BuildingLevels); break; + case Metadata::FMD_LEVEL: res.push_back(Props::Level); break; case Metadata::FMD_TURN_LANES: case Metadata::FMD_TURN_LANES_FORWARD: case Metadata::FMD_TURN_LANES_BACKWARD: