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/** Element type name. */
270[[nodiscard]] KOSM_EXPORT const char* typeName(Type type);
271
272/** A relation role name key.
273 * See DataSet::role().
274 */
275class Role : public StringKey
276{
277public:
278 constexpr inline Role() = default;
279private:
280 friend class Member;
281 explicit constexpr inline Role(const char *keyData) : StringKey(keyData) {}
282};
283
284/** A member in a relation. */
285class Member {
286public:
287 [[nodiscard]] inline bool operator==(const Member &other) const { return id == other.id && m_roleAndType == other.m_roleAndType; }
288
289 Id id;
290
291 [[nodiscard]] constexpr inline Role role() const
292 {
293 return Role(m_roleAndType.get());
294 }
295 constexpr inline void setRole(Role role)
296 {
297 m_roleAndType.set(role.name());
298 }
299
300 [[nodiscard]] constexpr inline Type type() const
301 {
302 return static_cast<Type>(m_roleAndType.tag());
303 }
304 constexpr inline void setType(Type type)
305 {
306 m_roleAndType.setTag(static_cast<uint8_t>(type));
307 }
308
309private:
311};
312
313/** An OSM relation. */
314class KOSM_EXPORT Relation {
315public:
316 explicit Relation() = default;
317 Relation(const Relation&) = default;
318 Relation(Relation &&other) noexcept
319 {
320 *this = std::move(other);
321 }
322 Relation& operator=(const Relation &other) = default;
323 Relation& operator=(Relation &&other) noexcept
324 {
325 id = other.id;
326 bbox = other.bbox;
327 std::swap(members, other.members);
328 std::swap(tags, other.tags);
329 return *this;
330 }
331
332 [[nodiscard]] constexpr inline bool operator<(const Relation &other) const { return id < other.id; }
333
334 [[nodiscard]] QString url() const;
335
336 Id id;
337 mutable BoundingBox bbox;
338 std::vector<Member> members;
339 std::vector<Tag> tags;
340};
341
342/** A set of nodes, ways and relations. */
343class KOSM_EXPORT DataSet {
344public:
345 explicit DataSet();
346 DataSet(const DataSet&) = delete;
347 DataSet(DataSet &&other) noexcept;
348 ~DataSet();
349
350 DataSet& operator=(const DataSet&) = delete;
351 DataSet& operator=(DataSet &&) noexcept;
352
353 /** Find a node by its id.
354 * @returns @c nullptr if the node doesn't exist.
355 */
356 [[nodiscard]] const Node* node(Id id) const;
357
358 /** Find a way by its id.
359 * @returns @c nullptr if the way doesn't exist.
360 */
361 [[nodiscard]] const Way* way(Id id) const;
362 [[nodiscard]] Way* way(Id id);
363
364 /** Find a relation by its id.
365 * @returns @c nullptr if the relation doesn't exist.
366 */
367 [[nodiscard]] const Relation* relation(Id id) const;
368
369 void addNode(Node &&node);
370 void addWay(Way &&way);
371 void addRelation(Relation &&rel);
372
373 /** Look up a tag key for the given tag name, if it exists.
374 * If no key exists, an empty/invalid/null key is returned.
375 * Use this for tag lookup, not for creating/adding tags.
376 */
377 [[nodiscard]] TagKey tagKey(const char *keyName) const;
378
379 /** Create a tag key for the given tag name. If none exist yet a new one is created.
380 * Use this for creating tags, not for lookup, prefer tagKey() for that.
381 * @param keyMemOpt specifies whether @p keyName is persisent for the lifetime of this
382 * instance and thus can be used without requiring a copy. If the memory is transient
383 * the string is copied if needed, and released in the DataSet destructor.
384 */
385 [[nodiscard]] TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt = StringMemory::Transient);
386
387 /** Looks up a role name key.
388 * @see tagKey()
389 */
390 [[nodiscard]] Role role(const char *roleName) const;
391 /** Creates a role name key.
392 * @see makeTagKey()
393 */
394 [[nodiscard]] Role makeRole(const char *roleName, StringMemory memOpt = StringMemory::Transient);
395
396 /** Create a unique id for internal use (ie. one that will not clash with official OSM ids). */
397 [[nodiscard]] Id nextInternalId() const;
398
399 std::vector<Node> nodes;
400 std::vector<Way> ways;
401 std::vector<Relation> relations;
402
403 // HACK this needs a proper solution for dynamic memory management eventually
404 /** Dynamically created nodes for overlays with new geometry. */
405 const std::vector<Node> *transientNodes = nullptr;
406
407private:
408 template <typename T> T stringKey(const char *name, const std::vector<T> &registry) const;
409 template <typename T> T makeStringKey(const char *name, StringMemory memOpt, std::vector<T> &registry);
410
411 StringKeyRegistry<TagKey> m_tagKeyRegistry;
412 StringKeyRegistry<Role> m_roleRegistry;
413};
414
415/** Returns the tag value for @p key of @p elem. */
416template <typename Elem>
417[[nodiscard]] inline QByteArray tagValue(const Elem& elem, TagKey key)
418{
419 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), key);
420 if (it != elem.tags.end() && (*it).key == key) {
421 return (*it).value;
422 }
423 return {};
424}
425
426/** Returns the tag value for key name @p keyName of @p elem.
427 * @warning This is slow due to doing a linear search and string comparissons.
428 * Where possible avoid this in favor of tagValue().
429 */
430template <typename Elem>
431[[nodiscard]] inline QByteArray tagValue(const Elem& elem, const char *keyName)
432{
433 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [keyName](const auto &tag) { return std::strcmp(tag.key.name(), keyName) == 0; });
434 if (it != elem.tags.end()) {
435 return (*it).value;
436 }
437 return {};
438}
439
440/** Returns the localized version of the tag value for key name @p keyName of @p elem.
441 * @warning This is slow due to doing a linear search and string comparissons.
442 */
443template <typename Elem>
444[[nodiscard]] inline QByteArray tagValue(const Elem& elem, const OSM::Languages &languages, const char *keyName)
445{
446 const auto keyLen = std::strlen(keyName);
447 for (const auto &lang : languages.languages) {
448 for (const auto &tag : elem.tags) {
449 if (std::strlen(tag.key.name()) != keyLen + lang.size() + 1) {
450 continue;
451 }
452 if (std::strncmp(tag.key.name(), keyName, keyLen) == 0 && tag.key.name()[keyLen] == ':'
453 && std::strncmp(tag.key.name() + keyLen + 1, lang.c_str(), lang.size()) == 0) {
454 return tag.value;
455 }
456 }
457 }
458
459 // fall back to generic value, if present
460 const auto v = tagValue(elem, keyName);
461 if (!v.isEmpty()) {
462 return v;
463 }
464
465 // check if there is at least one in any language we can use
466 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [keyName, keyLen](const auto &tag) {
467 return std::strlen(tag.key.name()) == keyLen + 3 // primitive check whether this is a plausible language rather than some other qualifier
468 && std::strncmp(tag.key.name(), keyName, keyLen) == 0
469 && tag.key.name()[keyLen] == ':';
470 });
471 if (it != elem.tags.end()) {
472 return (*it).value;
473 }
474 return {};
475}
476
477/** Inserts a new tag, or replaces an existing one with the same key. */
478template <typename Elem>
479inline void setTag(Elem &elem, Tag &&tag)
480{
481 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), tag);
482 if (it == elem.tags.end() || (*it).key != tag.key) {
483 elem.tags.insert(it, std::move(tag));
484 } else {
485 (*it) = std::move(tag);
486 }
487}
488
489/** Inserts a new tag, or updates an existing one. */
490template <typename Elem>
491inline void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
492{
493 setTag(elem, Tag(key, std::move(value)));
494}
495
496/** Removes a tag from the given element. */
497template <typename Elem>
498inline void removeTag(Elem &elem, TagKey key)
499{
500 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), key);
501 if (it != elem.tags.end() && (*it).key == key) {
502 elem.tags.erase(it);
503 }
504}
505
506template <typename Elem>
507[[nodiscard]] inline bool operator<(const Elem &elem, Id id)
508{
509 return elem.id < id;
510}
511
512}
513
514KOSM_EXPORT QDebug operator<<(QDebug debug, OSM::Coordinate coord);
515KOSM_EXPORT QDebug operator<<(QDebug debug, OSM::BoundingBox bbox);
516
517Q_DECLARE_METATYPE(OSM::BoundingBox)
518
519#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:343
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:285
An OSM node.
Definition datatypes.h:204
An OSM relation.
Definition datatypes.h:314
A relation role name key.
Definition datatypes.h:276
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
KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, const MovingCursor &cursor)
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:491
void setTag(Elem &elem, Tag &&tag)
Inserts a new tag, or replaces an existing one with the same key.
Definition datatypes.h:479
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:417
KOSM_EXPORT const char * typeName(Type type)
Element type name.
Definition datatypes.cpp:11
Type
Element type.
Definition datatypes.h:262
void removeTag(Elem &elem, TagKey key)
Removes a tag from the given element.
Definition datatypes.h:498
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:16:37 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.