Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error management: revise error codes and expose them to the public API #2487

Merged
merged 11 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions docs/source/development/reference/datatypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,102 @@ Info structures
Date of last update of the init-file.


.. _error_codes:

Error codes
-----------

.. versionadded:: 8.0.0

Three classes of errors are defined below. The belonging of a given error
code to a class can bit tested with a binary and test. The error class itself
can be used as an error value in some rare cases where the error does not
fit into a more precise error value.

Those error codes are still quite generic for a number of them. Details on the
actual errors will be typically logged with the PJ_LOG_ERROR level.

Errors in class PROJ_ERR_INVALID_OP
+++++++++++++++++++++++++++++++++++

.. c:macro:: PROJ_ERR_INVALID_OP

Class of error codes typically related to coordinate operation initalization,
typically when creating a PJ* object from a PROJ string.

.. note:: some of them can also be emitted during coordinate transformation,
like PROJ_ERR_INVALID_OP_FILE_NOT_FOUND_OR_INVALID in case the resource loading
is differed until it is really needed.

.. c:macro:: PROJ_ERR_INVALID_OP_WRONG_SYNTAX

Invalid pipeline structure, missing +proj argument, etc.

.. c:macro:: PROJ_ERR_INVALID_OP_MISSING_ARG

Missing required operation parameter

.. c:macro:: PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE

One of the operation parameter has an illegal value.

.. c:macro:: PROJ_ERR_INVALID_OP_MUTUALLY_EXCLUSIVE_ARGS

Mutually exclusive arguments

.. c:macro:: PROJ_ERR_INVALID_OP_FILE_NOT_FOUND_OR_INVALID

File not found or with invalid content (particular case of PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE)

Errors in class PROJ_ERR_COORD_TRANSFM
++++++++++++++++++++++++++++++++++++++

.. c:macro:: PROJ_ERR_COORD_TRANSFM

Class of error codes related to transformation on a specific coordinate.

.. c:macro:: PROJ_ERR_COORD_TRANSFM_INVALID_COORD

Invalid input coordinate. e.g a latitude > 90°.

.. c:macro:: PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN

Coordinate is outside of the projection domain. e.g approximate mercator with \|longitude - lon_0\| > 90°,
or iterative convergence method failed.

.. c:macro:: PROJ_ERR_COORD_TRANSFM_NO_OPERATION

No operation found, e.g if no match the required accuracy, or if ballpark transformations
were asked to not be used and they would be only such candidate.

.. c:macro:: PROJ_ERR_COORD_TRANSFM_OUTSIDE_GRID

Point to transform falls outside grid/subgrid/TIN.

.. c:macro:: PROJ_ERR_COORD_TRANSFM_GRID_AT_NODATA

Point to transform falls in a grid cell that evaluates to nodata.

Errors in class PROJ_ERR_OTHER
++++++++++++++++++++++++++++++

.. c:macro:: PROJ_ERR_OTHER

Class of error codes that do not fit into one of the above class.

.. c:macro:: PROJ_ERR_OTHER_API_MISUSE

Error related to a misuse of PROJ API.

.. c:macro:: PROJ_ERR_OTHER_NO_INVERSE_OP

No inverse method available

.. c:macro:: PROJ_ERR_OTHER_NETWORK_ERROR

Failure when accessing a network resource.


Logging
-------------------------------------------------------------------------------

Expand Down
31 changes: 30 additions & 1 deletion docs/source/development/reference/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,22 @@ Coordinate transformation

Batch transform an array of :c:type:`PJ_COORD`.

Performs transformation on all points, even if errors occur on some points
(new to 8.0. Previous versions would exit early in case of failure on a given point)

Individual points that fail to transform will have their components set to
``HUGE_VAL``

:param P: Transformation object
:type P: :c:type:`PJ` *
:param `direction`: Transformation direction.
:type `direction`: PJ_DIRECTION
:param n: Number of coordinates in :c:data:`coord`
:type n: `size_t`
:returns: `int` 0 if all observations are transformed without error, otherwise returns error number
:returns: `int` 0 if all observations are transformed without error, otherwise returns error number.
This error number will be a precise error number if all coordinates that fail to transform
for the same reason, or a generic error code if they fail for different
reasons.


Error reporting
Expand All @@ -388,6 +397,8 @@ Error reporting
context is read. A text representation of the error number can be retrieved
with :c:func:`proj_errno_string`.

Consult :ref:`error_codes` for the list of error codes (PROJ >= 8.0)

:param P: Transformation object
:type P: :c:type:`PJ` *

Expand All @@ -400,6 +411,8 @@ Error reporting
transformation. A text representation of the error number can be retrieved
with :c:func:`proj_errno_string`.

Consult :ref:`error_codes` for the list of error codes (PROJ >= 8.0)

:param ctx: threading context.
:type ctx: :c:type:`PJ_CONTEXT` *

Expand Down Expand Up @@ -464,6 +477,22 @@ Error reporting

Get a text representation of an error number.

.. deprecated:: This function is potentially thread-unsafe, replaced by :c:func:`proj_context_errno_string`.

:param err: Error number.
:type err: `int`

:returns: `const char*` String with description of error.

.. c:function:: const char* proj_context_errno_string(PJ_CONTEXT* ctx, int err)

.. versionadded:: 8.0.0

Get a text representation of an error number.

:param ctx: threading context.
:type ctx: :c:type:`PJ_CONTEXT` *

:param err: Error number.
:type err: `int`

Expand Down
3 changes: 2 additions & 1 deletion scripts/reference_exported_symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ proj_context_create
proj_context_delete_cpp_context(projCppContext*)
proj_context_destroy
proj_context_errno
proj_context_errno_string
proj_context_get_database_metadata
proj_context_get_database_path
proj_context_get_url_endpoint
Expand Down Expand Up @@ -992,7 +993,7 @@ proj_list_get_count
proj_list_operations
proj_list_prime_meridians
proj_list_units
proj_log_error(PJconsts*, char const*, ...)
proj_log_error(PJconsts const*, char const*, ...)
proj_log_func
proj_log_level
proj_lp_dist
Expand Down
76 changes: 46 additions & 30 deletions src/4D_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) {
return HUGE_VAL;

if (n < 1) {
proj_errno_set (P, EINVAL);
proj_log_error(P, _("n should be >= 1"));
proj_errno_set (P, PROJ_ERR_OTHER_API_MISUSE);
return HUGE_VAL;
}

Expand Down Expand Up @@ -294,7 +295,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
if( iRetry > 0 ) {
const int oldErrno = proj_errno_reset(P);
if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) {
pj_log(P->ctx, PJ_LOG_DEBUG, proj_errno_string(oldErrno));
pj_log(P->ctx, PJ_LOG_DEBUG, proj_context_errno_string(P->ctx, oldErrno));
}
pj_log(P->ctx, PJ_LOG_DEBUG,
"Did not result in valid result. "
Expand All @@ -312,7 +313,7 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
}
PJ_COORD res = direction == PJ_FWD ?
pj_fwd4d( coord, alt.pj ) : pj_inv4d( coord, alt.pj );
if( proj_errno(alt.pj) == PJD_ERR_NETWORK_ERROR ) {
if( proj_errno(alt.pj) == PROJ_ERR_OTHER_NETWORK_ERROR ) {
return proj_coord_error ();
}
if( res.xyzt.x != HUGE_VAL ) {
Expand Down Expand Up @@ -359,21 +360,14 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
}
}

proj_errno_set (P, EINVAL);
proj_errno_set (P, PROJ_ERR_COORD_TRANSFM_NO_OPERATION);
return proj_coord_error ();
}

switch (direction) {
case PJ_FWD:
return pj_fwd4d (coord, P);
case PJ_INV:
return pj_inv4d (coord, P);
default:
break;
}

proj_errno_set (P, EINVAL);
return proj_coord_error ();
if (direction == PJ_FWD)
return pj_fwd4d (coord, P);
else
return pj_inv4d (coord, P);
}


Expand All @@ -383,18 +377,43 @@ int proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord)
/******************************************************************************
Batch transform an array of PJ_COORD.

Performs transformation on all points, even if errors occur on some points.

Individual points that fail to transform will have their components set to
HUGE_VAL

Returns 0 if all coordinates are transformed without error, otherwise
returns error number.
returns a precise error number if all coordinates that fail to transform
for the same reason, or a generic error code if they fail for different
reasons.
******************************************************************************/
size_t i;
int retErrno = 0;
bool hasSetRetErrno = false;
bool sameRetErrno = true;

for (i = 0; i < n; i++) {
proj_context_errno_set(P->ctx, 0);
coord[i] = proj_trans (P, direction, coord[i]);
if (proj_errno(P))
return proj_errno (P);
}
int thisErrno = proj_errno(P);
if( thisErrno != 0 )
{
if( !hasSetRetErrno )
{
retErrno = thisErrno;
hasSetRetErrno = true;
}
else if( sameRetErrno && retErrno != thisErrno )
{
sameRetErrno = false;
retErrno = PROJ_ERR_COORD_TRANSFM;
}
}
}

return 0;
proj_context_errno_set(P->ctx, retErrno);

return retErrno;
}


Expand Down Expand Up @@ -500,9 +519,6 @@ size_t proj_trans_generic (
break;
case PJ_IDENT:
return nmin;
default:
proj_errno_set (P, EINVAL);
return 0;
}

/* Arrays of length==0 are broadcast as the constant 0 */
Expand Down Expand Up @@ -771,22 +787,22 @@ PJ *pj_create_internal (PJ_CONTEXT *ctx, const char *definition) {
n = strlen (definition);
args = (char *) malloc (n + 1);
if (nullptr==args) {
proj_context_errno_set(ctx, ENOMEM);
proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
return nullptr;
}
strcpy (args, definition);

argc = pj_trim_argc (args);
if (argc==0) {
free (args);
proj_context_errno_set(ctx, PJD_ERR_NO_ARGS);
proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
return nullptr;
}

argv = pj_trim_argv (argc, args);
if (!argv) {
free(args);
proj_context_errno_set(ctx, ENOMEM);
proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
return nullptr;
}

Expand Down Expand Up @@ -821,14 +837,14 @@ indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm",
if (nullptr==ctx)
ctx = pj_get_default_ctx ();
if (nullptr==argv) {
proj_context_errno_set(ctx, PJD_ERR_NO_ARGS);
proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
return nullptr;
}

/* We assume that free format is used, and build a full proj_create compatible string */
c = pj_make_args (argc, argv);
if (nullptr==c) {
proj_context_errno_set(ctx, ENOMEM);
proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP /* ENOMEM */);
return nullptr;
}

Expand All @@ -849,14 +865,14 @@ Same as proj_create_argv() but calls pj_create_internal() instead of proj_create
if (nullptr==ctx)
ctx = pj_get_default_ctx ();
if (nullptr==argv) {
proj_context_errno_set(ctx, PJD_ERR_NO_ARGS);
proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
return nullptr;
}

/* We assume that free format is used, and build a full proj_create compatible string */
c = pj_make_args (argc, argv);
if (nullptr==c) {
proj_context_errno_set(ctx, ENOMEM);
proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
return nullptr;
}

Expand Down
4 changes: 2 additions & 2 deletions src/aasincos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ aasin(PJ_CONTEXT *ctx,double v) {

if ((av = fabs(v)) >= 1.) {
if (av > ONE_TOL)
proj_context_errno_set( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE );
proj_context_errno_set( ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN );
return (v < 0. ? -M_HALFPI : M_HALFPI);
}
return asin(v);
Expand All @@ -26,7 +26,7 @@ aacos(PJ_CONTEXT *ctx, double v) {

if ((av = fabs(v)) >= 1.) {
if (av > ONE_TOL)
proj_context_errno_set( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE );
proj_context_errno_set( ctx, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN );
return (v < 0. ? M_PI : 0.);
}
return acos(v);
Expand Down
Loading