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:
-rw-r--r--.clang-format1
-rw-r--r--base/base_tests/range_iterator_test.cpp6
-rw-r--r--base/range_iterator.hpp40
-rw-r--r--base/string_utils.hpp24
-rw-r--r--editor/editor_tests/editor_tests.pro2
-rw-r--r--editor/editor_tests/xml_feature_test.cpp52
-rw-r--r--editor/xml_feature.cpp250
-rw-r--r--editor/xml_feature.hpp38
-rw-r--r--indexer/feature.cpp93
-rw-r--r--indexer/feature.hpp9
-rw-r--r--indexer/feature_data.cpp62
-rw-r--r--indexer/feature_data.hpp3
-rw-r--r--indexer/feature_meta.cpp37
-rw-r--r--indexer/indexer_tests/feature_xml_test.cpp86
-rw-r--r--indexer/indexer_tests/indexer_tests.pro4
-rw-r--r--omim.pro2
16 files changed, 385 insertions, 324 deletions
diff --git a/.clang-format b/.clang-format
index 722aaa9f48..cc78dcce32 100644
--- a/.clang-format
+++ b/.clang-format
@@ -8,6 +8,7 @@ IndentWidth: 2
Language: Cpp
AccessModifierOffset: -2
AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
diff --git a/base/base_tests/range_iterator_test.cpp b/base/base_tests/range_iterator_test.cpp
index 29b857283d..5efc3a0c8c 100644
--- a/base/base_tests/range_iterator_test.cpp
+++ b/base/base_tests/range_iterator_test.cpp
@@ -6,7 +6,7 @@
UNIT_TEST(RangeIterator)
{
- using namespace base;
+ using namespace my;
{
vector<int> result;
@@ -37,7 +37,7 @@ UNIT_TEST(RangeIterator)
}
{
- TEST_EQUAL(std::vector<int>(MakeRangeIterator(0), MakeRangeIterator(5)),
- (std::vector<int>{0, 1, 2, 3, 4}), ());
+ TEST_EQUAL(vector<int>(MakeRangeIterator(0), MakeRangeIterator(5)),
+ (vector<int>{0, 1, 2, 3, 4}), ());
}
}
diff --git a/base/range_iterator.hpp b/base/range_iterator.hpp
index bb799aa76e..feb2572314 100644
--- a/base/range_iterator.hpp
+++ b/base/range_iterator.hpp
@@ -1,19 +1,23 @@
#include "std/iterator.hpp"
#include "std/type_traits.hpp"
-namespace base
+namespace my
{
+// RagneItrator allows to write for loops as follows:
+// for (auto const i : range(N))
+// ...
+// for (auto const i : range(M, N))
+// ...
+// And initialize stl containers like this:
+// vector<int> mySequence(MakeRangeIterator(0), MakeRangeIterator(10));
template <typename TCounter>
struct RangeIterator
{
- explicit RangeIterator(TCounter const current):
- m_current(current)
- {
- }
-
+ explicit RangeIterator(TCounter const current) : m_current(current) {}
RangeIterator & operator++() { ++m_current; return *this; }
+ RangeIterator operator++(int) { return RangeIterator(m_current++); }
RangeIterator & operator--() { --m_current; return *this; }
-
+ RangeIterator operator--(int) { return RangeIterator(m_current--); }
bool operator==(RangeIterator const & it) const { return m_current == it.m_current; }
bool operator!=(RangeIterator const & it) const { return !(*this == it); }
@@ -21,12 +25,12 @@ struct RangeIterator
TCounter m_current;
};
-} // namespace base
+} // namespace my
namespace std
{
template <typename T>
-struct iterator_traits<base::RangeIterator<T>>
+struct iterator_traits<my::RangeIterator<T>>
{
using difference_type = T;
using value_type = T;
@@ -36,17 +40,15 @@ struct iterator_traits<base::RangeIterator<T>>
};
} // namespace std
-namespace base
+namespace my
{
template <typename TCounter, bool forward>
struct RangeWrapper
{
-
using value_type = typename std::remove_cv<TCounter>::type;
using iterator_base = RangeIterator<value_type>;
- using iterator = typename std::conditional<forward,
- iterator_base,
- std::reverse_iterator<iterator_base>>::type;
+ using iterator =
+ typename std::conditional<forward, iterator_base, std::reverse_iterator<iterator_base>>::type;
RangeWrapper(TCounter const from, TCounter const to):
m_begin(from),
@@ -61,24 +63,28 @@ struct RangeWrapper
value_type const m_end;
};
+// Use this helper to iterate through 0 to `to'.
template <typename TCounter>
RangeWrapper<TCounter, true> range(TCounter const to)
{
return {{}, to};
}
+// Use this helper to iterate through `from' to `to'.
template <typename TCounter>
RangeWrapper<TCounter, true> range(TCounter const from, TCounter const to)
{
return {from, to};
}
+// Use this helper to iterate through `from' to 0.
template <typename TCounter>
-RangeWrapper<TCounter, false> reverse_range(TCounter const to)
+RangeWrapper<TCounter, false> reverse_range(TCounter const from)
{
- return {to, {}};
+ return {from, {}};
}
+// Use this helper to iterate through `from' to `to'.
template <typename TCounter>
RangeWrapper<TCounter, false> reverse_range(TCounter const from, TCounter const to)
{
@@ -90,4 +96,4 @@ RangeIterator<TCounter> MakeRangeIterator(TCounter const counter)
{
return RangeIterator<TCounter>(counter);
}
-} // namespace base
+} // namespace my
diff --git a/base/string_utils.hpp b/base/string_utils.hpp
index 723d72689e..f0cf1287da 100644
--- a/base/string_utils.hpp
+++ b/base/string_utils.hpp
@@ -110,7 +110,7 @@ public:
move();
}
- /// Use default-constructed iterator for operator == to determin an end of a token stream.
+ /// Use default-constructed iterator for operator == to determine an end of a token stream.
TokenizeIterator() = default;
/// Explicitly disabled, because we're storing iterators for string
@@ -288,16 +288,15 @@ bool IsHTML(string const & utf8);
/// Compare str1 and str2 and return if they are equal except for mismatchedSymbolsNum symbols
bool AlmostEqual(string const & str1, string const & str2, size_t mismatchedCount);
-/*
-template <typename ItT, typename DelimiterT>
-typename ItT::value_type JoinStrings(ItT begin, ItT end, DelimiterT const & delimiter)
+template <typename TIterator, typename TDelimiter>
+typename TIterator::value_type JoinStrings(TIterator begin, TIterator end,
+ TDelimiter const & delimiter)
{
- typedef typename ItT::value_type StringT;
+ if (begin == end)
+ return {};
- if (begin == end) return StringT();
-
- StringT result = *begin++;
- for (ItT it = begin; it != end; ++it)
+ auto result = *begin++;
+ for (TIterator it = begin; it != end; ++it)
{
result += delimiter;
result += *it;
@@ -306,13 +305,12 @@ typename ItT::value_type JoinStrings(ItT begin, ItT end, DelimiterT const & deli
return result;
}
-template <typename ContainerT, typename DelimiterT>
-typename ContainerT::value_type JoinStrings(ContainerT const & container,
- DelimiterT const & delimiter)
+template <typename TContainer, typename TDelimiter>
+typename TContainer::value_type JoinStrings(TContainer const & container,
+ TDelimiter const & delimiter)
{
return JoinStrings(container.begin(), container.end(), delimiter);
}
-*/
template <typename TFn>
void ForEachMatched(string const & s, regex const & regex, TFn && fn)
diff --git a/editor/editor_tests/editor_tests.pro b/editor/editor_tests/editor_tests.pro
index 9d0056090b..5d176882f1 100644
--- a/editor/editor_tests/editor_tests.pro
+++ b/editor/editor_tests/editor_tests.pro
@@ -4,7 +4,7 @@ CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../..
-DEPENDENCIES = editor base coding geometry opening_hours pugixml
+DEPENDENCIES = editor geometry coding base opening_hours pugixml
include($$ROOT_DIR/common.pri)
diff --git a/editor/editor_tests/xml_feature_test.cpp b/editor/editor_tests/xml_feature_test.cpp
index b1e6451283..5ebdf6f476 100644
--- a/editor/editor_tests/xml_feature_test.cpp
+++ b/editor/editor_tests/xml_feature_test.cpp
@@ -6,6 +6,7 @@
#include "base/timer.hpp"
+#include "std/map.hpp"
#include "std/sstream.hpp"
#include "3party/pugixml/src/pugixml.hpp"
@@ -58,9 +59,7 @@ UNIT_TEST(XMLFeature_Setters)
feature.SetHouse("10");
feature.SetTagValue("opening_hours", "Mo-Fr 08:15-17:30");
-
- feature.SetType("amenity|atm");
- feature.SetHeader(0xaf);
+ feature.SetTagValue("amenity", "atm");
stringstream sstr;
feature.Save(sstr);
@@ -88,14 +87,13 @@ UNIT_TEST(XMLFeature_Setters)
<tag
k="amenity"
v="atm" />
- <mapswithme:header>0xaf</mapswithme:header>
</node>
)";
TEST_EQUAL(sstr.str(), expectedString, ());
}
-UNIT_TEST(XMLFeatureFromXml)
+UNIT_TEST(XMLFeature_FromXml)
{
auto const srcString = R"(<?xml version="1.0"?>
<node
@@ -120,7 +118,6 @@ UNIT_TEST(XMLFeatureFromXml)
<tag
k="amenity"
v="atm" />
- <mapswithme:header>0xaf</mapswithme:header>
</node>
)";
@@ -144,8 +141,47 @@ UNIT_TEST(XMLFeatureFromXml)
TEST_EQUAL(feature.GetName("No such language"), "", ());
TEST_EQUAL(feature.GetTagValue("opening_hours"), "Mo-Fr 08:15-17:30", ());
+ TEST_EQUAL(feature.GetTagValue("amenity"), "atm", ());
TEST_EQUAL(my::TimestampToString(feature.GetModificationTime()), "2015-11-27T21:13:32Z", ());
+}
+
+UNIT_TEST(XMLFeature_ForEachName)
+{
+ auto const srcString = R"(<?xml version="1.0"?>
+<node
+ lat="55.7978998"
+ lon="37.474528"
+ timestamp="2015-11-27T21:13:32Z">
+ <tag
+ k="name"
+ v="Gorki Park" />
+ <tag
+ k="name:en"
+ v="Gorki Park" />
+ <tag
+ k="name:ru"
+ v="Парк Горького" />
+ <tag
+ k="addr:housenumber"
+ v="10" />
+ <tag
+ k="opening_hours"
+ v="Mo-Fr 08:15-17:30" />
+ <tag
+ k="amenity"
+ v="atm" />
+</node>
+)";
+
+ XMLFeature feature(srcString);
+ map<string, string> names;
+
+ feature.ForEachName([&names](string const & lang, string const & name)
+ {
+ names.emplace(lang, name);
+ });
- TEST_EQUAL(feature.GetType(), "amenity|atm", ());
- TEST_EQUAL(feature.GetHeader(), 0xaf, ());
+ TEST_EQUAL(names, (map<string, string>{
+ {"default", "Gorki Park"}, {"en", "Gorki Park"}, {"ru", "Парк Горького"}}),
+ ());
}
diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp
index c319595721..eb980ac1fe 100644
--- a/editor/xml_feature.cpp
+++ b/editor/xml_feature.cpp
@@ -1,6 +1,7 @@
#include "editor/xml_feature.hpp"
#include "base/assert.hpp"
+#include "base/macros.hpp"
#include "base/string_utils.hpp"
#include "base/timer.hpp"
@@ -9,8 +10,6 @@
#include "std/set.hpp"
#include "std/unordered_set.hpp"
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/classification.hpp>
#include <boost/functional/hash.hpp>
#include "3party/pugixml/src/pugixml.hpp"
@@ -28,11 +27,15 @@ m2::PointD PointFromLatLon(pugi::xml_node const node)
{
double lat, lon;
if (!strings::to_double(node.attribute("lat").value(), lat))
+ {
MYTHROW(editor::XMLFeatureNoLatLonError,
("Can't parse lat attribute: " + string(node.attribute("lat").value())));
+ }
if (!strings::to_double(node.attribute("lon").value(), lon))
+ {
MYTHROW(editor::XMLFeatureNoLatLonError,
("Can't parse lon attribute: " + string(node.attribute("lon").value())));
+ }
return MercatorBounds::FromLatLon(lat, lon);
}
@@ -41,192 +44,24 @@ void ValidateNode(pugi::xml_node const & node)
if (!node)
MYTHROW(editor::XMLFeatureNoNodeError, ("Document has no node"));
- try
- {
- PointFromLatLon(node);
- }
- catch(editor::XMLFeatureError const &)
- {
- throw;
- }
-
- // TODO(mgsergio): try parse header.
- if (!node.child("mapswithme:header"))
- MYTHROW(editor::XMLFeatureNoHeaderError, ("Node has no mapswithme:header child"));
+ // Check if point can be parsed. Throws if it's can't.
+ UNUSED_VALUE(PointFromLatLon(node));
if (!node.attribute("timestamp"))
MYTHROW(editor::XMLFeatureNoTimestampError, ("Node has no timestamp attribute"));
}
-
-bool IsConvertible(string const & k, string const & v)
-{
- using TStringPair = pair<string, string>;
- static unordered_set<TStringPair, boost::hash<TStringPair>> const
- convertibleTypePairs =
- {
- { "aeroway", "aerodrome" },
- { "aeroway", "airport" },
- { "amenity", "atm" },
- { "amenity", "bank" },
- { "amenity", "bar" },
- { "amenity", "bbq" },
- { "amenity", "bench" },
- { "amenity", "bicycle_rental" },
- { "amenity", "bureau_de_change" },
- { "amenity", "bus_station" },
- { "amenity", "cafe" },
- { "amenity", "car_rental" },
- { "amenity", "car_sharing" },
- { "amenity", "casino" },
- { "amenity", "cinema" },
- { "amenity", "college" },
- { "amenity", "doctors" },
- { "amenity", "drinking_water" },
- { "amenity", "embassy" },
- { "amenity", "fast_food" },
- { "amenity", "ferry_terminal" },
- { "amenity", "fire_station" },
- { "amenity", "fountain" },
- { "amenity", "fuel" },
- { "amenity", "grave_yard" },
- { "amenity", "hospital" },
- { "amenity", "hunting_stand" },
- { "amenity", "kindergarten" },
- { "amenity", "library" },
- { "amenity", "marketplace" },
- { "amenity", "nightclub" },
- { "amenity", "parking" },
- { "amenity", "pharmacy" },
- { "amenity", "place_of_worship" },
- { "amenity", "police" },
- { "amenity", "post_box" },
- { "amenity", "post_office" },
- { "amenity", "pub" },
- { "amenity", "recycling" },
- { "amenity", "restaurant" },
- { "amenity", "school" },
- { "amenity", "shelter" },
- { "amenity", "taxi" },
- { "amenity", "telephone" },
- { "amenity", "theatre" },
- { "amenity", "toilets" },
- { "amenity", "townhall" },
- { "amenity", "university" },
- { "amenity", "waste_disposal" },
- { "highway", "bus_stop" },
- { "highway", "speed_camera" },
- { "historic", "archaeological_site" },
- { "historic", "castle" },
- { "historic", "memorial" },
- { "historic", "monument" },
- { "historic", "ruins" },
- { "internet", "access" },
- { "internet", "access|wlan" },
- { "landuse", "cemetery" },
- { "leisure", "garden" },
- { "leisure", "pitch" },
- { "leisure", "playground" },
- { "leisure", "sports_centre" },
- { "leisure", "stadium" },
- { "leisure", "swimming_pool" },
- { "natural", "peak" },
- { "natural", "spring" },
- { "natural", "waterfall" },
- { "office", "company" },
- { "office", "estate_agent" },
- { "office", "government" },
- { "office", "lawyer" },
- { "office", "telecommunication" },
- { "place", "farm" },
- { "place", "hamlet" },
- { "place", "village" },
- { "railway", "halt" },
- { "railway", "station" },
- { "railway", "subway_entrance" },
- { "railway", "tram_stop" },
- { "shop", "alcohol" },
- { "shop", "bakery" },
- { "shop", "beauty" },
- { "shop", "beverages" },
- { "shop", "bicycle" },
- { "shop", "books" },
- { "shop", "butcher" },
- { "shop", "car" },
- { "shop", "car_repair" },
- { "shop", "chemist" },
- { "shop", "clothes" },
- { "shop", "computer" },
- { "shop", "confectionery" },
- { "shop", "convenience" },
- { "shop", "department_store" },
- { "shop", "doityourself" },
- { "shop", "electronics" },
- { "shop", "florist" },
- { "shop", "furniture" },
- { "shop", "garden_centre" },
- { "shop", "gift" },
- { "shop", "greengrocer" },
- { "shop", "hairdresser" },
- { "shop", "hardware" },
- { "shop", "jewelry" },
- { "shop", "kiosk" },
- { "shop", "laundry" },
- { "shop", "mall" },
- { "shop", "mobile_phone" },
- { "shop", "optician" },
- { "shop", "shoes" },
- { "shop", "sports" },
- { "shop", "supermarket" },
- { "shop", "toys" },
- { "tourism", "alpine_hut" },
- { "tourism", "artwork" },
- { "tourism", "attraction" },
- { "tourism", "camp_site" },
- { "tourism", "caravan_site" },
- { "tourism", "guest_house" },
- { "tourism", "hostel" },
- { "tourism", "hotel" },
- { "tourism", "information" },
- { "tourism", "motel" },
- { "tourism", "museum" },
- { "tourism", "picnic_site" },
- { "tourism", "viewpoint" },
- { "waterway", "waterfall" }
- };
-
- return convertibleTypePairs.find(make_pair(k, v)) != end(convertibleTypePairs);
-}
-
-pair<string, string> SplitMapsmeType(string const & type)
-{
- vector<string> parts;
- boost::split(parts, type, boost::is_any_of("|"));
- // Current implementations supports only types with one pipe.
- ASSERT(parts.size() == 2, ("Too many parts in type: " + type));
- return make_pair(parts[0], parts[1]);
-}
-
-bool ReadHexByte(string const & str, uint8_t & byte)
-{
- stringstream sstr(str);
- uint32_t result;
- sstr >> std::hex >> result;
- byte = static_cast<uint8_t>(result);
- return !sstr.fail();
-}
-
-bool WriteHexByte(uint8_t const byte, string & str)
-{
- stringstream sstr;
- sstr << std::hex << std::showbase << std::showbase << static_cast<uint32_t>(byte);
- str = sstr.str();
- return !sstr.fail();
-}
} // namespace
-
namespace editor
{
+char const * const XMLFeature::kLastModified = "timestamp";
+char const * const XMLFeature::kHouseNumber = "addr:housenumber";
+char const * const XMLFeature::kDefaultName = "name";
+char const * const XMLFeature::kLocalName = "name:";
+char const * const XMLFeature::kDefaultLang =
+ StringUtf8Multilang::GetLangByCode(StringUtf8Multilang::DEFAULT_CODE);
+;
+
XMLFeature::XMLFeature()
{
m_document.append_child("node");
@@ -256,44 +91,6 @@ void XMLFeature::Save(ostream & ost) const
m_document.save(ost, " ", pugi::format_indent_attributes);
}
-string XMLFeature::GetType() const
-{
- for (auto const tag : GetRootNode().select_nodes("tag"))
- {
- string const key = tag.node().attribute("k").value();
- string const val = tag.node().attribute("v").value();
- // Handle only the first appropriate tag in first version.
- if (IsConvertible(key, val))
- return key + '|' + val;
- }
-
- return "";
-}
-
-void XMLFeature::SetType(string const & type)
-{
- auto const p = SplitMapsmeType(type);
- if (IsConvertible(p.first, p.second))
- SetTagValue(p.first, p.second);
-}
-
-uint8_t XMLFeature::GetHeader() const
-{
- auto const node = GetRootNode().select_node("mapswithme:header");
- uint8_t result;
- ReadHexByte(node.node().text().get(), result);
- return result;
-}
-
-void XMLFeature::SetHeader(uint8_t const header)
-{
- auto node = GetRootNode().child("mapswithme:header");
- node = node ? node : GetRootNode().append_child("mapswithme:header");
- string hex;
- WriteHexByte(header, hex);
- node.text() = hex.data();
-}
-
m2::PointD XMLFeature::GetCenter() const
{
return PointFromLatLon(GetRootNode());
@@ -309,7 +106,7 @@ void XMLFeature::SetCenter(m2::PointD const & mercatorCenter)
string XMLFeature::GetName(string const & lang) const
{
- auto const suffix = lang == "default" || lang.empty() ? "" : ":" + lang;
+ auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
return GetTagValue("name" + suffix);
}
@@ -320,12 +117,12 @@ string XMLFeature::GetName(uint8_t const langCode) const
void XMLFeature::SetName(string const & name)
{
- SetName("default", name);
+ SetName(kDefaultLang, name);
}
void XMLFeature::SetName(string const & lang, string const & name)
{
- auto const suffix = lang == "default" || lang.empty() ? "" : ":" + lang;
+ auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
SetTagValue("name" + suffix, name);
}
@@ -336,23 +133,22 @@ void XMLFeature::SetName(uint8_t const langCode, string const & name)
string XMLFeature::GetHouse() const
{
- return GetTagValue("addr:housenumber");
+ return GetTagValue(kHouseNumber);
}
void XMLFeature::SetHouse(string const & house)
{
- SetTagValue("addr:housenumber", house);
+ SetTagValue(kHouseNumber, house);
}
time_t XMLFeature::GetModificationTime() const
{
- auto const node = GetRootNode();
- return my::StringToTimestamp(node.attribute("timestamp").value());
+ return my::StringToTimestamp(GetRootNode().attribute("timestamp").value());
}
void XMLFeature::SetModificationTime(time_t const time)
{
- SetAttribute("timestamp", my::TimestampToString(time));
+ SetAttribute(kLastModified, my::TimestampToString(time));
}
bool XMLFeature::HasTag(string const & key) const
@@ -402,7 +198,7 @@ void XMLFeature::SetAttribute(string const & key, string const & value)
? GetRootNode().attribute(key.data())
: GetRootNode().append_attribute(key.data());
- node = value.data();
+ node.set_value(value.data());
}
pugi::xml_node const XMLFeature::GetRootNode() const
diff --git a/editor/xml_feature.hpp b/editor/xml_feature.hpp
index d5b4369ea4..da7f12a011 100644
--- a/editor/xml_feature.hpp
+++ b/editor/xml_feature.hpp
@@ -2,18 +2,17 @@
#include "geometry/point2d.hpp"
+#include "coding/multilang_utf8_string.hpp"
+
+#include "base/string_utils.hpp"
+
#include "std/ctime.hpp"
#include "std/iostream.hpp"
-#include "std/map.hpp"
-
-#include "coding/multilang_utf8_string.hpp"
#include "3party/pugixml/src/pugixml.hpp"
-
namespace editor
{
-
DECLARE_EXCEPTION(XMLFeatureError, RootException);
DECLARE_EXCEPTION(XMLFeatureNoNodeError, XMLFeatureError);
DECLARE_EXCEPTION(XMLFeatureNoLatLonError, XMLFeatureError);
@@ -22,12 +21,18 @@ DECLARE_EXCEPTION(XMLFeatureNoHeaderError, XMLFeatureError);
class XMLFeature
{
+ static char const * const kLastModified;
+ static char const * const kHouseNumber;
+ static char const * const kDefaultName;
+ static char const * const kLocalName;
+ static char const * const kDefaultLang;
+
public:
XMLFeature();
XMLFeature(string const & xml);
XMLFeature(pugi::xml_document const & xml);
XMLFeature(pugi::xml_node const & xml);
-
+ XMLFeature(XMLFeature const & feature) : XMLFeature(feature.m_document) {}
void Save(ostream & ost) const;
m2::PointD GetCenter() const;
@@ -36,16 +41,23 @@ public:
string GetType() const;
void SetType(string const & type);
- uint8_t GetHeader() const;
- void SetHeader(uint8_t const header);
-
string GetName(string const & lang) const;
string GetName(uint8_t const langCode = StringUtf8Multilang::DEFAULT_CODE) const;
template <typename TFunc>
void ForEachName(TFunc && func) const
{
- //TODO(mgsergio): implement me :)
+ static auto const kPrefixLen = strlen(kLocalName);
+ auto const tags = GetRootNode().select_nodes("tag");
+ for (auto const & tag : tags)
+ {
+ string const & key = tag.node().attribute("k").value();
+
+ if (strings::StartsWith(key, kLocalName))
+ func(key.substr(kPrefixLen), tag.node().attribute("v").value());
+ else if (key == kDefaultName)
+ func(kDefaultLang, tag.node().attribute("v").value());
+ }
}
void SetName(string const & name);
@@ -62,6 +74,12 @@ public:
bool HasAttribute(string const & key) const;
bool HasKey(string const & key) const;
+ template <typename TFunc>
+ void ForEachTag(TFunc && func) const
+ {
+ // TODO(mgsergio): implement me.
+ }
+
string GetTagValue(string const & key) const;
void SetTagValue(string const & key, string const value);
diff --git a/indexer/feature.cpp b/indexer/feature.cpp
index 9c4583d6d5..124ef9e91b 100644
--- a/indexer/feature.cpp
+++ b/indexer/feature.cpp
@@ -10,8 +10,7 @@
#include "platform/preferred_languages.hpp"
-#include "defines.hpp" // just for file extensions
-
+#include "base/range_iterator.hpp"
using namespace feature;
@@ -29,6 +28,96 @@ void FeatureBase::Deserialize(feature::LoaderBase * pLoader, TBuffer buffer)
m_header = m_pLoader->GetHeader();
}
+FeatureType FeatureType::FromXML(string const & xml)
+{
+ pugi::xml_document document;
+ document.load(xml.data());
+ return FromXML(document);
+}
+
+FeatureType FeatureType::FromXML(editor::XMLFeature const & xml)
+{
+ FeatureType feature;
+ feature.m_bTrianglesParsed = false;
+ feature.m_bPointsParsed = false;
+
+ feature.m_center = xml.GetCenter();
+
+ xml.ForEachName([&feature](string const & lang, string const & name)
+ {
+ feature.m_params.name.AddString(lang, name);
+ });
+ feature.m_params.house.Set(xml.GetHouse());
+
+ // TODO(mgsergio):
+ // feature.m_params.ref =
+ // feature.m_params.layer =
+ // feature.m_params.rank =
+ feature.m_bCommonParsed = true;
+
+ // EGeomType
+
+ // for (auto const i : range(feature.GetTypesCount()))
+ // m_types[i] =
+ // Does the order matter? If so what order should be?
+ // TODO(mgsergio): Only features with single type are currently supported.
+ // TODO(mgsergio): Replace hardcode with real values.
+ feature.m_types[0] = classif().GetTypeByPath({"amenity", "atm"});
+ feature.m_bTypesParsed = true;
+
+ for (auto const i : my::range(1u, static_cast<uint32_t>(feature::Metadata::FMD_COUNT)))
+ {
+ auto const type = static_cast<feature::Metadata::EType>(i);
+ auto const attributeName = DebugPrint(type);
+ if (xml.HasTag(attributeName))
+ feature.m_metadata.Set(type, xml.GetTagValue(attributeName));
+ }
+ feature.m_bMetadataParsed = true;
+
+ // TODO(mgsergio): Get types count and GeomType from xml.
+ // Only feature::GEOM_POINT is now supported.
+ auto constexpr kOnlyOneTypeCount = 1;
+ feature.m_header = CalculateHeader(kOnlyOneTypeCount, feature::GEOM_POINT, feature.m_params);
+ feature.m_bHeader2Parsed = true;
+
+ return feature;
+}
+
+editor::XMLFeature FeatureType::ToXML() const
+{
+ editor::XMLFeature feature;
+
+ feature.SetCenter(GetCenter());
+
+ ForEachNameRef([&feature](uint8_t const & lang, string const & name)
+ {
+ feature.SetName(lang, name);
+ return true;
+ });
+
+ feature.SetHouse(GetHouseNumber());
+
+ // TODO(mgsergio):
+ // feature.m_params.ref =
+ // feature.m_params.layer =
+ // feature.m_params.rank =
+
+ // for (auto const i : range(feature.GetTypesCount()))
+ // m_types[i] =
+ // Does the order matter? If so what order should be?
+ // TODO(mgsergio): Only features with single type are currently supported.
+ ParseTypes();
+ feature.SetTagValue("amenity", "atm"); // TODO(mgsergio): Replace hardcode with real values.
+
+ for (auto const type : m_metadata.GetPresentTypes())
+ {
+ auto const attributeName = DebugPrint(type);
+ feature.SetTagValue(attributeName, m_metadata.Get(type));
+ }
+
+ return feature;
+}
+
void FeatureBase::ParseTypes() const
{
if (!m_bTypesParsed)
diff --git a/indexer/feature.hpp b/indexer/feature.hpp
index c431e129f8..693e705cfd 100644
--- a/indexer/feature.hpp
+++ b/indexer/feature.hpp
@@ -7,6 +7,8 @@
#include "base/buffer_vector.hpp"
+#include "editor/xml_feature.hpp"
+
#include "std/string.hpp"
@@ -85,7 +87,7 @@ public:
return false;
ParseCommon();
- m_params.name.ForEachRef(functor);
+ m_params.name.ForEachRef(forward<T>(functor));
return true;
}
@@ -152,6 +154,11 @@ class FeatureType : public FeatureBase
public:
void Deserialize(feature::LoaderBase * pLoader, TBuffer buffer);
+ static FeatureType FromXML(string const & xml);
+ static FeatureType FromXML(editor::XMLFeature const & xml);
+
+ editor::XMLFeature ToXML() const;
+
inline void SetID(FeatureID const & id) { m_id = id; }
inline FeatureID GetID() const { return m_id; }
diff --git a/indexer/feature_data.cpp b/indexer/feature_data.cpp
index 7292dc4109..bf9b144978 100644
--- a/indexer/feature_data.cpp
+++ b/indexer/feature_data.cpp
@@ -3,6 +3,7 @@
#include "indexer/classificator.hpp"
#include "indexer/feature.hpp"
+#include "base/assert.hpp"
#include "base/stl_add.hpp"
#include "std/bind.hpp"
@@ -97,7 +98,45 @@ public:
}
};
+} // namespace
+
+namespace feature
+{
+uint8_t CalculateHeader(uint32_t const typesCount, uint8_t const geomType,
+ FeatureParamsBase const & params)
+{
+ ASSERT(typesCount != 0, ("Feature should have at least one type."));
+ uint8_t header = static_cast<uint8_t>(typesCount - 1);
+
+ if (!params.name.IsEmpty())
+ header |= HEADER_HAS_NAME;
+
+ if (params.layer != 0)
+ header |= HEADER_HAS_LAYER;
+
+ header |= geomType;
+
+ // Geometry type for additional info is only one.
+ switch (geomType)
+ {
+ case HEADER_GEOM_POINT:
+ if (params.rank != 0)
+ header |= HEADER_HAS_ADDINFO;
+ break;
+ case HEADER_GEOM_LINE:
+ if (!params.ref.empty())
+ header |= HEADER_HAS_ADDINFO;
+ break;
+ case HEADER_GEOM_AREA:
+ case HEADER_GEOM_POINT_EX:
+ if (!params.house.IsEmpty())
+ header |= HEADER_HAS_ADDINFO;
+ break;
+ }
+
+ return header;
}
+} // namespace feature
void TypesHolder::SortBySpec()
{
@@ -447,28 +486,7 @@ bool FeatureParams::CheckValid() const
uint8_t FeatureParams::GetHeader() const
{
- uint8_t header = static_cast<uint8_t>(m_Types.size() - 1);
-
- if (!name.IsEmpty())
- header |= HEADER_HAS_NAME;
-
- if (layer != 0)
- header |= HEADER_HAS_LAYER;
-
- uint8_t const typeMask = GetTypeMask();
- header |= typeMask;
-
- // Geometry type for additional info is only one.
- switch (GetTypeMask())
- {
- case HEADER_GEOM_POINT: if (rank != 0) header |= HEADER_HAS_ADDINFO; break;
- case HEADER_GEOM_LINE: if (!ref.empty()) header |= HEADER_HAS_ADDINFO; break;
- case HEADER_GEOM_AREA:
- case HEADER_GEOM_POINT_EX:
- if (!house.IsEmpty()) header |= HEADER_HAS_ADDINFO; break;
- }
-
- return header;
+ return CalculateHeader(m_Types.size(), GetTypeMask(), *this);
}
uint32_t FeatureParams::GetIndexForType(uint32_t t)
diff --git a/indexer/feature_data.hpp b/indexer/feature_data.hpp
index 26f37d5c00..17e27a9afc 100644
--- a/indexer/feature_data.hpp
+++ b/indexer/feature_data.hpp
@@ -13,6 +13,7 @@
#include "indexer/feature_meta.hpp"
+struct FeatureParamsBase;
class FeatureBase;
namespace feature
@@ -113,6 +114,8 @@ namespace feature
{
return t.DebugPrint();
}
+ uint8_t CalculateHeader(uint32_t const typesCount, uint8_t const geomType,
+ FeatureParamsBase const & params);
}
/// Feature description struct.
diff --git a/indexer/feature_meta.cpp b/indexer/feature_meta.cpp
index 9a0902698f..ea6b559832 100644
--- a/indexer/feature_meta.cpp
+++ b/indexer/feature_meta.cpp
@@ -43,29 +43,30 @@ string Metadata::GetWikiURL() const
} // namespace feature
+// Prints types in osm-friendly format.
string DebugPrint(feature::Metadata::EType type)
{
using feature::Metadata;
switch (type)
{
- case Metadata::FMD_CUISINE: return "Cuisine";
- case Metadata::FMD_OPEN_HOURS: return "Open Hours";
- case Metadata::FMD_PHONE_NUMBER: return "Phone";
- case Metadata::FMD_FAX_NUMBER: return "Fax";
- case Metadata::FMD_STARS: return "Stars";
- case Metadata::FMD_OPERATOR: return "Operator";
- case Metadata::FMD_URL: return "URL";
- case Metadata::FMD_WEBSITE: return "Website";
- case Metadata::FMD_INTERNET: return "Internet";
- case Metadata::FMD_ELE: return "Elevation";
- case Metadata::FMD_TURN_LANES: return "Turn Lanes";
- case Metadata::FMD_TURN_LANES_FORWARD: return "Turn Lanes Forward";
- case Metadata::FMD_TURN_LANES_BACKWARD: return "Turn Lanes Backward";
- case Metadata::FMD_EMAIL: return "Email";
- case Metadata::FMD_POSTCODE: return "Postcode";
- case Metadata::FMD_WIKIPEDIA: return "Wikipedia";
- case Metadata::FMD_MAXSPEED: return "Maxspeed";
- case Metadata::FMD_FLATS: return "Flats";
+ case Metadata::FMD_CUISINE: return "cuisine";
+ case Metadata::FMD_OPEN_HOURS: return "opening_hours";
+ case Metadata::FMD_PHONE_NUMBER: return "phone";
+ case Metadata::FMD_FAX_NUMBER: return "fax";
+ case Metadata::FMD_STARS: return "stars";
+ case Metadata::FMD_OPERATOR: return "operator";
+ case Metadata::FMD_URL: return "url";
+ case Metadata::FMD_WEBSITE: return "website";
+ case Metadata::FMD_INTERNET: return "internet_access";
+ case Metadata::FMD_ELE: return "elevation";
+ case Metadata::FMD_TURN_LANES: return "turn:lanes";
+ case Metadata::FMD_TURN_LANES_FORWARD: return "turn:lanes:forward";
+ case Metadata::FMD_TURN_LANES_BACKWARD: return "turn:lanes:backward";
+ case Metadata::FMD_EMAIL: return "email";
+ case Metadata::FMD_POSTCODE: return "addr:postcode";
+ case Metadata::FMD_WIKIPEDIA: return "wikipedia";
+ case Metadata::FMD_MAXSPEED: return "maxspeed";
+ case Metadata::FMD_FLATS: return "addr:flats";
case Metadata::FMD_COUNT: CHECK(false, ("FMD_COUNT can not be used as a type."));
};
diff --git a/indexer/indexer_tests/feature_xml_test.cpp b/indexer/indexer_tests/feature_xml_test.cpp
new file mode 100644
index 0000000000..c2ccbc1c27
--- /dev/null
+++ b/indexer/indexer_tests/feature_xml_test.cpp
@@ -0,0 +1,86 @@
+#include "testing/testing.hpp"
+
+#include "indexer/classificator_loader.hpp"
+#include "indexer/feature.hpp"
+
+#include "base/string_utils.hpp"
+
+#include "std/sstream.hpp"
+
+namespace
+{
+struct TestSetUp
+{
+ TestSetUp() { classificator::Load(); }
+};
+
+TestSetUp g_testSetUp;
+
+// Sort tags and compare as strings.
+void CompareFeatureXML(string const & d1, string const & d2)
+{
+ pugi::xml_document xml1, xml2;
+ xml1.load(d1.data());
+ xml2.load(d2.data());
+
+ xml1.child("node").remove_attribute("timestamp");
+ xml2.child("node").remove_attribute("timestamp");
+
+ stringstream ss1, ss2;
+ xml1.save(ss1);
+ xml2.save(ss2);
+
+ vector<string> v1(strings::SimpleTokenizer(ss1.str(), "\n"), strings::SimpleTokenizer());
+ vector<string> v2(strings::SimpleTokenizer(ss2.str(), "\n"), strings::SimpleTokenizer());
+
+ TEST(v1.size() >= 3, ());
+ TEST(v2.size() >= 3, ());
+
+ // Sort all except <xml ...>, <node ...> and </node>
+ sort(begin(v1) + 2, end(v1) - 1);
+ sort(begin(v2) + 2, end(v2) - 1);
+
+ // Format back to string to have a nice readable error message.
+ auto s1 = strings::JoinStrings(v1, "\n");
+ auto s2 = strings::JoinStrings(v2, "\n");
+
+ TEST_EQUAL(s1, s2, ());
+}
+} // namespace
+
+UNIT_TEST(FeatureType_FromXMLAndBackToXML)
+{
+ auto const xml = R"(<?xml version="1.0"?>
+<node
+ lat="55.7978998"
+ lon="37.474528"
+ timestamp="2015-11-27T21:13:32Z">
+ <tag
+ k="name"
+ v="Gorki Park" />
+ <tag
+ k="name:en"
+ v="Gorki Park" />
+ <tag
+ k="name:ru"
+ v="Парк Горького" />
+ <tag
+ k="addr:housenumber"
+ v="10" />
+ <tag
+ k="opening_hours"
+ v="Mo-Fr 08:15-17:30" />
+ <tag
+ k="amenity"
+ v="atm" />
+</node>
+)";
+
+ auto const feature = FeatureType::FromXML(xml);
+ auto const xmlFeature = feature.ToXML();
+
+ stringstream sstr;
+ xmlFeature.Save(sstr);
+
+ CompareFeatureXML(xml, sstr.str());
+}
diff --git a/indexer/indexer_tests/indexer_tests.pro b/indexer/indexer_tests/indexer_tests.pro
index f33620ce69..e00bbcafcc 100644
--- a/indexer/indexer_tests/indexer_tests.pro
+++ b/indexer/indexer_tests/indexer_tests.pro
@@ -4,7 +4,8 @@ CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../..
-DEPENDENCIES = indexer platform geometry coding base protobuf tomcrypt pugixml succinct opening_hours
+DEPENDENCIES = indexer platform editor geometry coding base protobuf tomcrypt \
+ succinct opening_hours pugixml \
include($$ROOT_DIR/common.pri)
@@ -21,6 +22,7 @@ SOURCES += \
checker_test.cpp \
drules_selector_parser_test.cpp \
feature_metadata_test.cpp \
+ feature_xml_test.cpp \
features_offsets_table_test.cpp \
geometry_coding_test.cpp \
geometry_serialization_test.cpp \
diff --git a/omim.pro b/omim.pro
index b4e71d2e80..0e343b2fe6 100644
--- a/omim.pro
+++ b/omim.pro
@@ -93,7 +93,7 @@ SUBDIRS = 3party base coding geometry editor indexer routing search
SUBDIRS *= geometry_tests
indexer_tests.subdir = indexer/indexer_tests
- indexer_tests.depends = 3party base coding geometry indexer
+ indexer_tests.depends = 3party base coding geometry indexer editor
SUBDIRS *= indexer_tests
platform_tests.subdir = platform/platform_tests