File indexing completed on 2024-12-08 10:16:03
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #ifndef OSM_DATATYPES_H 0008 #define OSM_DATATYPES_H 0009 0010 #include "internal.h" 0011 0012 #include <QByteArray> 0013 #include <QDebug> 0014 #include <QLocale> 0015 #include <QString> 0016 0017 #include <cstdint> 0018 #include <cstring> 0019 #include <vector> 0020 0021 namespace OSM { 0022 0023 class DataSet; 0024 class Member; 0025 0026 /** OSM element identifier. */ 0027 typedef int64_t Id; 0028 0029 /** Coordinate, stored as 1e7 * degree to avoid floating point precision issues, 0030 * and offset to unsigned values to make the z-order curve work. 0031 * Can be in an invalid state with coordinates out of range, see isValid(). 0032 * @see https://en.wikipedia.org/wiki/Z-order_curve for the z-order curve stuff 0033 */ 0034 class Coordinate { 0035 public: 0036 Coordinate() = default; 0037 explicit constexpr Coordinate(double lat, double lon) 0038 : latitude((lat + 90.0) * 10'000'000) 0039 , longitude((lon + 180.0) * 10'000'000) 0040 {} 0041 explicit constexpr Coordinate(uint32_t lat, uint32_t lon) 0042 : latitude(lat) 0043 , longitude(lon) 0044 {} 0045 0046 /** Create a coordinate from a z-order curve index. */ 0047 explicit constexpr Coordinate(uint64_t z) 0048 : latitude(0) 0049 , longitude(0) 0050 { 0051 for (int i = 0; i < 32; ++i) { 0052 latitude += (z & (1ull << (i * 2))) >> i; 0053 longitude += (z & (1ull << (1 + i * 2))) >> (i + 1); 0054 } 0055 } 0056 0057 constexpr inline bool isValid() const 0058 { 0059 return latitude != std::numeric_limits<uint32_t>::max() && longitude != std::numeric_limits<uint32_t>::max(); 0060 } 0061 constexpr inline bool operator==(Coordinate other) const 0062 { 0063 return latitude == other.latitude && longitude == other.longitude; 0064 } 0065 0066 /** Z-order curve value for this coordinate. */ 0067 constexpr inline uint64_t z() const 0068 { 0069 uint64_t z = 0; 0070 for (int i = 0; i < 32; ++i) { 0071 z += ((uint64_t)latitude & (1 << i)) << i; 0072 z += ((uint64_t)longitude & (1 << i)) << (i + 1); 0073 } 0074 return z; 0075 } 0076 0077 constexpr inline double latF() const 0078 { 0079 return (latitude / 10'000'000.0) - 90.0; 0080 } 0081 constexpr inline double lonF() const 0082 { 0083 return (longitude / 10'000'000.0) - 180.0; 0084 } 0085 0086 uint32_t latitude = std::numeric_limits<uint32_t>::max(); 0087 uint32_t longitude = std::numeric_limits<uint32_t>::max(); 0088 }; 0089 0090 0091 /** Bounding box, ie. a pair of coordinates. */ 0092 class BoundingBox { 0093 public: 0094 constexpr BoundingBox() = default; 0095 constexpr inline BoundingBox(Coordinate c1, Coordinate c2) 0096 : min(c1) 0097 , max(c2) 0098 {} 0099 constexpr inline bool isValid() const 0100 { 0101 return min.isValid() && max.isValid(); 0102 } 0103 constexpr inline bool operator==(BoundingBox other) const 0104 { 0105 return min == other.min && max == other.max; 0106 } 0107 0108 constexpr inline uint32_t width() const 0109 { 0110 return max.longitude - min.longitude; 0111 } 0112 constexpr inline uint32_t height() const 0113 { 0114 return max.latitude - min.latitude; 0115 } 0116 0117 constexpr inline double widthF() const 0118 { 0119 return width() / 10'000'000.0; 0120 } 0121 constexpr inline double heightF() const 0122 { 0123 return height() / 10'000'000.0; 0124 } 0125 0126 constexpr inline Coordinate center() const 0127 { 0128 return Coordinate(min.latitude + height() / 2, min.longitude + width() / 2); 0129 } 0130 0131 Coordinate min; 0132 Coordinate max; 0133 }; 0134 0135 constexpr inline BoundingBox unite(BoundingBox bbox1, BoundingBox bbox2) 0136 { 0137 if (!bbox1.isValid()) { 0138 return bbox2; 0139 } 0140 if (!bbox2.isValid()) { 0141 return bbox1; 0142 } 0143 BoundingBox ret; 0144 ret.min.latitude = std::min(bbox1.min.latitude, bbox2.min.latitude); 0145 ret.min.longitude = std::min(bbox1.min.longitude, bbox2.min.longitude); 0146 ret.max.latitude = std::max(bbox1.max.latitude, bbox2.max.latitude); 0147 ret.max.longitude = std::max(bbox1.max.longitude, bbox2.max.longitude); 0148 return ret; 0149 } 0150 0151 constexpr inline bool intersects(BoundingBox bbox1, BoundingBox bbox2) 0152 { 0153 return !(bbox2.min.latitude > bbox1.max.latitude || bbox2.max.latitude < bbox1.min.latitude 0154 || bbox2.min.longitude > bbox1.max.longitude || bbox2.max.longitude < bbox1.min.longitude); 0155 } 0156 0157 constexpr inline bool contains(BoundingBox bbox, Coordinate coord) 0158 { 0159 return bbox.min.latitude <= coord.latitude && bbox.max.latitude >= coord.latitude 0160 && bbox.min.longitude <= coord.longitude && bbox.max.longitude >= coord.longitude; 0161 } 0162 0163 constexpr inline uint32_t latitudeDistance(BoundingBox bbox1, BoundingBox bbox2) 0164 { 0165 return bbox1.max.latitude < bbox2.min.latitude ? bbox2.min.latitude - bbox1.max.latitude : bbox1.min.latitude - bbox2.max.latitude; 0166 } 0167 0168 constexpr inline uint32_t longitudeDifference(BoundingBox bbox1, BoundingBox bbox2) 0169 { 0170 return bbox1.max.longitude < bbox2.min.longitude ? bbox2.min.longitude - bbox1.max.longitude : bbox1.min.longitude - bbox2.max.longitude; 0171 } 0172 0173 /** Base class for unique string keys. */ 0174 class StringKey 0175 { 0176 public: 0177 constexpr inline const char* name() const { return key; } 0178 constexpr inline bool isNull() const { return !key; } 0179 0180 // yes, pointer compare is enough here 0181 inline constexpr bool operator<(StringKey other) const { return key < other.key; } 0182 inline constexpr bool operator==(StringKey other) const { return key == other.key; } 0183 inline constexpr bool operator!=(StringKey other) const { return key != other.key; } 0184 0185 protected: 0186 constexpr inline StringKey() = default; 0187 explicit constexpr inline StringKey(const char *keyData) : key(keyData) {} 0188 0189 private: 0190 const char* key = nullptr; 0191 }; 0192 0193 /** A key of an OSM tag. 0194 * See DataSet::tagKey(). 0195 */ 0196 class TagKey : public StringKey 0197 { 0198 public: 0199 constexpr inline TagKey() = default; 0200 private: 0201 explicit constexpr inline TagKey(const char *keyData) : StringKey(keyData) {} 0202 friend class DataSet; 0203 }; 0204 0205 /** An OSM element tag. */ 0206 class Tag { 0207 public: 0208 inline constexpr bool operator<(const Tag &other) const { return key < other.key; } 0209 0210 TagKey key; 0211 QByteArray value; 0212 }; 0213 0214 /** An OSM node. */ 0215 class Node { 0216 public: 0217 constexpr inline bool operator<(const Node &other) const { return id < other.id; } 0218 0219 QString url() const; 0220 0221 Id id; 0222 Coordinate coordinate; 0223 std::vector<Tag> tags; 0224 }; 0225 0226 /** An OSM way. */ 0227 class Way { 0228 public: 0229 constexpr inline bool operator<(const Way &other) const { return id < other.id; } 0230 0231 bool isClosed() const; 0232 0233 QString url() const; 0234 0235 Id id; 0236 mutable BoundingBox bbox; 0237 std::vector<Id> nodes; 0238 std::vector<Tag> tags; 0239 }; 0240 0241 /** Element type. */ 0242 enum class Type : uint8_t { 0243 Null, 0244 Node, 0245 Way, 0246 Relation 0247 }; 0248 0249 /** A relation role name key. 0250 * See DataSet::role(). 0251 */ 0252 class Role : public StringKey 0253 { 0254 public: 0255 constexpr inline Role() = default; 0256 private: 0257 friend class DataSet; 0258 friend class Member; 0259 explicit constexpr inline Role(const char *keyData) : StringKey(keyData) {} 0260 }; 0261 0262 /** A member in a relation. */ 0263 class Member { 0264 public: 0265 inline bool operator==(const Member &other) const { return id == other.id && m_roleAndType == other.m_roleAndType; } 0266 0267 Id id; 0268 0269 constexpr inline Role role() const 0270 { 0271 return Role(m_roleAndType.get()); 0272 } 0273 constexpr inline void setRole(Role role) 0274 { 0275 m_roleAndType.set(role.name()); 0276 } 0277 0278 constexpr inline Type type() const 0279 { 0280 return static_cast<Type>(m_roleAndType.tag()); 0281 } 0282 constexpr inline void setType(Type type) 0283 { 0284 m_roleAndType.setTag(static_cast<uint8_t>(type)); 0285 } 0286 0287 private: 0288 Internal::TaggedPointer<const char> m_roleAndType; 0289 }; 0290 0291 /** An OSM relation. */ 0292 class Relation { 0293 public: 0294 constexpr inline bool operator<(const Relation &other) const { return id < other.id; } 0295 0296 QString url() const; 0297 0298 Id id; 0299 mutable BoundingBox bbox; 0300 std::vector<Member> members; 0301 std::vector<Tag> tags; 0302 }; 0303 0304 /** A set of nodes, ways and relations. */ 0305 class DataSet { 0306 public: 0307 explicit DataSet(); 0308 DataSet(const DataSet&) = delete; 0309 DataSet(DataSet &&other); 0310 ~DataSet(); 0311 0312 DataSet& operator=(const DataSet&) = delete; 0313 DataSet& operator=(DataSet &&); 0314 0315 /** Find a node by its id. 0316 * @returns @c nullptr if the node doesn't exist. 0317 */ 0318 const Node* node(Id id) const; 0319 0320 /** Find a way by its id. 0321 * @returns @c nullptr if the way doesn't exist. 0322 */ 0323 const Way* way(Id id) const; 0324 OSM::Way* way(Id id); 0325 0326 /** Find a relation by its id. 0327 * @returns @c nullptr if the relation doesn't exist. 0328 */ 0329 const Relation* relation(Id id) const; 0330 0331 void addNode(Node &&node); 0332 void addWay(Way &&way); 0333 void addRelation(Relation &&rel); 0334 0335 /** Look up a tag key for the given tag name, if it exists. 0336 * If no key exists, an empty/invalid/null key is returned. 0337 * Use this for tag lookup, not for creating/adding tags. 0338 */ 0339 TagKey tagKey(const char *keyName) const; 0340 0341 enum StringMemory { StringIsPersistent, StringIsTransient }; 0342 /** Create a tag key for the given tag name. If none exist yet a new one is created. 0343 * Use this for creating tags, not for lookup, prefer tagKey() for that. 0344 * @param keyMemOpt specifies whether @p keyName is persisent for the lifetime of this 0345 * instance and thus can be used without requiring a copy. If the memory is transient 0346 * the string is copied if needed, and released in the DataSet destructor. 0347 */ 0348 TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt = StringIsTransient); 0349 0350 /** Looks up a role name key. 0351 * @see tagKey() 0352 */ 0353 Role role(const char *roleName) const; 0354 /** Creates a role name key. 0355 * @see makeTagKey() 0356 */ 0357 Role makeRole(const char *roleName, StringMemory memOpt = StringIsTransient); 0358 0359 /** Create a unique id for internal use (ie. one that will not clash with official OSM ids). */ 0360 Id nextInternalId() const; 0361 0362 std::vector<Node> nodes; 0363 std::vector<Way> ways; 0364 std::vector<Relation> relations; 0365 0366 private: 0367 template <typename T> T stringKey(const char *name, const std::vector<T> ®istry) const; 0368 template <typename T> T makeStringKey(const char *name, StringMemory memOpt, std::vector<T> ®istry); 0369 0370 std::vector<TagKey> m_tagKeyRegistry; 0371 std::vector<Role> m_roleRegistry; 0372 std::vector<char*> m_stringPool; 0373 }; 0374 0375 /** Returns the tag value for @p key of @p elem. */ 0376 template <typename Elem> 0377 inline QByteArray tagValue(const Elem& elem, TagKey key) 0378 { 0379 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), key, [](const auto &lhs, const auto &rhs) { return lhs.key < rhs; }); 0380 if (it != elem.tags.end() && (*it).key == key) { 0381 return (*it).value; 0382 } 0383 return {}; 0384 } 0385 0386 /** Returns the tag value for key name @p keyName of @p elem. 0387 * @warning This is slow due to doing a linear search and string comparissons. 0388 * Where possible avoid this in favor of tagValue(). 0389 */ 0390 template <typename Elem> 0391 inline QByteArray tagValue(const Elem& elem, const char *keyName) 0392 { 0393 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [keyName](const auto &tag) { return std::strcmp(tag.key.name(), keyName) == 0; }); 0394 if (it != elem.tags.end()) { 0395 return (*it).value; 0396 } 0397 return {}; 0398 } 0399 0400 /** Returns the localized version of the tag value for key name @p keyName of @p elem. 0401 * @warning This is slow due to doing a linear search and string comparissons. 0402 */ 0403 template <typename Elem> 0404 inline QByteArray tagValue(const Elem& elem, const char *keyName, const QLocale &locale) 0405 { 0406 QByteArray key(keyName); 0407 key.push_back(':'); 0408 const auto baseLen = key.size(); 0409 for (const auto &lang : locale.uiLanguages()) { 0410 key.resize(baseLen); 0411 key.append(lang.toUtf8()); 0412 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [key](const auto &tag) { return std::strcmp(tag.key.name(), key.constData()) == 0; }); 0413 if (it != elem.tags.end()) { 0414 return (*it).value; 0415 } 0416 0417 const auto idx = lang.indexOf(QLatin1Char('-')); 0418 if (idx > 0) { 0419 key.resize(baseLen); 0420 key.append(QStringView(lang).left(idx).toUtf8()); 0421 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [key](const auto &tag) { return std::strcmp(tag.key.name(), key.constData()) == 0; }); 0422 if (it != elem.tags.end()) { 0423 return (*it).value; 0424 } 0425 } 0426 } 0427 0428 // fall back to generic value, if present 0429 const auto v = tagValue(elem, keyName); 0430 if (!v.isEmpty()) { 0431 return v; 0432 } 0433 0434 // check if there is at least one in any language we can use 0435 key.resize(baseLen); 0436 const auto it = std::find_if(elem.tags.begin(), elem.tags.end(), [key, baseLen](const auto &tag) { return std::strncmp(tag.key.name(), key.constData(), baseLen) == 0; }); 0437 if (it != elem.tags.end()) { 0438 return (*it).value; 0439 } 0440 return {}; 0441 } 0442 0443 /** Inserts a new tag, or replaces an existing one with the same key. */ 0444 template <typename Elem> 0445 inline void setTag(Elem &elem, Tag &&tag) 0446 { 0447 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), tag); 0448 if (it == elem.tags.end() || (*it).key != tag.key) { 0449 elem.tags.insert(it, std::move(tag)); 0450 } else { 0451 (*it) = std::move(tag); 0452 } 0453 } 0454 0455 /** Inserts a new tag, or updates an existing one. */ 0456 template <typename Elem> 0457 inline void setTagValue(Elem &elem, TagKey key, const QByteArray &value) 0458 { 0459 Tag tag{ key, value }; 0460 setTag(elem, std::move(tag)); 0461 } 0462 0463 template <typename Elem> 0464 inline bool operator<(const Elem &elem, Id id) 0465 { 0466 return elem.id < id; 0467 } 0468 0469 } 0470 0471 QDebug operator<<(QDebug debug, OSM::Coordinate coord); 0472 QDebug operator<<(QDebug debug, OSM::BoundingBox bbox); 0473 0474 #endif // OSM_DATATYPES_H