From fdd990191788adc4d172b87cd0d4198992674746 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 2 Jan 2023 19:26:33 +0100 Subject: [PATCH 01/11] proj_create_crs_to_crs_from_pj(): add a ONLY_BEST=YES option (fixes #3533) Also add a --only-best switch to cs2cs ONLY_BEST=YES/NO: (PROJ >= 9.2) Can be set to YES to cause PROJ to error out if the best transformation, known of PROJ, and usable by PROJ if all grids known and usable by PROJ were accessible, cannot be used. Best transformation should be understood as the transformation returned by :cpp:func:`proj_get_suggested_operation` if all known grids were accessible (either locally or through network). --- docs/source/apps/cs2cs.rst | 13 +++- .../development/reference/functions.rst | 8 +++ src/4D_api.cpp | 69 ++++++++++++++++++- src/apps/cs2cs.cpp | 10 ++- src/proj_internal.h | 1 + test/cli/testvarious | 28 ++++++++ test/cli/tv_out.dist | 15 ++++ 7 files changed, 141 insertions(+), 3 deletions(-) diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index fb6bb89768..b1499257c7 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -13,7 +13,8 @@ Synopsis | **cs2cs** [**-eEfIlrstvwW** [args]] | [[--area ] | [--bbox ]] - | [--authority ] [--no-ballpark] [--accuracy ] [--3d] + | [--authority ] [--3d] + | [--accuracy ] [--only-best] [--no-ballpark] | ([*+opt[=arg]* ...] [+to *+opt[=arg]* ...] | {source_crs} {target_crs}) | file ... @@ -166,6 +167,16 @@ The following control parameters can appear in any order: `south_lat` and `north_lat` in the [-90,90]. `west_long` is generally lower than `east_long`, except in the case where the area of interest crosses the antimeridian. +.. option:: --only-best + + .. versionadded:: 9.2.0 + + Error out if the best transformation, known of PROJ, and usable by PROJ if + all grids known and usable by PROJ were accessible, cannot be used. + Best transformation should be understood as the transformation returned by + :cpp:func:`proj_get_suggested_operation` if all known grids were + accessible (either locally or through network). + .. option:: --no-ballpark .. versionadded:: 8.0.0 diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index 38c81d865b..daf167a17d 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -201,6 +201,14 @@ paragraph for more details. - ALLOW_BALLPARK=YES/NO: can be set to NO to disallow the use of :term:`Ballpark transformation` in the candidate coordinate operations. + - ONLY_BEST=YES/NO: (PROJ >= 9.2) + Can be set to YES to cause PROJ to error out if the best + transformation, known of PROJ, and usable by PROJ if all grids known and + usable by PROJ were accessible, cannot be used. Best transformation should + be understood as the transformation returned by + :cpp:func:`proj_get_suggested_operation` if all known grids were + accessible (either locally or through network). + - FORCE_OVER=YES/NO: can be set to YES to force the ``+over`` flag on the transformation returned by this function. See :ref:`longitude_wrapping` diff --git a/src/4D_api.cpp b/src/4D_api.cpp index e02cbf73ca..fddf5762ba 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -348,6 +348,28 @@ similarly, but prefers the 2D resp. 3D interfaces if available. if( res.xyzt.x != HUGE_VAL ) { return res; } + else if( P->errorIfBestTransformationNotAvailable ) { + std::string msg("Attempt to use coordinate operation "); + msg += alt.name; + msg += " failed."; + int gridUsed = proj_coordoperation_get_grid_used_count(P->ctx, alt.pj); + for( int i = 0; i < gridUsed; ++i ) + { + const char* gridName = ""; + int available = FALSE; + if( proj_coordoperation_get_grid_used( + P->ctx, alt.pj, i, &gridName, nullptr, nullptr, + nullptr, nullptr, nullptr, &available) && + !available ) + { + msg += " Grid "; + msg += gridName; + msg += " is not available."; + } + } + pj_log(P->ctx, PJ_LOG_ERROR, msg.c_str()); + return res; + } if( iRetry == N_MAX_RETRY ) { break; } @@ -1899,6 +1921,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons double accuracy = -1; bool allowBallparkTransformations = true; bool forceOver = false; + bool errorIfBestTransformationNotAvailable = false; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "AUTHORITY="))) { @@ -1915,6 +1938,16 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons "Invalid value for ALLOW_BALLPARK option."); return nullptr; } + } else if ((value = getOptionValue(*iter, "ONLY_BEST="))) { + if( ci_equal(value, "yes") ) + errorIfBestTransformationNotAvailable = true; + else if( ci_equal(value, "no") ) + errorIfBestTransformationNotAvailable = false; + else { + ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, + "Invalid value for ONLY_BEST option."); + return nullptr; + } } else if ((value = getOptionValue(*iter, "FORCE_OVER="))) { if (ci_equal(value, "yes")) { @@ -1963,7 +1996,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); proj_operation_factory_context_set_grid_availability_use( ctx, operation_ctx, - proj_context_is_network_enabled(ctx) ? + (errorIfBestTransformationNotAvailable || proj_context_is_network_enabled(ctx)) ? PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE: PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); @@ -1984,7 +2017,11 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->forceOver = forceOver; + const int old_debug_level = ctx->debug_level; + if( errorIfBestTransformationNotAvailable ) + ctx->debug_level = PJ_LOG_NONE; PJ* P = proj_list_get(ctx, op_list, 0); + ctx->debug_level = old_debug_level; assert(P); if( P == nullptr || op_count == 1 || @@ -1992,11 +2029,40 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons proj_get_type(target_crs) == PJ_TYPE_GEOCENTRIC_CRS ) { proj_list_destroy(op_list); ctx->forceOver = false; + + if( P != nullptr && + errorIfBestTransformationNotAvailable && + !proj_coordoperation_is_instantiable(ctx, P) ) + { + std::string msg("Attempt to use coordinate operation "); + msg += proj_get_name(P); + msg += " failed."; + int gridUsed = proj_coordoperation_get_grid_used_count(ctx, P); + for( int i = 0; i < gridUsed; ++i ) + { + const char* gridName = ""; + int available = FALSE; + if( proj_coordoperation_get_grid_used( + ctx, P, i, &gridName, nullptr, nullptr, + nullptr, nullptr, nullptr, &available) && + !available ) + { + msg += " Grid "; + msg += gridName; + msg += " is not available."; + } + } + pj_log(ctx, PJ_LOG_ERROR, msg.c_str()); + } + return P; } + if( errorIfBestTransformationNotAvailable ) + ctx->debug_level = PJ_LOG_NONE; auto preparedOpList = pj_create_prepared_operations(ctx, source_crs, target_crs, op_list); + ctx->debug_level = old_debug_level; ctx->forceOver = false; proj_list_destroy(op_list); @@ -2016,6 +2082,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return retP; } + P->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; P->alternativeCoordinateOperations = std::move(preparedOpList); // The returned P is rather dummy P->descr = "Set of coordinate operations"; diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index 7c565bb385..81d412db8c 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -78,7 +78,8 @@ static const char *oterr = "*\t*"; /* output line for unprojectable input */ static const char *usage = "%s\nusage: %s [-dDeEfIlrstvwW [args]]\n" " [[--area name_or_code] | [--bbox west_long,south_lat,east_long,north_lat]]\n" - " [--authority {name}] [--accuracy {accuracy}] [--no-ballpark] [--3d]\n" + " [--authority {name}] [--3d]\n" + " [--accuracy {accuracy}] [--only-best] [--no-ballpark]\n" " [+opt[=arg] ...] [+to +opt[=arg] ...] [file ...]\n"; static double (*informat)(const char *, @@ -418,6 +419,7 @@ int main(int argc, char **argv) { const char* authority = nullptr; double accuracy = -1; bool allowBallpark = true; + bool errorIfBestTransformationNotAvailable = false; bool promoteTo3D = false; /* process run line arguments */ @@ -484,6 +486,9 @@ int main(int argc, char **argv) { else if (strcmp(*argv, "--no-ballpark") == 0 ) { allowBallpark = false; } + else if (strcmp(*argv, "--only-best") == 0 ) { + errorIfBestTransformationNotAvailable = true; + } else if (strcmp(*argv, "--3d") == 0 ) { promoteTo3D = true; } @@ -893,6 +898,9 @@ int main(int argc, char **argv) { if( !allowBallpark ) { options.push_back("ALLOW_BALLPARK=NO"); } + if( errorIfBestTransformationNotAvailable ) { + options.push_back("ONLY_BEST=YES"); + } options.push_back(nullptr); transformation = proj_create_crs_to_crs_from_pj(nullptr, src, dst, pj_area, options.data()); diff --git a/src/proj_internal.h b/src/proj_internal.h index 119da01ae1..dc4944c429 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -594,6 +594,7 @@ struct PJconsts { **************************************************************************************/ std::vector alternativeCoordinateOperations{}; int iCurCoordOp = -1; + bool errorIfBestTransformationNotAvailable = false; /************************************************************************************* diff --git a/test/cli/testvarious b/test/cli/testvarious index 3e960bf1b8..454fd69df1 100755 --- a/test/cli/testvarious +++ b/test/cli/testvarious @@ -1082,6 +1082,34 @@ PROJ_DISPLAY_PROGRAM_NAME=NO $EXE -W10 +proj=latlong +datum=WGS84 +to +proj=latl 0 0 EOF +echo "##############################################################" >> ${OUT} +echo "Test cs2cs --only-best (working)" >> ${OUT} +# +$EXE --only-best NTF RGF93 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs --only-best (grid missing)" >> ${OUT} +# +$EXE --only-best EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs --only-best (grid missing)" >> ${OUT} +# +$EXE --only-best NAD27 NAD83 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs --only-best --no-ballpark (grid missing)" >> ${OUT} +# +$EXE --only-best --no-ballpark EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 < Date: Mon, 2 Jan 2023 21:21:34 +0100 Subject: [PATCH 02/11] proj_create_crs_to_crs(): emit a debug message if the best transformation cannot be used / cs2cs: add a --only-best=no switch --- docs/source/apps/cs2cs.rst | 16 ++++++++------- src/4D_api.cpp | 40 ++++++++++++++++++++++++++++++-------- src/apps/cs2cs.cpp | 20 +++++++++++++++---- src/ctx.cpp | 1 + src/proj_internal.h | 1 + test/cli/testvarious | 21 ++++++++++++++++++++ test/cli/tv_out.dist | 10 ++++++++++ 7 files changed, 90 insertions(+), 19 deletions(-) diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index b1499257c7..0a63e3508e 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -14,7 +14,7 @@ Synopsis | **cs2cs** [**-eEfIlrstvwW** [args]] | [[--area ] | [--bbox ]] | [--authority ] [--3d] - | [--accuracy ] [--only-best] [--no-ballpark] + | [--accuracy ] [--only-best[=yes|=no]] [--no-ballpark] | ([*+opt[=arg]* ...] [+to *+opt[=arg]* ...] | {source_crs} {target_crs}) | file ... @@ -167,15 +167,17 @@ The following control parameters can appear in any order: `south_lat` and `north_lat` in the [-90,90]. `west_long` is generally lower than `east_long`, except in the case where the area of interest crosses the antimeridian. -.. option:: --only-best +.. option:: --only-best[=yes|=no] .. versionadded:: 9.2.0 - Error out if the best transformation, known of PROJ, and usable by PROJ if - all grids known and usable by PROJ were accessible, cannot be used. - Best transformation should be understood as the transformation returned by - :cpp:func:`proj_get_suggested_operation` if all known grids were - accessible (either locally or through network). + If set to yes, error out if the best transformation, known of PROJ, and + usable by PROJ if all grids known and usable by PROJ were accessible, + cannot be used. + Best transformation should be understood as the most accurate transformation + available among all relevant for the point to transform, and if all known + grids required to perform such transformation were accessible (either locally + or through network). .. option:: --no-ballpark diff --git a/src/4D_api.cpp b/src/4D_api.cpp index fddf5762ba..8ac46842c2 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -348,7 +348,8 @@ similarly, but prefers the 2D resp. 3D interfaces if available. if( res.xyzt.x != HUGE_VAL ) { return res; } - else if( P->errorIfBestTransformationNotAvailable ) { + else if( P->errorIfBestTransformationNotAvailable || + P->ctx->warnIfBestTransformationNotAvailable ) { std::string msg("Attempt to use coordinate operation "); msg += alt.name; msg += " failed."; @@ -367,8 +368,18 @@ similarly, but prefers the 2D resp. 3D interfaces if available. msg += " is not available."; } } - pj_log(P->ctx, PJ_LOG_ERROR, msg.c_str()); - return res; + if( P->ctx->warnIfBestTransformationNotAvailable ) + { + msg += " This might become an error in a future PROJ major release. " + "Set the ONLY_BEST option to YES or NO. " + "This warning will no longer be emitted (for the current context)."; + P->ctx->warnIfBestTransformationNotAvailable = false; + } + pj_log(P->ctx, + P->errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, + msg.c_str()); + if( P->errorIfBestTransformationNotAvailable ) + return res; } if( iRetry == N_MAX_RETRY ) { break; @@ -1939,6 +1950,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return nullptr; } } else if ((value = getOptionValue(*iter, "ONLY_BEST="))) { + ctx->warnIfBestTransformationNotAvailable = false; if( ci_equal(value, "yes") ) errorIfBestTransformationNotAvailable = true; else if( ci_equal(value, "no") ) @@ -1996,7 +2008,9 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx, operation_ctx, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION); proj_operation_factory_context_set_grid_availability_use( ctx, operation_ctx, - (errorIfBestTransformationNotAvailable || proj_context_is_network_enabled(ctx)) ? + (errorIfBestTransformationNotAvailable || + ctx->warnIfBestTransformationNotAvailable || + proj_context_is_network_enabled(ctx)) ? PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE: PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); @@ -2018,7 +2032,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->forceOver = forceOver; const int old_debug_level = ctx->debug_level; - if( errorIfBestTransformationNotAvailable ) + if( errorIfBestTransformationNotAvailable || ctx->warnIfBestTransformationNotAvailable ) ctx->debug_level = PJ_LOG_NONE; PJ* P = proj_list_get(ctx, op_list, 0); ctx->debug_level = old_debug_level; @@ -2031,7 +2045,8 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->forceOver = false; if( P != nullptr && - errorIfBestTransformationNotAvailable && + (errorIfBestTransformationNotAvailable || + ctx->warnIfBestTransformationNotAvailable) && !proj_coordoperation_is_instantiable(ctx, P) ) { std::string msg("Attempt to use coordinate operation "); @@ -2052,13 +2067,22 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons msg += " is not available."; } } - pj_log(ctx, PJ_LOG_ERROR, msg.c_str()); + if( ctx->warnIfBestTransformationNotAvailable ) + { + msg += " This might become an error in a future PROJ major release." + "Set the ONLY_BEST option to YES or NO. " + "This warning will no longer be emitted (for the current context)."; + ctx->warnIfBestTransformationNotAvailable = false; + } + pj_log(ctx, + errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, + msg.c_str()); } return P; } - if( errorIfBestTransformationNotAvailable ) + if( errorIfBestTransformationNotAvailable || ctx->warnIfBestTransformationNotAvailable ) ctx->debug_level = PJ_LOG_NONE; auto preparedOpList = pj_create_prepared_operations(ctx, source_crs, target_crs, op_list); diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index 81d412db8c..426d59414e 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -79,7 +79,7 @@ static const char *usage = "%s\nusage: %s [-dDeEfIlrstvwW [args]]\n" " [[--area name_or_code] | [--bbox west_long,south_lat,east_long,north_lat]]\n" " [--authority {name}] [--3d]\n" - " [--accuracy {accuracy}] [--only-best] [--no-ballpark]\n" + " [--accuracy {accuracy}] [--only-best[=yes|=no]] [--no-ballpark]\n" " [+opt[=arg] ...] [+to +opt[=arg] ...] [file ...]\n"; static double (*informat)(const char *, @@ -419,6 +419,7 @@ int main(int argc, char **argv) { const char* authority = nullptr; double accuracy = -1; bool allowBallpark = true; + bool onlyBestSet = false; bool errorIfBestTransformationNotAvailable = false; bool promoteTo3D = false; @@ -486,9 +487,15 @@ int main(int argc, char **argv) { else if (strcmp(*argv, "--no-ballpark") == 0 ) { allowBallpark = false; } - else if (strcmp(*argv, "--only-best") == 0 ) { + else if (strcmp(*argv, "--only-best") == 0 || + strcmp(*argv, "--only-best=yes") == 0 ) { + onlyBestSet = true; errorIfBestTransformationNotAvailable = true; } + else if (strcmp(*argv, "--only-best=no") == 0 ) { + onlyBestSet = true; + errorIfBestTransformationNotAvailable = false; + } else if (strcmp(*argv, "--3d") == 0 ) { promoteTo3D = true; } @@ -898,8 +905,13 @@ int main(int argc, char **argv) { if( !allowBallpark ) { options.push_back("ALLOW_BALLPARK=NO"); } - if( errorIfBestTransformationNotAvailable ) { - options.push_back("ONLY_BEST=YES"); + if( onlyBestSet ) { + if( errorIfBestTransformationNotAvailable ) { + options.push_back("ONLY_BEST=YES"); + } + else { + options.push_back("ONLY_BEST=NO"); + } } options.push_back(nullptr); transformation = proj_create_crs_to_crs_from_pj(nullptr, src, dst, diff --git a/src/ctx.cpp b/src/ctx.cpp index a37329936e..83d4d60f02 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -150,6 +150,7 @@ pj_ctx::pj_ctx(const pj_ctx& other) : lastFullErrorMessage(std::string()), last_errno(0), debug_level(other.debug_level), + warnIfBestTransformationNotAvailable(other.warnIfBestTransformationNotAvailable), logger(other.logger), logger_app_data(other.logger_app_data), cpp_context(other.cpp_context ? other.cpp_context->clone(this) : nullptr), diff --git a/src/proj_internal.h b/src/proj_internal.h index dc4944c429..c66876e871 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -695,6 +695,7 @@ struct pj_ctx{ std::string lastFullErrorMessage{}; // used by proj_context_errno_string int last_errno = 0; int debug_level = PJ_LOG_ERROR; + bool warnIfBestTransformationNotAvailable = true; /* to remove in PROJ 10? */ void (*logger)(void *, int, const char *) = nullptr; void *logger_app_data = nullptr; struct projCppContext* cpp_context = nullptr; /* internal context for C++ code */ diff --git a/test/cli/testvarious b/test/cli/testvarious index 454fd69df1..3e17762684 100755 --- a/test/cli/testvarious +++ b/test/cli/testvarious @@ -1089,6 +1089,20 @@ $EXE --only-best NTF RGF93 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs (grid missing)" >> ${OUT} +# +$EXE EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs --only-best=no (grid missing)" >> ${OUT} +# +$EXE --only-best=no EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} echo "Test cs2cs --only-best (grid missing)" >> ${OUT} # @@ -1096,6 +1110,13 @@ $EXE --only-best EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs --only-best=yes (grid missing)" >> ${OUT} +# +$EXE --only-best=yes EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} echo "Test cs2cs --only-best (grid missing)" >> ${OUT} # diff --git a/test/cli/tv_out.dist b/test/cli/tv_out.dist index dc94c95521..a43bc7b205 100644 --- a/test/cli/tv_out.dist +++ b/test/cli/tv_out.dist @@ -532,10 +532,20 @@ program abnormally terminated Test cs2cs --only-best (working) 49 2 0 48d59'59.756"N 1d59'57.4"E 0.000 ############################################################## +Test cs2cs (grid missing) +49 2 0 49dN 2dE 0.000 +############################################################## +Test cs2cs --only-best=no (grid missing) +49 2 0 49dN 2dE 0.000 +############################################################## Test cs2cs --only-best (grid missing) Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. 49 2 0 * * inf ############################################################## +Test cs2cs --only-best=yes (grid missing) +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. +49 2 0 * * inf +############################################################## Test cs2cs --only-best (grid missing) Attempt to use coordinate operation NAD27 to NAD83 (7) failed. Grid us_noaa_nadcon5_nad27_nad83_1986_conus.tif is not available. 40 -100 0 * * inf From 27b2fa42fd7c810056fc14fa5a0b67202220d8ff Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 2 Jan 2023 21:34:39 +0100 Subject: [PATCH 03/11] proj_clone(): avoid emitting errors on tranformations where some are not instanciable --- src/iso19111/c_api.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 7c835c15ab..2f408cb2c1 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -521,10 +521,13 @@ PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) { if (newPj) { newPj->descr = "Set of coordinate operations"; newPj->ctx = ctx; + const int old_debug_level = ctx->debug_level; + ctx->debug_level = PJ_LOG_NONE; for (const auto &altOp : obj->alternativeCoordinateOperations) { newPj->alternativeCoordinateOperations.emplace_back( PJCoordOperation(ctx, altOp)); } + ctx->debug_level = old_debug_level; } return newPj; } From 30b2e97719adaa59147dc258ddf81ab6a7c897a6 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 11 Jan 2023 11:52:53 +0100 Subject: [PATCH 04/11] Add link to PROJ doc when ONLY_BEST warning/error triggers, and correctly propagate errorIfBestTransformationNotAvailable to alternate operations --- src/4D_api.cpp | 104 ++++++++++++++++++++----------------------- test/cli/tv_out.dist | 14 +++--- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 8ac46842c2..d78b177eca 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -273,6 +273,41 @@ int pj_get_suggested_operation(PJ_CONTEXT*, return iBest; } +/**************************************************************************************/ +static void warnAboutMissingGrid(PJ* P) +/**************************************************************************************/ +{ + std::string msg("Attempt to use coordinate operation "); + msg += proj_get_name(P); + msg += " failed."; + int gridUsed = proj_coordoperation_get_grid_used_count(P->ctx, P); + for( int i = 0; i < gridUsed; ++i ) + { + const char* gridName = ""; + int available = FALSE; + if( proj_coordoperation_get_grid_used( + P->ctx, P, i, &gridName, nullptr, nullptr, + nullptr, nullptr, nullptr, &available) && + !available ) + { + msg += " Grid "; + msg += gridName; + msg += " is not available. " + "Consult https://proj.org/resource_files.html for guidance."; + } + } + if( P->ctx->warnIfBestTransformationNotAvailable ) + { + msg += " This might become an error in a future PROJ major release. " + "Set the ONLY_BEST option to YES or NO. " + "This warning will no longer be emitted (for the current context)."; + P->ctx->warnIfBestTransformationNotAvailable = false; + } + pj_log(P->ctx, + P->errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, + msg.c_str()); +} + /**************************************************************************************/ PJ_COORD proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { /*************************************************************************************** @@ -350,34 +385,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available. } else if( P->errorIfBestTransformationNotAvailable || P->ctx->warnIfBestTransformationNotAvailable ) { - std::string msg("Attempt to use coordinate operation "); - msg += alt.name; - msg += " failed."; - int gridUsed = proj_coordoperation_get_grid_used_count(P->ctx, alt.pj); - for( int i = 0; i < gridUsed; ++i ) - { - const char* gridName = ""; - int available = FALSE; - if( proj_coordoperation_get_grid_used( - P->ctx, alt.pj, i, &gridName, nullptr, nullptr, - nullptr, nullptr, nullptr, &available) && - !available ) - { - msg += " Grid "; - msg += gridName; - msg += " is not available."; - } - } - if( P->ctx->warnIfBestTransformationNotAvailable ) - { - msg += " This might become an error in a future PROJ major release. " - "Set the ONLY_BEST option to YES or NO. " - "This warning will no longer be emitted (for the current context)."; - P->ctx->warnIfBestTransformationNotAvailable = false; - } - pj_log(P->ctx, - P->errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, - msg.c_str()); + warnAboutMissingGrid(alt.pj); if( P->errorIfBestTransformationNotAvailable ) return res; } @@ -2038,6 +2046,10 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->debug_level = old_debug_level; assert(P); + if( P != nullptr ) { + P->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; + } + if( P == nullptr || op_count == 1 || proj_get_type(source_crs) == PJ_TYPE_GEOCENTRIC_CRS || proj_get_type(target_crs) == PJ_TYPE_GEOCENTRIC_CRS ) { @@ -2049,34 +2061,11 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->warnIfBestTransformationNotAvailable) && !proj_coordoperation_is_instantiable(ctx, P) ) { - std::string msg("Attempt to use coordinate operation "); - msg += proj_get_name(P); - msg += " failed."; - int gridUsed = proj_coordoperation_get_grid_used_count(ctx, P); - for( int i = 0; i < gridUsed; ++i ) - { - const char* gridName = ""; - int available = FALSE; - if( proj_coordoperation_get_grid_used( - ctx, P, i, &gridName, nullptr, nullptr, - nullptr, nullptr, nullptr, &available) && - !available ) - { - msg += " Grid "; - msg += gridName; - msg += " is not available."; - } - } - if( ctx->warnIfBestTransformationNotAvailable ) - { - msg += " This might become an error in a future PROJ major release." - "Set the ONLY_BEST option to YES or NO. " - "This warning will no longer be emitted (for the current context)."; - ctx->warnIfBestTransformationNotAvailable = false; + warnAboutMissingGrid(P); + if( errorIfBestTransformationNotAvailable ) { + proj_destroy(P); + return nullptr; } - pj_log(ctx, - errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, - msg.c_str()); } return P; @@ -2097,6 +2086,10 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return nullptr; } + for( auto& op: preparedOpList ) { + op.pj->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; + } + // If there's finally juste a single result, return it directly if( preparedOpList.size() == 1 ) { @@ -2106,7 +2099,6 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return retP; } - P->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; P->alternativeCoordinateOperations = std::move(preparedOpList); // The returned P is rather dummy P->descr = "Set of coordinate operations"; diff --git a/test/cli/tv_out.dist b/test/cli/tv_out.dist index a43bc7b205..a5067a043b 100644 --- a/test/cli/tv_out.dist +++ b/test/cli/tv_out.dist @@ -539,17 +539,21 @@ Test cs2cs --only-best=no (grid missing) 49 2 0 49dN 2dE 0.000 ############################################################## Test cs2cs --only-best (grid missing) -Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. 49 2 0 * * inf ############################################################## Test cs2cs --only-best=yes (grid missing) -Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. 49 2 0 * * inf ############################################################## Test cs2cs --only-best (grid missing) -Attempt to use coordinate operation NAD27 to NAD83 (7) failed. Grid us_noaa_nadcon5_nad27_nad83_1986_conus.tif is not available. +Attempt to use coordinate operation NAD27 to NAD83 (7) failed. Grid us_noaa_nadcon5_nad27_nad83_1986_conus.tif is not available. Consult https://proj.org/resource_files.html for guidance. 40 -100 0 * * inf ############################################################## Test cs2cs --only-best --no-ballpark (grid missing) -Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. -49 2 0 * * inf +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. +Rel. 9.2.0, March 1st, 2023 +: +cannot initialize transformation +cause: File not found or invalid +program abnormally terminated From ec0ce09ef33b9b5fd89b10bf68224d36fb99534e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 11 Jan 2023 12:15:34 +0100 Subject: [PATCH 05/11] Add PROJ_ONLY_BEST_DEFAULT environment variable, and 'only_best_default' proj.ini setting --- data/proj.ini | 9 +++++++++ docs/source/apps/cs2cs.rst | 4 ++++ .../development/reference/functions.rst | 4 ++++ docs/source/resource_files.rst | 9 +++++++++ src/4D_api.cpp | 3 ++- src/ctx.cpp | 1 + src/filemanager.cpp | 16 ++++++++++++++++ src/proj_internal.h | 1 + test/cli/testvarious | 19 +++++++++++++++++++ test/cli/tv_out.dist | 8 ++++++++ 10 files changed, 73 insertions(+), 1 deletion(-) diff --git a/data/proj.ini b/data/proj.ini index 2353adb8ea..9267cdf64b 100644 --- a/data/proj.ini +++ b/data/proj.ini @@ -15,6 +15,15 @@ cache_size_MB = 300 cache_ttl_sec = 86400 +; Can be set to on so that by default the lack of a known resource files needed +; for the best transformation PROJ would normally use causes an error. This +; default value itself is overriden by the PROJ_ONLY_BEST_DEFAULT environment +; variable if set, and then by the ONLY_BEST setting that can be +; passed to the proj_create_crs_to_crs() method, or with the --only-best +; option of the cs2cs program. +; (added in PROJ 9.2) +only_best_default = off + ; Filename of the Certificate Authority (CA) bundle. ; Can be overriden with the PROJ_CURL_CA_BUNDLE / CURL_CA_BUNDLE environment variable. ; (added in PROJ 9.0) diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index 0a63e3508e..c6efe73a4f 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -178,6 +178,10 @@ The following control parameters can appear in any order: available among all relevant for the point to transform, and if all known grids required to perform such transformation were accessible (either locally or through network). + Note that the default value for this option can be also set with the + :envvar:`PROJ_ONLY_BEST_DEFAULT` environment variable, or with the + ``only_best_default`` setting of :ref:`proj-ini` (:option:`--only-best` + when specified overrides such default value). .. option:: --no-ballpark diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index daf167a17d..2bce1432f2 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -208,6 +208,10 @@ paragraph for more details. be understood as the transformation returned by :cpp:func:`proj_get_suggested_operation` if all known grids were accessible (either locally or through network). + Note that the default value for this option can be also set with the + :envvar:`PROJ_ONLY_BEST_DEFAULT` environment variable, or with the + ``only_best_default`` setting of :ref:`proj-ini` (the ONLY_BEST option + when specified overrides such default value). - FORCE_OVER=YES/NO: can be set to YES to force the ``+over`` flag on the transformation returned by this function. See :ref:`longitude_wrapping` diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst index 750c0e5a76..5314f450a7 100644 --- a/docs/source/resource_files.rst +++ b/docs/source/resource_files.rst @@ -136,6 +136,15 @@ Its default content is: cache_ttl_sec = 86400 + ; Can be set to on so that by default the lack of a known resource files needed + ; for the best transformation PROJ would normally use causes an error. This + ; default value itself is overriden by the PROJ_ONLY_BEST_DEFAULT environment + ; variable if set, and then by the ONLY_BEST setting that can be + ; passed to the proj_create_crs_to_crs() method, or with the --only-best + ; option of the cs2cs program. + ; (added in PROJ 9.2) + only_best_default = off + ; Filename of the Certificate Authority (CA) bundle. ; Can be overriden with the PROJ_CURL_CA_BUNDLE / CURL_CA_BUNDLE environment variable. ; (added in PROJ 9.0) diff --git a/src/4D_api.cpp b/src/4D_api.cpp index d78b177eca..d8e561fa40 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -1935,12 +1935,13 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons if( !ctx ) { ctx = pj_get_default_ctx(); } + pj_load_ini(ctx); // to set ctx->errorIfBestTransformationNotAvailableDefault const char* authority = nullptr; double accuracy = -1; bool allowBallparkTransformations = true; bool forceOver = false; - bool errorIfBestTransformationNotAvailable = false; + bool errorIfBestTransformationNotAvailable = ctx->errorIfBestTransformationNotAvailableDefault; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "AUTHORITY="))) { diff --git a/src/ctx.cpp b/src/ctx.cpp index 83d4d60f02..1a96b6a441 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -151,6 +151,7 @@ pj_ctx::pj_ctx(const pj_ctx& other) : last_errno(0), debug_level(other.debug_level), warnIfBestTransformationNotAvailable(other.warnIfBestTransformationNotAvailable), + errorIfBestTransformationNotAvailableDefault(other.errorIfBestTransformationNotAvailableDefault), logger(other.logger), logger_app_data(other.logger_app_data), cpp_context(other.cpp_context ? other.cpp_context->clone(this) : nullptr), diff --git a/src/filemanager.cpp b/src/filemanager.cpp index b493c62dbb..11c2fa8062 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -1819,6 +1819,16 @@ void pj_load_ini(PJ_CONTEXT *ctx) { ctx->ca_bundle_path = ca_bundle_path; } + // Load default value for errorIfBestTransformationNotAvailableDefault + // from environment first + const char *proj_only_best_default = getenv("PROJ_ONLY_BEST_DEFAULT"); + if (proj_only_best_default && proj_only_best_default[0] != '\0') { + ctx->errorIfBestTransformationNotAvailableDefault = + ci_equal(proj_only_best_default, "ON") || + ci_equal(proj_only_best_default, "YES") || + ci_equal(proj_only_best_default, "TRUE"); + } + ctx->iniFileLoaded = true; auto file = std::unique_ptr( reinterpret_cast(pj_open_lib_internal( @@ -1878,6 +1888,12 @@ void pj_load_ini(PJ_CONTEXT *ctx) { } } else if (ca_bundle_path == nullptr && key == "ca_bundle_path") { ctx->ca_bundle_path = value; + } else if (proj_only_best_default == nullptr && + key == "only_best_default") { + ctx->errorIfBestTransformationNotAvailableDefault = + ci_equal(value, "ON") || + ci_equal(value, "YES") || + ci_equal(value, "TRUE"); } } diff --git a/src/proj_internal.h b/src/proj_internal.h index c66876e871..3f2c67d54f 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -696,6 +696,7 @@ struct pj_ctx{ int last_errno = 0; int debug_level = PJ_LOG_ERROR; bool warnIfBestTransformationNotAvailable = true; /* to remove in PROJ 10? */ + bool errorIfBestTransformationNotAvailableDefault = false; void (*logger)(void *, int, const char *) = nullptr; void *logger_app_data = nullptr; struct projCppContext* cpp_context = nullptr; /* internal context for C++ code */ diff --git a/test/cli/testvarious b/test/cli/testvarious index 3e17762684..6061b811fc 100755 --- a/test/cli/testvarious +++ b/test/cli/testvarious @@ -1117,6 +1117,25 @@ $EXE --only-best=yes EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs (grid missing) with PROJ_ONLY_BEST_DEFAULT=YES" >> ${OUT} +# +PROJ_ONLY_BEST_DEFAULT=YES $EXE EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test cs2cs (grid missing) with only_best_default=on in proj.ini" >> ${OUT} + +echo "only_best_default=on" > proj.ini + +# +PROJ_DATA=$PWD:$PROJ_DATA $EXE EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <> ${OUT} echo "Test cs2cs --only-best (grid missing)" >> ${OUT} # diff --git a/test/cli/tv_out.dist b/test/cli/tv_out.dist index a5067a043b..bb66c886f6 100644 --- a/test/cli/tv_out.dist +++ b/test/cli/tv_out.dist @@ -546,6 +546,14 @@ Test cs2cs --only-best=yes (grid missing) Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. 49 2 0 * * inf ############################################################## +Test cs2cs (grid missing) with PROJ_ONLY_BEST_DEFAULT=YES +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. This might become an error in a future PROJ major release. Set the ONLY_BEST option to YES or NO. This warning will no longer be emitted (for the current context). +49 2 0 * * inf +############################################################## +Test cs2cs (grid missing) with only_best_default=on in proj.ini +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. This might become an error in a future PROJ major release. Set the ONLY_BEST option to YES or NO. This warning will no longer be emitted (for the current context). +49 2 0 * * inf +############################################################## Test cs2cs --only-best (grid missing) Attempt to use coordinate operation NAD27 to NAD83 (7) failed. Grid us_noaa_nadcon5_nad27_nad83_1986_conus.tif is not available. Consult https://proj.org/resource_files.html for guidance. 40 -100 0 * * inf From 4fb15a24c051861253528ac4d626b22b40939999 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 15:12:06 +0100 Subject: [PATCH 06/11] Update docs/source/apps/cs2cs.rst Co-authored-by: Kristian Evers --- docs/source/apps/cs2cs.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index c6efe73a4f..b59e33bdd9 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -171,13 +171,14 @@ The following control parameters can appear in any order: .. versionadded:: 9.2.0 - If set to yes, error out if the best transformation, known of PROJ, and - usable by PROJ if all grids known and usable by PROJ were accessible, - cannot be used. + Force `cs2cs` to only use the best transformation known by PROJ. + `cs2cs` will return an error if a grid needed for the best transformation is missing. + Best transformation should be understood as the most accurate transformation available among all relevant for the point to transform, and if all known grids required to perform such transformation were accessible (either locally or through network). + Note that the default value for this option can be also set with the :envvar:`PROJ_ONLY_BEST_DEFAULT` environment variable, or with the ``only_best_default`` setting of :ref:`proj-ini` (:option:`--only-best` From 62d2291651f042a5a46dbc1dad30eb31d2e434d3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 15:17:08 +0100 Subject: [PATCH 07/11] resource_files.rst: use literalinclude for proj.ini --- docs/source/resource_files.rst | 41 +--------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/docs/source/resource_files.rst b/docs/source/resource_files.rst index 5314f450a7..d84d6c366f 100644 --- a/docs/source/resource_files.rst +++ b/docs/source/resource_files.rst @@ -117,46 +117,7 @@ network related parameters. Its default content is: -:: - - [general] - ; Lines starting by ; are commented lines. - ; - - ; Network capabilities disabled by default. - ; Can be overridden with the PROJ_NETWORK=ON environment variable. - ; network = on - - ; Can be overridden with the PROJ_NETWORK_ENDPOINT environment variable. - cdn_endpoint = https://cdn.proj.org - - cache_enabled = on - - cache_size_MB = 300 - - cache_ttl_sec = 86400 - - ; Can be set to on so that by default the lack of a known resource files needed - ; for the best transformation PROJ would normally use causes an error. This - ; default value itself is overriden by the PROJ_ONLY_BEST_DEFAULT environment - ; variable if set, and then by the ONLY_BEST setting that can be - ; passed to the proj_create_crs_to_crs() method, or with the --only-best - ; option of the cs2cs program. - ; (added in PROJ 9.2) - only_best_default = off - - ; Filename of the Certificate Authority (CA) bundle. - ; Can be overriden with the PROJ_CURL_CA_BUNDLE / CURL_CA_BUNDLE environment variable. - ; (added in PROJ 9.0) - ; ca_bundle_path = /path/to/cabundle.pem - - ; Transverse Mercator (and UTM) default algorithm: auto, evenden_snyder or poder_engsager - ; * evenden_snyder is the fastest, but less accurate far from central meridian - ; * poder_engsager is slower, but more accurate far from central meridian - ; * default will auto-select between the two above depending on the coordinate - ; to transform and will use evenden_snyder if the error in doing so is below - ; 0.1 mm (for an ellipsoid of the size of Earth) - tmerc_default_algo = poder_engsager +.. literalinclude:: ../../data/proj.ini Transformation grids From b1db3f64af08f557245c6e49e00bd021ef594393 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 15:29:50 +0100 Subject: [PATCH 08/11] cs2cs: add example demonstrating --only-best usefulness --- docs/source/apps/cs2cs.rst | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index b59e33bdd9..86db28c3c2 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -302,12 +302,42 @@ The x-y output data will appear as three lines of: :: - 1402285.98 5076292.42 0.00 + 1402285.93 5076292.58 0.00 -.. note:: - To get those exact values, you have need to have all current grids installed - locally or use networking capabilities mentioned above. +To get those exact values, you have need to have all current grids installed +(in that instance the NADCON5 :file:`us_noaa_nadcon5_nad27_nad83_1986_conus.tif` grid) +locally or use networking capabilities mentioned above. + +To make sure you will get the optimal result, you may add :option:`--only-best`. +Assuming the above mentionned grid is *not* available, + +:: + + echo -111.5 45.25919444444 | cs2cs --only-best +proj=latlong +datum=NAD83 +to +proj=utm +zone=10 +datum=NAD27 + +would return: + +:: + + Attempt to use coordinate operation axis order change (2D) + Inverse of NAD27 to NAD83 (7) + axis order change (2D) + UTM zone 10N failed. Grid us_noaa_nadcon5_nad27_nad83_1986_conus.tif is not available. Consult https://proj.org/resource_files.html for guidance. + * * inf + +Otherwise, if you don't have the grid available and you don't specify :option:`--only-best`: + +:: + + echo -111.5 45.25919444444 | cs2cs --only-best +proj=latlong +datum=NAD83 +to +proj=utm +zone=10 +datum=NAD27 + +would return: + +:: + + 1402224.57 5076275.42 0.00 + +which is the result when the NAD27 and NAD83 datums are dealt as identical, +which is an approximation at a level of several tens of metres. + Using EPSG CRS codes -------------------- From 9724764d960101b10cfc5ca35c3872ca231bf3f3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 16:36:28 +0100 Subject: [PATCH 09/11] Fix related to force_over --- src/4D_api.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/4D_api.cpp b/src/4D_api.cpp index d8e561fa40..e962623932 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -2069,6 +2069,9 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons } } + if( P != nullptr ) { + P->over = forceOver; + } return P; } @@ -2088,6 +2091,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons } for( auto& op: preparedOpList ) { + op.pj->over = forceOver; op.pj->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; } @@ -2103,6 +2107,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons P->alternativeCoordinateOperations = std::move(preparedOpList); // The returned P is rather dummy P->descr = "Set of coordinate operations"; + P->over = forceOver; P->iso_obj = nullptr; P->fwd = nullptr; P->inv = nullptr; From 97157e3014c4492ba0b5b04e9dea7c2bfa3cdf44 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 16:32:45 +0100 Subject: [PATCH 10/11] best_only: move warning at the PJ* level --- src/4D_api.cpp | 22 +++++++++++++--------- src/ctx.cpp | 1 - src/proj_internal.h | 2 +- test/cli/tv_out.dist | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/4D_api.cpp b/src/4D_api.cpp index e962623932..18c23d1abf 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -296,12 +296,13 @@ static void warnAboutMissingGrid(PJ* P) "Consult https://proj.org/resource_files.html for guidance."; } } - if( P->ctx->warnIfBestTransformationNotAvailable ) + if( !P->errorIfBestTransformationNotAvailable && + P->warnIfBestTransformationNotAvailable ) { msg += " This might become an error in a future PROJ major release. " "Set the ONLY_BEST option to YES or NO. " - "This warning will no longer be emitted (for the current context)."; - P->ctx->warnIfBestTransformationNotAvailable = false; + "This warning will no longer be emitted (for the current transformation instance)."; + P->warnIfBestTransformationNotAvailable = false; } pj_log(P->ctx, P->errorIfBestTransformationNotAvailable ? PJ_LOG_ERROR : PJ_LOG_DEBUG, @@ -384,7 +385,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available. return res; } else if( P->errorIfBestTransformationNotAvailable || - P->ctx->warnIfBestTransformationNotAvailable ) { + P->warnIfBestTransformationNotAvailable ) { warnAboutMissingGrid(alt.pj); if( P->errorIfBestTransformationNotAvailable ) return res; @@ -1941,6 +1942,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons double accuracy = -1; bool allowBallparkTransformations = true; bool forceOver = false; + bool warnIfBestTransformationNotAvailable = true; bool errorIfBestTransformationNotAvailable = ctx->errorIfBestTransformationNotAvailableDefault; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; @@ -1959,7 +1961,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return nullptr; } } else if ((value = getOptionValue(*iter, "ONLY_BEST="))) { - ctx->warnIfBestTransformationNotAvailable = false; + warnIfBestTransformationNotAvailable = false; if( ci_equal(value, "yes") ) errorIfBestTransformationNotAvailable = true; else if( ci_equal(value, "no") ) @@ -2018,7 +2020,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons proj_operation_factory_context_set_grid_availability_use( ctx, operation_ctx, (errorIfBestTransformationNotAvailable || - ctx->warnIfBestTransformationNotAvailable || + warnIfBestTransformationNotAvailable || proj_context_is_network_enabled(ctx)) ? PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE: PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); @@ -2041,7 +2043,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons ctx->forceOver = forceOver; const int old_debug_level = ctx->debug_level; - if( errorIfBestTransformationNotAvailable || ctx->warnIfBestTransformationNotAvailable ) + if( errorIfBestTransformationNotAvailable || warnIfBestTransformationNotAvailable ) ctx->debug_level = PJ_LOG_NONE; PJ* P = proj_list_get(ctx, op_list, 0); ctx->debug_level = old_debug_level; @@ -2049,6 +2051,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons if( P != nullptr ) { P->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; + P->warnIfBestTransformationNotAvailable = warnIfBestTransformationNotAvailable; } if( P == nullptr || op_count == 1 || @@ -2059,7 +2062,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons if( P != nullptr && (errorIfBestTransformationNotAvailable || - ctx->warnIfBestTransformationNotAvailable) && + warnIfBestTransformationNotAvailable) && !proj_coordoperation_is_instantiable(ctx, P) ) { warnAboutMissingGrid(P); @@ -2075,7 +2078,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons return P; } - if( errorIfBestTransformationNotAvailable || ctx->warnIfBestTransformationNotAvailable ) + if( errorIfBestTransformationNotAvailable || warnIfBestTransformationNotAvailable ) ctx->debug_level = PJ_LOG_NONE; auto preparedOpList = pj_create_prepared_operations(ctx, source_crs, target_crs, op_list); @@ -2093,6 +2096,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons for( auto& op: preparedOpList ) { op.pj->over = forceOver; op.pj->errorIfBestTransformationNotAvailable = errorIfBestTransformationNotAvailable; + op.pj->warnIfBestTransformationNotAvailable = warnIfBestTransformationNotAvailable; } // If there's finally juste a single result, return it directly diff --git a/src/ctx.cpp b/src/ctx.cpp index 1a96b6a441..fed66a1eac 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -150,7 +150,6 @@ pj_ctx::pj_ctx(const pj_ctx& other) : lastFullErrorMessage(std::string()), last_errno(0), debug_level(other.debug_level), - warnIfBestTransformationNotAvailable(other.warnIfBestTransformationNotAvailable), errorIfBestTransformationNotAvailableDefault(other.errorIfBestTransformationNotAvailableDefault), logger(other.logger), logger_app_data(other.logger_app_data), diff --git a/src/proj_internal.h b/src/proj_internal.h index 3f2c67d54f..43c67c5d60 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -595,6 +595,7 @@ struct PJconsts { std::vector alternativeCoordinateOperations{}; int iCurCoordOp = -1; bool errorIfBestTransformationNotAvailable = false; + bool warnIfBestTransformationNotAvailable = true; /* to remove in PROJ 10? */ /************************************************************************************* @@ -695,7 +696,6 @@ struct pj_ctx{ std::string lastFullErrorMessage{}; // used by proj_context_errno_string int last_errno = 0; int debug_level = PJ_LOG_ERROR; - bool warnIfBestTransformationNotAvailable = true; /* to remove in PROJ 10? */ bool errorIfBestTransformationNotAvailableDefault = false; void (*logger)(void *, int, const char *) = nullptr; void *logger_app_data = nullptr; diff --git a/test/cli/tv_out.dist b/test/cli/tv_out.dist index bb66c886f6..ddb886d053 100644 --- a/test/cli/tv_out.dist +++ b/test/cli/tv_out.dist @@ -547,11 +547,11 @@ Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) fail 49 2 0 * * inf ############################################################## Test cs2cs (grid missing) with PROJ_ONLY_BEST_DEFAULT=YES -Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. This might become an error in a future PROJ major release. Set the ONLY_BEST option to YES or NO. This warning will no longer be emitted (for the current context). +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. 49 2 0 * * inf ############################################################## Test cs2cs (grid missing) with only_best_default=on in proj.ini -Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. This might become an error in a future PROJ major release. Set the ONLY_BEST option to YES or NO. This warning will no longer be emitted (for the current context). +Attempt to use coordinate operation Inverse of WGS 84 to EGM2008 height (1) failed. Grid us_nga_egm08_25.tif is not available. Consult https://proj.org/resource_files.html for guidance. 49 2 0 * * inf ############################################################## Test cs2cs --only-best (grid missing) From 9020ae28ff8b4332f5fde88da3d8364d11cb2799 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 12 Jan 2023 17:27:21 +0100 Subject: [PATCH 11/11] proj.ini: do not set 'only_best_default' at all; and when set to on/off, disable related warnings --- data/proj.ini | 7 ++++--- src/4D_api.cpp | 2 +- src/ctx.cpp | 1 + src/filemanager.cpp | 2 ++ src/proj_internal.h | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/data/proj.ini b/data/proj.ini index 9267cdf64b..252fa00301 100644 --- a/data/proj.ini +++ b/data/proj.ini @@ -16,13 +16,14 @@ cache_size_MB = 300 cache_ttl_sec = 86400 ; Can be set to on so that by default the lack of a known resource files needed -; for the best transformation PROJ would normally use causes an error. This -; default value itself is overriden by the PROJ_ONLY_BEST_DEFAULT environment +; for the best transformation PROJ would normally use causes an error, or off +; to accept missing resource files without errors or warnings. +; This default value itself is overriden by the PROJ_ONLY_BEST_DEFAULT environment ; variable if set, and then by the ONLY_BEST setting that can be ; passed to the proj_create_crs_to_crs() method, or with the --only-best ; option of the cs2cs program. ; (added in PROJ 9.2) -only_best_default = off +; only_best_default = on ; Filename of the Certificate Authority (CA) bundle. ; Can be overriden with the PROJ_CURL_CA_BUNDLE / CURL_CA_BUNDLE environment variable. diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 18c23d1abf..f7fd18bfed 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -1942,7 +1942,7 @@ PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, cons double accuracy = -1; bool allowBallparkTransformations = true; bool forceOver = false; - bool warnIfBestTransformationNotAvailable = true; + bool warnIfBestTransformationNotAvailable = ctx->warnIfBestTransformationNotAvailableDefault; bool errorIfBestTransformationNotAvailable = ctx->errorIfBestTransformationNotAvailableDefault; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; diff --git a/src/ctx.cpp b/src/ctx.cpp index fed66a1eac..78dcabe373 100644 --- a/src/ctx.cpp +++ b/src/ctx.cpp @@ -151,6 +151,7 @@ pj_ctx::pj_ctx(const pj_ctx& other) : last_errno(0), debug_level(other.debug_level), errorIfBestTransformationNotAvailableDefault(other.errorIfBestTransformationNotAvailableDefault), + warnIfBestTransformationNotAvailableDefault(other.warnIfBestTransformationNotAvailableDefault), logger(other.logger), logger_app_data(other.logger_app_data), cpp_context(other.cpp_context ? other.cpp_context->clone(this) : nullptr), diff --git a/src/filemanager.cpp b/src/filemanager.cpp index 11c2fa8062..9ee255dffe 100644 --- a/src/filemanager.cpp +++ b/src/filemanager.cpp @@ -1823,6 +1823,7 @@ void pj_load_ini(PJ_CONTEXT *ctx) { // from environment first const char *proj_only_best_default = getenv("PROJ_ONLY_BEST_DEFAULT"); if (proj_only_best_default && proj_only_best_default[0] != '\0') { + ctx->warnIfBestTransformationNotAvailableDefault = false; ctx->errorIfBestTransformationNotAvailableDefault = ci_equal(proj_only_best_default, "ON") || ci_equal(proj_only_best_default, "YES") || @@ -1890,6 +1891,7 @@ void pj_load_ini(PJ_CONTEXT *ctx) { ctx->ca_bundle_path = value; } else if (proj_only_best_default == nullptr && key == "only_best_default") { + ctx->warnIfBestTransformationNotAvailableDefault = false; ctx->errorIfBestTransformationNotAvailableDefault = ci_equal(value, "ON") || ci_equal(value, "YES") || diff --git a/src/proj_internal.h b/src/proj_internal.h index 43c67c5d60..203dca98e3 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -697,6 +697,7 @@ struct pj_ctx{ int last_errno = 0; int debug_level = PJ_LOG_ERROR; bool errorIfBestTransformationNotAvailableDefault = false; + bool warnIfBestTransformationNotAvailableDefault = true; void (*logger)(void *, int, const char *) = nullptr; void *logger_app_data = nullptr; struct projCppContext* cpp_context = nullptr; /* internal context for C++ code */