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