Errors and refusals¶
A refusal here is never a bug to work around. Each one stops a coordinate that
might be wrong, and each exception type names a different reason. Catch
GeodesyError for all of them, or a specific
subclass to handle one case.
The hierarchy¶
flowchart LR
GE[GeodeticEngineError] --> GD[GeodesyError]
GE --> PR[PersistableReferenceError]
GE --> GR[GeorepositoryError]
GE --> PB[ProjDbBuildError]
GD --> U[UnresolvableCRSError]
GD --> A[AmbiguousOperationError]
GD --> N[OperationNotAvailableError]
GD --> B[BallparkTransformationError]
GD --> G[MissingGridError]
GD --> E[MissingCoordinateEpochError]
GD --> T[TransformationFailedError]
T --> R[CoordinateOutOfRangeError]
GD --> UE[UnembeddableOperationError]
UE --> NC[NotCollapsibleError]
Everything the package raises on purpose derives from
GeodeticEngineError. The exceptions are
TypeError and ValueError for malformed arguments, such as points with the
wrong number of values.
When each is raised, with an example¶
UnresolvableCRSError¶
The CRS input cannot be turned into a CRS. The message says why. For a CRS a custom database build left out, it gives the reason recorded at build time rather than “not found”.
from geodetic_engine.geodesy import Transformation
Transformation("EPSG:not-a-crs", "EPSG:4326")
UnresolvableCRSError: could not resolve 'EPSG:not-a-crs' as a CRS: Invalid projection: EPSG:not-a-crs: (Internal Proj Error: proj_create: crs not found: EPSG:not-a-crs)
AmbiguousOperationError¶
A datum change with no operation named. Name one; Choosing an operation
shows how to choose. allow_any_operation=True no longer bypasses this. The
keyword is accepted only so older calls do not break.
Transformation("EPSG:4230", "EPSG:4326")
AmbiguousOperationError: EPSG:4230 to EPSG:4326 involves a datum change; every datum operation must be named explicitly. allow_any_operation no longer permits automatic selection or ballpark results
OperationNotAvailableError¶
The named operation cannot be applied between these CRSs, or does not exist. This package raises rather than let PROJ substitute another operation:
# EPSG:1133 is ED50 -> WGS 84; it has nothing to do with WGS 84 -> World Mercator.
Transformation("EPSG:4326", "EPSG:3395", operation="EPSG:1133")
OperationNotAvailableError: applying EPSG:1133 between 'ED50 (with axis order normalized for visualization)' and 'WGS 84 / World Mercator' would require an additional, unrequested datum change; name the full operation instead
Transformation("EPSG:4326", "EPSG:3395", operation="EPSG:99999999")
OperationNotAvailableError: EPSG:99999999 could not be built as a coordinate operation, and is not among the operations PROJ offers for EPSG:4326 to EPSG:3395: Invalid projection urn:ogc:def:coordinateOperation:EPSG::99999999.: (Internal Proj Error: proj_create: coordinate operation not found: EPSG:99999999)
BallparkTransformationError¶
The only path PROJ can build is a ballpark approximation. With stock EPSG data
this is usually pre-empted: a pair with no real transformation is also a datum
change with nothing to name, so AmbiguousOperationError comes first. Either
way, no approximate coordinate is returned:
# Jamaica 1875 -> GDA94: PROJ knows no transformation, only a ballpark.
Transformation("EPSG:4241", "EPSG:4283")
AmbiguousOperationError: EPSG:4241 to EPSG:4283 involves a datum change; every datum operation must be named explicitly. allow_any_operation no longer permits automatic selection or ballpark results
BallparkTransformationError is a last check. It is raised if the pipeline
PROJ built turns out to be a ballpark after every other check passed. Stock
EPSG data rarely gets this far.
MissingGridError¶
The operation reads a grid file that is not installed. PROJ alone would fall
back to another operation without saying so. This package names the grid and
stops. To show it, PROJ is temporarily pointed at a directory holding
proj.db but no grids, simulating a machine without proj-data:
import os
import shutil
import tempfile
from pathlib import Path
import pyproj
installed = pyproj.datadir.get_data_dir()
bare = Path(tempfile.mkdtemp()) / "proj-without-grids"
bare.mkdir()
shutil.copy(Path(installed) / "proj.db", bare / "proj.db")
pyproj.datadir.set_data_dir(str(bare))
try:
Transformation("EPSG:4979", "EPSG:3855", operation="EPSG:3858")
finally:
pyproj.datadir.set_data_dir(installed)
MissingGridError: transforming EPSG:4979 to EPSG:3855 needs 1 grid file(s) that are not installed: Und_min2.5x2.5_egm2008_isw=82_WGS84_TideFree
Install the named grid (projsync --file us_nga_egm08_25.tif) and the same
call succeeds. available_operations() shows
each candidate’s grids with available flags, so you can check before
transforming.
MissingCoordinateEpochError¶
The operation reads a coordinate epoch and none was given, or the one given is not finite:
tfm = Transformation("EPSG:4896", "EPSG:4938", operation="EPSG:6277")
tfm.transform([(-2593197.524, 5656917.6189, -1394397.8828)])
MissingCoordinateEpochError: a dynamic CRS or time-dependent operation requires a coordinate epoch; supply a finite observation year explicitly
This is raised by transform(), not by the constructor: whether an epoch is
needed is known up front (tfm.requires_epoch), but the epoch is given per
batch.
CoordinateOutOfRangeError¶
A latitude outside the range its axis unit allows, checked before PROJ is called. The usual cause is passing projected metres to a geographic CRS. The limit comes from the axis unit, so for a CRS in grads it is ±100, not ±90.
tfm = Transformation("EPSG:4230", "EPSG:4326", operation="EPSG:1612")
tfm.transform(590000, 6700000) # UTM metres handed to a geographic CRS
CoordinateOutOfRangeError: point 0 has geodetic latitude 6700000.0 degree, outside the valid range [-90, 90] for EPSG:4230; values are given in ('Lon', 'Lat') order and in ('degree', 'degree') -- projected coordinates in metres need a projected CRS
It is a subclass of TransformationFailedError, so code catching that still
works.
TransformationFailedError¶
PROJ ran and could not produce a finite value for every point and every target axis. The message includes PROJ’s own error.
UnembeddableOperationError and NotCollapsibleError¶
Raised when an operation must be written as the single, unit-less
ABRIDGEDTRANSFORMATION a bound CRS carries and cannot be. You see these when
building bound CRSs: through
Persistable references (OSDU), a custom database build, or directly through Helmert utilities:
from pyproj.crs import CoordinateOperation
from geodetic_engine.geodesy.utils import collapse_concatenated
collapse_concatenated(CoordinateOperation.from_epsg(1612)) # a single step: nothing to collapse
NotCollapsibleError: EPSG:1612 is not a concatenated operation with at least two steps, so there is nothing to collapse
Errors from other modules¶
Module |
Base class |
Raised for |
|---|---|---|
|
Malformed payloads, unsupported methods, unresolvable grids (Persistable references (OSDU)) |
|
|
Configuration, authentication, HTTP, truncated pagination (Georepository client) |
|
|
Anything that stops a database build (Custom database from Georepository) |