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

xml_feature.hpp « editor - github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 01793c8d89e84a2cce90b5d0e8bfc39d41384498 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#pragma once

#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"

#include "coding/string_utf8_multilang.hpp"

#include "base/string_utils.hpp"

#include <cstdint>
#include <ctime>
#include <iostream>
#include <vector>

#include "3party/pugixml/src/pugixml.hpp"

namespace osm
{
class EditableMapObject;
}

namespace editor
{
DECLARE_EXCEPTION(XMLFeatureError, RootException);
DECLARE_EXCEPTION(InvalidXML, XMLFeatureError);
DECLARE_EXCEPTION(NoLatLon, XMLFeatureError);
DECLARE_EXCEPTION(NoXY, XMLFeatureError);
DECLARE_EXCEPTION(NoTimestamp, XMLFeatureError);
DECLARE_EXCEPTION(NoHeader, XMLFeatureError);

class XMLFeature
{
  static constexpr char const * kDefaultName = "name";
  static constexpr char const * kLocalName = "name:";
  static constexpr char const * kIntlName = "int_name";
  static constexpr char const * kAltName = "alt_name";
  static constexpr char const * kOldName = "old_name";
  static constexpr char const * kDefaultLang = "default";
  static constexpr char const * kIntlLang = kIntlName;
  static constexpr char const * kAltLang = kAltName;
  static constexpr char const * kOldLang = kOldName;

public:
  // Used in point to string serialization.
  static constexpr int kLatLonTolerance = 7;

  enum class Type
  {
    Unknown,
    Node,
    Way,
    Relation
  };

  /// Creates empty node or way.
  XMLFeature(Type const type);
  XMLFeature(std::string const & xml);
  XMLFeature(pugi::xml_document const & xml);
  XMLFeature(pugi::xml_node const & xml);
  XMLFeature(XMLFeature const & feature);

  XMLFeature & operator=(XMLFeature const & feature);

  // TODO: It should make "deep" compare instead of converting to strings.
  // Strings comparison does not work if tags order is different but tags are equal.
  bool operator==(XMLFeature const & other) const;
  /// @returns nodes, ways and relations from osmXml. Vector can be empty.
  static std::vector<XMLFeature> FromOSM(std::string const & osmXml);

  void Save(std::ostream & ost) const;
  std::string ToOSMString() const;

  /// Tags from featureWithChanges are applied to this(osm) feature.
  void ApplyPatch(XMLFeature const & featureWithChanges);

  Type GetType() const;
  std::string GetTypeString() const;

  m2::PointD GetMercatorCenter() const;
  ms::LatLon GetCenter() const;
  void SetCenter(ms::LatLon const & ll);
  void SetCenter(m2::PointD const & mercatorCenter);

  std::vector<m2::PointD> GetGeometry() const;

  /// Sets geometry in mercator to match against FeatureType's geometry in mwm
  /// when megrating to a new mwm build.
  /// Geometry points are now stored in <nd x="..." y="..." /> nodes like in osm <way>.
  /// But they are not the same as osm's. I.e. osm's one stores reference to a <node>
  /// with it's own data and lat, lon. Here we store only cooridanes in mercator.
  template <typename Iterator>
  void SetGeometry(Iterator begin, Iterator end)
  {
    ASSERT_NOT_EQUAL(GetType(), Type::Unknown, ());
    ASSERT_NOT_EQUAL(GetType(), Type::Node, ());

    for (; begin != end; ++begin)
    {
      auto nd = GetRootNode().append_child("nd");
      nd.append_attribute("x") = strings::to_string_dac(begin->x, kLatLonTolerance).data();
      nd.append_attribute("y") = strings::to_string_dac(begin->y, kLatLonTolerance).data();
    }
  }

  template <typename Collection>
  void SetGeometry(Collection const & geometry)
  {
    SetGeometry(begin(geometry), end(geometry));
  }

  std::string GetName(std::string const & lang) const;
  std::string GetName(uint8_t const langCode = StringUtf8Multilang::kDefaultCode) const;

  template <typename Fn>
  void ForEachName(Fn && func) const
  {
    static auto const kPrefixLen = strlen(kLocalName);
    auto const tags = GetRootNode().select_nodes("tag");
    for (auto const & tag : tags)
    {
      std::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());
      else if (key == kIntlName)
        func(kIntlLang, tag.node().attribute("v").value());
      else if (key == kAltName)
        func(kAltLang, tag.node().attribute("v").value());
      else if (key == kOldName)
        func(kOldLang, tag.node().attribute("v").value());
    }
  }

  void SetName(std::string const & name);
  void SetName(std::string const & lang, std::string const & name);
  void SetName(uint8_t const langCode, std::string const & name);

  std::string GetHouse() const;
  void SetHouse(std::string const & house);

  std::string GetCuisine() const;
  void SetCuisine(std::string const & cuisine);

  /// Our and OSM modification time are equal.
  time_t GetModificationTime() const;
  void SetModificationTime(time_t const time);

  /// @name XML storage format helpers.
  //@{
  uint32_t GetMWMFeatureIndex() const;
  void SetMWMFeatureIndex(uint32_t index);

  /// @returns base::INVALID_TIME_STAMP if there were no any upload attempt.
  time_t GetUploadTime() const;
  void SetUploadTime(time_t const time);

  std::string GetUploadStatus() const;
  void SetUploadStatus(std::string const & status);

  std::string GetUploadError() const;
  void SetUploadError(std::string const & error);
  //@}

  bool HasAnyTags() const;
  bool HasTag(std::string const & key) const;
  bool HasAttribute(std::string const & key) const;
  bool HasKey(std::string const & key) const;

  template <typename Fn>
  void ForEachTag(Fn && func) const
  {
    for (auto const & tag : GetRootNode().select_nodes("tag"))
      func(tag.node().attribute("k").value(), tag.node().attribute("v").value());
  }

  std::string GetTagValue(std::string const & key) const;
  void SetTagValue(std::string const & key, std::string value);

  std::string GetAttribute(std::string const & key) const;
  void SetAttribute(std::string const & key, std::string const & value);

  bool AttachToParentNode(pugi::xml_node parent) const;

  static std::string TypeToString(Type type);
  static Type StringToType(std::string const & type);

private:
  pugi::xml_node const GetRootNode() const;
  pugi::xml_node GetRootNode();

  pugi::xml_document m_document;
};

/// Rewrites all but geometry and types.
/// Should be applied to existing features only (in mwm files).
void ApplyPatch(XMLFeature const & xml, osm::EditableMapObject & object);

/// @param serializeType if false, types are not serialized.
/// Useful for applying modifications to existing OSM features, to avoid issues when someone
/// has changed a type in OSM, but our users uploaded invalid outdated type after modifying feature.
XMLFeature ToXML(osm::EditableMapObject const & object, bool serializeType);

/// Creates new feature, including geometry and types.
/// @Note: only nodes (points) are supported at the moment.
bool FromXML(XMLFeature const & xml, osm::EditableMapObject & object);

std::string DebugPrint(XMLFeature const & feature);
std::string DebugPrint(XMLFeature::Type const type);
} // namespace editor