Results and provenance¶
Every call returns a TransformationResult
containing the coordinates and a record of how they were produced. The record
is enough to reproduce or audit the result later.
from geodetic_engine.geodesy import transform
result = transform("EPSG:4230", "EPSG:4326", (2.5, 63.5), operation="EPSG:1612")
What the result records¶
print("coordinates :", result.coordinates)
print("count :", result.count)
print("coordinate order:", result.coordinate_order) # always "xy"
print("source :", result.source_crs, result.source_axes, result.source_units)
print("target :", result.target_crs, result.target_axes, result.target_units)
print("epoch :", result.coordinate_epoch)
print("grids :", result.grids, "missing:", result.missing_grids)
coordinates : ((2.4981894757094416, 63.49961374934551),)
count : 1
coordinate order: xy
source : CoordinateReferenceSystem('EPSG:4230') ('Lat', 'Lon') ('degree', 'degree')
target : CoordinateReferenceSystem('EPSG:4326') ('Lat', 'Lon') ('degree', 'degree')
epoch : None
grids : () missing: ()
coordinate_order is always "xy", the order of the values: easting or
longitude first wherever the target has one. source_axes,
target_axes and the unit tuples are in EPSG’s declared order, which for a
geographic CRS is latitude first. They are reported side by side so the two
cannot be confused. The value order of each axis is
result.target_crs.value_axis_abbreviations, which is also what tells a
south/west or north/west CRS apart: those keep their declared order.
The applied operation¶
operation is an
AppliedOperation:
op = result.operation
print("requested :", op.requested)
print("applied :", op.authority_code, "-", op.name)
print("method :", op.method_name)
print("accuracy :", op.accuracy, "m")
print("route :", op.route)
print("steps :", op.steps)
print("ballpark :", op.ballpark)
print("requires epoch:", op.requires_epoch)
print("direction :", op.execution_direction)
print("bound :", op.bound_operations)
requested : EPSG:1612
applied : EPSG:1612 - ED50 to WGS 84 (23)
method : Position Vector transformation (geog2D domain)
accuracy : 1.0 m
route : transformer_group
steps : ('ED50 to WGS 84 (23)', 'axis order change (2D)')
ballpark : False
requires epoch: False
direction : TransformDirection.FORWARD
bound : ()
route records how the transformer was obtained
(OperationRoute):
Route |
Meaning |
|---|---|
|
Your named operation, found among the candidates PROJ offers for the pair |
|
Your named or stated operation, wrapped in same-datum conversions to fit the pair |
|
Taken from a bound CRS’s own definition; |
|
Nothing named, no datum change; PROJ’s conversion, recorded |
|
Legacy value in old serialised results; never produced now |
Replaying the pipeline¶
pipeline is the exact
PROJ pipeline that ran, including axis swaps, unit conversions and inversions.
Plain pyproj can replay it without this package:
print(result.pipeline)
proj=pipeline step proj=unitconvert xy_in=deg xy_out=rad step proj=push v_3 step proj=cart ellps=intl step proj=helmert x=-116.641 y=-56.931 z=-110.559 rx=0.893 ry=0.921 rz=-0.917 s=-3.52 convention=position_vector step inv proj=cart ellps=WGS84 step proj=pop v_3 step proj=unitconvert xy_in=rad xy_out=deg
from pyproj import Transformer
Transformer.from_pipeline(result.pipeline).transform(2.5, 63.5)
(2.4981894757094416, 63.49961374934551)
Use the pipeline, not the operation’s WKT, to reproduce a result.
to_wkt() gives the WKT2 of the
applied operation, but returns None when WKT2 cannot state it faithfully, for
example when a step was applied inverted:
print(result.operation.to_wkt()[:300], "...")
COORDINATEOPERATION["ED50 to WGS 84 (23)",SOURCECRS[GEOGCRS["ED50",DATUM["European Datum 1950",ELLIPSOID["International 1924",6378388,297,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["de ...
Which database answered¶
The same EPSG code can mean different parameters in different proj.db
files. Every result stores SHA-256 fingerprints of the databases PROJ was
reading:
result.database_fingerprints
(('/usr/local/share/proj/proj.db',
'3d3d8b279d100544b5737cd08a732335170fff09ee91166b4137919d6d019198'),)
A result produced from a custom database can therefore be traced back to that database, and through its build report to the register entries it was built from (Provenance).
Serialising¶
to_json() gives a JSON
string and
to_json_dict() gives the
same as a dict. Both include the full WKT of both CRSs, so the record does not
depend on a database being present when it is read:
import json
record = result.to_json_dict()
print(sorted(record))
print(json.dumps(record["operation"], indent=2)[:800])
['coordinate_epoch', 'coordinate_order', 'coordinates', 'database_fingerprints', 'grids', 'operation', 'pipeline', 'source_axes', 'source_crs', 'source_crs_wkt', 'source_units', 'target_axes', 'target_crs', 'target_crs_wkt', 'target_units']
{
"requested": "EPSG:1612",
"applied": "EPSG:1612",
"name": "ED50 to WGS 84 (23)",
"method": "Position Vector transformation (geog2D domain)",
"accuracy_m": 1.0,
"route": "transformer_group",
"steps": [
"ED50 to WGS 84 (23)",
"axis order change (2D)"
],
"ballpark": false,
"requires_epoch": false,
"execution_direction": "FORWARD",
"bound_operations": [],
"axis_order_corrected": false,
"definition": {
"type": "Transformation",
"name": "ED50 to WGS 84 (23)",
"source_crs": {
"type": "GeographicCRS",
"name": "ED50",
"datum": {
"type": "GeodeticReferenceFrame",
"name": "European Datum 1950",
"ellipsoid": {
"name": "International 1924",
"semi_major_axis": 6378388,
"inverse_flatt