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> &registry) const;
0368     template <typename T> T makeStringKey(const char *name, StringMemory memOpt, std::vector<T> &registry);
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