aboutsummaryrefslogtreecommitdiff
path: root/src/iso19111/common.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/iso19111/common.cpp')
-rw-r--r--src/iso19111/common.cpp1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
new file mode 100644
index 00000000..bd690924
--- /dev/null
+++ b/src/iso19111/common.cpp
@@ -0,0 +1,1115 @@
+/******************************************************************************
+ *
+ * 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/common.hpp"
+#include "proj/io.hpp"
+#include "proj/metadata.hpp"
+#include "proj/util.hpp"
+
+#include "proj/internal/internal.hpp"
+#include "proj/internal/io_internal.hpp"
+
+#include "proj.h"
+
+#include <cmath> // M_PI
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <vector>
+
+using namespace NS_PROJ::internal;
+using namespace NS_PROJ::io;
+using namespace NS_PROJ::metadata;
+using namespace NS_PROJ::util;
+
+#if 0
+namespace dropbox{ namespace oxygen {
+template<> nn<NS_PROJ::common::IdentifiedObjectPtr>::~nn() = default;
+template<> nn<NS_PROJ::common::ObjectDomainPtr>::~nn() = default;
+template<> nn<NS_PROJ::common::ObjectUsagePtr>::~nn() = default;
+template<> nn<NS_PROJ::common::UnitOfMeasurePtr>::~nn() = default;
+}}
+#endif
+
+NS_PROJ_START
+namespace common {
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct UnitOfMeasure::Private {
+ std::string name_{};
+ double toSI_ = 1.0;
+ UnitOfMeasure::Type type_{UnitOfMeasure::Type::UNKNOWN};
+ std::string codeSpace_{};
+ std::string code_{};
+
+ Private(const std::string &nameIn, double toSIIn,
+ UnitOfMeasure::Type typeIn, const std::string &codeSpaceIn,
+ const std::string &codeIn)
+ : name_(nameIn), toSI_(toSIIn), type_(typeIn), codeSpace_(codeSpaceIn),
+ code_(codeIn) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Creates a UnitOfMeasure. */
+UnitOfMeasure::UnitOfMeasure(const std::string &nameIn, double toSIIn,
+ UnitOfMeasure::Type typeIn,
+ const std::string &codeSpaceIn,
+ const std::string &codeIn)
+ : d(internal::make_unique<Private>(nameIn, toSIIn, typeIn, codeSpaceIn,
+ codeIn)) {}
+
+// ---------------------------------------------------------------------------
+
+UnitOfMeasure::UnitOfMeasure(const UnitOfMeasure &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+UnitOfMeasure::~UnitOfMeasure() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+UnitOfMeasure &UnitOfMeasure::operator=(const UnitOfMeasure &other) {
+ if (this != &other) {
+ *d = *(other.d);
+ }
+ return *this;
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+UnitOfMeasureNNPtr UnitOfMeasure::create(const UnitOfMeasure &other) {
+ return util::nn_make_shared<UnitOfMeasure>(other);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the name of the unit of measure. */
+const std::string &UnitOfMeasure::name() PROJ_PURE_DEFN { return d->name_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the conversion factor to the unit of the
+ * International System of Units of the same Type.
+ *
+ * For example, for foot, this would be 0.3048 (metre)
+ *
+ * @return the conversion factor, or 0 if no conversion exists.
+ */
+double UnitOfMeasure::conversionToSI() PROJ_PURE_DEFN { return d->toSI_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the type of the unit of measure.
+ */
+UnitOfMeasure::Type UnitOfMeasure::type() PROJ_PURE_DEFN { return d->type_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the code space of the unit of measure.
+ *
+ * For example "EPSG"
+ *
+ * @return the code space, or empty string.
+ */
+const std::string &UnitOfMeasure::codeSpace() PROJ_PURE_DEFN {
+ return d->codeSpace_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the code of the unit of measure.
+ *
+ * @return the code, or empty string.
+ */
+const std::string &UnitOfMeasure::code() PROJ_PURE_DEFN { return d->code_; }
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void UnitOfMeasure::_exportToWKT(
+ WKTFormatter *formatter,
+ const std::string &unitType) const // throw(FormattingException)
+{
+ const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
+
+ if (formatter->forceUNITKeyword() && type() != Type::PARAMETRIC) {
+ formatter->startNode(WKTConstants::UNIT, !codeSpace().empty());
+ } else if (!unitType.empty()) {
+ formatter->startNode(unitType, !codeSpace().empty());
+ } else {
+ if (isWKT2 && type() == Type::LINEAR) {
+ formatter->startNode(WKTConstants::LENGTHUNIT,
+ !codeSpace().empty());
+ } else if (isWKT2 && type() == Type::ANGULAR) {
+ formatter->startNode(WKTConstants::ANGLEUNIT, !codeSpace().empty());
+ } else if (isWKT2 && type() == Type::SCALE) {
+ formatter->startNode(WKTConstants::SCALEUNIT, !codeSpace().empty());
+ } else if (isWKT2 && type() == Type::TIME) {
+ formatter->startNode(WKTConstants::TIMEUNIT, !codeSpace().empty());
+ } else if (isWKT2 && type() == Type::PARAMETRIC) {
+ formatter->startNode(WKTConstants::PARAMETRICUNIT,
+ !codeSpace().empty());
+ } else {
+ formatter->startNode(WKTConstants::UNIT, !codeSpace().empty());
+ }
+ }
+
+ {
+ const auto &l_name = name();
+ const bool esri = formatter->useESRIDialect();
+ if (esri) {
+ if (ci_equal(l_name, "degree")) {
+ formatter->addQuotedString("Degree");
+ } else if (ci_equal(l_name, "grad")) {
+ formatter->addQuotedString("Grad");
+ } else if (ci_equal(l_name, "metre")) {
+ formatter->addQuotedString("Meter");
+ } else {
+ formatter->addQuotedString(l_name);
+ }
+ } else {
+ formatter->addQuotedString(l_name);
+ }
+ const auto &factor = conversionToSI();
+ if (!isWKT2 || factor != 0.0) {
+ // Some TIMEUNIT do not have a conversion factor
+ formatter->add(factor);
+ }
+ if (!codeSpace().empty() && formatter->outputId()) {
+ formatter->startNode(
+ isWKT2 ? WKTConstants::ID : WKTConstants::AUTHORITY, false);
+ formatter->addQuotedString(codeSpace());
+ const auto &l_code = code();
+ if (isWKT2) {
+ try {
+ (void)std::stoi(l_code);
+ formatter->add(l_code);
+ } catch (const std::exception &) {
+ formatter->addQuotedString(l_code);
+ }
+ } else {
+ formatter->addQuotedString(l_code);
+ }
+ formatter->endNode();
+ }
+ }
+ formatter->endNode();
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** Returns whether two units of measures are equal.
+ *
+ * The comparison is based on the name.
+ */
+bool UnitOfMeasure::operator==(const UnitOfMeasure &other) PROJ_PURE_DEFN {
+ return name() == other.name();
+}
+
+// ---------------------------------------------------------------------------
+
+/** Returns whether two units of measures are different.
+ *
+ * The comparison is based on the name.
+ */
+bool UnitOfMeasure::operator!=(const UnitOfMeasure &other) PROJ_PURE_DEFN {
+ return name() != other.name();
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+std::string UnitOfMeasure::exportToPROJString() const {
+ if (type() == Type::LINEAR) {
+ auto proj_units = proj_list_units();
+ for (int i = 0; proj_units[i].id != nullptr; i++) {
+ if (::fabs(proj_units[i].factor - conversionToSI()) <
+ 1e-10 * conversionToSI()) {
+ return proj_units[i].id;
+ }
+ }
+ } else if (type() == Type::ANGULAR) {
+ auto proj_angular_units = proj_list_angular_units();
+ for (int i = 0; proj_angular_units[i].id != nullptr; i++) {
+ if (::fabs(proj_angular_units[i].factor - conversionToSI()) <
+ 1e-10 * conversionToSI()) {
+ return proj_angular_units[i].id;
+ }
+ }
+ }
+ return std::string();
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool UnitOfMeasure::_isEquivalentTo(
+ const UnitOfMeasure &other, util::IComparable::Criterion criterion) const {
+ if (criterion == util::IComparable::Criterion::STRICT) {
+ return operator==(other);
+ }
+ return std::fabs(conversionToSI() - other.conversionToSI()) <=
+ 1e-10 * std::fabs(conversionToSI());
+}
+
+//! @endcond
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct Measure::Private {
+ double value_ = 0.0;
+ UnitOfMeasure unit_{};
+
+ Private(double valueIn, const UnitOfMeasure &unitIn)
+ : value_(valueIn), unit_(unitIn) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Measure.
+ */
+Measure::Measure(double valueIn, const UnitOfMeasure &unitIn)
+ : d(internal::make_unique<Private>(valueIn, unitIn)) {}
+
+// ---------------------------------------------------------------------------
+
+Measure::Measure(const Measure &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Measure::~Measure() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the unit of the Measure.
+ */
+const UnitOfMeasure &Measure::unit() PROJ_CONST_DEFN { return d->unit_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the value of the Measure, after conversion to the
+ * corresponding
+ * unit of the International System.
+ */
+double Measure::getSIValue() PROJ_CONST_DEFN {
+ return d->value_ * d->unit_.conversionToSI();
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the value of the measure, expressed in the unit()
+ */
+double Measure::value() PROJ_CONST_DEFN { return d->value_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the value of this measure expressed into the provided unit.
+ */
+double Measure::convertToUnit(const UnitOfMeasure &otherUnit) PROJ_CONST_DEFN {
+ return getSIValue() / otherUnit.conversionToSI();
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return whether two measures are equal.
+ *
+ * The comparison is done both on the value and the unit.
+ */
+bool Measure::operator==(const Measure &other) PROJ_CONST_DEFN {
+ return d->value_ == other.d->value_ && d->unit_ == other.d->unit_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns whether an object is equivalent to another one.
+ * @param other other object to compare to
+ * @param criterion comparaison criterion.
+ * @param maxRelativeError Maximum relative error allowed.
+ * @return true if objects are equivalent.
+ */
+bool Measure::_isEquivalentTo(const Measure &other,
+ util::IComparable::Criterion criterion,
+ double maxRelativeError) const {
+ if (criterion == util::IComparable::Criterion::STRICT) {
+ return operator==(other);
+ }
+ return std::fabs(getSIValue() - other.getSIValue()) <=
+ maxRelativeError * std::fabs(getSIValue());
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Scale.
+ *
+ * @param valueIn value
+ */
+Scale::Scale(double valueIn) : Measure(valueIn, UnitOfMeasure::SCALE_UNITY) {}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Scale.
+ *
+ * @param valueIn value
+ * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::SCALE
+ */
+Scale::Scale(double valueIn, const UnitOfMeasure &unitIn)
+ : Measure(valueIn, unitIn) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Scale::Scale(const Scale &) = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Scale::~Scale() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Angle.
+ *
+ * @param valueIn value
+ */
+Angle::Angle(double valueIn) : Measure(valueIn, UnitOfMeasure::DEGREE) {}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Angle.
+ *
+ * @param valueIn value
+ * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::ANGULAR
+ */
+Angle::Angle(double valueIn, const UnitOfMeasure &unitIn)
+ : Measure(valueIn, unitIn) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Angle::Angle(const Angle &) = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Angle::~Angle() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Length.
+ *
+ * @param valueIn value
+ */
+Length::Length(double valueIn) : Measure(valueIn, UnitOfMeasure::METRE) {}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a Length.
+ *
+ * @param valueIn value
+ * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::LINEAR
+ */
+Length::Length(double valueIn, const UnitOfMeasure &unitIn)
+ : Measure(valueIn, unitIn) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Length::Length(const Length &) = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+Length::~Length() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct DateTime::Private {
+ std::string str_{};
+
+ explicit Private(const std::string &str) : str_(str) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+DateTime::DateTime() : d(internal::make_unique<Private>(std::string())) {}
+
+// ---------------------------------------------------------------------------
+
+DateTime::DateTime(const std::string &str)
+ : d(internal::make_unique<Private>(str)) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+DateTime::DateTime(const DateTime &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+DateTime::~DateTime() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a DateTime. */
+DateTime DateTime::create(const std::string &str) { return DateTime(str); }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return whether the DateTime is ISO:8601 compliant.
+ *
+ * \remark The current implementation is really simplistic, and aimed at
+ * detecting date-times that are not ISO:8601 compliant.
+ */
+bool DateTime::isISO_8601() const {
+ return !d->str_.empty() && d->str_[0] >= '0' && d->str_[0] <= '9' &&
+ d->str_.find(' ') == std::string::npos;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the DateTime as a string.
+ */
+std::string DateTime::toString() const { return d->str_; }
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+// cppcheck-suppress copyCtorAndEqOperator
+struct IdentifiedObject::Private {
+ IdentifierNNPtr name{Identifier::create()};
+ std::vector<IdentifierNNPtr> identifiers{};
+ std::vector<GenericNameNNPtr> aliases{};
+ std::string remarks{};
+ bool isDeprecated{};
+
+ void setIdentifiers(const PropertyMap &properties);
+ void setName(const PropertyMap &properties);
+ void setAliases(const PropertyMap &properties);
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+IdentifiedObject::IdentifiedObject() : d(internal::make_unique<Private>()) {}
+
+// ---------------------------------------------------------------------------
+
+IdentifiedObject::IdentifiedObject(const IdentifiedObject &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+IdentifiedObject::~IdentifiedObject() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the name of the object.
+ *
+ * Generally, the only interesting field of the name will be
+ * name()->description().
+ */
+const IdentifierNNPtr &IdentifiedObject::name() PROJ_CONST_DEFN {
+ return d->name;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the name of the object.
+ *
+ * Return *(name()->description())
+ */
+const std::string &IdentifiedObject::nameStr() PROJ_CONST_DEFN {
+ return *(d->name->description());
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the identifier(s) of the object
+ *
+ * Generally, those will have Identifier::code() and Identifier::codeSpace()
+ * filled.
+ */
+const std::vector<IdentifierNNPtr> &
+IdentifiedObject::identifiers() PROJ_CONST_DEFN {
+ return d->identifiers;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the alias(es) of the object.
+ */
+const std::vector<GenericNameNNPtr> &
+IdentifiedObject::aliases() PROJ_CONST_DEFN {
+ return d->aliases;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the (first) alias of the object as a string.
+ *
+ * Shortcut for aliases()[0]->toFullyQualifiedName()->toString()
+ */
+std::string IdentifiedObject::alias() PROJ_CONST_DEFN {
+ if (d->aliases.empty())
+ return std::string();
+ return d->aliases[0]->toFullyQualifiedName()->toString();
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the EPSG code.
+ * @return code, or 0 if not found
+ */
+int IdentifiedObject::getEPSGCode() PROJ_CONST_DEFN {
+ for (const auto &id : identifiers()) {
+ if (ci_equal(*(id->codeSpace()), metadata::Identifier::EPSG)) {
+ return ::atoi(id->code().c_str());
+ }
+ }
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the remarks.
+ */
+const std::string &IdentifiedObject::remarks() PROJ_CONST_DEFN {
+ return d->remarks;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return whether the object is deprecated.
+ *
+ * \remark Extension of \ref ISO_19111_2018
+ */
+bool IdentifiedObject::isDeprecated() PROJ_CONST_DEFN {
+ return d->isDeprecated;
+}
+
+// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+
+void IdentifiedObject::Private::setName(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ const auto pVal = properties.get(NAME_KEY);
+ if (!pVal) {
+ return;
+ }
+ if (const auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) {
+ if (genVal->type() == BoxedValue::Type::STRING) {
+ name = Identifier::createFromDescription(genVal->stringValue());
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ NAME_KEY);
+ }
+ } else {
+ if (auto identifier =
+ util::nn_dynamic_pointer_cast<Identifier>(*pVal)) {
+ name = NN_NO_CHECK(identifier);
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ NAME_KEY);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void IdentifiedObject::Private::setIdentifiers(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ auto pVal = properties.get(IDENTIFIERS_KEY);
+ if (!pVal) {
+
+ pVal = properties.get(Identifier::CODE_KEY);
+ if (pVal) {
+ identifiers.push_back(
+ Identifier::create(std::string(), properties));
+ }
+ return;
+ }
+ if (auto identifier = util::nn_dynamic_pointer_cast<Identifier>(*pVal)) {
+ identifiers.clear();
+ identifiers.push_back(NN_NO_CHECK(identifier));
+ } else {
+ if (auto array = dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) {
+ identifiers.clear();
+ for (const auto &val : *array) {
+ identifier = util::nn_dynamic_pointer_cast<Identifier>(val);
+ if (identifier) {
+ identifiers.push_back(NN_NO_CHECK(identifier));
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ IDENTIFIERS_KEY);
+ }
+ }
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ IDENTIFIERS_KEY);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void IdentifiedObject::Private::setAliases(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ const auto pVal = properties.get(ALIAS_KEY);
+ if (!pVal) {
+ return;
+ }
+ if (auto l_name = util::nn_dynamic_pointer_cast<GenericName>(*pVal)) {
+ aliases.clear();
+ aliases.push_back(NN_NO_CHECK(l_name));
+ } else {
+ if (const auto array =
+ dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) {
+ aliases.clear();
+ for (const auto &val : *array) {
+ l_name = util::nn_dynamic_pointer_cast<GenericName>(val);
+ if (l_name) {
+ aliases.push_back(NN_NO_CHECK(l_name));
+ } else {
+ if (auto genVal =
+ dynamic_cast<const BoxedValue *>(val.get())) {
+ if (genVal->type() == BoxedValue::Type::STRING) {
+ aliases.push_back(NameFactory::createLocalName(
+ nullptr, genVal->stringValue()));
+ } else {
+ throw InvalidValueTypeException(
+ "Invalid value type for " + ALIAS_KEY);
+ }
+ } else {
+ throw InvalidValueTypeException(
+ "Invalid value type for " + ALIAS_KEY);
+ }
+ }
+ }
+ } else {
+ std::string temp;
+ if (properties.getStringValue(ALIAS_KEY, temp)) {
+ aliases.clear();
+ aliases.push_back(NameFactory::createLocalName(nullptr, temp));
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ ALIAS_KEY);
+ }
+ }
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+void IdentifiedObject::setProperties(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ d->setName(properties);
+ d->setIdentifiers(properties);
+ d->setAliases(properties);
+
+ properties.getStringValue(REMARKS_KEY, d->remarks);
+
+ {
+ const auto pVal = properties.get(DEPRECATED_KEY);
+ if (pVal) {
+ if (const auto genVal =
+ dynamic_cast<const BoxedValue *>(pVal->get())) {
+ if (genVal->type() == BoxedValue::Type::BOOLEAN) {
+ d->isDeprecated = genVal->booleanValue();
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ DEPRECATED_KEY);
+ }
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ DEPRECATED_KEY);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void IdentifiedObject::formatID(WKTFormatter *formatter) const {
+ const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
+ for (const auto &id : identifiers()) {
+ id->_exportToWKT(formatter);
+ if (!isWKT2) {
+ break;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void IdentifiedObject::formatRemarks(WKTFormatter *formatter) const {
+ if (!remarks().empty()) {
+ formatter->startNode(WKTConstants::REMARK, false);
+ formatter->addQuotedString(remarks());
+ formatter->endNode();
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+bool IdentifiedObject::_isEquivalentTo(
+ const util::IComparable *other,
+ util::IComparable::Criterion criterion) const {
+ auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other);
+ if (!otherIdObj)
+ return false;
+ return _isEquivalentTo(otherIdObj, criterion);
+}
+
+// ---------------------------------------------------------------------------
+
+bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj,
+ util::IComparable::Criterion criterion)
+ PROJ_CONST_DEFN {
+ if (criterion == util::IComparable::Criterion::STRICT) {
+ if (!ci_equal(nameStr(), otherIdObj->nameStr())) {
+ return false;
+ }
+ // TODO test id etc
+ } else {
+ if (!metadata::Identifier::isEquivalentName(
+ nameStr().c_str(), otherIdObj->nameStr().c_str())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct ObjectDomain::Private {
+ optional<std::string> scope_{};
+ ExtentPtr domainOfValidity_{};
+
+ Private(const optional<std::string> &scopeIn, const ExtentPtr &extent)
+ : scope_(scopeIn), domainOfValidity_(extent) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+ObjectDomain::ObjectDomain(const optional<std::string> &scopeIn,
+ const ExtentPtr &extent)
+ : d(internal::make_unique<Private>(scopeIn, extent)) {}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+ObjectDomain::ObjectDomain(const ObjectDomain &other)
+ : d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+ObjectDomain::~ObjectDomain() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the scope.
+ *
+ * @return the scope, or empty.
+ */
+const optional<std::string> &ObjectDomain::scope() PROJ_CONST_DEFN {
+ return d->scope_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the domain of validity.
+ *
+ * @return the domain of validity, or nullptr.
+ */
+const ExtentPtr &ObjectDomain::domainOfValidity() PROJ_CONST_DEFN {
+ return d->domainOfValidity_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instanciate a ObjectDomain.
+ */
+ObjectDomainNNPtr ObjectDomain::create(const optional<std::string> &scopeIn,
+ const ExtentPtr &extent) {
+ return ObjectDomain::nn_make_shared<ObjectDomain>(scopeIn, extent);
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void ObjectDomain::_exportToWKT(WKTFormatter *formatter) const {
+ if (d->scope_.has_value()) {
+ formatter->startNode(WKTConstants::SCOPE, false);
+ formatter->addQuotedString(*(d->scope_));
+ formatter->endNode();
+ } else if (formatter->use2018Keywords()) {
+ formatter->startNode(WKTConstants::SCOPE, false);
+ formatter->addQuotedString("unknown");
+ formatter->endNode();
+ }
+ if (d->domainOfValidity_) {
+ if (d->domainOfValidity_->description().has_value()) {
+ formatter->startNode(WKTConstants::AREA, false);
+ formatter->addQuotedString(*(d->domainOfValidity_->description()));
+ formatter->endNode();
+ }
+ if (d->domainOfValidity_->geographicElements().size() == 1) {
+ const auto bbox = dynamic_cast<const GeographicBoundingBox *>(
+ d->domainOfValidity_->geographicElements()[0].get());
+ if (bbox) {
+ formatter->startNode(WKTConstants::BBOX, false);
+ formatter->add(bbox->southBoundLatitude());
+ formatter->add(bbox->westBoundLongitude());
+ formatter->add(bbox->northBoundLatitude());
+ formatter->add(bbox->eastBoundLongitude());
+ formatter->endNode();
+ }
+ }
+ if (d->domainOfValidity_->verticalElements().size() == 1) {
+ auto extent = d->domainOfValidity_->verticalElements()[0];
+ formatter->startNode(WKTConstants::VERTICALEXTENT, false);
+ formatter->add(extent->minimumValue());
+ formatter->add(extent->maximumValue());
+ extent->unit()->_exportToWKT(formatter);
+ formatter->endNode();
+ }
+ if (d->domainOfValidity_->temporalElements().size() == 1) {
+ auto extent = d->domainOfValidity_->temporalElements()[0];
+ formatter->startNode(WKTConstants::TIMEEXTENT, false);
+ if (DateTime::create(extent->start()).isISO_8601()) {
+ formatter->add(extent->start());
+ } else {
+ formatter->addQuotedString(extent->start());
+ }
+ if (DateTime::create(extent->stop()).isISO_8601()) {
+ formatter->add(extent->stop());
+ } else {
+ formatter->addQuotedString(extent->stop());
+ }
+ formatter->endNode();
+ }
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool ObjectDomain::_isEquivalentTo(
+ const util::IComparable *other,
+ util::IComparable::Criterion criterion) const {
+ auto otherDomain = dynamic_cast<const ObjectDomain *>(other);
+ if (!otherDomain)
+ return false;
+ if (scope().has_value() != otherDomain->scope().has_value())
+ return false;
+ if (*scope() != *otherDomain->scope())
+ return false;
+ if ((domainOfValidity().get() != nullptr) ^
+ (otherDomain->domainOfValidity().get() != nullptr))
+ return false;
+ return domainOfValidity().get() == nullptr ||
+ domainOfValidity()->_isEquivalentTo(
+ otherDomain->domainOfValidity().get(), criterion);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct ObjectUsage::Private {
+ std::vector<ObjectDomainNNPtr> domains_{};
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+ObjectUsage::ObjectUsage() : d(internal::make_unique<Private>()) {}
+
+// ---------------------------------------------------------------------------
+
+ObjectUsage::ObjectUsage(const ObjectUsage &other)
+ : IdentifiedObject(other), d(internal::make_unique<Private>(*(other.d))) {}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+ObjectUsage::~ObjectUsage() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return the domains of the object.
+ */
+const std::vector<ObjectDomainNNPtr> &ObjectUsage::domains() PROJ_CONST_DEFN {
+ return d->domains_;
+}
+
+// ---------------------------------------------------------------------------
+
+void ObjectUsage::setProperties(
+ const PropertyMap &properties) // throw(InvalidValueTypeException)
+{
+ IdentifiedObject::setProperties(properties);
+
+ optional<std::string> scope;
+ properties.getStringValue(SCOPE_KEY, scope);
+
+ ExtentPtr domainOfValidity;
+ {
+ const auto pVal = properties.get(DOMAIN_OF_VALIDITY_KEY);
+ if (pVal) {
+ domainOfValidity = util::nn_dynamic_pointer_cast<Extent>(*pVal);
+ if (!domainOfValidity) {
+ throw InvalidValueTypeException("Invalid value type for " +
+ DOMAIN_OF_VALIDITY_KEY);
+ }
+ }
+ }
+
+ if (scope.has_value() || domainOfValidity) {
+ d->domains_.emplace_back(ObjectDomain::create(scope, domainOfValidity));
+ }
+
+ {
+ const auto pVal = properties.get(OBJECT_DOMAIN_KEY);
+ if (pVal) {
+ if (auto objectDomain =
+ util::nn_dynamic_pointer_cast<ObjectDomain>(*pVal)) {
+ d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
+ } else if (const auto array =
+ dynamic_cast<const ArrayOfBaseObject *>(
+ pVal->get())) {
+ for (const auto &val : *array) {
+ objectDomain =
+ util::nn_dynamic_pointer_cast<ObjectDomain>(val);
+ if (objectDomain) {
+ d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
+ } else {
+ throw InvalidValueTypeException(
+ "Invalid value type for " + OBJECT_DOMAIN_KEY);
+ }
+ }
+ } else {
+ throw InvalidValueTypeException("Invalid value type for " +
+ OBJECT_DOMAIN_KEY);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const {
+ const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
+ if (isWKT2 && formatter->outputId()) {
+ auto l_domains = domains();
+ if (!l_domains.empty()) {
+ if (formatter->use2018Keywords()) {
+ for (const auto &domain : l_domains) {
+ formatter->startNode(WKTConstants::USAGE, false);
+ domain->_exportToWKT(formatter);
+ formatter->endNode();
+ }
+ } else {
+ l_domains[0]->_exportToWKT(formatter);
+ }
+ }
+ }
+ if (formatter->outputId()) {
+ formatID(formatter);
+ }
+ if (isWKT2) {
+ formatRemarks(formatter);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool ObjectUsage::_isEquivalentTo(
+ const util::IComparable *other,
+ util::IComparable::Criterion criterion) const {
+ auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other);
+ if (!otherObjUsage)
+ return false;
+
+ // TODO: incomplete
+ return IdentifiedObject::_isEquivalentTo(other, criterion);
+}
+//! @endcond
+
+} // namespace common
+NS_PROJ_END