diff --git a/example.danra.yaml b/example.danra.yaml index 3700fbb..4079e86 100644 --- a/example.danra.yaml +++ b/example.danra.yaml @@ -47,21 +47,6 @@ inputs: dims: [x, y] attributes: crs_wkt: 'PROJCRS["DMI HARMONIE DANRA lambert projection", BASEGEOGCRS["DMI HARMONIE DANRA lambert CRS", DATUM["DMI HARMONIE DANRA lambert datum", ELLIPSOID["Sphere", 6367470, 0, LENGTHUNIT["metre", 1, ID["EPSG", 9001]]]], PRIMEM["Greenwich", 0, ANGLEUNIT["degree", 0.0174532925199433, ID["EPSG", 8901]]], ID["EPSG",4035]], CONVERSION["Lambert Conic Conformal (2SP)", METHOD["Lambert Conic Conformal (2SP)", ID["EPSG", 9802]], PARAMETER["Latitude of false origin", 56.7, ANGLEUNIT["degree", 0.0174532925199433, ID["EPSG", 8821]]], PARAMETER["Longitude of false origin", 25, ANGLEUNIT["degree", 0.0174532925199433, ID["EPSG", 8822]]], PARAMETER["Latitude of 1st standard parallel", 56.7, ANGLEUNIT["degree", 0.0174532925199433, ID["EPSG", 8823]]], PARAMETER["Latitude of 2nd standard parallel", 56.7, ANGLEUNIT["degree", 0.0174532925199433, ID["EPSG", 8824]]], PARAMETER["Easting at false origin", 0, LENGTHUNIT["metre", 1, ID["EPSG", 8826]]], PARAMETER["Northing at false origin", 0, LENGTHUNIT["metre", 1, ID["EPSG", 8827]]]], CS[Cartesian, 2], AXIS["(E)", east, ORDER[1], LENGTHUNIT["metre", 1, ID["EPSG", 9001]]], AXIS["(N)", north, ORDER[2], LENGTHUNIT["metre", 1, ID["EPSG", 9001]]], USAGE[AREA["Denmark and surrounding regions"], BBOX[47, -3, 65, 25], SCOPE["Danra reanalysis projection"]]]' - semi_major_axis: 6367470.0 - semi_minor_axis: 6367470.0 - inverse_flattening: 0.0 - reference_ellipsoid_name: 'Sphere' - longitude_of_prime_meridian: 0.0 - prime_meridian_name: 'Greenwich' - geographic_crs_name: 'DMI HARMONIE DANRA lambert CRS' - horizontal_datum_name: 'DMI HARMONIE DANRA lambert datum' - projected_crs_name: 'DMI HARMONIE DANRA lambert projection' - grid_mapping_name: 'lambert_conformal_conic' - standard_parallel: [0.0567, 0.0567] - latitude_of_projection_origin: 0.0567 - longitude_of_central_meridian: 0.025 - false_easting: 0.0 - false_northing: 0.0 dim_mapping: time: method: rename diff --git a/mllam_data_prep/create_dataset.py b/mllam_data_prep/create_dataset.py index 37c291d..e7f9832 100644 --- a/mllam_data_prep/create_dataset.py +++ b/mllam_data_prep/create_dataset.py @@ -14,7 +14,9 @@ from .ops.loading import load_and_subset_dataset from .ops.mapping import map_dims_and_variables from .ops.projection import ( + ProjectionCompatibilityWarning, ProjectionInconsistencyWarning, + check_projection_compatibility, get_projection_crs, validate_projection_consistency, ) @@ -225,6 +227,12 @@ def create_dataset(config: Config): except ProjectionInconsistencyWarning as e: logger.warning(f"Projection information might be ambiguous: {e}") projection = pyproj.CRS.from_cf(projections[0]) + try: + check_projection_compatibility(projection) + except ProjectionCompatibilityWarning: + logger.warning( + "WKT string should include a BBOX definition to be compatible with e.g. cartopy." + ) # TODO: generalize the retrieval of x and y coords # coords = (dataarrays_by_target[target_output_var][0]['x'], dataarrays_by_target[target_output_var][0]['y']) diff --git a/mllam_data_prep/ops/projection.py b/mllam_data_prep/ops/projection.py index c7ed4a5..970e286 100644 --- a/mllam_data_prep/ops/projection.py +++ b/mllam_data_prep/ops/projection.py @@ -11,6 +11,10 @@ class ProjectionInconsistencyWarning(Warning): pass +class ProjectionCompatibilityWarning(Warning): + pass + + def _extract_grid_mapping_names(grid_mapping_attr: str) -> List[str]: """Extract grid mapping names from the grid_mapping attribute. @@ -71,6 +75,73 @@ def _get_projection_mappings(dataset: xr.Dataset) -> Dict[str, Any]: return projections +def check_projection_compatibility(proj: Dict[str, Union[str, dict]]) -> None: + """Check if the projection is compatible with mllam software. + + Checks: + - if information is given as WKT string + - if the WKT string includes a BBOX definition + + Parameters + ---------- + proj : Dict[str, Union[str, dict]] + Projection information as a dictionary with the projection + variable names as keys and the projection attributes as values + + Examples + -------- + >>> proj1 = {"crs_wkt": "EPSG:4326"} + >>> check_projection_compatibility(proj1) + >>> crs_wkt = ( + ... 'GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",' + ... 'MEMBER["World Geodetic System 1984 (Transit)"],' + ... 'MEMBER["World Geodetic System 1984 (G730)"],' + ... 'MEMBER["World Geodetic System 1984 (G873)"],' + ... 'MEMBER["World Geodetic System 1984 (G1150)"],' + ... 'MEMBER["World Geodetic System 1984 (G1674)"],' + ... 'MEMBER["World Geodetic System 1984 (G1762)"],' + ... 'MEMBER["World Geodetic System 1984 (G2139)"],' + ... 'MEMBER["World Geodetic System 1984 (G2296)"],' + ... 'ELLIPSOID["WGS 84",6378137,298.257223563,' + ... 'LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],' + ... 'PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],' + ... 'CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,' + ... 'ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],' + ... 'AXIS["geodetic longitude (Lon)",east,ORDER[2],' + ... 'ANGLEUNIT["degree",0.0174532925199433]],' + ... 'USAGE[SCOPE["Horizontal component of 3D system."],' + ... 'AREA["World."],],ID["EPSG",4326]]' + ... ) + >>> proj2 = {"crs_wkt": crs_wkt} + >>> check_projection_compatibility(proj2) + Traceback (most recent call last): + ... + ProjectionCompatibilityWarning: WKT string should include a BBOX definition to be compatible with e.g. cartopy. + >>> proj3 = { + ... "semi_major_axis": 6367470.0, + ... "semi_minor_axis": 6367470.0, + ... "reference_ellipsoid_name": "Sphere", + ... "grid_mapping_name": "lambert_conformal_conic", + ... "standard_parallel": [0.0567, 0.0567], + ... "latitude_of_projection_origin": 0.0567, + ... "longitude_of_central_meridian": 0.025, + ... } + >>> check_projection_compatibility(proj3) + Traceback (most recent call last): + ... + KeyError: 'Projection information must contain a WKT string.' + """ + if "crs_wkt" not in proj: + raise KeyError("Projection information must contain a WKT string.") + + if "BBOX" not in pyproj.CRS.from_cf(proj).to_wkt(): + raise ProjectionCompatibilityWarning( + "WKT string should include a BBOX definition to be compatible with e.g. cartopy." + ) + + return + + def validate_projection_consistency( projections: List[Dict[str, Union[str, dict]]] ) -> None: