diff --git a/.github/workflows/clang_static_analyzer.yml b/.github/workflows/clang_static_analyzer.yml
index b6433b2433..1985c96a64 100644
--- a/.github/workflows/clang_static_analyzer.yml
+++ b/.github/workflows/clang_static_analyzer.yml
@@ -18,7 +18,7 @@ permissions:
jobs:
clang_static_analyzer:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- name: Checkout
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 59be3bdd84..902fed8c33 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -71,12 +71,13 @@ jobs:
cd %PROJ_BUILD%
set PROJ_DIR=%GITHUB_WORKSPACE%\proj_dir
:: Not directly linked to BUILD_SHARED_LIBS, but a way to test different C++ standard versions
- if "${{ env.BUILD_SHARED_LIBS }}"=="ON" (set EXTRA_CXX_FLAGS="/std:c++20")
+ if "${{ env.BUILD_SHARED_LIBS }}"=="ON" (set CMAKE_CXX_STANDARD="-DCMAKE_CXX_STANDARD=20")
if "${{ env.BUILD_TYPE }}"=="Release" (set CMAKE_UNITY_BUILD_OPT="-DCMAKE_UNITY_BUILD=ON")
cmake -D CMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" ^
-D BUILD_SHARED_LIBS="${{ env.BUILD_SHARED_LIBS }}" ^
-D CMAKE_C_FLAGS="/WX" ^
- -D CMAKE_CXX_FLAGS="/WX %EXTRA_CXX_FLAGS%" ^
+ -D CMAKE_CXX_FLAGS="/WX" ^
+ %CMAKE_CXX_STANDARD% ^
-D CMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake ^
-D CMAKE_INSTALL_PREFIX="%PROJ_DIR%" ^
-D PROJ_DB_CACHE_DIR=%PROJ_DB_CACHE_DIR% ^
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6cf51b3ee..62aebc9979 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,8 +21,8 @@ cmake_policy(SET CMP0054 NEW)
# Set C++ version
# Make CMAKE_CXX_STANDARD available as cache option overridable by user
-set(CMAKE_CXX_STANDARD 11
- CACHE STRING "C++ standard version to use (default is 11)")
+set(CMAKE_CXX_STANDARD 17
+ CACHE STRING "C++ standard version to use (default is 17)")
message(STATUS "Requiring C++${CMAKE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/docs/source/install.rst b/docs/source/install.rst
index 8c5a95a4b0..e280159fdc 100644
--- a/docs/source/install.rst
+++ b/docs/source/install.rst
@@ -146,7 +146,7 @@ Build requirements
++++++++++++++++++
- C99 compiler
-- C++11 compiler
+- C++17 compiler
- CMake >= 3.16
- SQLite3 >= 3.11: headers and library for target architecture, and sqlite3 executable for build architecture
- libtiff >= 4.0 (optional but recommended)
@@ -160,7 +160,7 @@ Test requirements
These are only required if testing is built (see :option:`BUILD_TESTING`, default ON)
-- GoogleTest (GTest) >= 1.8.1; if not found and :option:`TESTING_USE_NETWORK` is ON, then version 1.12.1 is fetched from GitHub and locally installed
+- GoogleTest (GTest) >= 1.8.1; if not found and :option:`TESTING_USE_NETWORK` is ON, then version 1.15.2 is fetched from GitHub and locally installed
- Python >= 3.7
- `importlib_metadata `_ only needed for Python 3.7
- One of either `PyYAML `_ or `ruamel.yaml `_
diff --git a/src/iso19111/coordinates.cpp b/src/iso19111/coordinates.cpp
index a51763d88e..21751f294a 100644
--- a/src/iso19111/coordinates.cpp
+++ b/src/iso19111/coordinates.cpp
@@ -213,13 +213,18 @@ CoordinateMetadataNNPtr
CoordinateMetadata::promoteTo3D(const std::string &newName,
const io::DatabaseContextPtr &dbContext) const {
auto crs = d->crs_->promoteTo3D(newName, dbContext);
- auto coordinateMetadata(
- d->coordinateEpoch_.has_value()
- ? CoordinateMetadata::nn_make_shared(
- crs, coordinateEpochAsDecimalYear())
- : CoordinateMetadata::nn_make_shared(crs));
- coordinateMetadata->assignSelf(coordinateMetadata);
- return coordinateMetadata;
+ if (d->coordinateEpoch_.has_value()) {
+ auto coordinateMetadata(
+ CoordinateMetadata::nn_make_shared(
+ crs, coordinateEpochAsDecimalYear()));
+ coordinateMetadata->assignSelf(coordinateMetadata);
+ return coordinateMetadata;
+ } else {
+ auto coordinateMetadata(
+ CoordinateMetadata::nn_make_shared(crs));
+ coordinateMetadata->assignSelf(coordinateMetadata);
+ return coordinateMetadata;
+ }
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index 8c89c2d70c..9baa1d0faf 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -818,13 +818,17 @@ EllipsoidNNPtr Ellipsoid::createSphere(const util::PropertyMap &properties,
EllipsoidNNPtr Ellipsoid::createFlattenedSphere(
const util::PropertyMap &properties, const common::Length &semiMajorAxisIn,
const common::Scale &invFlattening, const std::string &celestialBody) {
- auto ellipsoid(invFlattening.value() == 0
- ? Ellipsoid::nn_make_shared(semiMajorAxisIn,
- celestialBody)
- : Ellipsoid::nn_make_shared(
- semiMajorAxisIn, invFlattening, celestialBody));
- ellipsoid->setProperties(properties);
- return ellipsoid;
+ if (invFlattening.value() == 0) {
+ auto ellipsoid(Ellipsoid::nn_make_shared(semiMajorAxisIn,
+ celestialBody));
+ ellipsoid->setProperties(properties);
+ return ellipsoid;
+ } else {
+ auto ellipsoid(Ellipsoid::nn_make_shared(
+ semiMajorAxisIn, invFlattening, celestialBody));
+ ellipsoid->setProperties(properties);
+ return ellipsoid;
+ }
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 92aa1ed7b9..29059b83ca 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -3114,9 +3114,9 @@ std::string
DatabaseContext::suggestsCodeFor(const common::IdentifiedObjectNNPtr &object,
const std::string &authName,
bool numericCode) {
- const char *tableName = "";
+ const char *tableName = "prime_meridian";
if (dynamic_cast(object.get())) {
- tableName = "prime_meridian";
+ // tableName = "prime_meridian";
} else if (dynamic_cast(object.get())) {
tableName = "ellipsoid";
} else if (dynamic_cast(
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 3695408442..a7ac827049 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -2832,9 +2832,9 @@ WKTParser::Private::buildCS(const WKTNodeNNPtr &node, /* maybe null */
children[1]->GP()->value()));
}
} else {
- const char *csTypeCStr = "";
+ const char *csTypeCStr = CartesianCS::WKT2_TYPE;
if (ci_equal(parentNodeName, WKTConstants::GEOCCS)) {
- csTypeCStr = CartesianCS::WKT2_TYPE;
+ // csTypeCStr = CartesianCS::WKT2_TYPE;
isGeocentric = true;
if (axisCount == 0) {
auto unit =
diff --git a/src/iso19111/operation/parametervalue.cpp b/src/iso19111/operation/parametervalue.cpp
new file mode 100644
index 0000000000..33f907fe7c
--- /dev/null
+++ b/src/iso19111/operation/parametervalue.cpp
@@ -0,0 +1,333 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: ISO19111:2019 implementation
+ * Author: Even Rouault
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault
+ *
+ * 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/coordinateoperation.hpp"
+#include "proj/util.hpp"
+
+#include "proj/internal/internal.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace NS_PROJ::internal;
+
+// ---------------------------------------------------------------------------
+
+NS_PROJ_START
+namespace operation {
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+struct ParameterValue::Private {
+ ParameterValue::Type type_{ParameterValue::Type::STRING};
+ std::unique_ptr measure_{};
+ std::unique_ptr stringValue_{};
+ int integerValue_{};
+ bool booleanValue_{};
+
+ explicit Private(const common::Measure &valueIn)
+ : type_(ParameterValue::Type::MEASURE),
+ measure_(internal::make_unique(valueIn)) {}
+
+ Private(const std::string &stringValueIn, ParameterValue::Type typeIn)
+ : type_(typeIn),
+ stringValue_(internal::make_unique(stringValueIn)) {}
+
+ explicit Private(int integerValueIn)
+ : type_(ParameterValue::Type::INTEGER), integerValue_(integerValueIn) {}
+
+ explicit Private(bool booleanValueIn)
+ : type_(ParameterValue::Type::BOOLEAN), booleanValue_(booleanValueIn) {}
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+ParameterValue::~ParameterValue() = default;
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+ParameterValue::ParameterValue(const common::Measure &measureIn)
+ : d(internal::make_unique(measureIn)) {}
+
+// ---------------------------------------------------------------------------
+
+ParameterValue::ParameterValue(const std::string &stringValueIn,
+ ParameterValue::Type typeIn)
+ : d(internal::make_unique(stringValueIn, typeIn)) {}
+
+// ---------------------------------------------------------------------------
+
+ParameterValue::ParameterValue(int integerValueIn)
+ : d(internal::make_unique(integerValueIn)) {}
+
+// ---------------------------------------------------------------------------
+
+ParameterValue::ParameterValue(bool booleanValueIn)
+ : d(internal::make_unique(booleanValueIn)) {}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a Measure (i.e. a value associated
+ * with a
+ * unit)
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr ParameterValue::create(const common::Measure &measureIn) {
+ return ParameterValue::nn_make_shared(measureIn);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a string value.
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr ParameterValue::create(const char *stringValueIn) {
+ return ParameterValue::nn_make_shared(
+ std::string(stringValueIn), ParameterValue::Type::STRING);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a string value.
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr ParameterValue::create(const std::string &stringValueIn) {
+ return ParameterValue::nn_make_shared(
+ stringValueIn, ParameterValue::Type::STRING);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a filename.
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr
+ParameterValue::createFilename(const std::string &stringValueIn) {
+ return ParameterValue::nn_make_shared(
+ stringValueIn, ParameterValue::Type::FILENAME);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a integer value.
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr ParameterValue::create(int integerValueIn) {
+ return ParameterValue::nn_make_shared(integerValueIn);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Instantiate a ParameterValue from a boolean value.
+ *
+ * @return a new ParameterValue.
+ */
+ParameterValueNNPtr ParameterValue::create(bool booleanValueIn) {
+ return ParameterValue::nn_make_shared(booleanValueIn);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the type of a parameter value.
+ *
+ * @return the type.
+ */
+const ParameterValue::Type &ParameterValue::type() PROJ_PURE_DEFN {
+ return d->type_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the value as a Measure (assumes type() == Type::MEASURE)
+ * @return the value as a Measure.
+ */
+const common::Measure &ParameterValue::value() PROJ_PURE_DEFN {
+ return *d->measure_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the value as a string (assumes type() == Type::STRING)
+ * @return the value as a string.
+ */
+const std::string &ParameterValue::stringValue() PROJ_PURE_DEFN {
+ return *d->stringValue_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the value as a filename (assumes type() == Type::FILENAME)
+ * @return the value as a filename.
+ */
+const std::string &ParameterValue::valueFile() PROJ_PURE_DEFN {
+ return *d->stringValue_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the value as a integer (assumes type() == Type::INTEGER)
+ * @return the value as a integer.
+ */
+int ParameterValue::integerValue() PROJ_PURE_DEFN { return d->integerValue_; }
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the value as a boolean (assumes type() == Type::BOOLEAN)
+ * @return the value as a boolean.
+ */
+bool ParameterValue::booleanValue() PROJ_PURE_DEFN { return d->booleanValue_; }
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const {
+ const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
+
+ const auto &l_type = type();
+ if (l_type == Type::MEASURE) {
+ const auto &l_value = value();
+ if (formatter->abridgedTransformation()) {
+ const auto &unit = l_value.unit();
+ const auto &unitType = unit.type();
+ if (unitType == common::UnitOfMeasure::Type::LINEAR) {
+ formatter->add(l_value.getSIValue());
+ } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) {
+ formatter->add(
+ l_value.convertToUnit(common::UnitOfMeasure::ARC_SECOND));
+ } else if (unit == common::UnitOfMeasure::PARTS_PER_MILLION) {
+ formatter->add(1.0 + l_value.value() * 1e-6);
+ } else {
+ formatter->add(l_value.value());
+ }
+ } else {
+ const auto &unit = l_value.unit();
+ if (isWKT2) {
+ formatter->add(l_value.value());
+ } else {
+ // In WKT1, as we don't output the natural unit, output to the
+ // registered linear / angular unit.
+ const auto &unitType = unit.type();
+ if (unitType == common::UnitOfMeasure::Type::LINEAR) {
+ const auto &targetUnit = *(formatter->axisLinearUnit());
+ if (targetUnit.conversionToSI() == 0.0) {
+ throw io::FormattingException(
+ "cannot convert value to target linear unit");
+ }
+ formatter->add(l_value.convertToUnit(targetUnit));
+ } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) {
+ const auto &targetUnit = *(formatter->axisAngularUnit());
+ if (targetUnit.conversionToSI() == 0.0) {
+ throw io::FormattingException(
+ "cannot convert value to target angular unit");
+ }
+ formatter->add(l_value.convertToUnit(targetUnit));
+ } else {
+ formatter->add(l_value.getSIValue());
+ }
+ }
+ if (isWKT2 && unit != common::UnitOfMeasure::NONE) {
+ if (!formatter
+ ->primeMeridianOrParameterUnitOmittedIfSameAsAxis() ||
+ (unit != common::UnitOfMeasure::SCALE_UNITY &&
+ unit != *(formatter->axisLinearUnit()) &&
+ unit != *(formatter->axisAngularUnit()))) {
+ unit._exportToWKT(formatter);
+ }
+ }
+ }
+ } else if (l_type == Type::STRING || l_type == Type::FILENAME) {
+ formatter->addQuotedString(stringValue());
+ } else if (l_type == Type::INTEGER) {
+ formatter->add(integerValue());
+ } else {
+ throw io::FormattingException("boolean parameter value not handled");
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+bool ParameterValue::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &) const {
+ auto otherPV = dynamic_cast(other);
+ if (otherPV == nullptr) {
+ return false;
+ }
+ if (type() != otherPV->type()) {
+ return false;
+ }
+ switch (type()) {
+ case Type::MEASURE: {
+ return value()._isEquivalentTo(otherPV->value(), criterion, 2e-10);
+ }
+
+ case Type::STRING:
+ case Type::FILENAME: {
+ return stringValue() == otherPV->stringValue();
+ }
+
+ case Type::INTEGER: {
+ return integerValue() == otherPV->integerValue();
+ }
+
+ case Type::BOOLEAN: {
+ return booleanValue() == otherPV->booleanValue();
+ }
+
+ default: {
+ assert(false);
+ break;
+ }
+ }
+ return true;
+}
+//! @endcond
+// ---------------------------------------------------------------------------
+
+} // namespace operation
+
+NS_PROJ_END
diff --git a/src/iso19111/operation/singleoperation.cpp b/src/iso19111/operation/singleoperation.cpp
index a0016b9003..48fee7090b 100644
--- a/src/iso19111/operation/singleoperation.cpp
+++ b/src/iso19111/operation/singleoperation.cpp
@@ -45,6 +45,7 @@
#include "operationmethod_private.hpp"
#include "oputils.hpp"
#include "parammappings.hpp"
+#include "vectorofvaluesparams.hpp"
// PROJ include order is sensitive
// clang-format off
@@ -2285,18 +2286,13 @@ createNTv1(const util::PropertyMap &properties,
const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn,
const std::string &filename,
const std::vector &accuracies) {
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)};
+ const VectorOfValues values{ParameterValue::createFilename(filename)};
return Transformation::create(
properties, sourceCRSIn, targetCRSIn, nullptr,
- createMethodMapNameEPSGCode(EPSG_CODE_METHOD_NTV1),
- {OperationParameter::create(
- util::PropertyMap()
- .set(common::IdentifiedObject::NAME_KEY,
- EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)
- .set(metadata::Identifier::CODESPACE_KEY,
- metadata::Identifier::EPSG)
- .set(metadata::Identifier::CODE_KEY,
- EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE))},
- {ParameterValue::createFilename(filename)}, accuracies);
+ createMethodMapNameEPSGCode(EPSG_CODE_METHOD_NTV1), parameters, values,
+ accuracies);
}
//! @endcond
@@ -2417,7 +2413,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
const auto &l_accuracies = coordinateOperationAccuracies();
if (projGridFormat == "GTiff") {
- auto parameters = std::vector{
+ const VectorOfParameters parameters{
methodEPSGCode == EPSG_CODE_METHOD_NADCON5_3D
? OperationParameter::create(util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY,
@@ -2430,7 +2426,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
methodEPSGCode == EPSG_CODE_METHOD_NADCON5_3D)
? PROJ_WKT2_NAME_METHOD_GENERAL_SHIFT_GTIFF
: PROJ_WKT2_NAME_METHOD_HORIZONTAL_SHIFT_GTIFF);
- auto values = std::vector{
+ const VectorOfValues values{
ParameterValue::createFilename(projFilename)};
if (inverseDirection) {
return Transformation::create(
@@ -2471,13 +2467,12 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
l_targetCRS, projFilename, l_accuracies);
}
} else if (projGridFormat == "CTable2") {
- auto parameters =
- std::vector{createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)};
auto methodProperties =
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
PROJ_WKT2_NAME_METHOD_CTABLE2);
- auto values = std::vector{
+ const VectorOfValues values{
ParameterValue::createFilename(projFilename)};
if (inverseDirection) {
return Transformation::create(
@@ -2528,9 +2523,10 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters = std::vector{
- createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
#ifdef disabled_for_now
if (inverseDirection) {
return Transformation::create(
@@ -2538,8 +2534,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
self.as_nullable().get(), true, false),
l_targetCRS, l_sourceCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()),
- parameters,
- {ParameterValue::createFilename(projFilename)},
+ parameters, values,
coordinateOperationAccuracies())
->inverseAsTransformation();
} else
@@ -2549,8 +2544,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
- coordinateOperationAccuracies());
+ values, coordinateOperationAccuracies());
}
}
}
@@ -2583,14 +2577,14 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters =
- std::vector{createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_GEOCENTRIC_TRANSLATION_FILE)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_GEOCENTRIC_TRANSLATION_FILE)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
return Transformation::create(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
- createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
+ createSimilarPropertiesMethod(method()), parameters, values,
coordinateOperationAccuracies());
}
}
@@ -2622,14 +2616,14 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters =
- std::vector{createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
return Transformation::create(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
- createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
+ createSimilarPropertiesMethod(method()), parameters, values,
coordinateOperationAccuracies());
}
}
@@ -2661,14 +2655,14 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters =
- std::vector{createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
return Transformation::create(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
- createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
+ createSimilarPropertiesMethod(method()), parameters, values,
coordinateOperationAccuracies());
}
}
@@ -2712,16 +2706,17 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters = std::vector{
+ const VectorOfParameters parameters{
createOpParamNameEPSGCode(parameterCode)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
if (inverseDirection) {
return Transformation::create(
createPropertiesForInverse(
self.as_nullable().get(), true, false),
l_targetCRS, l_sourceCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()),
- parameters,
- {ParameterValue::createFilename(projFilename)},
+ parameters, values,
coordinateOperationAccuracies())
->inverseAsTransformation();
} else {
@@ -2729,8 +2724,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
- coordinateOperationAccuracies());
+ values, coordinateOperationAccuracies());
}
}
}
@@ -2786,18 +2780,18 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
}
auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull);
auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull);
- auto parameters = std::vector{
+ const VectorOfParameters parameters{
createOpParamNameEPSGCode(
gridTransf.gridFilenameParamEPSGCode)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
if (inverseDirection) {
return Transformation::create(
createPropertiesForInverse(
self.as_nullable().get(), true, false),
l_targetCRS, l_sourceCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()),
- parameters,
- {ParameterValue::createFilename(
- projFilename)},
+ parameters, values,
coordinateOperationAccuracies())
->inverseAsTransformation();
} else {
@@ -2805,8 +2799,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
createSimilarPropertiesOperation(self), l_sourceCRS,
l_targetCRS, l_interpolationCRS,
createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
- coordinateOperationAccuracies());
+ values, coordinateOperationAccuracies());
}
}
}
@@ -2817,284 +2810,6 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames(
return self;
}
-// ---------------------------------------------------------------------------
-
-//! @cond Doxygen_Suppress
-struct ParameterValue::Private {
- ParameterValue::Type type_{ParameterValue::Type::STRING};
- std::unique_ptr measure_{};
- std::unique_ptr stringValue_{};
- int integerValue_{};
- bool booleanValue_{};
-
- explicit Private(const common::Measure &valueIn)
- : type_(ParameterValue::Type::MEASURE),
- measure_(internal::make_unique(valueIn)) {}
-
- Private(const std::string &stringValueIn, ParameterValue::Type typeIn)
- : type_(typeIn),
- stringValue_(internal::make_unique(stringValueIn)) {}
-
- explicit Private(int integerValueIn)
- : type_(ParameterValue::Type::INTEGER), integerValue_(integerValueIn) {}
-
- explicit Private(bool booleanValueIn)
- : type_(ParameterValue::Type::BOOLEAN), booleanValue_(booleanValueIn) {}
-};
-//! @endcond
-
-// ---------------------------------------------------------------------------
-
-//! @cond Doxygen_Suppress
-ParameterValue::~ParameterValue() = default;
-//! @endcond
-
-// ---------------------------------------------------------------------------
-
-ParameterValue::ParameterValue(const common::Measure &measureIn)
- : d(internal::make_unique(measureIn)) {}
-
-// ---------------------------------------------------------------------------
-
-ParameterValue::ParameterValue(const std::string &stringValueIn,
- ParameterValue::Type typeIn)
- : d(internal::make_unique(stringValueIn, typeIn)) {}
-
-// ---------------------------------------------------------------------------
-
-ParameterValue::ParameterValue(int integerValueIn)
- : d(internal::make_unique(integerValueIn)) {}
-
-// ---------------------------------------------------------------------------
-
-ParameterValue::ParameterValue(bool booleanValueIn)
- : d(internal::make_unique(booleanValueIn)) {}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a Measure (i.e. a value associated
- * with a
- * unit)
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr ParameterValue::create(const common::Measure &measureIn) {
- return ParameterValue::nn_make_shared(measureIn);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a string value.
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr ParameterValue::create(const char *stringValueIn) {
- return ParameterValue::nn_make_shared(
- std::string(stringValueIn), ParameterValue::Type::STRING);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a string value.
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr ParameterValue::create(const std::string &stringValueIn) {
- return ParameterValue::nn_make_shared(
- stringValueIn, ParameterValue::Type::STRING);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a filename.
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr
-ParameterValue::createFilename(const std::string &stringValueIn) {
- return ParameterValue::nn_make_shared(
- stringValueIn, ParameterValue::Type::FILENAME);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a integer value.
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr ParameterValue::create(int integerValueIn) {
- return ParameterValue::nn_make_shared(integerValueIn);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Instantiate a ParameterValue from a boolean value.
- *
- * @return a new ParameterValue.
- */
-ParameterValueNNPtr ParameterValue::create(bool booleanValueIn) {
- return ParameterValue::nn_make_shared(booleanValueIn);
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the type of a parameter value.
- *
- * @return the type.
- */
-const ParameterValue::Type &ParameterValue::type() PROJ_PURE_DEFN {
- return d->type_;
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the value as a Measure (assumes type() == Type::MEASURE)
- * @return the value as a Measure.
- */
-const common::Measure &ParameterValue::value() PROJ_PURE_DEFN {
- return *d->measure_;
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the value as a string (assumes type() == Type::STRING)
- * @return the value as a string.
- */
-const std::string &ParameterValue::stringValue() PROJ_PURE_DEFN {
- return *d->stringValue_;
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the value as a filename (assumes type() == Type::FILENAME)
- * @return the value as a filename.
- */
-const std::string &ParameterValue::valueFile() PROJ_PURE_DEFN {
- return *d->stringValue_;
-}
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the value as a integer (assumes type() == Type::INTEGER)
- * @return the value as a integer.
- */
-int ParameterValue::integerValue() PROJ_PURE_DEFN { return d->integerValue_; }
-
-// ---------------------------------------------------------------------------
-
-/** \brief Returns the value as a boolean (assumes type() == Type::BOOLEAN)
- * @return the value as a boolean.
- */
-bool ParameterValue::booleanValue() PROJ_PURE_DEFN { return d->booleanValue_; }
-
-// ---------------------------------------------------------------------------
-
-//! @cond Doxygen_Suppress
-void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const {
- const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
-
- const auto &l_type = type();
- if (l_type == Type::MEASURE) {
- const auto &l_value = value();
- if (formatter->abridgedTransformation()) {
- const auto &unit = l_value.unit();
- const auto &unitType = unit.type();
- if (unitType == common::UnitOfMeasure::Type::LINEAR) {
- formatter->add(l_value.getSIValue());
- } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) {
- formatter->add(
- l_value.convertToUnit(common::UnitOfMeasure::ARC_SECOND));
- } else if (unit == common::UnitOfMeasure::PARTS_PER_MILLION) {
- formatter->add(1.0 + l_value.value() * 1e-6);
- } else {
- formatter->add(l_value.value());
- }
- } else {
- const auto &unit = l_value.unit();
- if (isWKT2) {
- formatter->add(l_value.value());
- } else {
- // In WKT1, as we don't output the natural unit, output to the
- // registered linear / angular unit.
- const auto &unitType = unit.type();
- if (unitType == common::UnitOfMeasure::Type::LINEAR) {
- const auto &targetUnit = *(formatter->axisLinearUnit());
- if (targetUnit.conversionToSI() == 0.0) {
- throw io::FormattingException(
- "cannot convert value to target linear unit");
- }
- formatter->add(l_value.convertToUnit(targetUnit));
- } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) {
- const auto &targetUnit = *(formatter->axisAngularUnit());
- if (targetUnit.conversionToSI() == 0.0) {
- throw io::FormattingException(
- "cannot convert value to target angular unit");
- }
- formatter->add(l_value.convertToUnit(targetUnit));
- } else {
- formatter->add(l_value.getSIValue());
- }
- }
- if (isWKT2 && unit != common::UnitOfMeasure::NONE) {
- if (!formatter
- ->primeMeridianOrParameterUnitOmittedIfSameAsAxis() ||
- (unit != common::UnitOfMeasure::SCALE_UNITY &&
- unit != *(formatter->axisLinearUnit()) &&
- unit != *(formatter->axisAngularUnit()))) {
- unit._exportToWKT(formatter);
- }
- }
- }
- } else if (l_type == Type::STRING || l_type == Type::FILENAME) {
- formatter->addQuotedString(stringValue());
- } else if (l_type == Type::INTEGER) {
- formatter->add(integerValue());
- } else {
- throw io::FormattingException("boolean parameter value not handled");
- }
-}
-//! @endcond
-
-// ---------------------------------------------------------------------------
-
-//! @cond Doxygen_Suppress
-bool ParameterValue::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion,
- const io::DatabaseContextPtr &) const {
- auto otherPV = dynamic_cast(other);
- if (otherPV == nullptr) {
- return false;
- }
- if (type() != otherPV->type()) {
- return false;
- }
- switch (type()) {
- case Type::MEASURE: {
- return value()._isEquivalentTo(otherPV->value(), criterion, 2e-10);
- }
-
- case Type::STRING:
- case Type::FILENAME: {
- return stringValue() == otherPV->stringValue();
- }
-
- case Type::INTEGER: {
- return integerValue() == otherPV->integerValue();
- }
-
- case Type::BOOLEAN: {
- return booleanValue() == otherPV->booleanValue();
- }
-
- default: {
- assert(false);
- break;
- }
- }
- return true;
-}
-//! @endcond
-
//! @cond Doxygen_Suppress
// ---------------------------------------------------------------------------
@@ -5182,13 +4897,13 @@ PointMotionOperation::substitutePROJAlternativeGridNames(
return self;
}
- auto parameters =
- std::vector{createOpParamNameEPSGCode(
- EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfParameters parameters{createOpParamNameEPSGCode(
+ EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)};
+ const VectorOfValues values{
+ ParameterValue::createFilename(projFilename)};
return PointMotionOperation::create(
createSimilarPropertiesOperation(self), sourceCRS(),
- createSimilarPropertiesMethod(method()), parameters,
- {ParameterValue::createFilename(projFilename)},
+ createSimilarPropertiesMethod(method()), parameters, values,
coordinateOperationAccuracies());
}
diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake
index ace425c32b..5e59b91fab 100644
--- a/src/lib_proj.cmake
+++ b/src/lib_proj.cmake
@@ -187,6 +187,7 @@ set(SRC_LIBPROJ_ISO19111
iso19111/operation/conversion.cpp
iso19111/operation/esriparammappings.cpp
iso19111/operation/oputils.cpp
+ iso19111/operation/parametervalue.cpp
iso19111/operation/parammappings.cpp
iso19111/operation/projbasedoperation.cpp
iso19111/operation/singleoperation.cpp
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 860927fefd..09d0c5ef16 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -60,10 +60,12 @@ elseif(HAS_NETWORK)
cmake_policy(SET CMP0135 NEW) # for DOWNLOAD_EXTRACT_TIMESTAMP option
endif()
+ set(GTEST_VERSION "1.15.2")
+
include(FetchContent)
FetchContent_Declare(
googletest
- URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip
+ URL https://github.com/google/googletest/archive/refs/tags/v${GTEST_VERSION}.zip
EXCLUDE_FROM_ALL # ignored before CMake 3.28
)