KOSMIndoorMap

datatypes.h
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#ifndef OSM_DATATYPES_H
8#define OSM_DATATYPES_H
9
10#include "kosm_export.h"
11#include "internal.h"
12#include "languages.h"
13#include "stringpool.h"
14
15#include <QByteArray>
16#include <QDebug>
17#include <QString>
18
19#include <cstdint>
20#include <cstring>
21#include <vector>
22
23/** Low-level types and functions to work with raw OSM data as efficiently as possible. */
24namespace OSM {
25
26class DataSet;
27class Member;
28
29/** OSM element identifier. */
30typedef int64_t Id;
31
32/** Coordinate, stored as 1e7 * degree to avoid floating point precision issues,
33 * and offset to unsigned values to make the z-order curve work.
34 * Can be in an invalid state with coordinates out of range, see isValid().
35 * @see https://en.wikipedia.org/wiki/Z-order_curve for the z-order curve stuff
36 */
38public:
39 Coordinate() = default;
40 explicit constexpr Coordinate(double lat, double lon)
41 : latitude((lat + 90.0) * 10'000'000)
42 , longitude((lon + 180.0) * 10'000'000)
43 {}
44 explicit constexpr Coordinate(uint32_t lat, uint32_t lon)
45 : latitude(lat)
46 , longitude(lon)
47 {}
48
49 /** Create a coordinate from a z-order curve index. */
50 explicit constexpr Coordinate(uint64_t z)
51 : latitude(0)
52 , longitude(0)
53 {
54 for (int i = 0; i < 32; ++i) {
55 latitude += (z & (1ull << (i * 2))) >> i;
56 longitude += (z & (1ull << (1 + i * 2))) >> (i + 1);
57 }
58 }
59
60 [[nodiscard]] constexpr inline bool isValid() const
61 {
62 return latitude != std::numeric_limits<uint32_t>::max() && longitude != std::numeric_limits<uint32_t>::max();
63 }
64 [[nodiscard]] constexpr inline bool operator==(Coordinate other) const
65 {
66 return latitude == other.latitude && longitude == other.longitude;
67 }
68
69 /** Z-order curve value for this coordinate. */
70 [[nodiscard]] constexpr inline uint64_t z() const
71 {
72 uint64_t z = 0;
73 for (int i = 0; i < 32; ++i) {
74 z += ((uint64_t)latitude & (1 << i)) << i;
75 z += ((uint64_t)longitude & (1 << i)) << (i + 1);
76 }
77 return z;
78 }
79
80 [[nodiscard]] constexpr inline double latF() const
81 {
82 return (latitude / 10'000'000.0) - 90.0;
83 }
84 [[nodiscard]] constexpr inline double lonF() const
85 {
86 return (longitude / 10'000'000.0) - 180.0;
87 }
88
89 uint32_t latitude = std::numeric_limits<uint32_t>::max();
90 uint32_t longitude = std::numeric_limits<uint32_t>::max();
91};
92
93
94/** Bounding box, ie. a pair of coordinates. */
96public:
97 constexpr BoundingBox() = default;
98 constexpr inline BoundingBox(Coordinate c1, Coordinate c2)
99 : min(c1)
100 , max(c2)
101 {}
102 [[nodiscard]] constexpr inline bool isValid() const
103 {
104 return min.isValid() && max.isValid();
105 }
106 [[nodiscard]] constexpr inline bool operator==(BoundingBox other) const
107 {
108 return min == other.min && max == other.max;
109 }
110
111 [[nodiscard]] constexpr inline uint32_t width() const
112 {
113 return max.longitude - min.longitude;
114 }
115 [[nodiscard]] constexpr inline uint32_t height() const
116 {
117 return max.latitude - min.latitude;
118 }
119
120 [[nodiscard]] constexpr inline double widthF() const
121 {
122 return width() / 10'000'000.0;
123 }
124 [[nodiscard]] constexpr inline double heightF() const
125 {
126 return height() / 10'000'000.0;
127 }
128
129 [[nodiscard]] constexpr inline Coordinate center() const
130 {
131 return Coordinate(min.latitude + height() / 2, min.longitude + width() / 2);
132 }
133
134 Coordinate min;
135 Coordinate max;
136};
137
138[[nodiscard]] constexpr inline BoundingBox unite(BoundingBox bbox1, BoundingBox bbox2)
139{
140 if (!bbox1.isValid()) {
141 return bbox2;
142 }
143 if (!bbox2.isValid()) {
144 return bbox1;
145 }
146 BoundingBox ret;
147 ret.min.latitude = std::min(bbox1.min.latitude, bbox2.min.latitude);
148 ret.min.longitude = std::min(bbox1.min.longitude, bbox2.min.longitude);
149 ret.max.latitude = std::max(bbox1.max.latitude, bbox2.max.latitude);
150 ret.max.longitude = std::max(bbox1.max.longitude, bbox2.max.longitude);
151 return ret;
152}
153
154[[nodiscard]] constexpr inline bool intersects(BoundingBox bbox1, BoundingBox bbox2)
155{
156 return !(bbox2.min.latitude > bbox1.max.latitude || bbox2.max.latitude < bbox1.min.latitude
157 || bbox2.min.longitude > bbox1.max.longitude || bbox2.max.longitude < bbox1.min.longitude);
158}
159
160[[nodiscard]] constexpr inline bool contains(BoundingBox bbox, Coordinate coord)
161{
162 return bbox.min.latitude <= coord.latitude && bbox.max.latitude >= coord.latitude
163 && bbox.min.longitude <= coord.longitude && bbox.max.longitude >= coord.longitude;
164}
165
166[[nodiscard]] constexpr inline uint32_t latitudeDistance(BoundingBox bbox1, BoundingBox bbox2)
167{
168 return bbox1.max.latitude < bbox2.min.latitude ? bbox2.min.latitude - bbox1.max.latitude : bbox1.min.latitude - bbox2.max.latitude;
169}
170
171[[nodiscard]] constexpr inline uint32_t longitudeDifference(BoundingBox bbox1, BoundingBox bbox2)
172{
173 return bbox1.max.longitude < bbox2.min.longitude ? bbox2.min.longitude - bbox1.max.longitude : bbox1.min.longitude - bbox2.max.longitude;
174}
175
176/** A key of an OSM tag.
177 * See DataSet::tagKey().
178 */
179class TagKey : public StringKey {};
180
181/** An OSM element tag. */
182class Tag {
183public:
184 Tag() = default;
185 inline Tag(TagKey _key, QByteArray &&_value)
186 : key(_key)
187 , value(std::move(_value))
188 {}
189 Tag(const Tag&) = default;
190 Tag(Tag &&Tag) noexcept = default;
191 Tag& operator=(const Tag &other) = default;
192 Tag& operator=(Tag &&other) noexcept = default;
193
194 [[nodiscard]] inline constexpr bool operator<(const Tag &other) const { return key < other.key; }
195
196 TagKey key;
197 QByteArray value;
198};
199
200[[nodiscard]] inline constexpr bool operator<(const Tag &lhs, TagKey rhs) { return lhs.key < rhs; }
201[[nodiscard]] inline constexpr bool operator<(TagKey lhs, const Tag &rhs) { return lhs < rhs.key; }
202
203/** An OSM node. */
204class KOSM_EXPORT Node {
205public:
206 explicit Node() = default;
207 Node(const Node&) = default;
208 Node(Node &&other) noexcept
209 {
210 *this = std::move(other);
211 }
212 Node& operator=(const Node &other) = default;
213 Node& operator=(Node &&other) noexcept
214 {
215 id = other.id;
216 coordinate = other.coordinate;
217 std::swap(tags, other.tags);
218 return *this;
219 }
220
221 [[nodiscard]] constexpr inline bool operator<(const Node &other) const { return id < other.id; }
222
223 [[nodiscard]] QString url() const;
224
225 Id id;
226 Coordinate coordinate;
227 std::vector<Tag> tags;
228};
229
230/** An OSM way. */
231class KOSM_EXPORT Way {
232public:
233 explicit Way() = default;
234 Way(const Way&) = default;
235 Way(Way &&other) noexcept
236 {
237 *this = std::move(other);
238 }
239 Way& operator=(const Way &other) = default;
240 Way& operator=(Way &&other) noexcept
241 {
242 id = other.id;
243 bbox = other.bbox;
244 std::swap(nodes, other.nodes);
245 std::swap(tags, other.tags);
246 return *this;
247 }
248
249 constexpr inline bool operator<(const Way &other) const { return id < other.id; }
250
251 bool isClosed() const;
252
253 QString url() const;
254
255 Id id;
256 mutable BoundingBox bbox;
257 std::vector<Id> nodes;
258 std::vector<Tag> tags;
259};
260
261/** Element type. */
262enum class Type : uint8_t {
263 Null,
264 Node,
265 Way,
267};
268
269/** A relation role name key.
270 * See DataSet::role().
271 */
272class Role : public StringKey
273{
274public:
275 constexpr inline Role() = default;
276private:
277 friend class Member;
278 explicit constexpr inline Role(const char *keyData) : StringKey(keyData) {}
279};
280
281/** A member in a relation. */
282class Member {
283public:
284 [[nodiscard]] inline bool operator==(const Member &other) const { return id == other.id && m_roleAndType == other.m_roleAndType; }
285
286 Id id;
287
288 [[nodiscard]] constexpr inline Role role() const
289 {
290 return Role(m_roleAndType.get());
291 }
292 constexpr inline void setRole(Role role)
293 {
294 m_roleAndType.set(role.name());
295 }
296
297 [[nodiscard]] constexpr inline Type type() const
298 {
299 return static_cast<Type>(m_roleAndType.tag());
300 }
301 constexpr inline void setType(Type type)
302 {
303 m_roleAndType.setTag(static_cast<uint8_t>(type));
304 }
305
306private:
308};
309
310/** An OSM relation. */
311class KOSM_EXPORT Relation {
312public:
313 explicit Relation() = default;
314 Relation(const Relation&) = default;
315 Relation(Relation &&other) noexcept
316 {
317 *this = std::move(other);
318 }
319 Relation& operator=(const Relation &other) = default;
320 Relation& operator=(Relation &&other) noexcept
321 {
322 id = other.id;
323 bbox = other.bbox;
324 std::swap(members, other.members);
325 std::swap(tags, other.tags);
326 return *this;
327 }
328
329 [[nodiscard]] constexpr inline bool operator<(const Relation &other) const { return id < other.id; }
330
331 [[nodiscard]] QString url() const;
332
333 Id id;
334 mutable BoundingBox bbox;
335 std::vector<Member> members;
336 std::vector<Tag> tags;
337};
338
339/** A set of nodes, ways and relations. */
340class KOSM_EXPORT DataSet {
341public:
342 explicit DataSet();
343 DataSet(const DataSet&) = delete;
344 DataSet(DataSet &&other) noexcept;
345 ~DataSet();
346
347 DataSet& operator=(const DataSet&) = delete;
348 DataSet& operator=(DataSet &&) noexcept;
349
350 /** Find a node by its id.
351 * @returns @c nullptr if the node doesn't exist.
352 */
353 [[nodiscard]] const Node* node(Id id) const;
354
355 /** Find a way by its id.
356 * @returns @c nullptr if the way doesn't exist.
357 */
358 [[nodiscard]] const Way* way(Id id) const;
359 [[nodiscard]] Way* way(Id id);
360
361 /** Find a relation by its id.
362 * @returns @c nullptr if the relation doesn't exist.
363 */
364 [[nodiscard]] const Relation* relation(Id id) const;
365
366 void addNode(Node &&node);
367 void addWay(Way &&way);
368 void addRelation(Relation &&rel);
369
370 /** Look up a tag key for the given tag name, if it exists.
371 * If no key exists, an empty/invalid/null key is returned.
372 * Use this for tag lookup, not for creating/adding tags.
373 */
374 [[nodiscard]] TagKey tagKey(const char *keyName) const;
375
376 /** Create a tag key for the given tag name. If none exist yet a new one is created.
377 * Use this for creating tags, not for lookup, prefer tagKey() for that.
378 * @param keyMemOpt specifies whether @p keyName is persisent for the lifetime of this
379 * instance and thus can be used without requiring a copy. If the memory is transient
380 * the string is copied if needed, and released in the DataSet destructor.
381 */
382 [[nodiscard]] TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt = StringMemory::Transient);
383
384 /** Looks up a role name key.
385 * @see tagKey()
386 */
387 [[nodiscard]] Role role(const char *roleName) const;
388 /** Creates a role name key.
389 * @see makeTagKey()
390 */
391 [[nodiscard]] Role makeRole(const char *roleName, StringMemory memOpt = StringMemory::Transient);
392
393 /** Create a unique id for internal use (ie. one that will not clash with official OSM ids). */
394 [[nodiscard]] Id nextInternalId() const;
395
396 std::vector<Node> nodes;
397 std::vector<Way> ways;
398 std::vector<Relation> relations;
399
400private:
401 template <typename T> T stringKey(const char *name, const std::vector<T> &registry) const;
402 template <typename T> T makeStringKey(const char *name, StringMemory memOpt, std::vector<T> &registry);
403
404 StringKeyRegistry<TagKey> m_tagKeyRegistry;
405 StringKeyRegistry<Role> m_roleRegistry;
406};
407
408/** Returns the tag value for @p key of @p elem. */
409template <typename Elem>
410[[nodiscard]] inline QByteArray tagValue(const Elem& elem, TagKey key)
411{
412 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), key);
413 if (it != elem.tags.end() && (*it).key == key) {
414 return (*it).value;
415 }
416 return {};
417}
418
419/** Returns the tag value for key name @p keyName of @p elem.
420 * @warning This is slow due to doing a linear search and string comparissons.
421 * Where possible avoid this in favor of tagValue().
422 */
423template <typename Elem>
424[[nodiscard]] inline QByteArray tagValue(const Elem& elem, const char *keyName)
425{
426 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [keyName](const auto &tag) { return std::strcmp(tag.key.name(), keyName) == 0; });
427 if (it != elem.tags.end()) {
428 return (*it).value;
429 }
430 return {};
431}
432
433/** Returns the localized version of the tag value for key name @p keyName of @p elem.
434 * @warning This is slow due to doing a linear search and string comparissons.
435 */
436template <typename Elem>
437[[nodiscard]] inline QByteArray tagValue(const Elem& elem, const OSM::Languages &languages, const char *keyName)
438{
439 const auto keyLen = std::strlen(keyName);
440 for (const auto &lang : languages.languages) {
441 for (const auto &tag : elem.tags) {
442 if (std::strlen(tag.key.name()) != keyLen + lang.size() + 1) {
443 continue;
444 }
445 if (std::strncmp(tag.key.name(), keyName, keyLen) == 0 && tag.key.name()[keyLen] == ':'
446 && std::strncmp(tag.key.name() + keyLen + 1, lang.c_str(), lang.size()) == 0) {
447 return tag.value;
448 }
449 }
450 }
451
452 // fall back to generic value, if present
453 const auto v = tagValue(elem, keyName);
454 if (!v.isEmpty()) {
455 return v;
456 }
457
458 // check if there is at least one in any language we can use
459 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [keyName, keyLen](const auto &tag) {
460 return std::strlen(tag.key.name()) == keyLen + 3 // primitive check whether this is a plausible language rather than some other qualifier
461 && std::strncmp(tag.key.name(), keyName, keyLen) == 0
462 && tag.key.name()[keyLen] == ':';
463 });
464 if (it != elem.tags.end()) {
465 return (*it).value;
466 }
467 return {};
468}
469
470/** Inserts a new tag, or replaces an existing one with the same key. */
471template <typename Elem>
472inline void setTag(Elem &elem, Tag &&tag)
473{
474 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), tag);
475 if (it == elem.tags.end() || (*it).key != tag.key) {
476 elem.tags.insert(it, std::move(tag));
477 } else {
478 (*it) = std::move(tag);
479 }
480}
481
482/** Inserts a new tag, or updates an existing one. */
483template <typename Elem>
484inline void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
485{
486 setTag(elem, Tag(key, std::move(value)));
487}
488
489/** Removes a tag from the given element. */
490template <typename Elem>
491inline void removeTag(Elem &elem, TagKey key)
492{
493 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), key);
494 if (it != elem.tags.end() && (*it).key == key) {
495 elem.tags.erase(it);
496 }
497}
498
499template <typename Elem>
500[[nodiscard]] inline bool operator<(const Elem &elem, Id id)
501{
502 return elem.id < id;
503}
504
505}
506
507KOSM_EXPORT QDebug operator<<(QDebug debug, OSM::Coordinate coord);
508KOSM_EXPORT QDebug operator<<(QDebug debug, OSM::BoundingBox bbox);
509
510Q_DECLARE_METATYPE(OSM::BoundingBox)
511
512#endif // OSM_DATATYPES_H
qreal longitude() const
qreal latitude() const
Bounding box, ie.
Definition datatypes.h:95
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition datatypes.h:37
constexpr uint64_t z() const
Z-order curve value for this coordinate.
Definition datatypes.h:70
constexpr Coordinate(uint64_t z)
Create a coordinate from a z-order curve index.
Definition datatypes.h:50
A set of nodes, ways and relations.
Definition datatypes.h:340
Pointer with the lower bits used for compact flag storage.
Definition internal.h:17
Languages in preference order to consider when looking up translated tag values.
Definition languages.h:25
A member in a relation.
Definition datatypes.h:282
An OSM node.
Definition datatypes.h:204
An OSM relation.
Definition datatypes.h:311
A relation role name key.
Definition datatypes.h:273
Registry of unique string keys.
Definition stringpool.h:40
Base class for unique string keys.
Definition stringpool.h:72
A key of an OSM tag.
Definition datatypes.h:179
An OSM element tag.
Definition datatypes.h:182
An OSM way.
Definition datatypes.h:231
Low-level types and functions to work with raw OSM data as efficiently as possible.
void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
Inserts a new tag, or updates an existing one.
Definition datatypes.h:484
void setTag(Elem &elem, Tag &&tag)
Inserts a new tag, or replaces an existing one with the same key.
Definition datatypes.h:472
int64_t Id
OSM element identifier.
Definition datatypes.h:30
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
Definition datatypes.h:410
Type
Element type.
Definition datatypes.h:262
void removeTag(Elem &elem, TagKey key)
Removes a tag from the given element.
Definition datatypes.h:491
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:46:39 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.