File indexing completed on 2024-11-24 04:46:23
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <QDebug> 0010 #include <QString> 0011 0012 #include <cstdint> 0013 #include <vector> 0014 0015 namespace OSM { 0016 0017 /** OSM element identifier. */ 0018 using Id = int64_t; 0019 0020 /** Coordinate, stored as 1e7 * degree to avoid floating point precision issues, 0021 * and offset to unsigned values to make the z-order curve work. 0022 * Can be in an invalid state with coordinates out of range, see isValid(). 0023 * @see https://en.wikipedia.org/wiki/Z-order_curve for the z-order curve stuff 0024 */ 0025 class Coordinate { 0026 public: 0027 Coordinate() = default; 0028 explicit constexpr Coordinate(double lat, double lon) 0029 : latitude((lat + 90.0) * 10'000'000) 0030 , longitude((lon + 180.0) * 10'000'000) 0031 {} 0032 explicit constexpr Coordinate(uint32_t lat, uint32_t lon) 0033 : latitude(lat) 0034 , longitude(lon) 0035 {} 0036 0037 /** Create a coordinate from a z-order curve index. */ 0038 explicit constexpr Coordinate(uint64_t z) 0039 : latitude(0) 0040 , longitude(0) 0041 { 0042 for (int i = 0; i < 32; ++i) { 0043 latitude += (z & (1ull << (i * 2))) >> i; 0044 longitude += (z & (1ull << (1 + i * 2))) >> (i + 1); 0045 } 0046 } 0047 0048 constexpr inline bool isValid() const 0049 { 0050 return latitude != std::numeric_limits<uint32_t>::max() && longitude != std::numeric_limits<uint32_t>::max(); 0051 } 0052 0053 /** Z-order curve value for this coordinate. */ 0054 constexpr inline uint64_t z() const 0055 { 0056 uint64_t z = 0; 0057 for (int i = 0; i < 32; ++i) { 0058 z += ((uint64_t)latitude & (1 << i)) << i; 0059 z += ((uint64_t)longitude & (1 << i)) << (i + 1); 0060 } 0061 return z; 0062 } 0063 0064 constexpr inline double latF() const 0065 { 0066 return (latitude / 10'000'000.0) - 90.0; 0067 } 0068 constexpr inline double lonF() const 0069 { 0070 return (longitude / 10'000'000.0) - 180.0; 0071 } 0072 0073 uint32_t latitude = std::numeric_limits<uint32_t>::max(); 0074 uint32_t longitude = std::numeric_limits<uint32_t>::max(); 0075 }; 0076 0077 0078 /** Bounding box, ie. a pair of coordinates. */ 0079 class BoundingBox { 0080 public: 0081 constexpr BoundingBox() = default; 0082 constexpr inline BoundingBox(Coordinate c1, Coordinate c2) 0083 : min(c1) 0084 , max(c2) 0085 {} 0086 constexpr inline bool isValid() const 0087 { 0088 return min.isValid() && max.isValid(); 0089 } 0090 0091 constexpr inline uint32_t width() const 0092 { 0093 return max.longitude - min.longitude; 0094 } 0095 constexpr inline uint32_t height() const 0096 { 0097 return max.latitude - min.latitude; 0098 } 0099 0100 constexpr inline Coordinate center() const 0101 { 0102 return Coordinate(min.latitude + height() / 2, min.longitude + width() / 2); 0103 } 0104 0105 Coordinate min; 0106 Coordinate max; 0107 }; 0108 0109 constexpr inline BoundingBox unite(BoundingBox bbox1, BoundingBox bbox2) 0110 { 0111 if (!bbox1.isValid()) { 0112 return bbox2; 0113 } 0114 if (!bbox2.isValid()) { 0115 return bbox1; 0116 } 0117 BoundingBox ret; 0118 ret.min.latitude = std::min(bbox1.min.latitude, bbox2.min.latitude); 0119 ret.min.longitude = std::min(bbox1.min.longitude, bbox2.min.longitude); 0120 ret.max.latitude = std::max(bbox1.max.latitude, bbox2.max.latitude); 0121 ret.max.longitude = std::max(bbox1.max.longitude, bbox2.max.longitude); 0122 return ret; 0123 } 0124 0125 constexpr inline bool intersects(BoundingBox bbox1, BoundingBox bbox2) 0126 { 0127 return !(bbox2.min.latitude > bbox1.max.latitude || bbox2.max.latitude < bbox1.min.latitude 0128 || bbox2.min.longitude > bbox1.max.longitude || bbox2.max.longitude < bbox1.min.longitude); 0129 } 0130 0131 constexpr inline bool contains(BoundingBox bbox, Coordinate coord) 0132 { 0133 return bbox.min.latitude <= coord.latitude && bbox.max.latitude >= coord.latitude 0134 && bbox.min.longitude <= coord.longitude && bbox.max.longitude >= coord.longitude; 0135 } 0136 0137 constexpr inline uint32_t latitudeDistance(BoundingBox bbox1, BoundingBox bbox2) 0138 { 0139 return bbox1.max.latitude < bbox2.min.latitude ? bbox2.min.latitude - bbox1.max.latitude : bbox1.min.latitude - bbox2.max.latitude; 0140 } 0141 0142 constexpr inline uint32_t longitudeDifference(BoundingBox bbox1, BoundingBox bbox2) 0143 { 0144 return bbox1.max.longitude < bbox2.min.longitude ? bbox2.min.longitude - bbox1.max.longitude : bbox1.min.longitude - bbox2.max.longitude; 0145 } 0146 0147 /** An OSM element tag. */ 0148 class Tag { 0149 public: 0150 inline bool operator<(const Tag &other) const { return key < other.key; } 0151 0152 QString key; 0153 QString value; 0154 }; 0155 0156 /** An OSM node. */ 0157 class Node { 0158 public: 0159 constexpr inline bool operator<(const Node &other) const { return id < other.id; } 0160 0161 QString url() const; 0162 0163 Id id; 0164 Coordinate coordinate; 0165 std::vector<Tag> tags; 0166 }; 0167 0168 /** An OSM way. */ 0169 class Way { 0170 public: 0171 constexpr inline bool operator<(const Way &other) const { return id < other.id; } 0172 0173 bool isClosed() const; 0174 0175 QString url() const; 0176 0177 Id id; 0178 mutable BoundingBox bbox; 0179 std::vector<Id> nodes; 0180 std::vector<Tag> tags; 0181 }; 0182 0183 /** Element type. */ 0184 enum class Type : uint8_t { 0185 Null, 0186 Node, 0187 Way, 0188 Relation 0189 }; 0190 0191 /** A member in a relation. */ 0192 // TODO this has 7 byte padding, can we make this more efficient? 0193 class Member { 0194 public: 0195 Id id; 0196 QString role; 0197 Type type; 0198 }; 0199 0200 /** An OSM relation. */ 0201 class Relation { 0202 public: 0203 constexpr inline bool operator<(const Relation &other) const { return id < other.id; } 0204 0205 QString url() const; 0206 0207 Id id; 0208 mutable BoundingBox bbox; 0209 std::vector<Member> members; 0210 std::vector<Tag> tags; 0211 }; 0212 0213 /** A set of nodes, ways and relations. */ 0214 class DataSet { 0215 public: 0216 void addNode(Node &&node); 0217 void addWay(Way &&way); 0218 void addRelation(Relation &&rel); 0219 0220 std::vector<Node> nodes; 0221 std::vector<Way> ways; 0222 std::vector<Relation> relations; 0223 }; 0224 0225 /** Returns the tag value for @p key of @p elem. */ 0226 template <typename Elem> 0227 inline QString tagValue(const Elem &elem, const QLatin1StringView &key) { 0228 const auto it = std::lower_bound( 0229 elem.tags.begin(), elem.tags.end(), key, 0230 [](const auto &lhs, const auto &rhs) { return lhs.key < rhs; }); 0231 if (it != elem.tags.end() && (*it).key == key) { 0232 return (*it).value; 0233 } 0234 return {}; 0235 } 0236 0237 /** Inserts a new tag, or replaces an existing one with the same key. */ 0238 template <typename Elem> 0239 inline void setTag(Elem &elem, Tag &&tag) 0240 { 0241 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), tag); 0242 if (it == elem.tags.end() || (*it).key != tag.key) { 0243 elem.tags.insert(it, std::move(tag)); 0244 } else { 0245 (*it) = std::move(tag); 0246 } 0247 } 0248 0249 /** Inserts a new tag, or updates an existing one. */ 0250 template <typename Elem> 0251 inline void setTagValue(Elem &elem, const QString &key, const QString &value) 0252 { 0253 Tag tag{ key, value }; 0254 setTag(elem, std::move(tag)); 0255 } 0256 0257 template <typename Elem> 0258 inline bool operator<(const Elem &elem, Id id) 0259 { 0260 return elem.id < id; 0261 } 0262 0263 } 0264 0265 QDebug operator<<(QDebug debug, OSM::Coordinate coord); 0266 QDebug operator<<(QDebug debug, OSM::BoundingBox bbox); 0267