From 9ec64c31e4cfa10b2c4035dc77a853c14d5ea9b9 Mon Sep 17 00:00:00 2001 From: Alex Zolotarev Date: Tue, 1 Mar 2016 13:02:03 +0300 Subject: [PATCH] Base class MapObject to store feature properties and query them in UI code. --- indexer/indexer.pro | 2 + indexer/map_object.cpp | 172 +++++++++++++++++++++++++++++++++++++++++ indexer/map_object.hpp | 147 +++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 indexer/map_object.cpp create mode 100644 indexer/map_object.hpp diff --git a/indexer/indexer.pro b/indexer/indexer.pro index 57db8dd371..7040add0a4 100644 --- a/indexer/indexer.pro +++ b/indexer/indexer.pro @@ -41,6 +41,7 @@ SOURCES += \ geometry_serialization.cpp \ index.cpp \ index_builder.cpp \ + map_object.cpp \ map_style.cpp \ map_style_reader.cpp \ mwm_set.cpp \ @@ -92,6 +93,7 @@ HEADERS += \ interval_index.hpp \ interval_index_builder.hpp \ interval_index_iface.hpp \ + map_object.hpp \ map_style.hpp \ map_style_reader.hpp \ mwm_set.hpp \ diff --git a/indexer/map_object.cpp b/indexer/map_object.cpp new file mode 100644 index 0000000000..27a455b332 --- /dev/null +++ b/indexer/map_object.cpp @@ -0,0 +1,172 @@ +#include "map_object.hpp" + +#include "indexer/categories_holder.hpp" +#include "indexer/cuisines.hpp" +#include "indexer/feature.hpp" +#include "indexer/feature_algo.hpp" + +#include "platform/measurement_utils.hpp" +#include "platform/preferred_languages.hpp" + +#include "base/logging.hpp" +#include "base/string_utils.hpp" + +namespace osm +{ +namespace +{ +constexpr char const * kWlan = "wlan"; +constexpr char const * kWired = "wired"; +constexpr char const * kYes = "yes"; +constexpr char const * kNo = "no"; +} + +string DebugPrint(osm::Internet internet) +{ + switch (internet) + { + case Internet::No: return kNo; + case Internet::Yes: return kYes; + case Internet::Wlan: return kWlan; + case Internet::Wired: return kWired; + case Internet::Unknown: break; + } + return {}; +} + +string DebugPrint(Props props) +{ + string k; + switch (props) + { + case osm::Props::Phone: k = "phone"; break; + case osm::Props::Fax: k = "fax"; break; + case osm::Props::Email: k = "email"; break; + case osm::Props::Website: k = "website"; break; + case osm::Props::Internet: k = "internet_access"; break; + case osm::Props::Cuisine: k = "cuisine"; break; + case osm::Props::OpeningHours: k = "opening_hours"; break; + case osm::Props::Stars: k = "stars"; break; + case osm::Props::Operator: k = "operator"; break; + case osm::Props::Elevation: k = "ele"; break; + case osm::Props::Wikipedia: k = "wikipedia"; break; + case osm::Props::Flats: k = "addr:flats"; break; + case osm::Props::BuildingLevels: k = "building:levels"; break; + } + return k; +} + +void MapObject::SetFromFeatureType(FeatureType const & ft) +{ + m_mercator = feature::GetCenter(ft); + m_name = ft.GetNames(); + m_types = feature::TypesHolder(ft); + m_metadata = ft.GetMetadata(); + m_featureID = ft.GetID(); + ASSERT(m_featureID.IsValid(), ()); +} + +FeatureID const & MapObject::GetID() const { return m_featureID; } +ms::LatLon MapObject::GetLatLon() const { return MercatorBounds::ToLatLon(m_mercator); } +m2::PointD const & MapObject::GetMercator() const { return m_mercator; } +feature::TypesHolder const & MapObject::GetTypes() const { return m_types; } +string MapObject::GetDefaultName() const +{ + string name; + UNUSED_VALUE(m_name.GetString(StringUtf8Multilang::kDefaultCode, name)); + return name; +} + +string MapObject::GetLocalizedType() const +{ + ASSERT(!m_types.Empty(), ()); + feature::TypesHolder copy(m_types); + copy.SortBySpec(); + CategoriesHolder const & categories = GetDefaultCategories(); + return categories.GetReadableFeatureType( + *copy.begin(), categories.MapLocaleToInteger(languages::GetCurrentOrig())); +} + +vector MapObject::AvailableProperties() const +{ + return MetadataToProps(m_metadata.GetPresentTypes()); +} + +string MapObject::GetPhone() const { return m_metadata.Get(feature::Metadata::FMD_PHONE_NUMBER); } +string MapObject::GetFax() const { return m_metadata.Get(feature::Metadata::FMD_FAX_NUMBER); } +string MapObject::GetEmail() const { return m_metadata.Get(feature::Metadata::FMD_EMAIL); } +string MapObject::GetWebsite() const +{ + string website = m_metadata.Get(feature::Metadata::FMD_WEBSITE); + if (website.empty()) + website = m_metadata.Get(feature::Metadata::FMD_URL); + return website; +} + +Internet MapObject::GetInternet() const +{ + string inet = m_metadata.Get(feature::Metadata::FMD_INTERNET); + strings::AsciiToLower(inet); + // Most popular case. + if (inet.empty()) + return Internet::Unknown; + if (inet.find(kWlan) != string::npos) + return Internet::Wlan; + if (inet.find(kWired) != string::npos) + return Internet::Wired; + if (inet == kYes) + return Internet::Yes; + if (inet == kNo) + return Internet::No; + return Internet::Unknown; +} + +vector MapObject::GetCuisines() const +{ + vector localized; + Cuisines::Instance().ParseAndLocalize(m_metadata.Get(feature::Metadata::FMD_CUISINE), localized); + return localized; +} + +string MapObject::FormatCuisines() const { return strings::JoinStrings(GetCuisines(), " • "); } +string MapObject::GetOpeningHours() const +{ + return m_metadata.Get(feature::Metadata::FMD_OPEN_HOURS); +} + +string MapObject::GetOperator() const { return m_metadata.Get(feature::Metadata::FMD_OPERATOR); } +int MapObject::GetStars() const +{ + // Most popular case. + if (m_metadata.Has(feature::Metadata::FMD_STARS)) + { + int count; + if (strings::to_int(m_metadata.Get(feature::Metadata::FMD_STARS), count)) + return count; + } + return 0; +} + +string MapObject::GetElevation() const +{ + if (m_metadata.Has(feature::Metadata::FMD_ELE)) + { + double value; + if (strings::to_double(m_metadata.Get(feature::Metadata::FMD_ELE), value)) + return MeasurementUtils::FormatAltitude(value); + else + LOG(LWARNING, + ("Invalid metadata for elevation:", m_metadata.Get(feature::Metadata::FMD_ELE))); + } + return {}; +} + +string MapObject::GetWikipediaLink() const { return m_metadata.GetWikiURL(); } +string MapObject::GetFlats() const { return m_metadata.Get(feature::Metadata::FMD_FLATS); } +string MapObject::GetBuildingLevels() const +{ + return m_metadata.Get(feature::Metadata::FMD_BUILDING_LEVELS); +} + +feature::Metadata const & MapObject::GetMetadata() const { return m_metadata; } +} // namespace osm diff --git a/indexer/map_object.hpp b/indexer/map_object.hpp new file mode 100644 index 0000000000..21d64530ae --- /dev/null +++ b/indexer/map_object.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include "indexer/feature_data.hpp" +#include "indexer/feature_decl.hpp" +#include "indexer/feature_meta.hpp" + +#include "geometry/latlon.hpp" +#include "geometry/mercator.hpp" + +#include "coding/multilang_utf8_string.hpp" + +#include "base/stl_helpers.hpp" + +#include "std/string.hpp" +#include "std/vector.hpp" + +class FeatureType; + +namespace osm +{ +class EditableMapObject; +/// OSM internet_access tag values. +enum class Internet +{ + Unknown, //!< Internet state is unknown (default). + Wlan, //!< Wireless Internet access is present. + Wired, //!< Wired Internet access is present. + Yes, //!< Unspecified Internet access is available. + No //!< There is definitely no any Internet access. +}; +string DebugPrint(Internet internet); + +/// Metadata fields in the sorted order, visible to users. +enum class Props +{ + OpeningHours, + Phone, + Fax, + Website, + Email, + Cuisine, + Stars, + Operator, + Elevation, + Internet, + Wikipedia, + Flats, + BuildingLevels +}; +string DebugPrint(Props props); + +class MapObject +{ +public: + void SetFromFeatureType(FeatureType const & ft); + + FeatureID const & GetID() const; + + ms::LatLon GetLatLon() const; + m2::PointD const & GetMercator() const; + + /// @returns "the best" type to display in UI. + string GetLocalizedType() const; + feature::TypesHolder const & GetTypes() const; + string GetDefaultName() const; + + /// @name Metadata fields. + //@{ + vector AvailableProperties() const; + string GetPhone() const; + string GetFax() const; + string GetEmail() const; + string GetWebsite() const; + Internet GetInternet() const; + /// @returns translated cuisine(s). + vector GetCuisines() const; + /// @returns translated and formatted cuisines. + string FormatCuisines() const; + string GetOpeningHours() const; + string GetOperator() const; + int GetStars() const; + /// @returns formatted elevation in feet or meters, or empty string. + string GetElevation() const; + /// @returns URL to Wikipedia or empty string. + string GetWikipediaLink() const; + string GetFlats() const; + string GetBuildingLevels() const; + //@} + + // TODO(Vlad, yunikkk): Use Props enum + getters instead of direct metadata access. + // TODO: Remove this method. + feature::Metadata const & GetMetadata() const; + +protected: + FeatureID m_featureID; + m2::PointD m_mercator; + StringUtf8Multilang m_name; + feature::TypesHolder m_types; + feature::Metadata m_metadata; +}; + +/// Helper to convert internal feature::Metadata::FMD_* enum into a users-visible one. +template +vector MetadataToProps(vector const & metadata) +{ + vector res; + using feature::Metadata; + for (auto const type : metadata) + { + switch (static_cast(type)) + { + case Metadata::FMD_CUISINE: res.push_back(Props::Cuisine); break; + case Metadata::FMD_OPEN_HOURS: res.push_back(Props::OpeningHours); break; + case Metadata::FMD_PHONE_NUMBER: res.push_back(Props::Phone); break; + case Metadata::FMD_FAX_NUMBER: res.push_back(Props::Fax); break; + case Metadata::FMD_STARS: res.push_back(Props::Stars); break; + case Metadata::FMD_OPERATOR: + res.push_back(Props::Operator); + break; + // Url is not used in UI and should be matched to Website. + case Metadata::FMD_URL: + case Metadata::FMD_WEBSITE: res.push_back(Props::Website); break; + case Metadata::FMD_INTERNET: res.push_back(Props::Internet); break; + case Metadata::FMD_ELE: res.push_back(Props::Elevation); break; + case Metadata::FMD_EMAIL: res.push_back(Props::Email); break; + 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_TURN_LANES: + case Metadata::FMD_TURN_LANES_FORWARD: + case Metadata::FMD_TURN_LANES_BACKWARD: + // Postcode should be processed separately, in the address. + case Metadata::FMD_POSTCODE: + case Metadata::FMD_MAXSPEED: + case Metadata::FMD_HEIGHT: + case Metadata::FMD_MIN_HEIGHT: + case Metadata::FMD_DENOMINATION: + case Metadata::FMD_TEST_ID: + case Metadata::FMD_COUNT: + break; + // Please add new cases when compiler issues an "unhandled switch case" warning here. + } + } + my::SortUnique(res); + return res; +} +} // namespace osm