aboutsummaryrefslogtreecommitdiff
path: root/src/metadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/metadata.cpp')
-rw-r--r--src/metadata.cpp1233
1 files changed, 1233 insertions, 0 deletions
diff --git a/src/metadata.cpp b/src/metadata.cpp
new file mode 100644
index 00000000..033782c9
--- /dev/null
+++ b/src/metadata.cpp
@@ -0,0 +1,1233 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: ISO19111:2018 implementation
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef FROM_PROJ_CPP
+#define FROM_PROJ_CPP
+#endif
+
+#include "proj/metadata.hpp"
+#include "proj/common.hpp"
+#include "proj/io.hpp"
+#include "proj/util.hpp"
+
+#include "proj/internal/internal.hpp"
+#include "proj/internal/io_internal.hpp"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+using namespace NS_PROJ::internal;
+using namespace NS_PROJ::io;
+using namespace NS_PROJ::util;
+
+#if 0
+namespace dropbox{ namespace oxygen {
+template<> nn<std::shared_ptr<NS_PROJ::metadata::Citation>>::~nn() = default;
+template<> nn<NS_PROJ::metadata::ExtentPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::GeographicBoundingBoxPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::GeographicExtentPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::VerticalExtentPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::TemporalExtentPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::IdentifierPtr>::~nn() = default;
+template<> nn<NS_PROJ::metadata::PositionalAccuracyPtr>::~nn() = default;
+}}
+#endif
+
+NS_PROJ_START
+namespace metadata {
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct Citation::Private {
+ optional<std::string> title{};
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Citation::Citation() : d(internal::make_unique<Private>()) {}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Constructs a citation by its title. */
+Citation::Citation(const std::string &titleIn)
+ : d(internal::make_unique<Private>()) {
+ d->title = titleIn;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Citation::Citation(const Citation &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+Citation::~Citation() = default;
+
+// ---------------------------------------------------------------------------
+
+Citation &Citation::operator=(const Citation &other) {
+ if (this != &other) {
+ *d = *other.d;
+ }
+ return *this;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the name by which the cited resource is known. */
+const optional<std::string> &Citation::title() PROJ_CONST_DEFN {
+ return d->title;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct GeographicExtent::Private {};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+GeographicExtent::GeographicExtent() : d(internal::make_unique<Private>()) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+GeographicExtent::~GeographicExtent() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct GeographicBoundingBox::Private {
+ double west_{};
+ double south_{};
+ double east_{};
+ double north_{};
+
+ Private(double west, double south, double east, double north)
+ : west_(west), south_(south), east_(east), north_(north) {}
+
+ bool intersects(const Private &other) const;
+
+ std::unique_ptr<Private> intersection(const Private &other) const;
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+GeographicBoundingBox::GeographicBoundingBox(double west, double south,
+ double east, double north)
+ : GeographicExtent(),
+ d(internal::make_unique<Private>(west, south, east, north)) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+GeographicBoundingBox::~GeographicBoundingBox() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the western-most coordinate of the limit of the dataset
+ * extent.
+ *
+ * The unit is degrees.
+ *
+ * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses
+ * the anti-meridian.
+ */
+double GeographicBoundingBox::westBoundLongitude() PROJ_CONST_DEFN {
+ return d->west_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the southern-most coordinate of the limit of the dataset
+ * extent.
+ *
+ * The unit is degrees.
+ */
+double GeographicBoundingBox::southBoundLatitude() PROJ_CONST_DEFN {
+ return d->south_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the eastern-most coordinate of the limit of the dataset
+ * extent.
+ *
+ * The unit is degrees.
+ *
+ * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses
+ * the anti-meridian.
+ */
+double GeographicBoundingBox::eastBoundLongitude() PROJ_CONST_DEFN {
+ return d->east_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the northern-most coordinate of the limit of the dataset
+ * extent.
+ *
+ * The unit is degrees.
+ */
+double GeographicBoundingBox::northBoundLatitude() PROJ_CONST_DEFN {
+ return d->north_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a GeographicBoundingBox.
+ *
+ * If east < west, then the bounding box crosses the anti-meridian.
+ *
+ * @param west Western-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param south Southern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param east Eastern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param north Northern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @return a new GeographicBoundingBox.
+ */
+GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west,
+ double south,
+ double east,
+ double north) {
+ return GeographicBoundingBox::nn_make_shared<GeographicBoundingBox>(
+ west, south, east, north);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool GeographicBoundingBox::_isEquivalentTo(
+ const util::IComparable *other, util::IComparable::Criterion) const {
+ auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other);
+ if (!otherExtent)
+ return false;
+ return d->west_ == otherExtent->d->west_ &&
+ d->south_ == otherExtent->d->south_ &&
+ d->east_ == otherExtent->d->east_ &&
+ d->north_ == otherExtent->d->north_;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+bool GeographicBoundingBox::contains(const GeographicExtentNNPtr &other) const {
+ auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get());
+ if (!otherExtent) {
+ return false;
+ }
+ const double W = d->west_;
+ const double E = d->east_;
+ const double N = d->north_;
+ const double S = d->south_;
+ const double oW = otherExtent->d->west_;
+ const double oE = otherExtent->d->east_;
+ const double oN = otherExtent->d->north_;
+ const double oS = otherExtent->d->south_;
+
+ if (!(S <= oS && N >= oN)) {
+ return false;
+ }
+
+ if (W == -180.0 && E == 180.0) {
+ return true;
+ }
+
+ if (oW == -180.0 && oE == 180.0) {
+ return false;
+ }
+
+ // Normal bounding box ?
+ if (W < E) {
+ if (oW < oE) {
+ return W <= oW && E >= oE;
+ } else {
+ return false;
+ }
+ // No: crossing antimerian
+ } else {
+ if (oW < oE) {
+ if (oW >= W) {
+ return true;
+ } else if (oE <= E) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return W <= oW && E >= oE;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool GeographicBoundingBox::Private::intersects(const Private &other) const {
+ const double W = west_;
+ const double E = east_;
+ const double N = north_;
+ const double S = south_;
+ const double oW = other.west_;
+ const double oE = other.east_;
+ const double oN = other.north_;
+ const double oS = other.south_;
+
+ if (N < oS || S > oN) {
+ return false;
+ }
+
+ if (W == -180.0 && E == 180.0 && oW > oE) {
+ return true;
+ }
+
+ if (oW == -180.0 && oE == 180.0 && W > E) {
+ return true;
+ }
+
+ // Normal bounding box ?
+ if (W <= E) {
+ if (oW < oE) {
+ if (std::max(W, oW) < std::min(E, oE)) {
+ return true;
+ }
+ return false;
+ }
+
+ return intersects(Private(oW, oS, 180.0, oN)) ||
+ intersects(Private(-180.0, oS, oE, oN));
+
+ // No: crossing antimerian
+ } else {
+ if (oW <= oE) {
+ return other.intersects(*this);
+ }
+
+ return true;
+ }
+}
+//! @endcond
+
+bool GeographicBoundingBox::intersects(
+ const GeographicExtentNNPtr &other) const {
+ auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get());
+ if (!otherExtent) {
+ return false;
+ }
+ return d->intersects(*(otherExtent->d));
+}
+
+// ---------------------------------------------------------------------------
+
+GeographicExtentPtr
+GeographicBoundingBox::intersection(const GeographicExtentNNPtr &other) const {
+ auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get());
+ if (!otherExtent) {
+ return nullptr;
+ }
+ auto ret = d->intersection(*(otherExtent->d));
+ if (ret) {
+ auto bbox = GeographicBoundingBox::create(ret->west_, ret->south_,
+ ret->east_, ret->north_);
+ return bbox.as_nullable();
+ }
+ return nullptr;
+}
+
+//! @cond Doxygen_Suppress
+std::unique_ptr<GeographicBoundingBox::Private>
+GeographicBoundingBox::Private::intersection(const Private &otherExtent) const {
+ const double W = west_;
+ const double E = east_;
+ const double N = north_;
+ const double S = south_;
+ const double oW = otherExtent.west_;
+ const double oE = otherExtent.east_;
+ const double oN = otherExtent.north_;
+ const double oS = otherExtent.south_;
+
+ if (N < oS || S > oN) {
+ return nullptr;
+ }
+
+ if (W == -180.0 && E == 180.0 && oW > oE) {
+ return internal::make_unique<Private>(oW, std::max(S, oS), oE,
+ std::min(N, oN));
+ }
+
+ if (oW == -180.0 && oE == 180.0 && W > E) {
+ return internal::make_unique<Private>(W, std::max(S, oS), E,
+ std::min(N, oN));
+ }
+
+ // Normal bounding box ?
+ if (W <= E) {
+ if (oW < oE) {
+ auto res = internal::make_unique<Private>(
+ std::max(W, oW), std::max(S, oS), std::min(E, oE),
+ std::min(N, oN));
+ if (res->west_ < res->east_) {
+ return res;
+ }
+ return nullptr;
+ }
+
+ // Return larger of two parts of the multipolygon
+ auto inter1 = intersection(Private(oW, oS, 180.0, oN));
+ auto inter2 = intersection(Private(-180.0, oS, oE, oN));
+ if (!inter1) {
+ return inter2;
+ }
+ if (!inter2) {
+ return inter1;
+ }
+ if (inter1->east_ - inter1->west_ > inter2->east_ - inter2->west_) {
+ return inter1;
+ }
+ return inter2;
+ // No: crossing antimerian
+ } else {
+ if (oW <= oE) {
+ return otherExtent.intersection(*this);
+ }
+
+ return internal::make_unique<Private>(std::max(W, oW), std::max(S, oS),
+ std::min(E, oE), std::min(N, oN));
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct VerticalExtent::Private {
+ double minimum_{};
+ double maximum_{};
+ common::UnitOfMeasureNNPtr unit_;
+
+ Private(double minimum, double maximum,
+ const common::UnitOfMeasureNNPtr &unit)
+ : minimum_(minimum), maximum_(maximum), unit_(unit) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+VerticalExtent::VerticalExtent(double minimumIn, double maximumIn,
+ const common::UnitOfMeasureNNPtr &unitIn)
+ : d(internal::make_unique<Private>(minimumIn, maximumIn, unitIn)) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+VerticalExtent::~VerticalExtent() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the minimum of the vertical extent.
+ */
+double VerticalExtent::minimumValue() PROJ_CONST_DEFN { return d->minimum_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the maximum of the vertical extent.
+ */
+double VerticalExtent::maximumValue() PROJ_CONST_DEFN { return d->maximum_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the unit of the vertical extent.
+ */
+common::UnitOfMeasureNNPtr &VerticalExtent::unit() PROJ_CONST_DEFN {
+ return d->unit_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a VerticalExtent.
+ *
+ * @param minimumIn minimum.
+ * @param maximumIn maximum.
+ * @param unitIn unit.
+ * @return a new VerticalExtent.
+ */
+VerticalExtentNNPtr
+VerticalExtent::create(double minimumIn, double maximumIn,
+ const common::UnitOfMeasureNNPtr &unitIn) {
+ return VerticalExtent::nn_make_shared<VerticalExtent>(minimumIn, maximumIn,
+ unitIn);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool VerticalExtent::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion) const {
+ auto otherExtent = dynamic_cast<const VerticalExtent *>(other);
+ if (!otherExtent)
+ return false;
+ return d->minimum_ == otherExtent->d->minimum_ &&
+ d->maximum_ == otherExtent->d->maximum_ &&
+ d->unit_ == otherExtent->d->unit_;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent contains the other one.
+ */
+bool VerticalExtent::contains(const VerticalExtentNNPtr &other) const {
+ const double thisUnitToSI = d->unit_->conversionToSI();
+ const double otherUnitToSI = other->d->unit_->conversionToSI();
+ return d->minimum_ * thisUnitToSI <= other->d->minimum_ * otherUnitToSI &&
+ d->maximum_ * thisUnitToSI >= other->d->maximum_ * otherUnitToSI;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent intersects the other one.
+ */
+bool VerticalExtent::intersects(const VerticalExtentNNPtr &other) const {
+ const double thisUnitToSI = d->unit_->conversionToSI();
+ const double otherUnitToSI = other->d->unit_->conversionToSI();
+ return d->minimum_ * thisUnitToSI <= other->d->maximum_ * otherUnitToSI &&
+ d->maximum_ * thisUnitToSI >= other->d->minimum_ * otherUnitToSI;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct TemporalExtent::Private {
+ std::string start_{};
+ std::string stop_{};
+
+ Private(const std::string &start, const std::string &stop)
+ : start_(start), stop_(stop) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+TemporalExtent::TemporalExtent(const std::string &startIn,
+ const std::string &stopIn)
+ : d(internal::make_unique<Private>(startIn, stopIn)) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+TemporalExtent::~TemporalExtent() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the start of the temporal extent.
+ */
+const std::string &TemporalExtent::start() PROJ_CONST_DEFN { return d->start_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the end of the temporal extent.
+ */
+const std::string &TemporalExtent::stop() PROJ_CONST_DEFN { return d->stop_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a TemporalExtent.
+ *
+ * @param start start.
+ * @param stop stop.
+ * @return a new TemporalExtent.
+ */
+TemporalExtentNNPtr TemporalExtent::create(const std::string &start,
+ const std::string &stop) {
+ return TemporalExtent::nn_make_shared<TemporalExtent>(start, stop);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool TemporalExtent::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion) const {
+ auto otherExtent = dynamic_cast<const TemporalExtent *>(other);
+ if (!otherExtent)
+ return false;
+ return start() == otherExtent->start() && stop() == otherExtent->stop();
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent contains the other one.
+ */
+bool TemporalExtent::contains(const TemporalExtentNNPtr &other) const {
+ return start() <= other->start() && stop() >= other->stop();
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent intersects the other one.
+ */
+bool TemporalExtent::intersects(const TemporalExtentNNPtr &other) const {
+ return start() <= other->stop() && stop() >= other->start();
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct Extent::Private {
+ optional<std::string> description_{};
+ std::vector<GeographicExtentNNPtr> geographicElements_{};
+ std::vector<VerticalExtentNNPtr> verticalElements_{};
+ std::vector<TemporalExtentNNPtr> temporalElements_{};
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Extent::Extent() : d(internal::make_unique<Private>()) {}
+
+// ---------------------------------------------------------------------------
+
+Extent::Extent(const Extent &other)
+ : d(internal::make_unique<Private>(*other.d)) {}
+
+// ---------------------------------------------------------------------------
+
+Extent::~Extent() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** Return a textual description of the extent.
+ *
+ * @return the description, or empty.
+ */
+const optional<std::string> &Extent::description() PROJ_CONST_DEFN {
+ return d->description_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** Return the geographic element(s) of the extent
+ *
+ * @return the geographic element(s), or empty.
+ */
+const std::vector<GeographicExtentNNPtr> &
+Extent::geographicElements() PROJ_CONST_DEFN {
+ return d->geographicElements_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** Return the vertical element(s) of the extent
+ *
+ * @return the vertical element(s), or empty.
+ */
+const std::vector<VerticalExtentNNPtr> &
+Extent::verticalElements() PROJ_CONST_DEFN {
+ return d->verticalElements_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** Return the temporal element(s) of the extent
+ *
+ * @return the temporal element(s), or empty.
+ */
+const std::vector<TemporalExtentNNPtr> &
+Extent::temporalElements() PROJ_CONST_DEFN {
+ return d->temporalElements_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Extent.
+ *
+ * @param descriptionIn Textual description, or empty.
+ * @param geographicElementsIn Geographic element(s), or empty.
+ * @param verticalElementsIn Vertical element(s), or empty.
+ * @param temporalElementsIn Temporal element(s), or empty.
+ * @return a new Extent.
+ */
+ExtentNNPtr
+Extent::create(const optional<std::string> &descriptionIn,
+ const std::vector<GeographicExtentNNPtr> &geographicElementsIn,
+ const std::vector<VerticalExtentNNPtr> &verticalElementsIn,
+ const std::vector<TemporalExtentNNPtr> &temporalElementsIn) {
+ auto extent = Extent::nn_make_shared<Extent>();
+ extent->assignSelf(extent);
+ extent->d->description_ = descriptionIn;
+ extent->d->geographicElements_ = geographicElementsIn;
+ extent->d->verticalElements_ = verticalElementsIn;
+ extent->d->temporalElements_ = temporalElementsIn;
+ return extent;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Extent from a bounding box
+ *
+ * @param west Western-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param south Southern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param east Eastern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param north Northern-most coordinate of the limit of the dataset extent (in
+ * degrees).
+ * @param descriptionIn Textual description, or empty.
+ * @return a new Extent.
+ */
+ExtentNNPtr
+Extent::createFromBBOX(double west, double south, double east, double north,
+ const util::optional<std::string> &descriptionIn) {
+ return create(
+ descriptionIn,
+ std::vector<GeographicExtentNNPtr>{
+ nn_static_pointer_cast<GeographicExtent>(
+ GeographicBoundingBox::create(west, south, east, north))},
+ std::vector<VerticalExtentNNPtr>(), std::vector<TemporalExtentNNPtr>());
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool Extent::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion) const {
+ auto otherExtent = dynamic_cast<const Extent *>(other);
+ bool ret =
+ (otherExtent &&
+ description().has_value() == otherExtent->description().has_value() &&
+ *description() == *otherExtent->description() &&
+ d->geographicElements_.size() ==
+ otherExtent->d->geographicElements_.size() &&
+ d->verticalElements_.size() ==
+ otherExtent->d->verticalElements_.size() &&
+ d->temporalElements_.size() ==
+ otherExtent->d->temporalElements_.size());
+ if (ret) {
+ for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) {
+ ret = d->geographicElements_[i]->_isEquivalentTo(
+ otherExtent->d->geographicElements_[i].get(), criterion);
+ }
+ for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) {
+ ret = d->verticalElements_[i]->_isEquivalentTo(
+ otherExtent->d->verticalElements_[i].get(), criterion);
+ }
+ for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) {
+ ret = d->temporalElements_[i]->_isEquivalentTo(
+ otherExtent->d->temporalElements_[i].get(), criterion);
+ }
+ }
+ return ret;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent contains the other one.
+ *
+ * Behaviour only well specified if each sub-extent category as at most
+ * one element.
+ */
+bool Extent::contains(const ExtentNNPtr &other) const {
+ bool res = true;
+ if (d->geographicElements_.size() == 1 &&
+ other->d->geographicElements_.size() == 1) {
+ res = d->geographicElements_[0]->contains(
+ other->d->geographicElements_[0]);
+ }
+ if (res && d->verticalElements_.size() == 1 &&
+ other->d->verticalElements_.size() == 1) {
+ res = d->verticalElements_[0]->contains(other->d->verticalElements_[0]);
+ }
+ if (res && d->temporalElements_.size() == 1 &&
+ other->d->temporalElements_.size() == 1) {
+ res = d->temporalElements_[0]->contains(other->d->temporalElements_[0]);
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether this extent intersects the other one.
+ *
+ * Behaviour only well specified if each sub-extent category as at most
+ * one element.
+ */
+bool Extent::intersects(const ExtentNNPtr &other) const {
+ bool res = true;
+ if (d->geographicElements_.size() == 1 &&
+ other->d->geographicElements_.size() == 1) {
+ res = d->geographicElements_[0]->intersects(
+ other->d->geographicElements_[0]);
+ }
+ if (res && d->verticalElements_.size() == 1 &&
+ other->d->verticalElements_.size() == 1) {
+ res =
+ d->verticalElements_[0]->intersects(other->d->verticalElements_[0]);
+ }
+ if (res && d->temporalElements_.size() == 1 &&
+ other->d->temporalElements_.size() == 1) {
+ res =
+ d->temporalElements_[0]->intersects(other->d->temporalElements_[0]);
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the intersection of this extent with another one.
+ *
+ * Behaviour only well specified if there is one single GeographicExtent
+ * in each object.
+ * Returns nullptr otherwise.
+ */
+ExtentPtr Extent::intersection(const ExtentNNPtr &other) const {
+ if (d->geographicElements_.size() == 1 &&
+ other->d->geographicElements_.size() == 1) {
+ if (contains(other)) {
+ return other.as_nullable();
+ }
+ auto self = util::nn_static_pointer_cast<Extent>(shared_from_this());
+ if (other->contains(self)) {
+ return self.as_nullable();
+ }
+ auto geogIntersection = d->geographicElements_[0]->intersection(
+ other->d->geographicElements_[0]);
+ if (geogIntersection) {
+ return create(util::optional<std::string>(),
+ std::vector<GeographicExtentNNPtr>{
+ NN_NO_CHECK(geogIntersection)},
+ std::vector<VerticalExtentNNPtr>{},
+ std::vector<TemporalExtentNNPtr>{});
+ }
+ }
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct Identifier::Private {
+ optional<Citation> authority_{};
+ std::string code_{};
+ optional<std::string> codeSpace_{};
+ optional<std::string> version_{};
+ optional<std::string> description_{};
+ optional<std::string> uri_{};
+
+ Private(const std::string &codeIn, const PropertyMap &properties)
+ : code_(codeIn) {
+ setProperties(properties);
+ }
+
+ private:
+ // cppcheck-suppress functionStatic
+ void setProperties(const PropertyMap &properties);
+};
+
+// ---------------------------------------------------------------------------
+
+void Identifier::Private::setProperties(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ {
+ auto oIter = properties.find(AUTHORITY_KEY);
+ if (oIter != properties.end()) {
+ if (auto genVal =
+ dynamic_cast<const BoxedValue *>(oIter->second.get())) {
+ if (genVal->type() == BoxedValue::Type::STRING) {
+ authority_ = Citation(genVal->stringValue());
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ AUTHORITY_KEY);
+ }
+ } else {
+ if (auto citation =
+ dynamic_cast<const Citation *>(oIter->second.get())) {
+ authority_ = *citation;
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ AUTHORITY_KEY);
+ }
+ }
+ }
+ }
+
+ {
+ auto oIter = properties.find(CODE_KEY);
+ if (oIter != properties.end()) {
+ if (auto genVal =
+ dynamic_cast<const BoxedValue *>(oIter->second.get())) {
+ if (genVal->type() == BoxedValue::Type::INTEGER) {
+ code_ = toString(genVal->integerValue());
+ } else if (genVal->type() == BoxedValue::Type::STRING) {
+ code_ = genVal->stringValue();
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ CODE_KEY);
+ }
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ CODE_KEY);
+ }
+ }
+ }
+
+ {
+ std::string temp;
+ if (properties.getStringValue(CODESPACE_KEY, temp)) {
+ codeSpace_ = temp;
+ }
+ }
+
+ {
+ std::string temp;
+ if (properties.getStringValue(VERSION_KEY, temp)) {
+ version_ = temp;
+ }
+ }
+
+ {
+ std::string temp;
+ if (properties.getStringValue(DESCRIPTION_KEY, temp)) {
+ description_ = temp;
+ }
+ }
+
+ {
+ std::string temp;
+ if (properties.getStringValue(URI_KEY, temp)) {
+ uri_ = temp;
+ }
+ }
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+Identifier::Identifier(const std::string &codeIn, const PropertyMap &properties)
+ : d(internal::make_unique<Private>(codeIn, properties)) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Identifier::Identifier(const Identifier &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+Identifier::~Identifier() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Identifier.
+ *
+ * @param codeIn Alphanumeric value identifying an instance in the codespace
+ * @param properties See \ref general_properties.
+ * Generally, the Identifier::CODESPACE_KEY should be set.
+ * @return a new Identifier.
+ */
+IdentifierNNPtr Identifier::create(const std::string &codeIn,
+ const PropertyMap &properties) {
+ return Identifier::nn_make_shared<Identifier>(codeIn, properties);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return a citation for the organization responsible for definition and
+ * maintenance of the code.
+ *
+ * @return the citation for the authority, or empty.
+ */
+const optional<Citation> &Identifier::authority() PROJ_CONST_DEFN {
+ return d->authority_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the alphanumeric value identifying an instance in the
+ * codespace.
+ *
+ * e.g. "4326" (for EPSG:4326 WGS 84 GeographicCRS)
+ *
+ * @return the code.
+ */
+const std::string &Identifier::code() PROJ_CONST_DEFN { return d->code_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the organization responsible for definition and maintenance of
+ * the code.
+ *
+ * e.g "EPSG"
+ *
+ * @return the authority codespace, or empty.
+ */
+const optional<std::string> &Identifier::codeSpace() PROJ_CONST_DEFN {
+ return d->codeSpace_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the version identifier for the namespace.
+ *
+ * When appropriate, the edition is identified by the effective date, coded
+ * using ISO 8601 date format.
+ *
+ * @return the version or empty.
+ */
+const optional<std::string> &Identifier::version() PROJ_CONST_DEFN {
+ return d->version_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the natural language description of the meaning of the code
+ * value.
+ *
+ * @return the description or empty.
+ */
+const optional<std::string> &Identifier::description() PROJ_CONST_DEFN {
+ return d->description_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the URI of the identifier.
+ *
+ * @return the URI or empty.
+ */
+const optional<std::string> &Identifier::uri() PROJ_CONST_DEFN {
+ return d->uri_;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void Identifier::_exportToWKT(WKTFormatter *formatter) const {
+ const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
+ const std::string &l_code = code();
+ const std::string &l_codeSpace = *codeSpace();
+ if (!l_codeSpace.empty() && !l_code.empty()) {
+ if (isWKT2) {
+ formatter->startNode(WKTConstants::ID, false);
+ formatter->addQuotedString(l_codeSpace);
+ try {
+ (void)std::stoi(l_code);
+ formatter->add(l_code);
+ } catch (const std::exception &) {
+ formatter->addQuotedString(l_code);
+ }
+ if (version().has_value()) {
+ auto l_version = *(version());
+ try {
+ (void)c_locale_stod(l_version);
+ formatter->add(l_version);
+ } catch (const std::exception &) {
+ formatter->addQuotedString(l_version);
+ }
+ }
+ if (authority().has_value() &&
+ *(authority()->title()) != l_codeSpace) {
+ formatter->startNode(WKTConstants::CITATION, false);
+ formatter->addQuotedString(*(authority()->title()));
+ formatter->endNode();
+ }
+ if (uri().has_value()) {
+ formatter->startNode(WKTConstants::URI, false);
+ formatter->addQuotedString(*(uri()));
+ formatter->endNode();
+ }
+ formatter->endNode();
+ } else {
+ formatter->startNode(WKTConstants::AUTHORITY, false);
+ formatter->addQuotedString(l_codeSpace);
+ formatter->addQuotedString(l_code);
+ formatter->endNode();
+ }
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+static bool isIgnoredChar(char ch) {
+ return ch == ' ' || ch == '_' || ch == '-' || ch == '/' || ch == '(' ||
+ ch == ')';
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+std::string Identifier::canonicalizeName(const std::string &str) {
+ std::string res;
+ const char *c_str = str.c_str();
+ for (size_t i = 0; c_str[i] != 0; ++i) {
+ const auto ch = c_str[i];
+ if (ch == ' ' && c_str[i + 1] == '+' && c_str[i + 2] == ' ') {
+ i += 2;
+ continue;
+ }
+ if (ch == '1' && !res.empty() &&
+ !(res.back() >= '0' && res.back() <= '9') && c_str[i + 1] == '9' &&
+ c_str[i + 2] >= '0' && c_str[i + 2] <= '9') {
+ ++i;
+ continue;
+ }
+ if (!isIgnoredChar(ch)) {
+ res.push_back(static_cast<char>(::tolower(ch)));
+ }
+ }
+ return res;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether two names are considered equivalent.
+ *
+ * Two names are equivalent by removing any space, underscore, dash, slash,
+ * { or } character from them, and comparing in a case insensitive way.
+ */
+bool Identifier::isEquivalentName(const char *a, const char *b) noexcept {
+ size_t i = 0;
+ size_t j = 0;
+ char lastValidA = 0;
+ char lastValidB = 0;
+ while (a[i] != 0 && b[j] != 0) {
+ const char aCh = a[i];
+ const char bCh = b[j];
+ if (aCh == ' ' && a[i + 1] == '+' && a[i + 2] == ' ') {
+ i += 3;
+ continue;
+ }
+ if (bCh == ' ' && b[j + 1] == '+' && b[j + 2] == ' ') {
+ j += 3;
+ continue;
+ }
+ if (isIgnoredChar(aCh)) {
+ ++i;
+ continue;
+ }
+ if (isIgnoredChar(bCh)) {
+ ++j;
+ continue;
+ }
+ if (aCh == '1' && !(lastValidA >= '0' && lastValidA <= '9') &&
+ a[i + 1] == '9' && a[i + 2] >= '0' && a[i + 2] <= '9') {
+ i += 2;
+ lastValidA = '9';
+ continue;
+ }
+ if (bCh == '1' && !(lastValidB >= '0' && lastValidB <= '9') &&
+ b[j + 1] == '9' && b[j + 2] >= '0' && b[j + 2] <= '9') {
+ j += 2;
+ lastValidB = '9';
+ continue;
+ }
+ if (::tolower(aCh) != ::tolower(bCh)) {
+ return false;
+ }
+ lastValidA = aCh;
+ lastValidB = bCh;
+ ++i;
+ ++j;
+ }
+ while (a[i] != 0 && isIgnoredChar(a[i])) {
+ ++i;
+ }
+ while (b[j] != 0 && isIgnoredChar(b[j])) {
+ ++j;
+ }
+ return a[i] == b[j];
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct PositionalAccuracy::Private {
+ std::string value_{};
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+PositionalAccuracy::PositionalAccuracy(const std::string &valueIn)
+ : d(internal::make_unique<Private>()) {
+ d->value_ = valueIn;
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+PositionalAccuracy::~PositionalAccuracy() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the value of the positional accuracy.
+ */
+const std::string &PositionalAccuracy::value() PROJ_CONST_DEFN {
+ return d->value_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a PositionalAccuracy.
+ *
+ * @param valueIn positional accuracy value.
+ * @return a new PositionalAccuracy.
+ */
+PositionalAccuracyNNPtr PositionalAccuracy::create(const std::string &valueIn) {
+ return PositionalAccuracy::nn_make_shared<PositionalAccuracy>(valueIn);
+}
+
+} // namespace metadata
+NS_PROJ_END