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

KDE's Doxygen guidelines are available online.