1 /*
2     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "datatypes.h"
8 
9 using namespace OSM;
10 
11 DataSet::DataSet() = default;
12 DataSet::DataSet(DataSet &&) = default;
~DataSet()13 DataSet::~DataSet()
14 {
15     std::for_each(m_stringPool.begin(), m_stringPool.end(), free);
16 }
17 
18 DataSet& DataSet::operator=(DataSet &&) = default;
19 
20 template<typename T>
makeStringKey(const char * name,DataSet::StringMemory memOpt,std::vector<T> & registry)21 T DataSet::makeStringKey(const char *name, DataSet::StringMemory memOpt, std::vector<T> &registry)
22 {
23     const auto it = std::lower_bound(registry.begin(), registry.end(), name, [](T lhs, const char *rhs) {
24         return std::strcmp(lhs.name(), rhs) < 0;
25     });
26     if (it == registry.end() || std::strcmp((*it).name(), name) != 0) {
27         if (memOpt == StringIsTransient) {
28             auto s = strdup(name);
29             m_stringPool.push_back(s);
30             name = s;
31         }
32         T k(name);
33         registry.insert(it, k);
34         return k;
35     }
36     return (*it);
37 }
38 
makeTagKey(const char * keyName,DataSet::StringMemory keyMemOpt)39 TagKey DataSet::makeTagKey(const char *keyName, DataSet::StringMemory keyMemOpt)
40 {
41     return makeStringKey(keyName, keyMemOpt, m_tagKeyRegistry);
42 }
43 
makeRole(const char * roleName,DataSet::StringMemory memOpt)44 Role DataSet::makeRole(const char *roleName, DataSet::StringMemory memOpt)
45 {
46     return makeStringKey(roleName, memOpt, m_roleRegistry);
47 }
48 
49 template <typename T>
stringKey(const char * name,const std::vector<T> & registry) const50 T DataSet::stringKey(const char *name, const std::vector<T> &registry) const
51 {
52     const auto it = std::lower_bound(registry.begin(), registry.end(), name, [](T lhs, const char *rhs) {
53         return std::strcmp(lhs.name(), rhs) < 0;
54     });
55     if (it == registry.end() || std::strcmp((*it).name(), name) != 0) {
56         return {};
57     }
58     return (*it);
59 }
60 
tagKey(const char * keyName) const61 TagKey DataSet::tagKey(const char *keyName) const
62 {
63     return stringKey(keyName, m_tagKeyRegistry);
64 }
65 
role(const char * roleName) const66 Role DataSet::role(const char *roleName) const
67 {
68     return stringKey(roleName, m_roleRegistry);
69 }
70 
node(Id id) const71 const Node* DataSet::node(Id id) const
72 {
73     const auto it = std::lower_bound(nodes.begin(), nodes.end(), id);
74     if (it != nodes.end() && (*it).id == id) {
75         return &(*it);
76     }
77     return nullptr;
78 }
79 
way(Id id) const80 const Way* DataSet::way(Id id) const
81 {
82     const auto it = std::lower_bound(ways.begin(), ways.end(), id);
83     if (it != ways.end() && (*it).id == id) {
84         return &(*it);
85     }
86     return nullptr;
87 }
88 
way(Id id)89 Way* DataSet::way(Id id)
90 {
91     const auto it = std::lower_bound(ways.begin(), ways.end(), id);
92     if (it != ways.end() && (*it).id == id) {
93         return &(*it);
94     }
95     return nullptr;
96 }
97 
relation(Id id) const98 const Relation* DataSet::relation(Id id) const
99 {
100     const auto it = std::lower_bound(relations.begin(), relations.end(), id);
101     if (it != relations.end() && (*it).id == id) {
102         return &(*it);
103     }
104     return nullptr;
105 }
106 
addNode(Node && node)107 void DataSet::addNode(Node &&node)
108 {
109     const auto it = std::lower_bound(nodes.begin(), nodes.end(), node);
110     if (it != nodes.end() && (*it).id == node.id) {
111         // do we need to merge something here?
112         return;
113     }
114     nodes.insert(it, std::move(node));
115 }
116 
addWay(Way && way)117 void DataSet::addWay(Way &&way)
118 {
119     const auto it = std::lower_bound(ways.begin(), ways.end(), way);
120     if (it != ways.end() && (*it).id == way.id) {
121         // already there?
122         return;
123     }
124     ways.insert(it, std::move(way));
125 }
126 
addRelation(Relation && rel)127 void DataSet::addRelation(Relation &&rel)
128 {
129     const auto it = std::lower_bound(relations.begin(), relations.end(), rel);
130     if (it != relations.end() && (*it).id == rel.id) {
131         // do we need to merge something here?
132         return;
133     }
134     relations.insert(it, std::move(rel));
135 }
136 
nextInternalId() const137 OSM::Id DataSet::nextInternalId() const
138 {
139     static OSM::Id nextId = 0;
140     return --nextId;
141 }
142 
143 // resolve ids for elements split in Marble vector tiles
144 template <typename T>
actualIdString(const T & elem)145 static QString actualIdString(const T &elem)
146 {
147     const auto mxoid = OSM::tagValue(elem, "mx:oid");
148     return mxoid.isEmpty() ? QString::number(elem.id) : QString::fromUtf8(mxoid);
149 }
150 
url() const151 QString OSM::Node::url() const
152 {
153     return QStringLiteral("https://openstreetmap.org/node/") + actualIdString(*this);
154 }
155 
isClosed() const156 bool OSM::Way::isClosed() const
157 {
158     return nodes.size() >= 2 && nodes.front() == nodes.back();
159 }
160 
url() const161 QString OSM::Way::url() const
162 {
163     return QStringLiteral("https://openstreetmap.org/way/") + actualIdString(*this);
164 }
165 
url() const166 QString OSM::Relation::url() const
167 {
168     return QStringLiteral("https://openstreetmap.org/relation/") + actualIdString(*this);
169 }
170 
operator <<(QDebug debug,OSM::Coordinate coord)171 QDebug operator<<(QDebug debug, OSM::Coordinate coord)
172 {
173     QDebugStateSaver saver(debug);
174     debug.nospace() << '(' << coord.latF() << ',' << coord.lonF() << ')';
175     return debug;
176 }
177 
operator <<(QDebug debug,OSM::BoundingBox bbox)178 QDebug operator<<(QDebug debug, OSM::BoundingBox bbox)
179 {
180     QDebugStateSaver saver(debug);
181     debug.nospace() << '[' << bbox.min.latF() << ',' << bbox.min.lonF() << '|' << bbox.max.latF() << ',' << bbox.max.lonF() << ']';
182     return debug;
183 }
184