Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Yershov <syershov@maps.me>2016-11-30 19:11:06 +0300
committerGitHub <noreply@github.com>2016-11-30 19:11:06 +0300
commitecaaa0ceddaf1206f423e607a621bfe421770011 (patch)
tree56385847659314b8c1b79f286b3703074fe60091
parent97bd14f752a3483d9590584df3bb9ca7851f6127 (diff)
parent153e4f2b8c0ea07b90b3f72455f201754577e751 (diff)
Merge pull request #4828 from Zverik/bannersbeta-508
[banners] Add banners infrastructure
-rw-r--r--data/banners.txt10
-rw-r--r--defines.hpp1
-rw-r--r--docs/CPP_STYLE.md2
-rw-r--r--generator/osm2meta.hpp1
-rw-r--r--indexer/CMakeLists.txt2
-rw-r--r--indexer/banners.cpp224
-rw-r--r--indexer/banners.hpp59
-rw-r--r--indexer/feature_meta.cpp3
-rw-r--r--indexer/feature_meta.hpp1
-rw-r--r--indexer/indexer.pro2
-rw-r--r--indexer/indexer_tests/banners_test.cpp70
-rw-r--r--indexer/indexer_tests/indexer_tests.pro1
-rw-r--r--indexer/map_object.hpp1
-rw-r--r--map/framework.cpp4
-rw-r--r--map/framework.hpp5
-rw-r--r--map/place_page_info.cpp27
-rw-r--r--map/place_page_info.hpp4
17 files changed, 398 insertions, 19 deletions
diff --git a/data/banners.txt b/data/banners.txt
new file mode 100644
index 0000000000..f202ac1ed6
--- /dev/null
+++ b/data/banners.txt
@@ -0,0 +1,10 @@
+[deliveryclub_ru]
+start = 2016-12-01
+end = 2017-01-15
+icon =
+url = http://fas.st/VtumFw
+aux = test
+
+[lamoda_ua]
+start = 2016-12-01
+end = 2017-01-15
diff --git a/defines.hpp b/defines.hpp
index 356b33d190..ff4d6e5338 100644
--- a/defines.hpp
+++ b/defines.hpp
@@ -79,5 +79,6 @@
#define REPLACED_TAGS_FILE "replaced_tags.txt"
#define MIXED_TAGS_FILE "mixed_tags.txt"
+#define BANNERS_FILE "banners.txt"
#define LOCALIZATION_DESCRIPTION_SUFFIX " Description"
diff --git a/docs/CPP_STYLE.md b/docs/CPP_STYLE.md
index 87bf2c28b5..0f9582abca 100644
--- a/docs/CPP_STYLE.md
+++ b/docs/CPP_STYLE.md
@@ -9,6 +9,8 @@ Below are our specific (but not all!) exceptions to the Google's coding standard
- File names are lowercase with underscores, like `file_reader.cpp`.
- We use `#pragma once` instead of the `#define` Guard in header files.
- We don't include system, std and boost headers directly, use `#include "std/<wrapper.hpp>"`.
+- Includes are sorted and grouped by directory, there should be newlines between different directories.
+- Order of directories in includes: "current_dir/current_file.hpp", other includes from the same dir, includes from other dirs sorted by dependencies: `coding, geometry, base, std, "defines.hpp", 3party` should go last in that order.
- We ARE using C++ exceptions.
- We are using all features of C++11 (the only known exception is thread_local which is not fully supported on all platforms).
- We don't use boost libraries which require linking (and prefer C++11 types over their boost counterparts).
diff --git a/generator/osm2meta.hpp b/generator/osm2meta.hpp
index f187a769f8..d455aa8ed2 100644
--- a/generator/osm2meta.hpp
+++ b/generator/osm2meta.hpp
@@ -97,6 +97,7 @@ public:
case Metadata::FMD_SPONSORED_ID: valid = ValidateAndFormat_sponsored_id(v); break;
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_TEST_ID:
case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type."));
diff --git a/indexer/CMakeLists.txt b/indexer/CMakeLists.txt
index e239721de0..3ae176faf9 100644
--- a/indexer/CMakeLists.txt
+++ b/indexer/CMakeLists.txt
@@ -6,6 +6,8 @@ set(
SRC
altitude_loader.cpp
altitude_loader.hpp
+ banners.cpp
+ banners.hpp
categories_holder_loader.cpp
categories_holder.cpp
categories_holder.hpp
diff --git a/indexer/banners.cpp b/indexer/banners.cpp
new file mode 100644
index 0000000000..509d069f4b
--- /dev/null
+++ b/indexer/banners.cpp
@@ -0,0 +1,224 @@
+#include "indexer/banners.hpp"
+#include "indexer/classificator.hpp"
+#include "indexer/feature.hpp"
+
+#include "platform/platform.hpp"
+
+#include "coding/reader_streambuf.hpp"
+
+#include "base/stl_add.hpp"
+#include "base/string_utils.hpp"
+
+#include "defines.hpp"
+
+namespace
+{
+time_t constexpr kEternity = 0;
+
+// Convert ISO date to unix time.
+bool StringToTimestamp(string const & s, time_t & result)
+{
+ istringstream is(s);
+ tm time;
+ is >> get_time(&time, "%F");
+ CHECK(!is.fail(), ("Wrong date format:", s, "(expecting YYYY-MM-DD)"));
+
+ time.tm_sec = time.tm_min = time.tm_hour = 0;
+
+ time_t timestamp = mktime(&time);
+ if (timestamp < 0)
+ return false;
+
+ result = timestamp;
+ return true;
+}
+} // namespace
+
+namespace banner
+{
+string Banner::GetProperty(string const & name) const
+{
+ if (name == "lang")
+ {
+ return "ru"; // TODO(@zverik): this variable, {mwmlang}, {country} etc.
+ }
+ else
+ {
+ auto const property = m_properties.find(name);
+ if (property != m_properties.end())
+ return property->second;
+ }
+ return {};
+}
+
+Banner::Banner(string const & id) : m_id(id)
+{
+ m_messageBase = "banner_" + id;
+ m_iconName = m_messageBase + ".png";
+ m_defaultUrl = "";
+ m_activeAfter = time(nullptr);
+ m_activeBefore = kEternity;
+}
+
+bool Banner::IsActive() const
+{
+ if (IsEmpty())
+ return false;
+ time_t const now = time(nullptr);
+ return now >= m_activeAfter && (m_activeBefore == kEternity || now < m_activeBefore);
+}
+
+void Banner::SetProperty(string const & name, string const & value)
+{
+ if (name == "messages")
+ {
+ m_messageBase = value;
+ }
+ else if (name == "icon")
+ {
+ m_iconName = value;
+ }
+ else if (name == "url")
+ {
+ CHECK(strings::StartsWith(value, "http://") || strings::StartsWith(value, "https://"),
+ ("URL without a protocol for banner", m_id));
+ m_defaultUrl = value;
+ }
+ else if (name == "start")
+ {
+ CHECK(StringToTimestamp(value, m_activeAfter), ("Wrong start date", value, "for banner", m_id));
+ }
+ else if (name == "end")
+ {
+ CHECK(StringToTimestamp(value, m_activeBefore), ("Wrong end date", value, "for banner", m_id));
+ m_activeBefore += 24 * 60 * 60; // Add a day so we don't miss one
+ }
+ else
+ {
+ m_properties.emplace(make_pair(name, value));
+ }
+}
+
+string Banner::GetFormattedUrl(string const & url) const
+{
+ string baseUrl = url.empty() ? m_defaultUrl : url;
+ auto start = baseUrl.find('{');
+ while (start != string::npos)
+ {
+ auto end = baseUrl.find('}', start + 1);
+ if (end == string::npos)
+ break;
+ string value = GetProperty(baseUrl.substr(start + 1, end - start - 1));
+ if (!value.empty())
+ {
+ baseUrl.replace(start, end - start + 1, value);
+ end -= end - start + 1 - value.length();
+ }
+ start = baseUrl.find('{', end + 1);
+ }
+ return baseUrl;
+}
+
+void BannerSet::ReadBanners(istream & s)
+{
+ m_banners.clear();
+
+ Banner banner;
+ string type;
+ int lineNumber = 1;
+ for (string line; getline(s, line); ++lineNumber)
+ {
+ strings::Trim(line);
+ if (line.empty() || line.front() == '#')
+ continue;
+
+ auto const equals = line.find('=');
+ if (equals == string::npos)
+ {
+ // Section header, should be in square brackets.
+ CHECK(line.front() == '[' && line.back() == ']', ("Unknown syntax at line", lineNumber));
+ strings::Trim(line, " \t[]");
+ CHECK(!line.empty(), ("Empty banner ID at line", lineNumber));
+ if (!banner.IsEmpty())
+ Add(banner, type);
+ banner = Banner(line);
+ type = "sponsored-banner-" + line;
+ }
+ else
+ {
+ // Variable definition, must be inside a section.
+ CHECK(!banner.IsEmpty(), ("Variable definition outside a section at line", lineNumber));
+ string name = line.substr(0, equals);
+ string value = line.substr(equals + 1);
+ strings::Trim(name);
+ CHECK(!name.empty(), ("Empty variable name at line", lineNumber));
+ strings::Trim(value);
+ if (name == "type")
+ type = value;
+ else
+ banner.SetProperty(name, value);
+ }
+ }
+ if (!banner.IsEmpty())
+ Add(banner, type);
+}
+
+void BannerSet::Add(Banner const & banner, string const & type)
+{
+ vector<string> v;
+ strings::Tokenize(type, "-", MakeBackInsertFunctor(v));
+ uint32_t const ctype = classif().GetTypeByPathSafe(v);
+ if (ctype == 0)
+ {
+ LOG(LWARNING, ("Missing type", type, "for a banner"));
+ }
+ else
+ {
+ CHECK(m_banners.find(ctype) == m_banners.end(), ("Duplicate banner type", type));
+ m_banners.emplace(make_pair(ctype, banner));
+ }
+}
+
+bool BannerSet::HasBannerForType(uint32_t type) const
+{
+ return m_banners.find(type) != m_banners.end();
+}
+
+Banner const & BannerSet::GetBannerForType(uint32_t type) const
+{
+ auto const result = m_banners.find(type);
+ CHECK(result != m_banners.end(), ("GetBannerForType() for absent banner"));
+ return result->second;
+}
+
+bool BannerSet::HasBannerForFeature(FeatureType const & ft) const
+{
+ bool result = false;
+ ft.ForEachType([this, &result](uint32_t type)
+ {
+ if (!result && HasBannerForType(type))
+ result = true;
+ });
+ return result;
+}
+
+Banner const & BannerSet::GetBannerForFeature(FeatureType const & ft) const
+{
+ vector<uint32_t> types;
+ ft.ForEachType([this, &types](uint32_t type)
+ {
+ if (types.empty() && HasBannerForType(type))
+ types.push_back(type);
+ });
+ CHECK(!types.empty(), ("No banners for the feature", ft));
+ return GetBannerForType(types.front());
+}
+
+void BannerSet::LoadBanners()
+{
+ auto reader = GetPlatform().GetReader(BANNERS_FILE);
+ ReaderStreamBuf buffer(move(reader));
+ istream s(&buffer);
+ ReadBanners(s);
+}
+} // namespace banner
diff --git a/indexer/banners.hpp b/indexer/banners.hpp
new file mode 100644
index 0000000000..9fb280e49a
--- /dev/null
+++ b/indexer/banners.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "std/ctime.hpp"
+#include "std/iostream.hpp"
+#include "std/string.hpp"
+#include "std/unordered_map.hpp"
+
+class FeatureType;
+
+namespace banner
+{
+class Banner
+{
+public:
+ Banner() = default;
+ explicit Banner(string const & id);
+
+ bool IsEmpty() const { return m_id.empty(); }
+ string GetMessageBase() const { return m_messageBase; }
+ string GetIconName() const { return m_iconName; }
+ string GetDefaultUrl() const { return m_defaultUrl; }
+ bool IsActive() const;
+
+ /// Replaces inline variables in the URL, uses the default banner URL if url is not specified.
+ string GetFormattedUrl(string const & url = {}) const;
+
+ /// Usually called from BannerSet.
+ void SetProperty(string const & name, string const & value);
+
+private:
+ string m_id;
+ string m_messageBase;
+ string m_iconName;
+ string m_defaultUrl;
+ time_t m_activeAfter;
+ time_t m_activeBefore;
+ unordered_map<string, string> m_properties;
+
+ string GetProperty(string const & name) const;
+};
+
+class BannerSet
+{
+public:
+ void LoadBanners();
+ void ReadBanners(istream & s);
+
+ bool HasBannerForType(uint32_t type) const;
+ Banner const & GetBannerForType(uint32_t type) const;
+
+ bool HasBannerForFeature(FeatureType const & ft) const;
+ Banner const & GetBannerForFeature(FeatureType const & ft) const;
+
+private:
+ unordered_map<uint32_t, Banner> m_banners;
+
+ void Add(Banner const & banner, string const & type);
+};
+}
diff --git a/indexer/feature_meta.cpp b/indexer/feature_meta.cpp
index 73e9451b2d..c1d34820fa 100644
--- a/indexer/feature_meta.cpp
+++ b/indexer/feature_meta.cpp
@@ -94,6 +94,8 @@ bool Metadata::TypeFromString(string const & k, Metadata::EType & outType)
outType = Metadata::FMD_PRICE_RATE;
else if (k == "rating:sponsored")
outType = Metadata::FMD_RATING;
+ else if (k == "banner_url")
+ outType = Metadata::FMD_BANNER_URL;
else
return false;
@@ -176,6 +178,7 @@ string DebugPrint(feature::Metadata::EType type)
case Metadata::FMD_SPONSORED_ID: return "ref:sponsored";
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_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 85cede6dc5..9b2a6862bd 100644
--- a/indexer/feature_meta.hpp
+++ b/indexer/feature_meta.hpp
@@ -126,6 +126,7 @@ public:
FMD_SPONSORED_ID = 24,
FMD_PRICE_RATE = 25,
FMD_RATING = 26,
+ FMD_BANNER_URL = 27,
FMD_COUNT
};
diff --git a/indexer/indexer.pro b/indexer/indexer.pro
index 57a5db455a..9b68c979fb 100644
--- a/indexer/indexer.pro
+++ b/indexer/indexer.pro
@@ -11,6 +11,7 @@ include($$ROOT_DIR/common.pri)
SOURCES += \
altitude_loader.cpp \
+ banners.cpp \
categories_holder.cpp \
categories_holder_loader.cpp \
categories_index.cpp \
@@ -64,6 +65,7 @@ SOURCES += \
HEADERS += \
altitude_loader.hpp \
+ banners.hpp \
categories_holder.hpp \
categories_index.hpp \
cell_coverer.hpp \
diff --git a/indexer/indexer_tests/banners_test.cpp b/indexer/indexer_tests/banners_test.cpp
new file mode 100644
index 0000000000..8fc773eeac
--- /dev/null
+++ b/indexer/indexer_tests/banners_test.cpp
@@ -0,0 +1,70 @@
+#include "testing/testing.hpp"
+
+#include "indexer/banners.hpp"
+#include "indexer/classificator.hpp"
+#include "indexer/classificator_loader.hpp"
+
+#include "std/iostream.hpp"
+
+using namespace banner;
+
+UNIT_TEST(Banners_Load)
+{
+ char const kBanners[] =
+ "# comment\n"
+ "[abc]\n"
+ "icon = test.png\n"
+ "start=2016-07-14\n"
+ "type= shop-clothes \n"
+ "\n"
+ "[error]\n"
+ "[re_123]\n"
+ "type=shop-shoes\n"
+ "url=http://{aux}.com\n"
+ " \t aux=\t\ttest \n"
+ "[future]\n"
+ "type=shop-wine\n"
+ "start=2028-01-01\n"
+ "end=2028-12-31\n"
+ "[final]\n"
+ "type=shop-pet\n"
+ "start=2016-07-13\n"
+ "end=2016-07-14\n"
+ "\t";
+
+ classificator::Load();
+ Classificator & c = classif();
+
+ BannerSet bs;
+ istringstream is(kBanners);
+ bs.ReadBanners(is);
+
+ TEST(bs.HasBannerForType(c.GetTypeByPath({"shop", "clothes"})), ());
+ Banner const & bannerAbc = bs.GetBannerForType(c.GetTypeByPath({"shop", "clothes"}));
+ TEST(!bannerAbc.IsEmpty(), ());
+ TEST_EQUAL(bannerAbc.GetIconName(), "test.png", ());
+ TEST_EQUAL(bannerAbc.GetMessageBase(), "banner_abc", ());
+ TEST_EQUAL(bannerAbc.GetDefaultUrl(), "", ());
+ TEST_EQUAL(bannerAbc.GetFormattedUrl("http://example.com"), "http://example.com", ());
+ TEST_EQUAL(bannerAbc.GetFormattedUrl(), "", ());
+ TEST(bannerAbc.IsActive(), ());
+
+ TEST(bs.HasBannerForType(c.GetTypeByPath({"shop", "shoes"})), ());
+ Banner const & bannerRe = bs.GetBannerForType(c.GetTypeByPath({"shop", "shoes"}));
+ TEST(!bannerRe.IsEmpty(), ());
+ TEST(bannerRe.IsActive(), ());
+ TEST_EQUAL(bannerRe.GetIconName(), "banner_re_123.png", ());
+ TEST_EQUAL(bannerRe.GetFormattedUrl(), "http://test.com", ());
+ TEST_EQUAL(bannerRe.GetFormattedUrl("http://ex.ru/{aux}?var={v}"), "http://ex.ru/test?var={v}", ());
+
+ TEST(bs.HasBannerForType(c.GetTypeByPath({"shop", "wine"})), ());
+ Banner const & bannerFuture = bs.GetBannerForType(c.GetTypeByPath({"shop", "wine"}));
+ TEST(!bannerFuture.IsEmpty(), ());
+ TEST(!bannerFuture.IsActive(), ());
+
+ TEST(bs.HasBannerForType(c.GetTypeByPath({"shop", "pet"})), ());
+ Banner const & bannerFinal = bs.GetBannerForType(c.GetTypeByPath({"shop", "pet"}));
+ TEST(!bannerFinal.IsEmpty(), ());
+ TEST(!bannerFinal.IsActive(), ());
+ TEST_EQUAL(bannerFinal.GetFormattedUrl("http://{aux}.ru"), "http://{aux}.ru", ());
+}
diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro
index 1ff165f0ac..79b5de4b4a 100644
--- a/indexer/indexer_tests/indexer_tests.pro
+++ b/indexer/indexer_tests/indexer_tests.pro
@@ -28,6 +28,7 @@ HEADERS += \
SOURCES += \
../../testing/testingmain.cpp \
+ banners_test.cpp \
categories_test.cpp \
cell_coverer_test.cpp \
cell_id_test.cpp \
diff --git a/indexer/map_object.hpp b/indexer/map_object.hpp
index e296e668b9..23c408b6ab 100644
--- a/indexer/map_object.hpp
+++ b/indexer/map_object.hpp
@@ -146,6 +146,7 @@ vector<Props> MetadataToProps(vector<T> const & metadata)
case Metadata::FMD_SPONSORED_ID:
case Metadata::FMD_PRICE_RATE:
case Metadata::FMD_RATING:
+ case Metadata::FMD_BANNER_URL:
case Metadata::FMD_TEST_ID:
case Metadata::FMD_COUNT:
break;
diff --git a/map/framework.cpp b/map/framework.cpp
index 7c70e76dcd..85446f0d11 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -387,6 +387,8 @@ Framework::Framework()
m_displayedCategories = make_unique<search::DisplayedCategories>(GetDefaultCategories());
+ m_bannerSet.LoadBanners();
+
// To avoid possible races - init country info getter once in constructor.
InitCountryInfoGetter();
LOG(LDEBUG, ("Country info getter initialized"));
@@ -844,6 +846,8 @@ void Framework::FillInfoFromFeatureType(FeatureType const & ft, place_page::Info
info.m_sponsoredDescriptionUrl = url;
}
+ if (m_bannerSet.HasBannerForFeature(ft))
+ info.m_banner = m_bannerSet.GetBannerForFeature(ft);
info.m_canEditOrAdd = featureStatus != osm::Editor::FeatureStatus::Obsolete && CanEditMap() &&
!info.IsSponsored();
diff --git a/map/framework.hpp b/map/framework.hpp
index 7c4850dfc6..e2b965113b 100644
--- a/map/framework.hpp
+++ b/map/framework.hpp
@@ -18,10 +18,11 @@
#include "drape/oglcontextfactory.hpp"
+#include "indexer/banners.hpp"
#include "indexer/data_header.hpp"
+#include "indexer/index_helpers.hpp"
#include "indexer/map_style.hpp"
#include "indexer/new_feature_categories.hpp"
-#include "indexer/index_helpers.hpp"
#include "editor/user_stats.hpp"
@@ -168,6 +169,8 @@ protected:
unique_ptr<BookingApi> m_bookingApi = make_unique<BookingApi>();
unique_ptr<uber::Api> m_uberApi = make_unique<uber::Api>();
+ banner::BannerSet m_bannerSet;
+
df::DrapeApi m_drapeApi;
bool m_isRenderingEnabled;
diff --git a/map/place_page_info.cpp b/map/place_page_info.cpp
index 0c0c46dc4b..d843e7a671 100644
--- a/map/place_page_info.cpp
+++ b/map/place_page_info.cpp
@@ -38,12 +38,7 @@ bool Info::HasWifi() const { return GetInternet() == osm::Internet::Wlan; }
bool Info::HasBanner() const
{
- // Dummy implementation.
- // auto const now = time(nullptr);
- // auto const bannerStartTime = strings::to_uint(m_metadata.Get(feature::Metadata::FMD_BANNER_FROM));
- // auto const bannerEndTime = strings::to_uint(m_metadata.Get(feature::Metadata::FMD_BANNER_TO));
- // return !(now < bannerStartTime || now > bannerEndTime);
- return true;
+ return !m_banner.IsEmpty() && m_banner.IsActive();
}
string Info::FormatNewBookmarkName() const
@@ -174,30 +169,26 @@ string Info::GetApproximatePricing() const
string Info::GetBannerTitleId() const
{
- // Dummy implementation.
- //return m_metadata.Get(feature::Metadata::FMD_BANNER_ID) + "title";
- return "title";
+ CHECK(!m_banner.IsEmpty(), ());
+ return m_banner.GetMessageBase() + "_title";
}
string Info::GetBannerMessageId() const
{
- // Dummy implementation.
- //return m_metadata.Get(feature::Metadata::FMD_BANNER_ID) + "message";
- return "message";
+ CHECK(!m_banner.IsEmpty(), ());
+ return m_banner.GetMessageBase() + "_message";
}
string Info::GetBannerIconId() const
{
- // Dummy implementation.
- //return m_metadata.Get(feature::Metadata::FMD_BANNER_ID) + "icon";
- return "icon";
+ CHECK(!m_banner.IsEmpty(), ());
+ return m_banner.GetIconName();
}
string Info::GetBannerUrl() const
{
- // Dummy implementation.
- //return m_metadata.Get(feature::Metadata::FMD_BANNER_URL);
- return "url";
+ CHECK(!m_banner.IsEmpty(), ());
+ return m_banner.GetFormattedUrl(m_metadata.Get(feature::Metadata::FMD_BANNER_URL));
}
void Info::SetMercator(m2::PointD const & mercator) { m_mercator = mercator; }
diff --git a/map/place_page_info.hpp b/map/place_page_info.hpp
index 81cdb82dbd..a7ef979169 100644
--- a/map/place_page_info.hpp
+++ b/map/place_page_info.hpp
@@ -4,6 +4,7 @@
#include "storage/index.hpp"
+#include "indexer/banners.hpp"
#include "indexer/feature_data.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/map_object.hpp"
@@ -111,6 +112,9 @@ public:
string m_sponsoredUrl;
string m_sponsoredDescriptionUrl;
+ /// A banner associated with the object.
+ banner::Banner m_banner;
+
/// Which country this MapObject is in.
/// For a country point it will be set to topmost node for country.
storage::TCountryId m_countryId = storage::kInvalidCountryId;