Choosing an operation¶
When a datum changes you must name the operation. You choose it with
available_operations(). It lists every operation
PROJ offers between two CRSs, including ones this package would refuse to run,
and describes each so you can decide.
Listing candidates¶
from geodetic_engine.geodesy import available_operations
candidates = available_operations("EPSG:4230", "EPSG:4326") # ED50 -> WGS 84
print(len(candidates), "candidates")
for c in candidates[:8]:
print(f"{c.authority_code or '(none)':14} {c.accuracy!s:>5} m usable={c.usable!s:5} {c.name}")
43 candidates
EPSG:1133 10.0 m usable=True ED50 to WGS 84 (1)
ESRI:108335 10.0 m usable=True ED_1950_To_WGS_1984_NGA_7PAR
EPSG:1612 1.0 m usable=True ED50 to WGS 84 (23)
EPSG:1311 1.0 m usable=True ED50 to WGS 84 (18)
EPSG:1134 6.0 m usable=True ED50 to WGS 84 (2)
EPSG:8569 1.5 m usable=True ED50 to WGS 84 (21)
EPSG:8047 1.5 m usable=True ED50 to WGS 84 (15)
EPSG:1139 7.0 m usable=True ED50 to WGS 84 (7)
They are in PROJ’s ranking order. Each is an
OperationCandidate:
Field |
Meaning |
|---|---|
|
|
|
As published |
|
Stated accuracy in metres, or None; a ballpark always has None |
|
Where the operation is valid, as an |
|
Whether it could be applied here: not a ballpark, and every grid installed |
|
Whether it is a ballpark approximation |
|
|
|
Whether it reads a coordinate epoch |
|
The individual operations, for a chain |
Filtering¶
available_operations filters with the same keywords pyproj’s
TransformerGroup uses:
precise = available_operations(
"EPSG:4230", "EPSG:4326",
authority="EPSG", # only EPSG's operations; "any" (default) searches all
accuracy=1.0, # stated accuracy of 1 m or better
allow_ballpark=False, # drop the ballpark fallback
allow_superseded=False, # drop operations EPSG has superseded
)
for c in precise:
print(f"{c.authority_code:10} {c.accuracy} m {c.area_of_use.name}")
EPSG:1612 1.0 m Norway - offshore north of 62°N. Also Svalbard - onshore and offshore.
EPSG:1311 1.0 m Denmark - offshore North Sea; Ireland - offshore; Netherlands - offshore; United Kingdom - UKCS offshore.
EPSG:15933 1.0 m Spain - mainland, Balearic Islands and Ceuta - onshore.
EPSG:1613 1.0 m Norway - offshore south of 62°N - North Sea.
EPSG:1989 1.0 m Portugal - mainland - onshore.
EPSG:1627 1.0 m Denmark - onshore.
EPSG:1998 1.0 m Germany - offshore North Sea.
EPSG:1629 1.0 m Gibraltar - onshore and offshore.
PROJ never offers deprecated EPSG operations, so there is no
allow_deprecated option.
Picking by area of use¶
A candidate is only valid inside its area of use. This picks the most accurate usable candidate whose area contains the point, then transforms with it. The point is in the Norwegian Sea, and the target is WGS 84 / UTM zone 31N:
from geodetic_engine.geodesy import Transformation
lon, lat = 4.12789451, 63.58496782 # offshore Norway, north of 62°N
def covers(area, lon, lat):
if area is None:
return False
west, south, east, north = area.bounds
if east < west: # crosses the antimeridian
return (lon >= west or lon <= east) and south <= lat <= north
return west <= lon <= east and south <= lat <= north
candidates = [
c for c in available_operations("EPSG:4230", "EPSG:32631")
if c.usable and c.accuracy is not None and covers(c.area_of_use, lon, lat)
]
best = min(candidates, key=lambda c: c.accuracy)
print("chosen:", best.name, f"({best.accuracy} m)")
print("steps :", best.references)
print("area :", best.area_of_use.name)
result = Transformation("EPSG:4230", "EPSG:32631", operation=best).transform((lon, lat))
print(result.coordinates)
chosen: ED50 to WGS 84 (23) + UTM zone 31N (1.0 m)
steps : ('EPSG:1612', 'EPSG:16031')
area : Norway - offshore north of 62°N. Also Svalbard - onshore and offshore.
((555895.7120983884, 7051218.751864074),)
The bounding box is coarse. It is the rectangle around the area, so a point
inside the box can still be outside the area described in words. Read
area_of_use.name before trusting the box.
This package does not check that your points are inside the operation’s area
of use. PROJ applies an operation anywhere, and a Helmert fitted to the North
Sea gives plausible numbers in Australia. Checking the area is your job, and
area_of_use is there for it.
Passing a candidate as the operation¶
Towards a projected CRS, PROJ adds the projection to each datum shift, so the
candidates have no code of their own. That is why best above had none.
Passing the candidate object, not its code, is how such a candidate is named.
It expands to its references, and each is checked independently against the
pipeline:
for c in available_operations("EPSG:4230", "EPSG:32631")[:4]:
print(f"{c.authority_code!s:6} {c.name:45} -> {c.references}")
None ED50 to WGS 84 (1) + UTM zone 31N -> ('EPSG:1133', 'EPSG:16031')
None ED_1950_To_WGS_1984_NGA_7PAR + UTM zone 31N -> ('ESRI:108335', 'EPSG:16031')
None ED50 to WGS 84 (23) + UTM zone 31N -> ('EPSG:1612', 'EPSG:16031')
None ED50 to WGS 84 (18) + UTM zone 31N -> ('EPSG:1311', 'EPSG:16031')
Ballpark candidates are listed but never applied¶
ballpark = [c for c in available_operations("EPSG:4230", "EPSG:4326") if c.ballpark]
for c in ballpark:
print(c.name, "| accuracy:", c.accuracy, "| usable:", c.usable)
Ballpark geographic offset from ED50 to WGS 84 | accuracy: None | usable: False
Jamaica 1875 has no published transformation at all, so between it and GDA94 a ballpark is PROJ’s only option. No result can be produced for that pair:
[(c.name, c.ballpark) for c in available_operations("EPSG:4241", "EPSG:4283")]
[('Ballpark geographic offset from Jamaica 1875 to GDA94', True)]
Exporting a candidate¶
A candidate can be exported as WKT2 or PROJJSON for review or storage:
candidate = available_operations("EPSG:4230", "EPSG:4326", authority="EPSG")[0]
print(candidate.to_wkt()[:400], "...")
CONCATENATEDOPERATION["ED50 to WGS 84 (1) (with axis order normalized for visualization)",SOURCECRS[GEOGCRS["ED50 (with axis order normalized for visualization)",DATUM["European Datum 1950",ELLIPSOID["International 1924",6378388,297,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic longitude (Lon)",east,ORDER[1],ANGLEUNIT["degree" ...
to_wkt() returns None when
WKT2 cannot faithfully describe the pipeline, for example when a step is
applied inverted.