Coordinate reference systems¶
CoordinateReferenceSystem wraps a
pyproj.crs.CRS and reports what EPSG declares about it: axis names,
abbreviations, directions and units, in the declared order. You rarely need to
build one, because transform() and
Transformation accept the same inputs. Use it
to check what a CRS expects before you transform into or out of it.
Resolving a CRS¶
Anything pyproj accepts works, plus OSDU persistableReference payloads:
from pyproj import CRS
from geodetic_engine.geodesy import CoordinateReferenceSystem
for value in (
"EPSG:4326", # authority code
4326, # bare integer: EPSG assumed
"urn:ogc:def:crs:EPSG::25832", # OGC URN
CRS.from_epsg(5941), # a pyproj CRS
"+proj=utm +zone=32 +ellps=GRS80 +units=m", # PROJ string
):
crs = CoordinateReferenceSystem.from_user_input(value)
print(f"{crs.authority_code or '-':12} {crs.name}")
EPSG:4326 WGS 84
EPSG:4326 WGS 84
EPSG:25832 ETRS89 / UTM zone 32N
EPSG:5941 NN2000:2018 height
EPSG:25832 unknown
A string that is a persistableReference is recognised automatically. Use
from_persistable_reference()
to require one, so any other input is refused. See
Persistable references (OSDU).
An unresolvable input raises
UnresolvableCRSError, not a raw pyproj error:
CoordinateReferenceSystem.from_user_input("EPSG:999999")
UnresolvableCRSError: could not resolve 'EPSG:999999' as a CRS: Invalid projection: EPSG:999999: (Internal Proj Error: proj_create: crs not found: EPSG:999999)
Declared axes versus value order¶
EPSG declares EPSG:4326 latitude-first. This package passes values
longitude-first. The CRS reports both:
wgs84 = CoordinateReferenceSystem.from_user_input("EPSG:4326")
print("declared :", wgs84.axis_abbreviations) # EPSG's order
print("values :", wgs84.value_axis_abbreviations) # the order you pass
print("mapping :", wgs84.value_axis_order) # value i is declared axis mapping[i]
print("units :", wgs84.axis_units)
print("dimension :", wgs84.dimension)
declared : ('Lat', 'Lon')
values : ('Lon', 'Lat')
mapping : (1, 0)
units : ('degree', 'degree')
dimension : 2
value_axis_order gives, for each value you pass, the index of the declared
axis it is. (1, 0) means the first value is the second declared axis
(longitude).
Each axis has full detail in
axes:
for axis in wgs84.axes:
print(axis)
AxisSpec(name='Geodetic latitude', abbrev='Lat', direction='north', unit_name='degree', unit_code='9122', unit_conversion_factor=0.017453292519943295)
AxisSpec(name='Geodetic longitude', abbrev='Lon', direction='east', unit_name='degree', unit_code='9122', unit_conversion_factor=0.017453292519943295)
Projected CRSs are usually declared easting-first, so declared order and value order agree:
utm = CoordinateReferenceSystem.from_user_input("EPSG:25832")
utm.axis_abbreviations, utm.value_axis_abbreviations, utm.axis_units
(('E', 'N'), ('E', 'N'), ('metre', 'metre'))
Some are not. EPSG:2044 (Hanoi 1972 / Gauss-Kruger zone 18) is declared
northing-first, and you still pass easting first:
gk = CoordinateReferenceSystem.from_user_input("EPSG:2044")
gk.axis_abbreviations, gk.value_axis_abbreviations
(('X', 'Y'), ('Y', 'X'))
Units¶
Units come from the CRS. This package does not convert them. Values go in and
come out in the CRS’s own axis units. EPSG:4807 (NTF (Paris)) is in grads,
with the pole at 100:
ntf_paris = CoordinateReferenceSystem.from_user_input("EPSG:4807")
ntf_paris.axis_units
('grad', 'grad')
A projected CRS in US survey feet reports that too:
texas = CoordinateReferenceSystem.from_user_input("EPSG:2278") # NAD83 / Texas South Central (ftUS)
texas.name, texas.axis_units
('NAD83 / Texas South Central (ftUS)', ('US survey foot', 'US survey foot'))
Vertical, compound and 3D CRSs¶
dimension is the
number of values each point has in this CRS:
for code in ("EPSG:5941", "EPSG:4979", "EPSG:6172", "EPSG:4978"):
crs = CoordinateReferenceSystem.from_user_input(code)
print(f"{code:10} dim={crs.dimension} {crs.value_axis_abbreviations!s:18} {crs.name}")
EPSG:5941 dim=1 ('H',) NN2000:2018 height
EPSG:4979 dim=3 ('Lon', 'Lat', 'h') WGS 84
EPSG:6172 dim=3 ('E', 'N', 'H') ETRS89-NOR [EUREF89] / UTM zone 32N + NN54 height
EPSG:4978 dim=3 ('X', 'Y', 'Z') WGS 84
EPSG:5941 is NN2000:2018 height (1D). EPSG:4979 is WGS 84 geographic 3D.
EPSG:6172 is a compound CRS, ETRS89 / UTM 32N + NN54 height. EPSG:4978 is
WGS 84 geocentric.
Dynamic CRSs¶
is_dynamic is true
for a datum with a frame reference epoch, such as an ITRF realisation. A
dynamic CRS does not by itself make a coordinate epoch mandatory. That
depends on whether the operation reads one; see
Time-dependent: a coordinate epoch.
for code in ("EPSG:4326", "EPSG:7789", "EPSG:4258"):
crs = CoordinateReferenceSystem.from_user_input(code)
print(f"{code:10} dynamic={crs.is_dynamic!s:5} {crs.name}")
EPSG:4326 dynamic=False WGS 84
EPSG:7789 dynamic=True ITRF2014
EPSG:4258 dynamic=False ETRS89
The underlying pyproj CRS¶
crs is the
pyproj.crs.CRS, for anything this wrapper does not expose.
definition is the
text it was resolved from.
wgs84.crs.datum.name, wgs84.definition
('World Geodetic System 1984 ensemble', 'EPSG:4326')