#pragma once #include #include #include #include #include namespace CesiumVectorData { struct GeoJsonObjectIterator; struct ConstGeoJsonObjectIterator; template struct ConstGeoJsonPrimitiveIterator; template struct ConstGeoJsonObjectTypeIterator; /** * @brief An iterator over all `Point` and `MultiPoint` objects in and including * a root GeoJSON object. */ using ConstGeoJsonPointIterator = ConstGeoJsonPrimitiveIterator; /** * @brief An iterator over all `LineString` and `MultiLineString` objects in and * including a root GeoJSON object. */ using ConstGeoJsonLineStringIterator = ConstGeoJsonPrimitiveIterator< GeoJsonLineString, GeoJsonMultiLineString, std::vector>; /** * @brief An iterator over all `Polygon` and `MultiPolygon` objects in and * including a root GeoJSON object. */ using ConstGeoJsonPolygonIterator = ConstGeoJsonPrimitiveIterator< GeoJsonPolygon, GeoJsonMultiPolygon, std::vector>>; /** * @brief An object in a GeoJSON document. */ struct GeoJsonObject { /** * @brief An object providing `begin` and `end` methods for creating iterators * of the given type for a \ref GeoJsonObject. */ template struct IteratorProvider { /** * @brief Returns an iterator pointing to the first element. */ TIterator begin() { return TIterator(*this->_pObject); } /** * @brief Returns an iterator pointing "past the end" of all the elements. */ TIterator end() { return TIterator(); } private: IteratorProvider(const GeoJsonObject* pObject) : _pObject(pObject) {} const GeoJsonObject* _pObject; friend struct GeoJsonObject; }; /** * @brief Returns an iterator pointing to this object. Iterating this will * provide all children of this object. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ GeoJsonObjectIterator begin(); /** * @brief Returns an iterator pointing "past the end" of the list of children * of this object. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ GeoJsonObjectIterator end(); /** * @brief Returns an iterator pointing to this object. Iterating this will * provide all children of this object. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ ConstGeoJsonObjectIterator begin() const; /** * @brief Returns an iterator pointing "past the end" of the list of children * of this object. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ ConstGeoJsonObjectIterator end() const; /** * @brief Allows iterating over all points defined in this object or any child * objects. This will include both `Point` objects and `MultiPoint` objects. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ IteratorProvider points() const; /** * @brief Allows iterating over all lines defined in this object or any child * objects. This will include both `LineString` objects and `MultiLineString` * objects. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ IteratorProvider lines() const; /** * @brief Allows iterating over all polygons defined in this object or any * child objects. This will include both `Polygon` objects and `MultiPolygon` * objects. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ IteratorProvider polygons() const; /** * @brief Returns all \ref GeoJsonObject values matching the given type in * this object or in any children. * * @note The iterator will only descend up to a depth of eight, which should * cover almost all GeoJSON documents. */ template IteratorProvider> allOfType() const { return IteratorProvider>(this); } /** * @brief Returns the bounding box defined in the GeoJSON for this object, if * any. */ const std::optional& getBoundingBox() const; /** * @brief Returns the bounding box defined in the GeoJSON for this object, if * any. */ std::optional& getBoundingBox(); /** * @brief Returns the \ref CesiumUtility::JsonValue::Object containing any * foreign members on this GeoJSON object. */ const CesiumUtility::JsonValue::Object& getForeignMembers() const; /** * @brief Returns the \ref CesiumUtility::JsonValue::Object containing any * foreign members on this GeoJSON object. */ CesiumUtility::JsonValue::Object& getForeignMembers(); /** * @brief Returns the style set on this GeoJSON object, if any. */ const std::optional& getStyle() const; /** * @brief Returns the style set on this GeoJSON object, if any. */ std::optional& getStyle(); /** * @brief Returns the \ref GeoJsonObjectType that this \ref GeoJsonObject * is wrapping. */ GeoJsonObjectType getType() const; /** * @brief Returns whether the `value` of this \ref GeoJsonObject is of the * given type. */ template bool isType() const { return std::holds_alternative(this->value); } /** * @brief Obtains a reference to the value of this \ref GeoJsonObject if the * value is of the given type. * * @exception std::bad_variant_access if the value is not of type `T` */ template const T& get() const { return std::get(this->value); } /** * @brief Obtains a reference to the value of this \ref GeoJsonObject if the * value is of the given type. * * @exception std::bad_variant_access if the value is not of type `T` */ template T& get() { return std::get(this->value); } /** * @brief Obtains a pointer to the value of this \ref GeoJsonObject if the * value is of the given type. Otherwise `nullptr` is returned. */ template const T* getIf() const { return std::get_if(&this->value); } /** * @brief Obtains a pointer to the value of this \ref GeoJsonObject if the * value is of the given type. Otherwise `nullptr` is returned. */ template T* getIf() { return std::get_if(&this->value); } /** * @brief Applies the visitor `visitor` to the value variant. */ template decltype(auto) visit(Visitor&& visitor) { return std::visit(std::forward(visitor), this->value); } /** * @brief Applies the visitor `visitor` to the value variant. */ template decltype(auto) visit(Visitor&& visitor) const { return std::visit(std::forward(visitor), this->value); } /** * @brief A variant containing the GeoJSON object. */ GeoJsonObjectVariant value; }; /** * @brief Iterates over a \ref GeoJsonObject and all of its children. */ struct GeoJsonObjectIterator { private: static constexpr size_t StackSize = 8; struct IteratorStackState { GeoJsonObject* pObject; int64_t nextPos; }; public: // Ignore Doxygen warnings for iterator tags. //! @cond Doxygen_Suppress using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = GeoJsonObject; using pointer = GeoJsonObject*; using reference = GeoJsonObject&; //! @endcond /** @brief Returns a reference to the current object. */ reference operator*() const { return *_pCurrentObject; } /** @brief Returns a pointer to the current object. */ pointer operator->() { return _pCurrentObject; } /** * @brief Attempts to find the Feature that contains the current item the * iterator is pointing to. * * If the iterator is pointing to a Feature, that Feature will be returned. */ GeoJsonObject* getFeature() const { for (int64_t i = this->_stackPos; i >= 0; i--) { if (this->_stack[(size_t)i].pObject->isType()) { return this->_stack[(size_t)i].pObject; } } return nullptr; } /** * @brief Returns `true` if this is an "end" iterator (points past the end of * all objects). */ bool isEnded() const { return this->_stackPos == -1 && this->_pCurrentObject == nullptr; } /** * @brief Iterates to the next \ref GeoJsonObject, returning this modified * iterator. */ GeoJsonObjectIterator& operator++() { this->iterate(); return *this; } /** * @brief Iterates to the next \ref GeoJsonObject, returning the previous * state of the iterator. */ GeoJsonObjectIterator operator++(int) { GeoJsonObjectIterator tmp = *this; this->iterate(); return tmp; } /** * @brief Checks if two \ref GeoJsonObjectIterator iterators are equal. */ friend bool operator==(const GeoJsonObjectIterator& a, const GeoJsonObjectIterator& b) { if (a._pCurrentObject != b._pCurrentObject || a._stackPos != b._stackPos) { return false; } if (a._stackPos >= 0 && b._stackPos >= 0) { if (a._stack[(size_t)a._stackPos].nextPos != b._stack[(size_t)b._stackPos].nextPos) { return false; } if (a._stack[(size_t)a._stackPos].pObject != b._stack[(size_t)b._stackPos].pObject) { return false; } } return true; } /** * @brief Checks if two \ref GeoJsonObjectIterator iterators are not equal. */ friend bool operator!=(const GeoJsonObjectIterator& a, const GeoJsonObjectIterator& b) { return !(a == b); } /** * @brief Creates a new \ref GeoJsonObjectIterator with the given \ref * GeoJsonObject as the root object. * * @param rootObject The root object of the new iterator. This will be the * first object returned. */ GeoJsonObjectIterator(GeoJsonObject& rootObject) : _stackPos(0), _pCurrentObject(nullptr) { this->_stack[0].pObject = &rootObject; this->_stack[0].nextPos = -1; this->iterate(); } /** * @brief Creates a new \ref GeoJsonObjectIterator without any \ref * GeoJsonObject. This is equivalent to an "end" iterator. */ GeoJsonObjectIterator() : _stackPos(-1), _pCurrentObject(nullptr) {} private: void handleChild(GeoJsonObject& child) { this->_pCurrentObject = &child; if ((this->_stackPos - 1) <= (int64_t)StackSize && (child.isType() || child.isType() || child.isType())) { ++this->_stackPos; this->_stack[(size_t)this->_stackPos].pObject = &child; this->_stack[(size_t)this->_stackPos].nextPos = 0; } } void iterate() { this->_pCurrentObject = nullptr; while (this->_stackPos >= 0 && this->_stackPos < (int64_t)StackSize && this->_pCurrentObject == nullptr) { IteratorStackState& stackState = this->_stack[(size_t)this->_stackPos]; GeoJsonObject* pNext = stackState.pObject; if (stackState.nextPos == -1) { this->_pCurrentObject = pNext; ++stackState.nextPos; continue; } else if ( GeoJsonGeometryCollection* pCollection = std::get_if(&pNext->value)) { if ((size_t)stackState.nextPos >= pCollection->geometries.size()) { // No children left --this->_stackPos; continue; } GeoJsonObject& child = pCollection->geometries[(size_t)stackState.nextPos]; ++stackState.nextPos; this->handleChild(child); continue; } else if ( GeoJsonFeatureCollection* pFeatureCollection = std::get_if(&pNext->value)) { if ((size_t)stackState.nextPos >= pFeatureCollection->features.size()) { // No children left --this->_stackPos; continue; } GeoJsonObject& child = pFeatureCollection->features[(size_t)stackState.nextPos]; ++stackState.nextPos; this->handleChild(child); continue; } else if ( GeoJsonFeature* pFeature = std::get_if(&pNext->value)) { const size_t expectedSize = pFeature->geometry == nullptr ? 0 : 1; if ((size_t)stackState.nextPos >= expectedSize) { // Feature only has zero or one child --this->_stackPos; continue; } ++stackState.nextPos; this->handleChild(*pFeature->geometry); continue; } else { // This object was a dud, try another --this->_stackPos; } } } std::array _stack; int64_t _stackPos = -1; GeoJsonObject* _pCurrentObject; friend struct ConstGeoJsonObjectIterator; }; /** * @brief The `const` equivalent of \ref GeoJsonObjectIterator. */ struct ConstGeoJsonObjectIterator { // Ignore Doxygen warnings for iterator tags. //! @cond Doxygen_Suppress using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = GeoJsonObject; using pointer = const GeoJsonObject*; using reference = const GeoJsonObject&; //! @endcond /** @brief Returns a reference to the current object. */ reference operator*() const { return *this->_it; } /** @brief Returns a pointer to the current object. */ pointer operator->() { return this->_it._pCurrentObject; } /** * @brief Attempts to find the Feature that contains the current item the * iterator is pointing to. * * If the iterator is pointing to a Feature, that Feature will be returned. */ const GeoJsonObject* getFeature() { return this->_it.getFeature(); } /** * @brief Returns `true` if this is an "end" iterator (points past the end of * all objects). */ bool isEnded() const { return this->_it.isEnded(); } /** * @brief Iterates to the next \ref GeoJsonObject, returning this modified * iterator. */ ConstGeoJsonObjectIterator& operator++() { ++this->_it; return *this; } /** * @brief Iterates to the next \ref GeoJsonObject, returning the previous * state of the iterator. */ ConstGeoJsonObjectIterator operator++(int) { ConstGeoJsonObjectIterator tmp = *this; ++this->_it; return tmp; } /** * @brief Checks if two \ref ConstGeoJsonObjectIterator iterators are equal. */ friend bool operator==( const ConstGeoJsonObjectIterator& a, const ConstGeoJsonObjectIterator& b) { return a._it == b._it; } /** * @brief Checks if two \ref ConstGeoJsonObjectIterator iterators are not * equal. */ friend bool operator!=( const ConstGeoJsonObjectIterator& a, const ConstGeoJsonObjectIterator& b) { return a._it != b._it; } /** * @brief Creates a new \ref ConstGeoJsonObjectIterator with the given \ref * GeoJsonObject as the root object. * * @param rootObject The root object of the new iterator. This will be the * first object returned. */ ConstGeoJsonObjectIterator(const GeoJsonObject& rootObject) : _it(const_cast(rootObject)) {} /** * @brief Creates a new \ref ConstGeoJsonObjectIterator without any \ref * GeoJsonObject. This is equivalent to an "end" iterator. */ ConstGeoJsonObjectIterator() = default; private: GeoJsonObjectIterator _it; }; /** * @brief Returns all geometry data of a given type from a \ref GeoJsonObject. * * @tparam TSingle The type of the "single" version of this geometry object. For * example, `Point`. * @tparam TMulti The type of the "multi" version of this geometry object. For * example, `MultiPoint`. * @tparam TValue The type of the geometry data included in both * `TSingle::coordinates` and `TMulti::coordinates[i]`. */ template struct ConstGeoJsonPrimitiveIterator { // Ignore Doxygen warnings for iterator tags. //! @cond Doxygen_Suppress using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = TValue; using pointer = const TValue*; using reference = const TValue&; //! @endcond /** * @brief Returns a reference to the current value. */ reference operator*() const { const TMulti* pMultiPoint = (*this->_it).template getIf(); if (pMultiPoint) { return pMultiPoint->coordinates[this->_currentMultiIdx]; } return (*this->_it).template get().coordinates; } /** * @brief Returns a pointer to the current value. */ pointer operator->() { return &**this; } /** * @brief Iterates to the next value, returning the modified iterator. */ ConstGeoJsonPrimitiveIterator& operator++() { this->iterate(); return *this; } /** * @brief Iterates to the next value, returning the previous state of the * iterator. */ ConstGeoJsonPrimitiveIterator operator++(int) { ConstGeoJsonPrimitiveIterator tmp = *this; this->iterate(); return tmp; } /** * @brief Checks if two \ref ConstGeoJsonPrimitiveIterator iterators are * equal. */ friend bool operator==( const ConstGeoJsonPrimitiveIterator& a, const ConstGeoJsonPrimitiveIterator& b) { return a._it == b._it; } /** * @brief Checks if two \ref ConstGeoJsonPrimitiveIterator iterators are not * equal. */ friend bool operator!=( const ConstGeoJsonPrimitiveIterator& a, const ConstGeoJsonPrimitiveIterator& b) { return a._it != b._it; } /** * @brief Creates a new \ref ConstGeoJsonPrimitiveIterator from the given root * \ref GeoJsonObject. */ ConstGeoJsonPrimitiveIterator(const GeoJsonObject& rootObject) : _it(const_cast(rootObject)) { if (!_it.isEnded() && !(_it->template isType() || _it->template isType())) { this->iterate(); } } /** * @brief Creates an empty \ref ConstGeoJsonPrimitiveIterator. */ ConstGeoJsonPrimitiveIterator() = default; private: void iterate() { if (!this->_it.isEnded() && this->_it->template isType()) { const TMulti& multi = this->_it->template get(); if ((int64_t)this->_currentMultiIdx < ((int64_t)multi.coordinates.size() - 1)) { ++this->_currentMultiIdx; return; } } this->_currentMultiIdx = 0; do { ++this->_it; } while (!this->_it.isEnded() && !(this->_it->template isType() || (this->_it->template isType() && !this->_it->template get().coordinates.empty()))); } GeoJsonObjectIterator _it; size_t _currentMultiIdx = 0; }; /** * @brief An iterator over all \ref GeoJsonObject objects that contain a value * of type `ObjectType`. */ template struct ConstGeoJsonObjectTypeIterator { // Ignore Doxygen warnings for iterator tags. //! @cond Doxygen_Suppress using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = TObject; using pointer = const TObject*; using reference = const TObject&; //! @endcond /** @brief Returns a reference to the current object. */ reference operator*() const { return (*this->_it).template get(); } /** @brief Returns a pointer to the current object. */ pointer operator->() { return &**this; } /** * @brief Iterates to the next `ObjectType`, returning this modified * iterator. */ ConstGeoJsonObjectTypeIterator& operator++() { this->iterate(); return *this; } /** * @brief Iterates to the next `ObjectType`, returning the previous * state of the iterator. */ ConstGeoJsonObjectTypeIterator operator++(int) { ConstGeoJsonObjectTypeIterator tmp = *this; this->iterate(); return tmp; } /** * @brief Checks if two \ref ConstGeoJsonObjectTypeIterator iterators are * equal. */ friend bool operator==( const ConstGeoJsonObjectTypeIterator& a, const ConstGeoJsonObjectTypeIterator& b) { return a._it == b._it; } /** * @brief Checks if two \ref ConstGeoJsonObjectTypeIterator iterators are not * equal. */ friend bool operator!=( const ConstGeoJsonObjectTypeIterator& a, const ConstGeoJsonObjectTypeIterator& b) { return a._it != b._it; } /** * @brief Creates a new \ref ConstGeoJsonObjectTypeIterator with the given * \ref GeoJsonObject as the root object. * * @param rootObject The root object of the new iterator. This will be the * first object returned. */ ConstGeoJsonObjectTypeIterator(const GeoJsonObject& rootObject) : _it(const_cast(rootObject)) { if (!this->_it.isEnded() && !this->_it->template isType()) { this->iterate(); } } /** * @brief Creates a new \ref ConstGeoJsonObjectTypeIterator without any \ref * GeoJsonObject. This is equivalent to an "end" iterator. */ ConstGeoJsonObjectTypeIterator() = default; private: void iterate() { do { ++this->_it; } while (!this->_it.isEnded() && !this->_it->template isType()); } ConstGeoJsonObjectIterator _it; size_t _currentMultiPointIdx = 0; }; } // namespace CesiumVectorData