diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp index d4b666e207..5608022146 100644 --- a/include/proj/coordinateoperation.hpp +++ b/include/proj/coordinateoperation.hpp @@ -1704,8 +1704,8 @@ class PROJ_GCC_DLL Transformation : public SingleOperation { PROJ_INTERNAL const std::string & getPROJ4NadgridsCompatibleFilename() const; - PROJ_FOR_TEST std::vector - getTOWGS84Parameters() const; // throw(io::FormattingException) + PROJ_FOR_TEST std::vector getTOWGS84Parameters( + bool canThrowException) const; // throw(io::FormattingException) PROJ_INTERNAL const std::string &getHeightToGeographic3DFilename() const; diff --git a/include/proj/coordinatesystem.hpp b/include/proj/coordinatesystem.hpp index 5aa444db23..8f92dbb497 100644 --- a/include/proj/coordinatesystem.hpp +++ b/include/proj/coordinatesystem.hpp @@ -60,10 +60,10 @@ class AxisDirection : public util::CodeList { valueOf(const std::string &nameIn) noexcept; //! @endcond - AxisDirection(const AxisDirection&) = delete; - AxisDirection& operator=(const AxisDirection&) = delete; - AxisDirection(AxisDirection&&) = delete; - AxisDirection& operator=(AxisDirection&&) = delete; + AxisDirection(const AxisDirection &) = delete; + AxisDirection &operator=(const AxisDirection &) = delete; + AxisDirection(AxisDirection &&) = delete; + AxisDirection &operator=(AxisDirection &&) = delete; PROJ_DLL static const AxisDirection NORTH; PROJ_DLL static const AxisDirection NORTH_NORTH_EAST; diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 9035283b5b..c71a34a8e8 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -758,7 +758,7 @@ osgeo::proj::operation::Transformation::createTimeDependentPositionVector(osgeo: osgeo::proj::operation::Transformation::createTOWGS84(dropbox::oxygen::nn > const&, std::vector > const&) osgeo::proj::operation::Transformation::createVERTCON(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, std::string const&, std::vector >, std::allocator > > > const&) osgeo::proj::operation::Transformation::createVerticalOffset(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, osgeo::proj::common::Length const&, std::vector >, std::allocator > > > const&) -osgeo::proj::operation::Transformation::getTOWGS84Parameters() const +osgeo::proj::operation::Transformation::getTOWGS84Parameters(bool) const osgeo::proj::operation::Transformation::inverse() const osgeo::proj::operation::Transformation::sourceCRS() const osgeo::proj::operation::Transformation::targetCRS() const diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 9d378bf7d5..3d8459b170 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -194,41 +194,53 @@ getDBcontextNoException(PJ_CONTEXT *ctx, const char *function) { PJ *pj_obj_create(PJ_CONTEXT *ctx, const BaseObjectNNPtr &objIn) { auto coordop = dynamic_cast(objIn.get()); if (coordop) { - auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); - try { - auto formatter = PROJStringFormatter::create( - PROJStringFormatter::Convention::PROJ_5, std::move(dbContext)); - auto projString = coordop->exportToPROJString(formatter.get()); - if (proj_context_is_network_enabled(ctx)) { - ctx->defer_grid_opening = true; - } - auto pj = pj_create_internal(ctx, projString.c_str()); - ctx->defer_grid_opening = false; - if (pj) { - pj->iso_obj = objIn; - pj->iso_obj_is_coordinate_operation = true; - auto sourceEpoch = coordop->sourceCoordinateEpoch(); - auto targetEpoch = coordop->targetCoordinateEpoch(); - if (sourceEpoch.has_value()) { - if (!targetEpoch.has_value()) { - pj->hasCoordinateEpoch = true; - pj->coordinateEpoch = - sourceEpoch->coordinateEpoch().convertToUnit( - common::UnitOfMeasure::YEAR); - } - } else { - if (targetEpoch.has_value()) { - pj->hasCoordinateEpoch = true; - pj->coordinateEpoch = - targetEpoch->coordinateEpoch().convertToUnit( - common::UnitOfMeasure::YEAR); + auto singleOp = dynamic_cast(coordop); + bool bTryToExportToProj = true; + if (singleOp && singleOp->method()->nameStr() == "unnamed") { + // Can happen for example when the GDAL GeoTIFF SRS builder + // creates a dummy conversion when building the SRS, before setting + // the final map projection. This avoids exportToPROJString() from + // throwing an exception. + bTryToExportToProj = false; + } + if (bTryToExportToProj) { + auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + try { + auto formatter = PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + std::move(dbContext)); + auto projString = coordop->exportToPROJString(formatter.get()); + if (proj_context_is_network_enabled(ctx)) { + ctx->defer_grid_opening = true; + } + auto pj = pj_create_internal(ctx, projString.c_str()); + ctx->defer_grid_opening = false; + if (pj) { + pj->iso_obj = objIn; + pj->iso_obj_is_coordinate_operation = true; + auto sourceEpoch = coordop->sourceCoordinateEpoch(); + auto targetEpoch = coordop->targetCoordinateEpoch(); + if (sourceEpoch.has_value()) { + if (!targetEpoch.has_value()) { + pj->hasCoordinateEpoch = true; + pj->coordinateEpoch = + sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR); + } + } else { + if (targetEpoch.has_value()) { + pj->hasCoordinateEpoch = true; + pj->coordinateEpoch = + targetEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR); + } } + return pj; } - return pj; + } catch (const std::exception &) { + // Silence, since we may not always be able to export as a + // PROJ string. } - } catch (const std::exception &) { - // Silence, since we may not always be able to export as a - // PROJ string. } } auto pj = pj_new(); @@ -7749,16 +7761,19 @@ int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx, } return FALSE; } - try { - auto values = transf->getTOWGS84Parameters(); + + const auto values = transf->getTOWGS84Parameters(false); + if (!values.empty()) { for (int i = 0; i < value_count && static_cast(i) < values.size(); i++) { out_values[i] = values[i]; } return TRUE; - } catch (const std::exception &e) { + } else { if (emit_error_if_incompatible) { - proj_log_error(ctx, __FUNCTION__, e.what()); + proj_log_error(ctx, __FUNCTION__, + "Transformation cannot be formatted as WKT1 TOWGS84 " + "parameters"); } return FALSE; } diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index d858f7778d..c7014f97b1 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -696,11 +696,10 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( const auto takeIntoAccountCandidate = [&](const operation::TransformationNNPtr &transf) { - try { - transf->getTOWGS84Parameters(); - } catch (const std::exception &) { + if (transf->getTOWGS84Parameters(false).empty()) { return; } + candidateCount++; if (candidateBoundCRS == nullptr) { candidateCount = 1; @@ -1426,13 +1425,12 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName, auto base3DCRS = boundCRS->baseCRS()->promoteTo3D( newName, dbContext, verticalAxisIfNotAlreadyPresent); auto transf = boundCRS->transformation(); - try { - transf->getTOWGS84Parameters(); + if (!transf->getTOWGS84Parameters(false).empty()) { return BoundCRS::create( createProperties(), base3DCRS, boundCRS->hubCRS()->promoteTo3D(std::string(), dbContext), transf->promoteTo3D(std::string(), dbContext)); - } catch (const io::FormattingException &) { + } else { return BoundCRS::create(base3DCRS, boundCRS->hubCRS(), std::move(transf)); } @@ -1481,13 +1479,12 @@ CRSNNPtr CRS::demoteTo2D(const std::string &newName, else if (auto boundCRS = dynamic_cast(this)) { auto base2DCRS = boundCRS->baseCRS()->demoteTo2D(newName, dbContext); auto transf = boundCRS->transformation(); - try { - transf->getTOWGS84Parameters(); + if (!transf->getTOWGS84Parameters(false).empty()) { return BoundCRS::create( base2DCRS, boundCRS->hubCRS()->demoteTo2D(std::string(), dbContext), transf->demoteTo2D(std::string(), dbContext)); - } catch (const io::FormattingException &) { + } else { return BoundCRS::create(base2DCRS, boundCRS->hubCRS(), transf); } } @@ -5985,7 +5982,7 @@ void BoundCRS::_exportToWKT(io::WKTFormatter *formatter) const { io::FormattingException::Throw( "Cannot export BoundCRS with non-WGS 84 hub CRS in WKT1"); } - auto params = d->transformation()->getTOWGS84Parameters(); + auto params = d->transformation()->getTOWGS84Parameters(true); if (!formatter->useESRIDialect()) { formatter->setTOWGS84Parameters(params); } @@ -6075,7 +6072,7 @@ void BoundCRS::_exportToPROJString( formatter->setHDatumExtension(std::string()); } else { if (isTOWGS84Compatible()) { - auto params = transformation()->getTOWGS84Parameters(); + auto params = transformation()->getTOWGS84Parameters(true); formatter->setTOWGS84Parameters(params); } crs_exportable->_exportToPROJString(formatter); @@ -6137,7 +6134,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { } bool refIsNullTransform = false; if (isTOWGS84Compatible()) { - auto params = transformation()->getTOWGS84Parameters(); + auto params = transformation()->getTOWGS84Parameters(true); if (params == std::vector{0, 0, 0, 0, 0, 0, 0}) { refIsNullTransform = true; } diff --git a/src/iso19111/operation/singleoperation.cpp b/src/iso19111/operation/singleoperation.cpp index 72f10173fd..4778966c79 100644 --- a/src/iso19111/operation/singleoperation.cpp +++ b/src/iso19111/operation/singleoperation.cpp @@ -1443,8 +1443,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, isTOWGS84Transf(otherMethodEPSGCode)) { auto transf = static_cast(this); auto otherTransf = static_cast(otherSO); - auto params = transf->getTOWGS84Parameters(); - auto otherParams = otherTransf->getTOWGS84Parameters(); + auto params = transf->getTOWGS84Parameters(true); + auto otherParams = otherTransf->getTOWGS84Parameters(true); assert(params.size() == 7); assert(otherParams.size() == 7); for (size_t i = 0; i < 7; i++) { diff --git a/src/iso19111/operation/transformation.cpp b/src/iso19111/operation/transformation.cpp index 3d25f54c30..5dd77282b9 100644 --- a/src/iso19111/operation/transformation.cpp +++ b/src/iso19111/operation/transformation.cpp @@ -184,12 +184,14 @@ Transformation::demoteTo2D(const std::string &, * can be used as the value of the WKT1 TOWGS84 parameter or * PROJ +towgs84 parameter. * + * @param canThrowException if true, an exception is thrown if the method fails, + * otherwise an empty vector is returned in case of failure. * @return a vector of 7 values if valid, otherwise a io::FormattingException * is thrown. * @throws io::FormattingException */ -std::vector -Transformation::getTOWGS84Parameters() const // throw(io::FormattingException) +std::vector Transformation::getTOWGS84Parameters( + bool canThrowException) const // throw(io::FormattingException) { // GDAL WKT1 assumes EPSG:9606 / Position Vector convention @@ -297,6 +299,8 @@ Transformation::getTOWGS84Parameters() const // throw(io::FormattingException) (foundRotX && foundRotY && foundRotZ && foundScale))) { return params; } else { + if (!canThrowException) + return {}; throw io::FormattingException( "Missing required parameter values in transformation"); } @@ -321,6 +325,8 @@ Transformation::getTOWGS84Parameters() const // throw(io::FormattingException) } #endif + if (!canThrowException) + return {}; throw io::FormattingException( "Transformation cannot be formatted as WKT1 TOWGS84 parameters"); } diff --git a/src/projections/imoll.cpp b/src/projections/imoll.cpp index c312e8af54..1f2639e011 100644 --- a/src/projections/imoll.cpp +++ b/src/projections/imoll.cpp @@ -233,7 +233,7 @@ static double compute_zone_x_boundary(PJ *P, double lam, double phi) { lp2.phi = phi; xy1 = imoll_s_forward(lp1, P); xy2 = imoll_s_forward(lp2, P); - return (xy1.x + xy2.x)/2.; + return (xy1.x + xy2.x) / 2.; } PJ *PJ_PROJECTION(imoll) { diff --git a/src/projections/imoll_o.cpp b/src/projections/imoll_o.cpp index a3adfb35f6..0602711d02 100644 --- a/src/projections/imoll_o.cpp +++ b/src/projections/imoll_o.cpp @@ -237,8 +237,8 @@ pj_imoll_o_compute_zone_offset(struct pj_imoll_o_ns::pj_imoll_o_data *Q, return (xy2.x + Q->pj[zone2 - 1]->x0) - (xy1.x + Q->pj[zone1 - 1]->x0); } -static double -pj_imoll_o_compute_zone_x_boundary(PJ *P, double lam, double phi) { +static double pj_imoll_o_compute_zone_x_boundary(PJ *P, double lam, + double phi) { PJ_LP lp1, lp2; PJ_XY xy1, xy2; @@ -248,7 +248,7 @@ pj_imoll_o_compute_zone_x_boundary(PJ *P, double lam, double phi) { lp2.phi = phi; xy1 = imoll_o_s_forward(lp1, P); xy2 = imoll_o_s_forward(lp2, P); - return (xy1.x + xy2.x)/2.; + return (xy1.x + xy2.x) / 2.; } PJ *PJ_PROJECTION(imoll_o) { diff --git a/test/unit/gie_self_tests.cpp b/test/unit/gie_self_tests.cpp index 8165b18670..3807b452a4 100644 --- a/test/unit/gie_self_tests.cpp +++ b/test/unit/gie_self_tests.cpp @@ -302,8 +302,8 @@ TEST_F(gieTest, proj_create_crs_to_crs) { TEST_F(gieTest, proj_create_crs_to_crs_EPSG_4326) { - auto P = - proj_create_crs_to_crs(PJ_DEFAULT_CTX, "EPSG:4326", "EPSG:32631", nullptr); + auto P = proj_create_crs_to_crs(PJ_DEFAULT_CTX, "EPSG:4326", "EPSG:32631", + nullptr); ASSERT_TRUE(P != nullptr); PJ_COORD a, b; diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index cc0cbcaecf..0786ad76c2 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -5033,7 +5033,7 @@ TEST(wkt_parse, BOUNDCRS_transformation_from_names) { EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), projcrs->baseCRS()->nameStr()); - auto params = crs->transformation()->getTOWGS84Parameters(); + auto params = crs->transformation()->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; ASSERT_EQ(params.size(), expected.size()); for (int i = 0; i < 7; i++) { @@ -5082,7 +5082,7 @@ TEST(wkt_parse, BOUNDCRS_transformation_from_codes) { EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), projcrs->baseCRS()->nameStr()); - auto params = crs->transformation()->getTOWGS84Parameters(); + auto params = crs->transformation()->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; ASSERT_EQ(params.size(), expected.size()); for (int i = 0; i < 7; i++) { @@ -5224,7 +5224,7 @@ TEST(wkt_parse, geogcs_TOWGS84_3terms) { ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr); EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), "my GEOGCRS"); - auto params = crs->transformation()->getTOWGS84Parameters(); + auto params = crs->transformation()->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0}; ASSERT_EQ(params.size(), expected.size()); for (int i = 0; i < 7; i++) { @@ -5270,7 +5270,7 @@ TEST(wkt_parse, projcs_TOWGS84_7terms) { ASSERT_TRUE(crs->transformation()->sourceCRS() != nullptr); EXPECT_EQ(crs->transformation()->sourceCRS()->nameStr(), "my GEOGCRS"); - auto params = crs->transformation()->getTOWGS84Parameters(); + auto params = crs->transformation()->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; ASSERT_EQ(params.size(), expected.size()); for (int i = 0; i < 7; i++) { @@ -5313,7 +5313,7 @@ TEST(io, projcs_TOWGS84_7terms_autocorrect) { auto crs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(crs != nullptr); - auto params = crs->transformation()->getTOWGS84Parameters(); + auto params = crs->transformation()->getTOWGS84Parameters(true); auto expected = std::vector{-106.8686, 52.2978, -103.7239, 0.3366, -0.457, 1.8422, -1.2747}; ASSERT_EQ(params.size(), expected.size()); diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index ed8f247efe..41c93ad3c0 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -555,7 +555,7 @@ TEST(operation, transformation_createGeocentricTranslations) { 2.0, 3.0, std::vector()); EXPECT_TRUE(transf->validateParameters().empty()); - auto params = transf->getTOWGS84Parameters(); + auto params = transf->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0}; EXPECT_EQ(params, expected); @@ -570,7 +570,7 @@ TEST(operation, transformation_createGeocentricTranslations) { inv_transf_as_transf->sourceCRS()->nameStr()); auto expected_inv = std::vector{-1.0, -2.0, -3.0, 0.0, 0.0, 0.0, 0.0}; - EXPECT_EQ(inv_transf_as_transf->getTOWGS84Parameters(), expected_inv); + EXPECT_EQ(inv_transf_as_transf->getTOWGS84Parameters(true), expected_inv); EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +proj=axisswap +order=2,1 +step " @@ -677,7 +677,7 @@ TEST(operation, transformation_createPositionVector) { ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1U); auto expected = std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; - EXPECT_EQ(transf->getTOWGS84Parameters(), expected); + EXPECT_EQ(transf->getTOWGS84Parameters(true), expected); EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), "+proj=pipeline +step +proj=axisswap +order=2,1 +step " @@ -744,7 +744,7 @@ TEST(operation, transformation_createCoordinateFrameRotation) { std::vector()); EXPECT_TRUE(transf->validateParameters().empty()); - auto params = transf->getTOWGS84Parameters(); + auto params = transf->getTOWGS84Parameters(true); auto expected = std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; EXPECT_EQ(params, expected);