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