diff options
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 |
