from pyproj import CRS
from pyproj.exceptions import CRSError
# Planetary CRS registry
PLANETARY_CRS = {
"mercury": {
"planetocentric": CRS("IAU_2015:19900"),
"planetographic": CRS.from_proj4(
CRS("IAU_2015:19900").to_proj4() + " +axis=wnu"
),
},
"venus": {
"planetocentric": CRS("IAU_2015:29900"),
},
"moon": {
"planetocentric": CRS("IAU_2015:30100"),
},
"earth": {
"planetocentric": CRS("IAU_2015:39900"),
"planetographic": CRS("IAU_2015:39901"),
},
"mars": {
"planetocentric": CRS("IAU_2015:49900"),
"planetographic": CRS("IAU_2015:49901"),
},
"ceres": {
"planetocentric": CRS("IAU_2015:200000100"),
"planetographic": CRS("IAU_2015:200000101"),
},
"vesta": {
# Planetocentric, Claudia crater at 146 E
"planetocentric": CRS("IAU_2015:200000400"),
# Claudia double prime (same as IAU_2015)
"claudia_dp": CRS("IAU_2015:200000400"),
# Claudia Prime, Claudia crater at 136 E
"claudia_p": CRS.from_proj4("+proj=longlat +R=255000 +lon_0=+10 +no_defs"),
# Dawn Claudia, Claudia crater at 356 E
"dawn_claudia": CRS.from_proj4("+proj=longlat +R=255000 +lon_0=-210 +no_defs"),
# Not Implemented: IAU-2000, Claudia crater at 4.3N, 145 E
},
"europa": {
"planetocentric": CRS("IAU_2015:50200"),
},
"ganymede": {
"planetocentric": CRS("IAU_2015:50300"),
"planetographic": CRS("IAU_2015:50301"),
},
"callisto": {
"planetocentric": CRS("IAU_2015:50400"),
"planetographic": CRS("IAU_2015:50401"),
},
"enceladus": {
"planetocentric": CRS("IAU_2015:60200"),
"planetographic": CRS.from_proj4(
CRS("IAU_2015:60200").to_proj4() + " +axis=wnu"
),
},
"tethys": {
"planetocentric": CRS("IAU_2015:60300"),
"planetographic": CRS.from_proj4(
CRS("IAU_2015:60300").to_proj4() + " +axis=wnu"
),
},
"dione": {
"planetocentric": CRS("IAU_2015:60400"),
"planetographic": CRS.from_proj4(
CRS("IAU_2015:60400").to_proj4() + " +axis=wnu"
),
},
"rhea": {
"planetocentric": CRS("IAU_2015:60500"),
"planetographic": CRS.from_proj4(
CRS("IAU_2015:60500").to_proj4() + " +axis=wnu"
),
},
"iapetus": {
"planetocentric": CRS("IAU_2015:60800"),
"planetographic": CRS("IAU_2015:60801"),
},
"pluto": {
"planetocentric": CRS("IAU_2015:99900"),
},
}
# Set the default CRS fro the body, else assume planetocentric
DEFAULT_CRS = {
"ceres": "planetographic",
"ganymede": "planetographic",
"enceladus": "planetographic",
"tethys": "planetographic",
"dione": "planetographic",
"rhea": "planetographic",
"iapetus": "planetographic",
"mercury": "planetographic",
}
ALL_BODIES = list(PLANETARY_CRS.keys())
[docs]def get_crs(body: str, system: str | CRS = "default") -> CRS:
"""Retrieves a CRS object from the registry or parse system if valid pyproj CRS."""
# Note CRSes need always_xy to interpret the lat,lon as y,x in Proj geometries
body = body.lower()
system = system.lower() if isinstance(system, str) else system
try:
# Should fail for any keys in PLANETARY_CRS
return CRS.from_user_input(system)
except CRSError:
try:
system = (
DEFAULT_CRS.get(body, "planetocentric")
if system == "default"
else str(system)
)
return CRS(PLANETARY_CRS[body][system])
except KeyError as err:
if body not in PLANETARY_CRS:
raise ValueError(
f"Body '{body}' is not supported. Choose one of {ALL_BODIES} or open a feature request."
) from err
raise ValueError(
f"Unknown Planetary body and system combo: '{body}', '{system}'."
) from err