diff options
Diffstat (limited to 'src/metadata.cpp')
| -rw-r--r-- | src/metadata.cpp | 1233 |
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 |
