aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.github/workflows/clang_linux/start.sh2
-rwxr-xr-x.github/workflows/linux_gcc_32bit/start.sh4
-rwxr-xr-x.github/workflows/linux_gcc_4_8/start.sh10
-rw-r--r--docs/docbuild/Dockerfile2
-rw-r--r--docs/source/community/code_contributions.rst2
-rw-r--r--docs/source/development/bindings.rst4
-rw-r--r--docs/source/development/reference/datatypes.rst2
-rw-r--r--docs/source/development/reference/functions.rst4
-rw-r--r--docs/source/operations/conversions/cart.rst2
-rw-r--r--docs/source/operations/operations_computation.rst8
-rw-r--r--docs/source/operations/transformations/affine.rst2
-rw-r--r--docs/source/operations/transformations/defmodel.rst2
-rw-r--r--docs/source/operations/transformations/deformation.rst2
-rw-r--r--docs/source/operations/transformations/helmert.rst2
-rw-r--r--docs/source/operations/transformations/horner.rst2
-rw-r--r--docs/source/operations/transformations/tinshift.rst2
-rw-r--r--docs/source/operations/transformations/vgridshift.rst2
-rw-r--r--src/iso19111/operation/coordinateoperationfactory.cpp110
-rw-r--r--test/unit/test_operationfactory.cpp443
-rwxr-xr-xtravis/before_install_pip.sh8
20 files changed, 570 insertions, 45 deletions
diff --git a/.github/workflows/clang_linux/start.sh b/.github/workflows/clang_linux/start.sh
index 83d2f8c2..a2b6c631 100755
--- a/.github/workflows/clang_linux/start.sh
+++ b/.github/workflows/clang_linux/start.sh
@@ -7,7 +7,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
sudo autoconf automake libtool clang++-10 python3-clang-10 make cmake ccache pkg-config tar zip \
sqlite3 libsqlite3-dev libtiff-dev libcurl4-openssl-dev jq python3-pip
-pip3 install --user jsonschema
+python3 -m pip install --user jsonschema
cd "$WORK_DIR"
diff --git a/.github/workflows/linux_gcc_32bit/start.sh b/.github/workflows/linux_gcc_32bit/start.sh
index 42746fe8..81f1dc39 100755
--- a/.github/workflows/linux_gcc_32bit/start.sh
+++ b/.github/workflows/linux_gcc_32bit/start.sh
@@ -11,14 +11,14 @@ ARCH=i386
dpkg --add-architecture i386
apt update -y
-DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends -o APT::Immediate-Configure=0 \
autoconf automake libtool gcc-multilib g++-multilib g++ sqlite3 \
python3-pip \
make cmake ccache pkg-config tar zip \
libsqlite3-dev:$ARCH libtiff-dev:$ARCH libcurl4-openssl-dev:$ARCH \
jq
-pip3 install --user jsonschema
+python3 -m pip install --user jsonschema
export PATH=$HOME/.local/bin:$PATH
export CXXFLAGS='-g -O2 -m32 -D_GLIBCXX_ASSERTIONS'
diff --git a/.github/workflows/linux_gcc_4_8/start.sh b/.github/workflows/linux_gcc_4_8/start.sh
index 2493ea1a..eb03d78d 100755
--- a/.github/workflows/linux_gcc_4_8/start.sh
+++ b/.github/workflows/linux_gcc_4_8/start.sh
@@ -15,11 +15,11 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libsqlite3-dev libtiff-dev libcurl4-openssl-dev \
jq lcov
-pip3 install --user --upgrade pip
-echo `pip3 --version`
-pip3 config --user set global.progress_bar off
-pip3 install --user jsonschema
-pip3 install --user cmake==3.9.6
+python3 -m pip install --user --upgrade "pip < 21.0"
+echo `python3 -m pip --version`
+python3 -m pip config --user set global.progress_bar off
+python3 -m pip install --user jsonschema
+python3 -m pip install --user cmake==3.9.6
export PATH=$HOME/.local/bin:$PATH
diff --git a/docs/docbuild/Dockerfile b/docs/docbuild/Dockerfile
index 4f726930..95841d9c 100644
--- a/docs/docbuild/Dockerfile
+++ b/docs/docbuild/Dockerfile
@@ -6,7 +6,7 @@ RUN apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
texlive-latex-extra git latex-cjk-all texlive-lang-all \
graphviz python3-matplotlib wget unzip enchant
-RUN pip3 install Sphinx breathe \
+RUN python3 -m pip install Sphinx breathe \
sphinx_bootstrap_theme awscli sphinxcontrib-bibtex \
sphinx_rtd_theme recommonmark sphinx-markdown-tables \
sphinxcontrib-spelling
diff --git a/docs/source/community/code_contributions.rst b/docs/source/community/code_contributions.rst
index 17d358ee..7f8c88fa 100644
--- a/docs/source/community/code_contributions.rst
+++ b/docs/source/community/code_contributions.rst
@@ -60,7 +60,7 @@ Coding style
^^^^^^^^^^^^
The parts of the code base that has started its life as C++ is formatted with
-``clang-format`` using the sript ``scripts/reformat_cpp.sh``. This is mostly
+``clang-format`` using the script ``scripts/reformat_cpp.sh``. This is mostly
contained to the code in `src/iso19111/` but a few other `.cpp`-files are
covered as well.
diff --git a/docs/source/development/bindings.rst b/docs/source/development/bindings.rst
index 26becb92..c2d13077 100644
--- a/docs/source/development/bindings.rst
+++ b/docs/source/development/bindings.rst
@@ -30,8 +30,8 @@ Go bindings and idiomatic wrapper for PROJ
Julia
=====
-`Proj4.jl <https://github.com/JuliaGeo/Proj4.jl>`_"
-Low-level bindings and a Julian API over PROJ.
+`Proj4.jl <https://github.com/JuliaGeo/Proj4.jl>`_:
+Julia bindings and idiomatic wrapper for PROJ.
TCL
========
diff --git a/docs/source/development/reference/datatypes.rst b/docs/source/development/reference/datatypes.rst
index ed219daf..cb90e8db 100644
--- a/docs/source/development/reference/datatypes.rst
+++ b/docs/source/development/reference/datatypes.rst
@@ -824,7 +824,7 @@ Errors in class PROJ_ERR_INVALID_OP
.. c:macro:: PROJ_ERR_INVALID_OP
- Class of error codes typically related to coordinate operation initalization,
+ Class of error codes typically related to coordinate operation initialization,
typically when creating a PJ* object from a PROJ string.
.. note:: some of them can also be emitted during coordinate transformation,
diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst
index 34fe59ed..d06179f3 100644
--- a/docs/source/development/reference/functions.rst
+++ b/docs/source/development/reference/functions.rst
@@ -180,7 +180,7 @@ paragraph for more details.
coordinate reference systems.
This is the same as :c:func:`proj_create_crs_to_crs` except that the source and
- target CRS are passed as PJ* objects which must of the CRS variety.
+ target CRS are passed as PJ* objects which must be of the CRS variety.
:param `options`: a list of NUL terminated options, or NULL.
@@ -285,7 +285,7 @@ Coordinate transformation
3. of length one, i.e. a constant, which will be treated as a fully
populated array of that constant value
- .. note:: Even though he coordinate components are named :c:data:`x`, :c:data:`y`,
+ .. note:: Even though the coordinate components are named :c:data:`x`, :c:data:`y`,
:c:data:`z` and :c:data:`t`, axis ordering of the to and from CRS
is respected. Transformations exhibit the same behavior
as if they were gathered in a :c:type:`PJ_COORD` struct.
diff --git a/docs/source/operations/conversions/cart.rst b/docs/source/operations/conversions/cart.rst
index 111eb7d2..239b1772 100644
--- a/docs/source/operations/conversions/cart.rst
+++ b/docs/source/operations/conversions/cart.rst
@@ -21,7 +21,7 @@ Convert geodetic coordinates to cartesian coordinates (in the forward path).
This conversion converts geodetic coordinate values (longitude, latitude,
elevation above ellipsoid) to their geocentric (X, Y, Z) representation, where
the first axis (X) points from the Earth centre to the point of longitude=0,
-latitude=0, the second axis (Y) points from the Earth centrer to the point of
+latitude=0, the second axis (Y) points from the Earth centre to the point of
longitude=90, latitude=0 and the third axis (Z) points to the North pole.
Usage
diff --git a/docs/source/operations/operations_computation.rst b/docs/source/operations/operations_computation.rst
index f1453257..af177f6c 100644
--- a/docs/source/operations/operations_computation.rst
+++ b/docs/source/operations/operations_computation.rst
@@ -27,7 +27,7 @@ This document is meant as a plain text explanation of the code for developers,
but also as a in-depth examination of what happens under the hood for curious PROJ
users. It is important to keep in mind that it is not meant to be the ultimate
source of truth of how coordinate operations should be computed. There are clearly
-implementation choices and compromises that can be questionned.
+implementation choices and compromises that can be questioned.
Let us start with an example to research operations between the NAD27 and NAD83
geographic CRS:
@@ -123,7 +123,7 @@ must be retained or discarded:
The sorting algorithm determines the order of relevance of the operations we got.
A comparison function compares pair of operations to determine which of the
-two is the most releavant. This is implemented by the :cpp:func:`operator ()`
+two is the most relevant. This is implemented by the :cpp:func:`operator ()`
method of the SortFunction structure.
When comparing two operations, the following criteria are used. The tests are
performed in the order they are listed below:
@@ -163,7 +163,7 @@ In a number of situations, the source and/or target CRS do not have an identifie
The first step is to try to find in the ``proj.db`` a CRS of the same nature of
the CRS to identify and whose name exactly matches the one provided to the
:c:func:`createOperations` method. If there is exactly one match and that the CRS are
-"computationnaly" equivalent, then use the code of the CRS for further computations.
+"computationally" equivalent, then use the code of the CRS for further computations.
If this search did not succeed, or if the previous case with known CRS identifiers
did not result in matches in the database, the search will be based on the
@@ -195,7 +195,7 @@ one can define a geographic 2D CRS (latitude, longitude), a geographic 3D crs
case, the EPSG dataset has also included two extra definitions corresponding to a
longitude, latitude, [ellipsoidal height] coordinate system, as found in the official
French IGNF registry. This IGNF registry has also definitions for a geographic 2D
-CRS (with an extra subtelty with an entry using decimal degree as unit and another
+CRS (with an extra subtlety with an entry using decimal degree as unit and another
one degree-minute-second), geographic 3D and geocentric.
For EPSG:6258, there are 7 matching (non-deprecated) geodetic CRSs:
diff --git a/docs/source/operations/transformations/affine.rst b/docs/source/operations/transformations/affine.rst
index 8b1681b3..d2a4e26b 100644
--- a/docs/source/operations/transformations/affine.rst
+++ b/docs/source/operations/transformations/affine.rst
@@ -7,7 +7,7 @@ Affine transformation
.. versionadded:: 6.0.0
The affine transformation applies translation and scaling/rotation terms on the
-x,y,z coordinates, and translation and scaling on the temporal cordinate.
+x,y,z coordinates, and translation and scaling on the temporal coordinate.
+---------------------+----------------------------------------------------------+
| **Alias** | affine |
diff --git a/docs/source/operations/transformations/defmodel.rst b/docs/source/operations/transformations/defmodel.rst
index a3f75d93..77906924 100644
--- a/docs/source/operations/transformations/defmodel.rst
+++ b/docs/source/operations/transformations/defmodel.rst
@@ -56,7 +56,7 @@ Required
Example
-------------------------------------------------------------------------------
-Transformating a point with the LINZ NZGD2000 deformation model:
+Transforming a point with the LINZ NZGD2000 deformation model:
::
diff --git a/docs/source/operations/transformations/deformation.rst b/docs/source/operations/transformations/deformation.rst
index e6cb9b88..84b87949 100644
--- a/docs/source/operations/transformations/deformation.rst
+++ b/docs/source/operations/transformations/deformation.rst
@@ -167,7 +167,7 @@ represented as a grid of velocities in three dimensions. Coordinate corrections
applied in cartesian space. For a given coordinate, :math:`(X, Y, Z)`, velocities
:math:`(V_X, V_Y, V_Z)` can be interpolated from the gridded model. The time span
between :math:`t_{obs}` and :math:`t_c` determine the magnitude of the coordinate
-correcton as seen in eq. :eq:`apply_velocity` below.
+correction as seen in eq. :eq:`apply_velocity` below.
.. math::
:label: apply_velocity
diff --git a/docs/source/operations/transformations/helmert.rst b/docs/source/operations/transformations/helmert.rst
index 34acc332..c0f6934f 100644
--- a/docs/source/operations/transformations/helmert.rst
+++ b/docs/source/operations/transformations/helmert.rst
@@ -349,7 +349,7 @@ For :math:`\mathbf{R}`, this yields:
\end{bmatrix}
-Using the small angle approxition the rotation matrix can be simplified to
+Using the small angle approximation the rotation matrix can be simplified to
.. math::
:label: rot_approx
diff --git a/docs/source/operations/transformations/horner.rst b/docs/source/operations/transformations/horner.rst
index 5c88c292..ad09e8ab 100644
--- a/docs/source/operations/transformations/horner.rst
+++ b/docs/source/operations/transformations/horner.rst
@@ -17,7 +17,7 @@ Horner polynomial evaluation
+-----------------+-------------------------------------------------------------------+
The Horner polynomial evaluation scheme is used for transformations between reference
-frames where one or both are inhomogenous or internally distorted. This will typically
+frames where one or both are inhomogeneous or internally distorted. This will typically
be reference frames created before the introduction of space geodetic techniques such
as GPS.
diff --git a/docs/source/operations/transformations/tinshift.rst b/docs/source/operations/transformations/tinshift.rst
index c29d233a..ee7ae4a6 100644
--- a/docs/source/operations/transformations/tinshift.rst
+++ b/docs/source/operations/transformations/tinshift.rst
@@ -43,7 +43,7 @@ Required
Example
-------------------------------------------------------------------------------
-Transformating a point with the Finland EPSG:2393 ("KKJ / Finland Uniform
+Transforming a point with the Finland EPSG:2393 ("KKJ / Finland Uniform
Coordinate System") projected CRS to EPSG:3067 ("ETRS89 / TM35FIN(E,N)")
::
diff --git a/docs/source/operations/transformations/vgridshift.rst b/docs/source/operations/transformations/vgridshift.rst
index d4f1f97f..5a31ae8f 100644
--- a/docs/source/operations/transformations/vgridshift.rst
+++ b/docs/source/operations/transformations/vgridshift.rst
@@ -126,7 +126,7 @@ Optional
Z_{target} = Z_{source} + multiplier \times gridvalue
The multiplier can be used to control whether the gridvalue should be added
- or sustracted, and if unit conversion must be done (the multiplied gridvalue
+ or subtracted, and if unit conversion must be done (the multiplied gridvalue
must be expressed in metre).
Note that the default is `-1.0` for historical reasons.
diff --git a/src/iso19111/operation/coordinateoperationfactory.cpp b/src/iso19111/operation/coordinateoperationfactory.cpp
index b8a9bcdf..51262ff6 100644
--- a/src/iso19111/operation/coordinateoperationfactory.cpp
+++ b/src/iso19111/operation/coordinateoperationfactory.cpp
@@ -3285,21 +3285,62 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid(
ENTER_FUNCTION();
- const auto useTransf = [&targetCRS, &context,
+ const auto useTransf = [&sourceCRS, &targetCRS, &context,
vertDst](const CoordinateOperationNNPtr &op) {
+
+ // If the source geographic CRS has a non-metre vertical unit, we need
+ // to create an intermediate and operation to do the vertical unit
+ // conversion from that vertical unit to the one of the geographic CRS
+ // of the source of the operation
+ const auto geogCRS =
+ dynamic_cast<const crs::GeographicCRS *>(sourceCRS.get());
+ assert(geogCRS);
+ const auto &srcAxisList = geogCRS->coordinateSystem()->axisList();
+ CoordinateOperationPtr opPtr;
+ const auto opSourceCRSGeog =
+ dynamic_cast<const crs::GeographicCRS *>(op->sourceCRS().get());
+ // I assume opSourceCRSGeog should always be null in practice...
+ if (opSourceCRSGeog && srcAxisList.size() == 3 &&
+ srcAxisList[2]->unit().conversionToSI() != 1) {
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable()
+ : nullptr;
+ auto tmpCRSWithSrcZ =
+ opSourceCRSGeog->demoteTo2D(std::string(), dbContext)
+ ->promoteTo3D(std::string(), dbContext, srcAxisList[2]);
+
+ std::vector<CoordinateOperationNNPtr> opsUnitConvert;
+ createOperationsGeogToGeog(
+ opsUnitConvert, tmpCRSWithSrcZ, NN_NO_CHECK(op->sourceCRS()),
+ context,
+ dynamic_cast<const crs::GeographicCRS *>(tmpCRSWithSrcZ.get()),
+ opSourceCRSGeog);
+ assert(opsUnitConvert.size() == 1);
+ opPtr = opsUnitConvert.front().as_nullable();
+ }
+
+ std::vector<CoordinateOperationNNPtr> ops;
+ if (opPtr)
+ ops.emplace_back(NN_NO_CHECK(opPtr));
+ ops.emplace_back(op);
+
const auto targetOp =
dynamic_cast<const crs::VerticalCRS *>(op->targetCRS().get());
assert(targetOp);
if (targetOp->_isEquivalentTo(
vertDst, util::IComparable::Criterion::EQUIVALENT)) {
- return op;
+ auto ret = ConcatenatedOperation::createComputeMetadata(
+ ops, disallowEmptyIntersection);
+ return ret;
}
std::vector<CoordinateOperationNNPtr> tmp;
createOperationsVertToVert(NN_NO_CHECK(op->targetCRS()), targetCRS,
context, targetOp, vertDst, tmp);
assert(!tmp.empty());
+ ops.emplace_back(tmp.front());
auto ret = ConcatenatedOperation::createComputeMetadata(
- {op, tmp.front()}, disallowEmptyIntersection);
+ ops, disallowEmptyIntersection);
return ret;
};
@@ -3331,10 +3372,16 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid(
};
const auto &axis = vertDst->coordinateSystem()->axisList()[0];
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable()
+ : nullptr;
+
const auto geogSrcCRS =
dynamic_cast<crs::GeographicCRS *>(model->interpolationCRS().get())
? NN_NO_CHECK(model->interpolationCRS())
- : sourceCRS;
+ : sourceCRS->demoteTo2D(std::string(), dbContext)
+ ->promoteTo3D(std::string(), dbContext);
const auto vertCRSMetre =
axis->unit() == common::UnitOfMeasure::METRE &&
axis->direction() == cs::AxisDirection::UP
@@ -3356,7 +3403,6 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid(
std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
const auto &modelAccuracies = model->coordinateOperationAccuracies();
if (modelAccuracies.empty()) {
- const auto &authFactory = context.context->getAuthorityFactory();
if (authFactory) {
const auto transformationsForGrid =
io::DatabaseContext::getTransformationsForGridName(
@@ -3476,8 +3522,8 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private::
std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private::
createOperationsGeogToVertWithAlternativeGeog(
- const crs::CRSNNPtr & /*sourceCRS*/, // geographic CRS
- const crs::CRSNNPtr &targetCRS, // vertical CRS
+ const crs::CRSNNPtr &sourceCRS, // geographic CRS
+ const crs::CRSNNPtr &targetCRS, // vertical CRS
Private::Context &context) {
ENTER_FUNCTION();
@@ -3501,10 +3547,39 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private::
// Generally EPSG has operations from GeogCrs to VertCRS
auto ops = findOpsInRegistryDirectTo(targetCRS, context);
+ const auto geogCRS =
+ dynamic_cast<const crs::GeographicCRS *>(sourceCRS.get());
+ assert(geogCRS);
+ const auto &srcAxisList = geogCRS->coordinateSystem()->axisList();
for (const auto &op : ops) {
- const auto tmpCRS = op->sourceCRS();
- if (tmpCRS && dynamic_cast<const crs::GeographicCRS *>(tmpCRS.get())) {
- res.emplace_back(op);
+ const auto tmpCRS =
+ dynamic_cast<const crs::GeographicCRS *>(op->sourceCRS().get());
+ if (tmpCRS) {
+ if (srcAxisList.size() == 3 &&
+ srcAxisList[2]->unit().conversionToSI() != 1) {
+
+ const auto &authFactory =
+ context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory->databaseContext().as_nullable();
+ auto tmpCRSWithSrcZ =
+ tmpCRS->demoteTo2D(std::string(), dbContext)
+ ->promoteTo3D(std::string(), dbContext, srcAxisList[2]);
+
+ std::vector<CoordinateOperationNNPtr> opsUnitConvert;
+ createOperationsGeogToGeog(
+ opsUnitConvert, tmpCRSWithSrcZ,
+ NN_NO_CHECK(op->sourceCRS()), context,
+ dynamic_cast<const crs::GeographicCRS *>(
+ tmpCRSWithSrcZ.get()),
+ tmpCRS);
+ assert(opsUnitConvert.size() == 1);
+ auto concat = ConcatenatedOperation::createComputeMetadata(
+ {opsUnitConvert.front(), op}, disallowEmptyIntersection);
+ res.emplace_back(concat);
+ } else {
+ res.emplace_back(op);
+ }
}
}
@@ -4564,11 +4639,18 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
!srcGeogCRS->_isEquivalentTo(
geogDst, util::IComparable::Criterion::EQUIVALENT) &&
!srcGeogCRS->is2DPartOf3D(NN_NO_CHECK(geogDst), dbContext)) {
- auto verticalTransformsTmp = createOperations(
- componentsSrc[1],
+ auto geogCRSTmp =
NN_NO_CHECK(srcGeogCRS)
- ->promoteTo3D(std::string(), dbContext),
- context);
+ ->demoteTo2D(std::string(), dbContext)
+ ->promoteTo3D(
+ std::string(), dbContext,
+ geogDst->coordinateSystem()->axisList().size() == 3
+ ? geogDst->coordinateSystem()->axisList()[2]
+ : cs::VerticalCS::createGravityRelatedHeight(
+ common::UnitOfMeasure::METRE)
+ ->axisList()[0]);
+ auto verticalTransformsTmp =
+ createOperations(componentsSrc[1], geogCRSTmp, context);
bool foundRegisteredTransform = false;
foundRegisteredTransformWithAllGridsAvailable = false;
for (const auto &op : verticalTransformsTmp) {
diff --git a/test/unit/test_operationfactory.cpp b/test/unit/test_operationfactory.cpp
index 15467a69..32c872ac 100644
--- a/test/unit/test_operationfactory.cpp
+++ b/test/unit/test_operationfactory.cpp
@@ -5867,6 +5867,449 @@ TEST(operation, createOperation_on_crs_with_bound_crs_and_wktext) {
// ---------------------------------------------------------------------------
+TEST(operation, compoundCRS_to_proj_string_with_non_metre_height) {
+ auto objSrc =
+ createFromUserInput("EPSG:6318+5703", DatabaseContext::create(), false);
+ auto src = nn_dynamic_pointer_cast<CRS>(objSrc);
+ ASSERT_TRUE(src != nullptr);
+
+ auto objDst = PROJStringParser().createFromPROJString(
+ "+proj=longlat +ellps=GRS80 +vunits=us-ft +type=crs");
+ auto dst = nn_dynamic_pointer_cast<CRS>(objDst);
+ ASSERT_TRUE(dst != nullptr);
+
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
+ ctxt->setSpatialCriterion(
+ CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ NN_NO_CHECK(src), NN_NO_CHECK(dst), ctxt);
+ ASSERT_GT(list.size(), 1U);
+ // What is important to check here is the vertical unit conversion
+ EXPECT_EQ(
+ list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=axisswap +order=2,1 "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=vgridshift +grids=us_noaa_g2018u0.tif +multiplier=1 "
+ "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=us-ft");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(operation, compoundCRS_to_PROJJSON_with_non_metre_height) {
+ auto srcPROJJSON =
+ "{\n"
+ " \"$schema\": "
+ "\"https://proj.org/schemas/v0.2/projjson.schema.json\",\n"
+ " \"type\": \"CompoundCRS\",\n"
+ " \"name\": \"Compound CRS NAD83(2011) / Nebraska (ftUS) + North "
+ "American Vertical Datum 1988 + PROJ us_noaa_g2012bu0.tif\",\n"
+ " \"components\": [\n"
+ " {\n"
+ " \"type\": \"ProjectedCRS\",\n"
+ " \"name\": \"NAD83(2011) / Nebraska (ftUS)\",\n"
+ " \"base_crs\": {\n"
+ " \"name\": \"NAD83(2011)\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"NAD83 (National Spatial Reference System "
+ "2011)\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 6318\n"
+ " }\n"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"SPCS83 Nebraska zone (US Survey feet)\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Lambert Conic Conformal (2SP)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9802\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of false origin\",\n"
+ " \"value\": 39.8333333333333,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8821\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of false origin\",\n"
+ " \"value\": -100,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8822\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 1st standard parallel\",\n"
+ " \"value\": 43,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8823\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 2nd standard parallel\",\n"
+ " \"value\": 40,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8824\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Easting at false origin\",\n"
+ " \"value\": 1640416.6667,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8826\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing at false origin\",\n"
+ " \"value\": 0,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8827\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 15396\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"Cartesian\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Easting\",\n"
+ " \"abbreviation\": \"X\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing\",\n"
+ " \"abbreviation\": \"Y\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"type\": \"VerticalCRS\",\n"
+ " \"name\": \"North American Vertical Datum 1988 + PROJ "
+ "us_noaa_g2012bu0.tif\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"VerticalReferenceFrame\",\n"
+ " \"name\": \"North American Vertical Datum 1988\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 5103\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"vertical\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Gravity-related height\",\n"
+ " \"abbreviation\": \"H\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"geoid_model\": {\n"
+ " \"name\": \"PROJ us_noaa_g2012bu0.tif\",\n"
+ " \"interpolation_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"NAD83(2011)\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"NAD83 (National Spatial Reference System "
+ "2011)\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Ellipsoidal height\",\n"
+ " \"abbreviation\": \"h\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 6319\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ "}";
+ auto objSrc =
+ createFromUserInput(srcPROJJSON, DatabaseContext::create(), false);
+ auto src = nn_dynamic_pointer_cast<CRS>(objSrc);
+ ASSERT_TRUE(src != nullptr);
+
+ // The untypical potentially a bit buggy thing (and what caused a bug)
+ // is the US-ft unit for the vertical axis of the base CRS ...
+ // When outputing that to WKT, and
+ // re-exporting to PROJJSON, one gets metre, which conforms more to the
+ // official definition of NAD83(2011) 3D.
+ // The vertical unit of the base CRS shouldn't matter much anyway, so this
+ // is valid.
+ auto dstPROJJSON =
+ "{\n"
+ " \"$schema\": "
+ "\"https://proj.org/schemas/v0.2/projjson.schema.json\",\n"
+ " \"type\": \"ProjectedCRS\",\n"
+ " \"name\": \"Projected CRS NAD83(2011) / UTM zone 14N with "
+ "ellipsoidal NAD83(2011) height\",\n"
+ " \"base_crs\": {\n"
+ " \"name\": \"NAD83(2011)\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"NAD83 (National Spatial Reference System 2011)\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 1116\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Ellipsoidal height\",\n"
+ " \"abbreviation\": \"h\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"UTM zone 14N\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Transverse Mercator\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9807\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of natural origin\",\n"
+ " \"value\": 0,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8801\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of natural origin\",\n"
+ " \"value\": -99,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8802\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Scale factor at natural origin\",\n"
+ " \"value\": 0.9996,\n"
+ " \"unit\": \"unity\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8805\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"False easting\",\n"
+ " \"value\": 500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8806\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"False northing\",\n"
+ " \"value\": 0,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8807\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 16014\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"Cartesian\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Easting\",\n"
+ " \"abbreviation\": \"E\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing\",\n"
+ " \"abbreviation\": \"N\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Ellipsoidal height\",\n"
+ " \"abbreviation\": \"h\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"US survey foot\",\n"
+ " \"conversion_factor\": 0.304800609601219\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ auto objDst =
+ createFromUserInput(dstPROJJSON, DatabaseContext::create(), false);
+ auto dst = nn_dynamic_pointer_cast<CRS>(objDst);
+ ASSERT_TRUE(dst != nullptr);
+
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
+ ctxt->setSpatialCriterion(
+ CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ NN_NO_CHECK(src), NN_NO_CHECK(dst), ctxt);
+ ASSERT_GT(list.size(), 1U);
+ // What is important to check here is the vertical unit conversion
+ EXPECT_EQ(
+ list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=us-ft +xy_out=m "
+ "+step +inv +proj=lcc +lat_0=39.8333333333333 +lon_0=-100 +lat_1=43 "
+ "+lat_2=40 +x_0=500000.00001016 +y_0=0 +ellps=GRS80 "
+ "+step +proj=unitconvert +z_in=us-ft +z_out=m "
+ "+step +proj=vgridshift +grids=us_noaa_g2012bu0.tif +multiplier=1 "
+ "+step +proj=utm +zone=14 +ellps=GRS80 "
+ "+step +proj=unitconvert +xy_in=m +z_in=m +xy_out=us-ft +z_out=us-ft");
+}
+
+// ---------------------------------------------------------------------------
+
TEST(operation, createOperation_ossfuzz_18587) {
auto objSrc =
createFromUserInput("EPSG:4326", DatabaseContext::create(), false);
diff --git a/travis/before_install_pip.sh b/travis/before_install_pip.sh
index 30a7f767..3685bbf7 100755
--- a/travis/before_install_pip.sh
+++ b/travis/before_install_pip.sh
@@ -4,7 +4,7 @@
# "global" before_install script.
# Configure Python pip
-pip3 install --user --upgrade pip
-echo `pip3 --version`
-pip3 config --user set global.progress_bar off
-pip3 install --user jsonschema
+python3 -m pip install --user --upgrade "pip < 21.0"
+echo `python3 -m pip --version`
+python3 -m pip config --user set global.progress_bar off
+python3 -m pip install --user jsonschema