Skip to content

Commit

Permalink
proj_create_crs_to_crs_from_pj(): add a ONLY_BEST=YES option (fixes #…
Browse files Browse the repository at this point in the history
…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).
  • Loading branch information
rouault committed Jan 2, 2023
1 parent 9d1ddec commit 621ce5e
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 3 deletions.
13 changes: 12 additions & 1 deletion docs/source/apps/cs2cs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Synopsis

| **cs2cs** [**-eEfIlrstvwW** [args]]
| [[--area <name_or_code>] | [--bbox <west_long,south_lat,east_long,north_lat>]]
| [--authority <name>] [--no-ballpark] [--accuracy <accuracy>] [--3d]
| [--authority <name>] [--3d]
| [--accuracy <accuracy>] [--only-best] [--no-ballpark]
| ([*+opt[=arg]* ...] [+to *+opt[=arg]* ...] | {source_crs} {target_crs})
| file ...
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions docs/source/development/reference/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
69 changes: 68 additions & 1 deletion src/4D_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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="))) {
Expand All @@ -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")) {
Expand Down Expand Up @@ -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);

Expand All @@ -1984,19 +2017,52 @@ 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 ||
proj_get_type(source_crs) == PJ_TYPE_GEOCENTRIC_CRS ||
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);
Expand All @@ -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";
Expand Down
10 changes: 9 additions & 1 deletion src/apps/cs2cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 *,
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions src/proj_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ struct PJconsts {
**************************************************************************************/
std::vector<PJCoordOperation> alternativeCoordinateOperations{};
int iCurCoordOp = -1;
bool errorIfBestTransformationNotAvailable = false;

/*************************************************************************************
Expand Down
28 changes: 28 additions & 0 deletions test/cli/testvarious
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<EOF
49 2 0
EOF

echo "##############################################################" >> ${OUT}
echo "Test cs2cs --only-best (grid missing)" >> ${OUT}
#
$EXE --only-best EPSG:4326+3855 EPSG:4979 -E >>${OUT} 2>&1 <<EOF
49 2 0
EOF

echo "##############################################################" >> ${OUT}
echo "Test cs2cs --only-best (grid missing)" >> ${OUT}
#
$EXE --only-best NAD27 NAD83 -E >>${OUT} 2>&1 <<EOF
40 -100 0
EOF

echo "##############################################################" >> ${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 <<EOF
49 2 0
EOF


# Done!
# do 'diff' with distribution results
Expand Down
15 changes: 15 additions & 0 deletions test/cli/tv_out.dist
Original file line number Diff line number Diff line change
Expand Up @@ -528,3 +528,18 @@ Test cs2cs -W10

-W argument missing or not in range [0,8]
program abnormally terminated
##############################################################
Test cs2cs --only-best (working)
49 2 0 48d59'59.756"N 1d59'57.4"E 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 (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
##############################################################
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

0 comments on commit 621ce5e

Please sign in to comment.