From 072cb294758c033c0c3920d762aec1324399e602 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Fri, 8 Apr 2022 13:40:42 +0100 Subject: [PATCH 01/15] Support in helpers --- .../fileformats/_nc_load_rules/helpers.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 954250b4a2..19f5568944 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -131,6 +131,7 @@ CF_ATTR_BOUNDS = "bounds" CF_ATTR_CALENDAR = "calendar" CF_ATTR_CLIMATOLOGY = "climatology" +CF_ATTR_GRID_DATUM = "horizontal_datum_name" CF_ATTR_GRID_INVERSE_FLATTENING = "inverse_flattening" CF_ATTR_GRID_EARTH_RADIUS = "earth_radius" CF_ATTR_GRID_MAPPING_NAME = "grid_mapping_name" @@ -285,11 +286,14 @@ def build_rotated_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + rcs = iris.coord_systems.RotatedGeogCS( north_pole_latitude, north_pole_longitude, north_pole_grid_lon, ellipsoid, + datum=datum, ) return rcs @@ -335,6 +339,8 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.TransverseMercator( latitude_of_projection_origin, longitude_of_central_meridian, @@ -342,6 +348,7 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): false_northing, scale_factor_at_central_meridian, ellipsoid, + datum=datum, ) return cs @@ -376,6 +383,8 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.LambertConformal( latitude_of_projection_origin, longitude_of_central_meridian, @@ -383,6 +392,7 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): false_northing, standard_parallel, ellipsoid, + datum=datum, ) return cs @@ -416,6 +426,8 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.Stereographic( latitude_of_projection_origin, longitude_of_projection_origin, @@ -423,6 +435,7 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): false_northing, true_scale_lat=None, ellipsoid=ellipsoid, + datum=datum, ) return cs @@ -457,6 +470,8 @@ def build_mercator_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.Mercator( longitude_of_projection_origin, ellipsoid=ellipsoid, @@ -464,6 +479,7 @@ def build_mercator_coordinate_system(engine, cf_grid_var): scale_factor_at_projection_origin=scale_factor_at_projection_origin, false_easting=false_easting, false_northing=false_northing, + datum=datum, ) return cs @@ -495,12 +511,15 @@ def build_lambert_azimuthal_equal_area_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.LambertAzimuthalEqualArea( latitude_of_projection_origin, longitude_of_projection_origin, false_easting, false_northing, ellipsoid, + datum=datum, ) return cs @@ -535,6 +554,8 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.AlbersEqualArea( latitude_of_projection_origin, longitude_of_central_meridian, @@ -542,6 +563,7 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): false_northing, standard_parallels, ellipsoid, + datum=datum, ) return cs @@ -576,6 +598,8 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.VerticalPerspective( latitude_of_projection_origin, longitude_of_projection_origin, @@ -583,6 +607,7 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): false_easting, false_northing, ellipsoid, + datum=datum, ) return cs @@ -620,6 +645,8 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + cs = iris.coord_systems.Geostationary( latitude_of_projection_origin, longitude_of_projection_origin, @@ -628,6 +655,7 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): false_easting, false_northing, ellipsoid, + datum=datum, ) return cs From 3eedcf1a2682239166c747a8ba15430f2fb0f590 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Fri, 8 Apr 2022 16:09:45 +0100 Subject: [PATCH 02/15] coord_systems changes --- lib/iris/coord_systems.py | 121 ++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 17 deletions(-) diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index 510aafcb48..1d6e980d59 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -14,6 +14,8 @@ import cartopy.crs as ccrs import numpy as np +import iris.Future + def _arg_default(value, default, cast_as=float): """Apply a default value and type for an optional kwarg.""" @@ -137,6 +139,7 @@ def __init__( semi_minor_axis=None, inverse_flattening=None, longitude_of_prime_meridian=None, + datum=None, ): """ Creates a new GeogCS. @@ -144,24 +147,27 @@ def __init__( Kwargs: * semi_major_axis, semi_minor_axis: - Axes of ellipsoid, in metres. At least one must be given - (see note below). + Axes of ellipsoid, in metres. At least one must be given (see note + below). * inverse_flattening: - Can be omitted if both axes given (see note below). - Defaults to 0.0 . + Can be omitted if both axes given (see note below). Defaults to 0.0 + . * longitude_of_prime_meridian: - Specifies the prime meridian on the ellipsoid, in degrees. - Defaults to 0.0 . + Specifies the prime meridian on the ellipsoid, in degrees. Defaults + to 0.0 . + + * datum: + If given, specifies the datum of the coordinate system. Only respected if + iris.Future.datum_support is set. If just semi_major_axis is set, with no semi_minor_axis or inverse_flattening, then a perfect sphere is created from the given radius. - If just two of semi_major_axis, semi_minor_axis, and - inverse_flattening are given the missing element is calculated from the - formula: + If just two of semi_major_axis, semi_minor_axis, and inverse_flattening + are given the missing element is calculated from the formula: :math:`flattening = (major - minor) / major` Currently, Iris will not allow over-specification (all three ellipsoid @@ -169,9 +175,9 @@ def __init__( Examples:: - cs = GeogCS(6371229) - pp_cs = GeogCS(iris.fileformats.pp.EARTH_RADIUS) - airy1830 = GeogCS(semi_major_axis=6377563.396, + cs = GeogCS(6371229) pp_cs = + GeogCS(iris.fileformats.pp.EARTH_RADIUS) airy1830 = + GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909) airy1830 = GeogCS(semi_major_axis=6377563.396, inverse_flattening=299.3249646) @@ -246,6 +252,8 @@ def __init__( longitude_of_prime_meridian, 0 ) + self.datum = _arg_default(datum, None) + def _pretty_attrs(self): attrs = [("semi_major_axis", self.semi_major_axis)] if self.semi_major_axis != self.semi_minor_axis: @@ -305,11 +313,19 @@ def as_cartopy_projection(self): def as_cartopy_globe(self): # Explicitly set `ellipse` to None as a workaround for # Cartopy setting WGS84 as the default. - return ccrs.Globe( - semimajor_axis=self.semi_major_axis, - semiminor_axis=self.semi_minor_axis, - ellipse=None, - ) + if iris.FUTURE.datum_support: + return ccrs.Globe( + semimajor_axis=self.semi_major_axis, + semiminor_axis=self.semi_minor_axis, + ellipse=None, + datum=self.datum, + ) + else: + return ccrs.Globe( + semimajor_axis=self.semi_major_axis, + semiminor_axis=self.semi_minor_axis, + ellipse=None, + ) class RotatedGeogCS(CoordSystem): @@ -326,6 +342,7 @@ def __init__( grid_north_pole_longitude, north_pole_grid_longitude=None, ellipsoid=None, + datum=None, ): """ Constructs a coordinate system with rotated pole, on an @@ -348,6 +365,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datum of the coordinate system. Only respected if + iris.Future.datum_support is set. + Examples:: rotated_cs = RotatedGeogCS(30, 30) @@ -369,6 +390,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def _pretty_attrs(self): attrs = [ ("grid_north_pole_latitude", self.grid_north_pole_latitude), @@ -445,6 +468,7 @@ def __init__( false_northing=None, scale_factor_at_central_meridian=None, ellipsoid=None, + datum=None, ): """ Constructs a TransverseMercator object. @@ -476,6 +500,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datum of the coordinate system. Only respected if + iris.Future.datum_support is set. + Example:: airy1830 = GeogCS(6377563.396, 6356256.909) @@ -507,6 +535,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "TransverseMercator(latitude_of_projection_origin={!r}, " @@ -550,6 +580,7 @@ def __init__(self): -100000, 0.9996012717, GeogCS(6377563.396, 6356256.909), + "OSGB_1936", ) def as_cartopy_crs(self): @@ -574,6 +605,7 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, + datum=None, ): """ Constructs an Orthographic coord system. @@ -597,6 +629,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -617,6 +653,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "Orthographic(latitude_of_projection_origin={!r}, " @@ -665,6 +703,7 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, + datum=None, ): """ Constructs a Vertical Perspective coord system. @@ -692,6 +731,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -716,6 +759,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "Vertical Perspective(latitude_of_projection_origin={!r}, " @@ -765,6 +810,7 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, + datum=None, ): """ @@ -795,6 +841,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -829,6 +879,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "Geostationary(latitude_of_projection_origin={!r}, " @@ -878,6 +930,7 @@ def __init__( false_northing=None, true_scale_lat=None, ellipsoid=None, + datum=None, ): """ Constructs a Stereographic coord system. @@ -904,6 +957,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. @@ -928,6 +985,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "Stereographic(central_lat={!r}, central_lon={!r}, " @@ -975,6 +1034,7 @@ def __init__( false_northing=None, secant_latitudes=None, ellipsoid=None, + datum=None, ): """ Constructs a LambertConformal coord system. @@ -1000,6 +1060,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + .. note: Default arguments are for the familiar USA map: @@ -1029,6 +1093,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "LambertConformal(central_lat={!r}, central_lon={!r}, " @@ -1086,6 +1152,7 @@ def __init__( scale_factor_at_projection_origin=None, false_easting=None, false_northing=None, + datum=None, ): """ Constructs a Mercator coord system. @@ -1110,6 +1177,10 @@ def __init__( * false_northing: Y offset from the planar origin in metres. Defaults to 0.0. + * datum: + If given, specifies the datumof the coordinate system. Only + respected if iris.Future.daum_support is set. + Note: Only one of ``standard_parallel`` and ``scale_factor_at_projection_origin`` should be included. @@ -1147,6 +1218,8 @@ def __init__( #: Y offset from the planar origin in metres. self.false_northing = _arg_default(false_northing, 0) + self.datum = _arg_default(datum, None) + def __repr__(self): res = ( "Mercator(longitude_of_projection_origin=" @@ -1191,6 +1264,7 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, + datum=None, ): """ Constructs a Lambert Azimuthal Equal Area coord system. @@ -1212,6 +1286,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( @@ -1232,6 +1310,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "LambertAzimuthalEqualArea(latitude_of_projection_origin={!r}," @@ -1276,6 +1356,7 @@ def __init__( false_northing=None, standard_parallels=None, ellipsoid=None, + datum=None, ): """ Constructs a Albers Conical Equal Area coord system. @@ -1302,6 +1383,10 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * datum: + If given, specifies the datumof the coordinate system. Only respected if + iris.Future.daum_support is set. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( @@ -1327,6 +1412,8 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid + self.datum = _arg_default(datum, None) + def __repr__(self): return ( "AlbersEqualArea(latitude_of_projection_origin={!r}," From f69c1009c91f2a331110344a9f5bb9ab74a3bd39 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Mon, 11 Apr 2022 16:10:03 +0100 Subject: [PATCH 03/15] More additions --- lib/iris/__init__.py | 4 +- lib/iris/coord_systems.py | 148 ++++++--- .../tests/results/coord_systems/Mercator.xml | 2 - .../results/coord_systems/Stereographic.xml | 2 +- .../coord_systems/TransverseMercator_osgb.xml | 2 +- lib/iris/tests/results/netcdf/netcdf_laea.cml | 8 +- lib/iris/tests/results/netcdf/netcdf_lcc.cml | 8 +- lib/iris/tests/results/netcdf/netcdf_merc.cml | 8 +- .../results/netcdf/netcdf_merc_false.cml | 8 +- .../netcdf/netcdf_merc_scale_factor.cml | 8 +- .../tests/results/netcdf/netcdf_stereo.cml | 8 +- .../netcdf/netcdf_tmerc_and_climatology.cml | 8 +- lib/iris/tests/results/nimrod/load_2flds.cml | 8 +- .../results/nimrod/probability_fields.cml | 168 +++++------ .../nimrod/u1096_ng_bmr04_precip_2km.cml | 4 +- .../u1096_ng_bsr05_precip_accum60_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_cloud3d0060_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_cloud_2km.cml | 28 +- .../nimrod/u1096_ng_ek00_convection_2km.cml | 36 +-- .../nimrod/u1096_ng_ek00_convwind_2km.cml | 24 +- .../nimrod/u1096_ng_ek00_frzlev_2km.cml | 32 +- .../nimrod/u1096_ng_ek00_height_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_precip_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_precipaccum_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_preciptype_2km.cml | 36 +-- .../nimrod/u1096_ng_ek00_pressure_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_radiation_2km.cml | 28 +- .../nimrod/u1096_ng_ek00_radiationuv_2km.cml | 12 +- .../results/nimrod/u1096_ng_ek00_refl_2km.cml | 4 +- .../u1096_ng_ek00_relhumidity3d0060_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_relhumidity_2km.cml | 4 +- .../results/nimrod/u1096_ng_ek00_snow_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_soil3d0060_2km.cml | 16 +- .../results/nimrod/u1096_ng_ek00_soil_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_temperature_2km.cml | 16 +- .../nimrod/u1096_ng_ek00_visibility_2km.cml | 20 +- .../results/nimrod/u1096_ng_ek00_wind_2km.cml | 281 ------------------ .../nimrod/u1096_ng_ek00_winduv3d0015_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_winduv_2km.cml | 8 +- .../results/nimrod/u1096_ng_ek01_cape_2km.cml | 24 +- ...u1096_ng_ek07_precip0540_accum180_18km.cml | 43 --- .../results/nimrod/u1096_ng_umqv_fog_2km.cml | 4 +- lib/iris/tests/test_coordsystem.py | 79 +++-- 43 files changed, 460 insertions(+), 705 deletions(-) delete mode 100644 lib/iris/tests/results/coord_systems/Mercator.xml delete mode 100644 lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml delete mode 100644 lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml diff --git a/lib/iris/__init__.py b/lib/iris/__init__.py index 3e847acad7..59dc3f74aa 100644 --- a/lib/iris/__init__.py +++ b/lib/iris/__init__.py @@ -136,7 +136,7 @@ def callback(cube, field, filename): class Future(threading.local): """Run-time configuration controller.""" - def __init__(self): + def __init__(self, datum_support=False): """ A container for run-time options controls. @@ -159,7 +159,7 @@ def __init__(self): # for the structure of this class. # # self.__dict__['example_future_flag'] = example_future_flag - pass + self.__dict__["datum_support"] = datum_support def __repr__(self): diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index 1d6e980d59..4ca3eb304f 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -14,7 +14,7 @@ import cartopy.crs as ccrs import numpy as np -import iris.Future +from . import FUTURE def _arg_default(value, default, cast_as=float): @@ -160,7 +160,7 @@ def __init__( * datum: If given, specifies the datum of the coordinate system. Only respected if - iris.Future.datum_support is set. + iris.FUTURE.datum_support is set. If just semi_major_axis is set, with no semi_minor_axis or inverse_flattening, then a perfect sphere is created from the given @@ -252,7 +252,7 @@ def __init__( longitude_of_prime_meridian, 0 ) - self.datum = _arg_default(datum, None) + self.datum = datum def _pretty_attrs(self): attrs = [("semi_major_axis", self.semi_major_axis)] @@ -313,7 +313,7 @@ def as_cartopy_projection(self): def as_cartopy_globe(self): # Explicitly set `ellipse` to None as a workaround for # Cartopy setting WGS84 as the default. - if iris.FUTURE.datum_support: + if FUTURE.datum_support: return ccrs.Globe( semimajor_axis=self.semi_major_axis, semiminor_axis=self.semi_minor_axis, @@ -367,7 +367,7 @@ def __init__( * datum: If given, specifies the datum of the coordinate system. Only respected if - iris.Future.datum_support is set. + iris.FUTURE.datum_support is set. Examples:: @@ -390,7 +390,7 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def _pretty_attrs(self): attrs = [ @@ -502,7 +502,7 @@ def __init__( * datum: If given, specifies the datum of the coordinate system. Only respected if - iris.Future.datum_support is set. + iris.FUTURE.datum_support is set. Example:: @@ -535,20 +535,22 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "TransverseMercator(latitude_of_projection_origin={!r}, " "longitude_of_central_meridian={!r}, false_easting={!r}, " "false_northing={!r}, scale_factor_at_central_meridian={!r}, " - "ellipsoid={!r})".format( + "ellipsoid={!r}, " + "datum={!r})".format( self.latitude_of_projection_origin, self.longitude_of_central_meridian, self.false_easting, self.false_northing, self.scale_factor_at_central_meridian, self.ellipsoid, + self.datum, ) ) @@ -630,8 +632,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ #: True latitude of planar origin in degrees. @@ -653,24 +655,31 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "Orthographic(latitude_of_projection_origin={!r}, " "longitude_of_projection_origin={!r}, " "false_easting={!r}, false_northing={!r}, " - "ellipsoid={!r})".format( + "ellipsoid={!r}), " + "datum={!r}".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.false_easting, self.false_northing, self.ellipsoid, + self.datum, ) ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) warnings.warn( "Discarding false_easting and false_northing that are " @@ -732,8 +741,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ #: True latitude of planar origin in degrees. @@ -759,7 +768,7 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( @@ -767,18 +776,25 @@ def __repr__(self): "longitude_of_projection_origin={!r}, " "perspective_point_height={!r}, " "false_easting={!r}, false_northing={!r}, " - "ellipsoid={!r})".format( + "ellipsoid={!r}, " + "datum={!r})".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.perspective_point_height, self.false_easting, self.false_northing, self.ellipsoid, + self.datum, ) ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.NearsidePerspective( central_latitude=self.latitude_of_projection_origin, @@ -842,8 +858,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ #: True latitude of planar origin in degrees. @@ -879,7 +895,7 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( @@ -887,7 +903,8 @@ def __repr__(self): "longitude_of_projection_origin={!r}, " "perspective_point_height={!r}, false_easting={!r}, " "false_northing={!r}, sweep_angle_axis={!r}, " - "ellipsoid={!r}".format( + "ellipsoid={!r}, " + "datum={!r})".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.perspective_point_height, @@ -895,11 +912,17 @@ def __repr__(self): self.false_northing, self.sweep_angle_axis, self.ellipsoid, + self.datum, ) ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Geostationary( central_longitude=self.longitude_of_projection_origin, @@ -958,8 +981,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ @@ -985,25 +1008,32 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "Stereographic(central_lat={!r}, central_lon={!r}, " "false_easting={!r}, false_northing={!r}, " "true_scale_lat={!r}, " - "ellipsoid={!r})".format( + "ellipsoid={!r}, " + "datum={!r})".format( self.central_lat, self.central_lon, self.false_easting, self.false_northing, self.true_scale_lat, self.ellipsoid, + self.datum, ) ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Stereographic( self.central_lat, @@ -1061,8 +1091,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. .. note: @@ -1093,19 +1123,20 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "LambertConformal(central_lat={!r}, central_lon={!r}, " "false_easting={!r}, false_northing={!r}, " - "secant_latitudes={!r}, ellipsoid={!r})".format( + "secant_latitudes={!r}, ellipsoid={!r}, datum={!r})".format( self.central_lat, self.central_lon, self.false_easting, self.false_northing, self.secant_latitudes, self.ellipsoid, + self.datum, ) ) @@ -1120,7 +1151,12 @@ def as_cartopy_crs(self): else: cutoff = None - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.LambertConformal( central_longitude=self.central_lon, @@ -1218,7 +1254,7 @@ def __init__( #: Y offset from the planar origin in metres. self.false_northing = _arg_default(false_northing, 0) - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): res = ( @@ -1229,12 +1265,18 @@ def __repr__(self): "scale_factor_at_projection_origin=" "{self.scale_factor_at_projection_origin!r}, " "false_easting={self.false_easting!r}, " - "false_northing={self.false_northing!r})" + "false_northing={self.false_northing!r}, " + "datum={self.datum!r})" ) return res.format(self=self) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Mercator( central_longitude=self.longitude_of_projection_origin, @@ -1287,8 +1329,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ #: True latitude of planar origin in degrees. @@ -1310,23 +1352,29 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "LambertAzimuthalEqualArea(latitude_of_projection_origin={!r}," " longitude_of_projection_origin={!r}, false_easting={!r}," - " false_northing={!r}, ellipsoid={!r})" + " false_northing={!r}, ellipsoid={!r}, datum={!r})" ).format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.false_easting, self.false_northing, self.ellipsoid, + self.datum, ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.LambertAzimuthalEqualArea( central_longitude=self.longitude_of_projection_origin, @@ -1384,8 +1432,8 @@ def __init__( If given, defines the ellipsoid. * datum: - If given, specifies the datumof the coordinate system. Only respected if - iris.Future.daum_support is set. + If given, specifies the datum of the coordinate system. Only respected if + iris.FUTURE.daum_support is set. """ #: True latitude of planar origin in degrees. @@ -1412,14 +1460,14 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = _arg_default(datum, None) + self.datum = datum def __repr__(self): return ( "AlbersEqualArea(latitude_of_projection_origin={!r}," " longitude_of_central_meridian={!r}, false_easting={!r}," " false_northing={!r}, standard_parallels={!r}," - " ellipsoid={!r})" + " ellipsoid={!r}, datum={!r})" ).format( self.latitude_of_projection_origin, self.longitude_of_central_meridian, @@ -1427,10 +1475,16 @@ def __repr__(self): self.false_northing, self.standard_parallels, self.ellipsoid, + self.datum, ) def as_cartopy_crs(self): - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + if FUTURE.datum_support: + globe = self._ellipsoid_to_globe( + self.ellipsoid, ccrs.Globe(datum=self.datum) + ) + else: + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.AlbersEqualArea( central_longitude=self.longitude_of_central_meridian, diff --git a/lib/iris/tests/results/coord_systems/Mercator.xml b/lib/iris/tests/results/coord_systems/Mercator.xml deleted file mode 100644 index 4ea768b41e..0000000000 --- a/lib/iris/tests/results/coord_systems/Mercator.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/lib/iris/tests/results/coord_systems/Stereographic.xml b/lib/iris/tests/results/coord_systems/Stereographic.xml index bb12cd94cc..7e3437b064 100644 --- a/lib/iris/tests/results/coord_systems/Stereographic.xml +++ b/lib/iris/tests/results/coord_systems/Stereographic.xml @@ -1,2 +1,2 @@ - + diff --git a/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml b/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml index 6176d01aa5..67a07d9a81 100644 --- a/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml +++ b/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml @@ -1,2 +1,2 @@ - + diff --git a/lib/iris/tests/results/netcdf/netcdf_laea.cml b/lib/iris/tests/results/netcdf/netcdf_laea.cml index ad23114038..720d5c51da 100644 --- a/lib/iris/tests/results/netcdf/netcdf_laea.cml +++ b/lib/iris/tests/results/netcdf/netcdf_laea.cml @@ -31,12 +31,12 @@ [5262500.0, 5701785.71429], [5701785.71429, 6141071.42857], [6141071.42857, 6580357.14286], - [6580357.14286, 7019642.85714]]" id="b71cdf0e" points="[650000.0, 1089285.71429, 1528571.42857, + [6580357.14286, 7019642.85714]]" id="aa58b139" points="[650000.0, 1089285.71429, 1528571.42857, 1967857.14286, 2407142.85714, 2846428.57143, 3285714.28571, 3725000.0, 4164285.71429, 4603571.42857, 5042857.14286, 5482142.85714, 5921428.57143, 6360714.28571, 6800000.0]" shape="(15,)" standard_name="projection_x_coordinate" units="Unit('m')" value_type="float64" var_name="projection_x_coordinate"> - + @@ -54,12 +54,12 @@ [4500000.0, 4871428.57143], [4871428.57143, 5242857.14286], [5242857.14286, 5614285.71429], - [5614285.71429, 5985714.28571]]" id="f1f8b7cb" points="[600000.0, 971428.571429, 1342857.14286, + [5614285.71429, 5985714.28571]]" id="1bc9bec2" points="[600000.0, 971428.571429, 1342857.14286, 1714285.71429, 2085714.28571, 2457142.85714, 2828571.42857, 3200000.0, 3571428.57143, 3942857.14286, 4314285.71429, 4685714.28571, 5057142.85714, 5428571.42857, 5800000.0]" shape="(15,)" standard_name="projection_y_coordinate" units="Unit('m')" value_type="float64" var_name="projection_y_coordinate"> - + diff --git a/lib/iris/tests/results/netcdf/netcdf_lcc.cml b/lib/iris/tests/results/netcdf/netcdf_lcc.cml index 7ea53e6600..070ff2d267 100644 --- a/lib/iris/tests/results/netcdf/netcdf_lcc.cml +++ b/lib/iris/tests/results/netcdf/netcdf_lcc.cml @@ -48,7 +48,7 @@ ..., 11.7454500198, 11.7587404251, 11.77202034]]" shape="(60, 60)" standard_name="longitude" units="Unit('degrees')" value_type="float64" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc.cml b/lib/iris/tests/results/netcdf/netcdf_merc.cml index 831a8fdaa7..ee5e318b7c 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc.cml @@ -53,15 +53,15 @@ 45.5158, 45.9993]]" shape="(192, 192)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc_false.cml b/lib/iris/tests/results/netcdf/netcdf_merc_false.cml index 1e50aa6e65..9667f35c73 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc_false.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc_false.cml @@ -6,17 +6,17 @@ - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml b/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml index c9ad4ca33f..1a3e4c73b4 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml @@ -6,13 +6,13 @@ - - + + - - + + diff --git a/lib/iris/tests/results/netcdf/netcdf_stereo.cml b/lib/iris/tests/results/netcdf/netcdf_stereo.cml index b07304cd62..343d4e5e2f 100644 --- a/lib/iris/tests/results/netcdf/netcdf_stereo.cml +++ b/lib/iris/tests/results/netcdf/netcdf_stereo.cml @@ -54,15 +54,15 @@ 10.449, 10.5996]]" shape="(160, 256)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml b/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml index 2d909ba57e..53503cbc76 100644 --- a/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml +++ b/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml @@ -50,15 +50,15 @@ 1.99301683617]]" shape="(290, 180)" standard_name="longitude" units="Unit('degrees')" value_type="float64" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/nimrod/load_2flds.cml b/lib/iris/tests/results/nimrod/load_2flds.cml index b068657d40..40cd824337 100644 --- a/lib/iris/tests/results/nimrod/load_2flds.cml +++ b/lib/iris/tests/results/nimrod/load_2flds.cml @@ -17,15 +17,15 @@ - - + - - + diff --git a/lib/iris/tests/results/nimrod/probability_fields.cml b/lib/iris/tests/results/nimrod/probability_fields.cml index 7add3e75a4..068e29195d 100644 --- a/lib/iris/tests/results/nimrod/probability_fields.cml +++ b/lib/iris/tests/results/nimrod/probability_fields.cml @@ -29,12 +29,12 @@ - + - + @@ -79,12 +79,12 @@ - + - + @@ -122,12 +122,12 @@ - + - + @@ -169,12 +169,12 @@ - + - + @@ -221,12 +221,12 @@ - + - + @@ -282,12 +282,12 @@ - + - + @@ -322,12 +322,12 @@ - + - + @@ -369,12 +369,12 @@ - + - + @@ -413,12 +413,12 @@ - + - + @@ -452,12 +452,12 @@ - + - + @@ -488,12 +488,12 @@ - + - + @@ -528,12 +528,12 @@ - + - + @@ -564,12 +564,12 @@ - + - + @@ -604,12 +604,12 @@ - + - + @@ -648,12 +648,12 @@ - + - + @@ -692,12 +692,12 @@ - + - + @@ -733,12 +733,12 @@ - + - + @@ -770,12 +770,12 @@ - + - + @@ -811,12 +811,12 @@ - + - + @@ -857,12 +857,12 @@ - + - + @@ -905,12 +905,12 @@ - + - + @@ -953,12 +953,12 @@ - + - + @@ -996,12 +996,12 @@ - + - + @@ -1033,12 +1033,12 @@ - + - + @@ -1074,12 +1074,12 @@ - + - + @@ -1114,12 +1114,12 @@ - + - + @@ -1157,12 +1157,12 @@ - + - + @@ -1204,12 +1204,12 @@ - + - + @@ -1247,12 +1247,12 @@ - + - + @@ -1294,12 +1294,12 @@ - + - + @@ -1337,12 +1337,12 @@ - + - + @@ -1380,12 +1380,12 @@ - + - + @@ -1428,12 +1428,12 @@ - + - + @@ -1479,12 +1479,12 @@ - + - + @@ -1529,12 +1529,12 @@ - + - + @@ -1576,12 +1576,12 @@ - + - + @@ -1619,12 +1619,12 @@ - + - + @@ -1667,12 +1667,12 @@ - + - + @@ -1718,12 +1718,12 @@ - + - + @@ -1768,12 +1768,12 @@ - + - + @@ -1811,12 +1811,12 @@ - + - + @@ -1858,12 +1858,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml index 31518dd321..0cea405e39 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml @@ -23,12 +23,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml index 80cb1834c0..56f74cebdf 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml @@ -22,12 +22,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml index 68ec95555c..a3d83527bd 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml @@ -38,12 +38,12 @@ - + - + @@ -88,12 +88,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml index c6bc6f0419..439d4d0afd 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -100,12 +100,12 @@ - + - + @@ -140,12 +140,12 @@ - + - + @@ -191,12 +191,12 @@ - + - + @@ -230,12 +230,12 @@ - + - + @@ -272,12 +272,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml index e6c99f9e50..a9c5903fe4 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -67,12 +67,12 @@ - + - + @@ -106,12 +106,12 @@ - + - + @@ -145,12 +145,12 @@ - + - + @@ -189,12 +189,12 @@ - + - + @@ -235,12 +235,12 @@ - + - + @@ -274,12 +274,12 @@ - + - + @@ -313,12 +313,12 @@ - + - + @@ -355,12 +355,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml index 2f52a93277..e523ff80f7 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + @@ -212,12 +212,12 @@ - + - + @@ -258,12 +258,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml index b2b47715a2..a2fca2e87b 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + @@ -139,12 +139,12 @@ - + - + @@ -179,12 +179,12 @@ - + - + @@ -218,12 +218,12 @@ - + - + @@ -258,12 +258,12 @@ - + - + @@ -298,12 +298,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml index 4fb1371250..1898df3284 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml @@ -21,12 +21,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml index 59776b5b74..a4389eb974 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -101,12 +101,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml index 0fa98e3bb6..48d71da6e6 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml @@ -23,12 +23,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml index 3fdf646e70..f293c0403d 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -101,12 +101,12 @@ - + - + @@ -140,12 +140,12 @@ - + - + @@ -180,12 +180,12 @@ - + - + @@ -220,12 +220,12 @@ - + - + @@ -259,12 +259,12 @@ - + - + @@ -299,12 +299,12 @@ - + - + @@ -339,12 +339,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml index edb0862676..475a97bb48 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml index 38f076f232..01095c7569 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + @@ -138,12 +138,12 @@ - + - + @@ -177,12 +177,12 @@ - + - + @@ -216,12 +216,12 @@ - + - + @@ -255,12 +255,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml index 35bed38591..98e8861545 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml index 4411ff9dd5..32480b454c 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml @@ -30,12 +30,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml index 8759dac5c7..f7dc257619 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml @@ -38,12 +38,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml index 9b7e7582d0..efeacb86f9 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml @@ -28,12 +28,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml index ce549ab3cd..f46bda9802 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -100,12 +100,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml index 9385bfc9ae..afe0b3d1e9 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml index a76971a1ed..d7cc29605e 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -104,12 +104,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml index 09677ff57a..ac3ab3b008 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml @@ -29,12 +29,12 @@ - + - + @@ -76,12 +76,12 @@ - + - + @@ -122,12 +122,12 @@ - + - + @@ -168,12 +168,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml index 8a0f50700c..ab44aaaaac 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + @@ -212,12 +212,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml deleted file mode 100644 index df2054e8af..0000000000 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml index 331ff59c74..923c164b9e 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml index aa14346e2f..bb247871a3 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml index 1756ac0205..c8a5d8247f 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -102,12 +102,12 @@ - + - + @@ -141,12 +141,12 @@ - + - + @@ -180,12 +180,12 @@ - + - + @@ -219,12 +219,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml deleted file mode 100644 index f4710dd36d..0000000000 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml index 57756ccc1d..f817152ab5 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml @@ -28,12 +28,12 @@ - + - + diff --git a/lib/iris/tests/test_coordsystem.py b/lib/iris/tests/test_coordsystem.py index 4229125969..433dee95c0 100644 --- a/lib/iris/tests/test_coordsystem.py +++ b/lib/iris/tests/test_coordsystem.py @@ -30,6 +30,7 @@ def osgb(): false_northing=100, scale_factor_at_central_meridian=0.9996012717, ellipsoid=GeogCS(6377563.396, 6356256.909), + datum="OSGB_1936", ) @@ -40,6 +41,7 @@ def stereo(): false_easting=100, false_northing=200, ellipsoid=GeogCS(6377563.396, 6356256.909), + datum="WGS_1984", ) @@ -163,28 +165,24 @@ def test_invalid_ellipsoid_params(self): class Test_GeogCS_repr(tests.IrisTest): def test_repr(self): - cs = GeogCS(6543210, 6500000) - expected = ( - "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0)" - ) + cs = GeogCS(6543210, 6500000, datum="WGS_1984") + expected = "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0, datum=WGS_1984)" self.assertEqual(expected, repr(cs)) class Test_GeogCS_str(tests.IrisTest): def test_str(self): - cs = GeogCS(6543210, 6500000) - expected = ( - "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0)" - ) + cs = GeogCS(6543210, 6500000, datum="WGS_1984") + expected = "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0, datum=WGS_1984)" self.assertEqual(expected, str(cs)) class Test_GeogCS_as_cartopy_globe(tests.IrisTest): def test_as_cartopy_globe(self): - cs = GeogCS(6543210, 6500000) + cs = GeogCS(6543210, 6500000, datum="WGS_1984") # Can't check equality directly, so use the proj4 params instead. res = cs.as_cartopy_globe().to_proj4_params() - expected = {"a": 6543210, "b": 6500000} + expected = {"a": 6543210, "b": 6500000, "datum": "WGS_1984"} self.assertEqual(res, expected) @@ -194,6 +192,7 @@ def test_as_cartopy_projection(self): "semi_major_axis": 6543210, "semi_minor_axis": 6500000, "longitude_of_prime_meridian": 30, + "datum": "WGS_1984", } cs = GeogCS(**geogcs_args) res = cs.as_cartopy_projection() @@ -202,6 +201,7 @@ def test_as_cartopy_projection(self): semimajor_axis=geogcs_args["semi_major_axis"], semiminor_axis=geogcs_args["semi_minor_axis"], ellipse=None, + datum="WGS_1984", ) expected = ccrs.PlateCarree( globe=globe, @@ -213,10 +213,13 @@ def test_as_cartopy_projection(self): class Test_GeogCS_as_cartopy_crs(tests.IrisTest): def test_as_cartopy_crs(self): - cs = GeogCS(6543210, 6500000) + cs = GeogCS(6543210, 6500000, datum="WGS_1984") res = cs.as_cartopy_crs() globe = ccrs.Globe( - semimajor_axis=6543210.0, semiminor_axis=6500000.0, ellipse=None + semimajor_axis=6543210.0, + semiminor_axis=6500000.0, + ellipse=None, + datum="WGS_1984", ) expected = ccrs.Geodetic(globe) self.assertEqual(res, expected) @@ -243,40 +246,52 @@ def test_init(self): class Test_RotatedGeogCS_repr(tests.IrisTest): def test_repr(self): rcs = RotatedGeogCS( - 30, 40, north_pole_grid_longitude=50, ellipsoid=GeogCS(6371229) + 30, + 40, + north_pole_grid_longitude=50, + ellipsoid=GeogCS(6371229), + datum="WGS_1984", ) expected = ( "RotatedGeogCS(30.0, 40.0, " - "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0))" + "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0), datum=WGS_1984)" ) self.assertEqual(expected, repr(rcs)) - rcs = RotatedGeogCS(30, 40, north_pole_grid_longitude=50) - expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0)" + rcs = RotatedGeogCS( + 30, 40, north_pole_grid_longitude=50, datum="WGS_1984" + ) + expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0, datum=WGS_1984)" self.assertEqual(expected, repr(rcs)) - rcs = RotatedGeogCS(30, 40) - expected = "RotatedGeogCS(30.0, 40.0)" + rcs = RotatedGeogCS(30, 40, datum="WGS_1984") + expected = "RotatedGeogCS(30.0, 40.0, datum=WGS_1984)" self.assertEqual(expected, repr(rcs)) class Test_RotatedGeogCS_str(tests.IrisTest): def test_str(self): rcs = RotatedGeogCS( - 30, 40, north_pole_grid_longitude=50, ellipsoid=GeogCS(6371229) + 30, + 40, + north_pole_grid_longitude=50, + ellipsoid=GeogCS(6371229), + datum="WGS_1984", ) expected = ( "RotatedGeogCS(30.0, 40.0, " - "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0))" + "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0), datum=WGS_1984)" ) self.assertEqual(expected, str(rcs)) - rcs = RotatedGeogCS(30, 40, north_pole_grid_longitude=50) - expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0)" + rcs = RotatedGeogCS( + 30, 40, north_pole_grid_longitude=50, datum="WGS_1984" + ) + expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0, datum=WGS_1984)" self.assertEqual(expected, str(rcs)) - rcs = RotatedGeogCS(30, 40) - expected = "RotatedGeogCS(30.0, 40.0)" + rcs = RotatedGeogCS(30, 40, datum="WGS_1984") + expected = "RotatedGeogCS(30.0, 40.0, datum=WGS_1984)" self.assertEqual(expected, str(rcs)) @@ -294,7 +309,7 @@ def test_osgb(self): expected = ( "TransverseMercator(latitude_of_projection_origin=49.0, longitude_of_central_meridian=-2.0, " "false_easting=-400.0, false_northing=100.0, scale_factor_at_central_meridian=0.9996012717, " - "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909))" + "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909), datum=WGS_1984)" ) self.assertEqual(expected, repr(tm)) @@ -309,6 +324,7 @@ def test_as_cartopy_crs(self): ellipsoid = GeogCS( semi_major_axis=6377563.396, semi_minor_axis=6356256.909 ) + datum = "WGS_1984" tmerc_cs = TransverseMercator( latitude_of_projection_origin, @@ -317,6 +333,7 @@ def test_as_cartopy_crs(self): false_northing, scale_factor_at_central_meridian, ellipsoid=ellipsoid, + datum=datum, ) expected = ccrs.TransverseMercator( @@ -329,6 +346,7 @@ def test_as_cartopy_crs(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, + datum=datum, ), ) @@ -346,6 +364,7 @@ def test_as_cartopy_projection(self): ellipsoid = GeogCS( semi_major_axis=6377563.396, semi_minor_axis=6356256.909 ) + datum = "WGS_1984" tmerc_cs = TransverseMercator( latitude_of_projection_origin, @@ -354,6 +373,7 @@ def test_as_cartopy_projection(self): false_northing, scale_factor_at_central_meridian, ellipsoid=ellipsoid, + datum=datum, ) expected = ccrs.TransverseMercator( @@ -366,6 +386,7 @@ def test_as_cartopy_projection(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, + datum="WGS_1984", ), ) @@ -385,7 +406,7 @@ def test_stereo(self): expected = ( "Stereographic(central_lat=-90.0, central_lon=-45.0, " "false_easting=100.0, false_northing=200.0, true_scale_lat=None, " - "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909))" + "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909), datum=WGS_1984)" ) self.assertEqual(expected, repr(st)) @@ -397,6 +418,7 @@ def test_as_cartopy_crs(self): false_easting = 100.0 false_northing = 200.0 ellipsoid = GeogCS(6377563.396, 6356256.909) + datum = "WGS_1984" st = Stereographic( central_lat=latitude_of_projection_origin, @@ -404,6 +426,7 @@ def test_as_cartopy_crs(self): false_easting=false_easting, false_northing=false_northing, ellipsoid=ellipsoid, + datum=datum, ) expected = ccrs.Stereographic( central_latitude=latitude_of_projection_origin, @@ -414,6 +437,7 @@ def test_as_cartopy_crs(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, + datum=datum, ), ) @@ -428,6 +452,7 @@ def test_as_cartopy_projection(self): false_easting = 100.0 false_northing = 200.0 ellipsoid = GeogCS(6377563.396, 6356256.909) + datum = "WGS_1984" st = Stereographic( central_lat=latitude_of_projection_origin, @@ -435,6 +460,7 @@ def test_as_cartopy_projection(self): false_easting=false_easting, false_northing=false_northing, ellipsoid=ellipsoid, + datum=datum, ) expected = ccrs.Stereographic( central_latitude=latitude_of_projection_origin, @@ -445,6 +471,7 @@ def test_as_cartopy_projection(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, + datum=datum, ), ) From 577aed2f35d3dab4f8615ff10a6b8729b9f03be8 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Wed, 13 Apr 2022 12:39:07 +0100 Subject: [PATCH 04/15] Revert some FUTURE guards and condense datum application --- lib/iris/coord_systems.py | 286 +++--------------- .../fileformats/_nc_load_rules/helpers.py | 46 ++- 2 files changed, 78 insertions(+), 254 deletions(-) diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index 4ca3eb304f..cb3a9d3599 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -14,8 +14,6 @@ import cartopy.crs as ccrs import numpy as np -from . import FUTURE - def _arg_default(value, default, cast_as=float): """Apply a default value and type for an optional kwarg.""" @@ -142,26 +140,22 @@ def __init__( datum=None, ): """ - Creates a new GeogCS. - - Kwargs: + Create a new GeogCS. + Parameters + ---------- * semi_major_axis, semi_minor_axis: Axes of ellipsoid, in metres. At least one must be given (see note below). - * inverse_flattening: - Can be omitted if both axes given (see note below). Defaults to 0.0 - . - + Can be omitted if both axes given (see note below). Default 0.0 * longitude_of_prime_meridian: - Specifies the prime meridian on the ellipsoid, in degrees. Defaults - to 0.0 . - + Specifies the prime meridian on the ellipsoid, in degrees. Default 0.0 * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.datum_support is set. + Name of the datum used to modify the ellipsoid. Default None + Notes + ----- If just semi_major_axis is set, with no semi_minor_axis or inverse_flattening, then a perfect sphere is created from the given radius. @@ -175,9 +169,9 @@ def __init__( Examples:: - cs = GeogCS(6371229) pp_cs = - GeogCS(iris.fileformats.pp.EARTH_RADIUS) airy1830 = - GeogCS(semi_major_axis=6377563.396, + cs = GeogCS(6371229) + pp_cs = GeogCS(iris.fileformats.pp.EARTH_RADIUS) + airy1830 = GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909) airy1830 = GeogCS(semi_major_axis=6377563.396, inverse_flattening=299.3249646) @@ -265,6 +259,13 @@ def _pretty_attrs(self): self.longitude_of_prime_meridian, ) ) + if self.datum is not None: + attrs.append( + ( + "datum", + self.datum, + ) + ) return attrs def __repr__(self): @@ -313,19 +314,12 @@ def as_cartopy_projection(self): def as_cartopy_globe(self): # Explicitly set `ellipse` to None as a workaround for # Cartopy setting WGS84 as the default. - if FUTURE.datum_support: - return ccrs.Globe( - semimajor_axis=self.semi_major_axis, - semiminor_axis=self.semi_minor_axis, - ellipse=None, - datum=self.datum, - ) - else: - return ccrs.Globe( - semimajor_axis=self.semi_major_axis, - semiminor_axis=self.semi_minor_axis, - ellipse=None, - ) + return ccrs.Globe( + semimajor_axis=self.semi_major_axis, + semiminor_axis=self.semi_minor_axis, + ellipse=None, + datum=self.datum, + ) class RotatedGeogCS(CoordSystem): @@ -342,7 +336,6 @@ def __init__( grid_north_pole_longitude, north_pole_grid_longitude=None, ellipsoid=None, - datum=None, ): """ Constructs a coordinate system with rotated pole, on an @@ -365,10 +358,6 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.datum_support is set. - Examples:: rotated_cs = RotatedGeogCS(30, 30) @@ -390,8 +379,6 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def _pretty_attrs(self): attrs = [ ("grid_north_pole_latitude", self.grid_north_pole_latitude), @@ -468,7 +455,6 @@ def __init__( false_northing=None, scale_factor_at_central_meridian=None, ellipsoid=None, - datum=None, ): """ Constructs a TransverseMercator object. @@ -500,10 +486,6 @@ def __init__( * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.datum_support is set. - Example:: airy1830 = GeogCS(6377563.396, 6356256.909) @@ -535,22 +517,18 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "TransverseMercator(latitude_of_projection_origin={!r}, " "longitude_of_central_meridian={!r}, false_easting={!r}, " "false_northing={!r}, scale_factor_at_central_meridian={!r}, " - "ellipsoid={!r}, " - "datum={!r})".format( + "ellipsoid={!r})".format( self.latitude_of_projection_origin, self.longitude_of_central_meridian, self.false_easting, self.false_northing, self.scale_factor_at_central_meridian, self.ellipsoid, - self.datum, ) ) @@ -582,7 +560,6 @@ def __init__(self): -100000, 0.9996012717, GeogCS(6377563.396, 6356256.909), - "OSGB_1936", ) def as_cartopy_crs(self): @@ -595,7 +572,6 @@ def as_cartopy_projection(self): class Orthographic(CoordSystem): """ An orthographic map projection. - """ grid_mapping_name = "orthographic" @@ -607,34 +583,21 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, - datum=None, ): """ Constructs an Orthographic coord system. - Args: - * latitude_of_projection_origin: True latitude of planar origin in degrees. - * longitude_of_projection_origin: True longitude of planar origin in degrees. - Kwargs: - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -655,31 +618,22 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "Orthographic(latitude_of_projection_origin={!r}, " "longitude_of_projection_origin={!r}, " "false_easting={!r}, false_northing={!r}, " - "ellipsoid={!r}), " - "datum={!r}".format( + "ellipsoid={!r})".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.false_easting, self.false_northing, self.ellipsoid, - self.datum, ) ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) warnings.warn( "Discarding false_easting and false_northing that are " @@ -699,7 +653,6 @@ def as_cartopy_projection(self): class VerticalPerspective(CoordSystem): """ A vertical/near-side perspective satellite image map projection. - """ grid_mapping_name = "vertical_perspective" @@ -712,38 +665,24 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, - datum=None, ): """ Constructs a Vertical Perspective coord system. - Args: - * latitude_of_projection_origin: True latitude of planar origin in degrees. - * longitude_of_projection_origin: True longitude of planar origin in degrees. - * perspective_point_height: Altitude of satellite in metres above the surface of the ellipsoid. - Kwargs: - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -768,33 +707,24 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "Vertical Perspective(latitude_of_projection_origin={!r}, " "longitude_of_projection_origin={!r}, " "perspective_point_height={!r}, " "false_easting={!r}, false_northing={!r}, " - "ellipsoid={!r}, " - "datum={!r})".format( + "ellipsoid={!r})".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.perspective_point_height, self.false_easting, self.false_northing, self.ellipsoid, - self.datum, ) ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.NearsidePerspective( central_latitude=self.latitude_of_projection_origin, @@ -812,7 +742,6 @@ def as_cartopy_projection(self): class Geostationary(CoordSystem): """ A geostationary satellite image map projection. - """ grid_mapping_name = "geostationary" @@ -826,41 +755,26 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, - datum=None, ): """ Constructs a Geostationary coord system. - Args: - * latitude_of_projection_origin: True latitude of planar origin in degrees. - * longitude_of_projection_origin: True longitude of planar origin in degrees. - * perspective_point_height: Altitude of satellite in metres above the surface of the ellipsoid. - * sweep_angle_axis (string): The axis along which the satellite instrument sweeps - 'x' or 'y'. - Kwargs: - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -895,16 +809,13 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "Geostationary(latitude_of_projection_origin={!r}, " "longitude_of_projection_origin={!r}, " "perspective_point_height={!r}, false_easting={!r}, " "false_northing={!r}, sweep_angle_axis={!r}, " - "ellipsoid={!r}, " - "datum={!r})".format( + "ellipsoid={!r}".format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.perspective_point_height, @@ -912,17 +823,11 @@ def __repr__(self): self.false_northing, self.sweep_angle_axis, self.ellipsoid, - self.datum, ) ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Geostationary( central_longitude=self.longitude_of_projection_origin, @@ -940,7 +845,6 @@ def as_cartopy_projection(self): class Stereographic(CoordSystem): """ A stereographic map projection. - """ grid_mapping_name = "stereographic" @@ -953,37 +857,23 @@ def __init__( false_northing=None, true_scale_lat=None, ellipsoid=None, - datum=None, ): """ Constructs a Stereographic coord system. - Args: - * central_lat: The latitude of the pole. - * central_lon: The central longitude, which aligns with the y axis. - Kwargs: - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * true_scale_lat: Latitude of true scale. - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. @@ -1008,32 +898,23 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "Stereographic(central_lat={!r}, central_lon={!r}, " "false_easting={!r}, false_northing={!r}, " "true_scale_lat={!r}, " - "ellipsoid={!r}, " - "datum={!r})".format( + "ellipsoid={!r})".format( self.central_lat, self.central_lon, self.false_easting, self.false_northing, self.true_scale_lat, self.ellipsoid, - self.datum, ) ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Stereographic( self.central_lat, @@ -1051,7 +932,6 @@ def as_cartopy_projection(self): class LambertConformal(CoordSystem): """ A coordinate system in the Lambert Conformal conic projection. - """ grid_mapping_name = "lambert_conformal_conic" @@ -1064,43 +944,28 @@ def __init__( false_northing=None, secant_latitudes=None, ellipsoid=None, - datum=None, ): """ Constructs a LambertConformal coord system. - Kwargs: - * central_lat: The latitude of "unitary scale". Defaults to 39.0 . - * central_lon: The central longitude. Defaults to -96.0 . - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * secant_latitudes (number or iterable of 1 or 2 numbers): Latitudes of secant intersection. One or two. Defaults to (33.0, 45.0). - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - .. note: - Default arguments are for the familiar USA map: central_lon=-96.0, central_lat=39.0, false_easting=0.0, false_northing=0.0, secant_latitudes=(33, 45) - """ #: True latitude of planar origin in degrees. @@ -1123,20 +988,17 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "LambertConformal(central_lat={!r}, central_lon={!r}, " "false_easting={!r}, false_northing={!r}, " - "secant_latitudes={!r}, ellipsoid={!r}, datum={!r})".format( + "secant_latitudes={!r}, ellipsoid={!r})".format( self.central_lat, self.central_lon, self.false_easting, self.false_northing, self.secant_latitudes, self.ellipsoid, - self.datum, ) ) @@ -1151,12 +1013,7 @@ def as_cartopy_crs(self): else: cutoff = None - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.LambertConformal( central_longitude=self.central_lon, @@ -1175,7 +1032,6 @@ def as_cartopy_projection(self): class Mercator(CoordSystem): """ A coordinate system in the Mercator projection. - """ grid_mapping_name = "mercator" @@ -1188,31 +1044,22 @@ def __init__( scale_factor_at_projection_origin=None, false_easting=None, false_northing=None, - datum=None, ): """ Constructs a Mercator coord system. - Kwargs: - * longitude_of_projection_origin: True longitude of planar origin in degrees. Defaults to 0.0 . - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - * standard_parallel: The latitude where the scale is 1. Defaults to 0.0 . - * scale_factor_at_projection_origin: Scale factor at natural origin. Defaults to unused. - * false_easting: X offset from the planar origin in metres. Defaults to 0.0. - * false_northing: Y offset from the planar origin in metres. Defaults to 0.0. - * datum: If given, specifies the datumof the coordinate system. Only respected if iris.Future.daum_support is set. @@ -1254,8 +1101,6 @@ def __init__( #: Y offset from the planar origin in metres. self.false_northing = _arg_default(false_northing, 0) - self.datum = datum - def __repr__(self): res = ( "Mercator(longitude_of_projection_origin=" @@ -1265,18 +1110,12 @@ def __repr__(self): "scale_factor_at_projection_origin=" "{self.scale_factor_at_projection_origin!r}, " "false_easting={self.false_easting!r}, " - "false_northing={self.false_northing!r}, " - "datum={self.datum!r})" + "false_northing={self.false_northing!r})" ) return res.format(self=self) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.Mercator( central_longitude=self.longitude_of_projection_origin, @@ -1294,7 +1133,6 @@ def as_cartopy_projection(self): class LambertAzimuthalEqualArea(CoordSystem): """ A coordinate system in the Lambert Azimuthal Equal Area projection. - """ grid_mapping_name = "lambert_azimuthal_equal_area" @@ -1306,32 +1144,20 @@ def __init__( false_easting=None, false_northing=None, ellipsoid=None, - datum=None, ): """ Constructs a Lambert Azimuthal Equal Area coord system. - Kwargs: - * latitude_of_projection_origin: True latitude of planar origin in degrees. Defaults to 0.0 . - * longitude_of_projection_origin: True longitude of planar origin in degrees. Defaults to 0.0 . - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( @@ -1352,29 +1178,21 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "LambertAzimuthalEqualArea(latitude_of_projection_origin={!r}," " longitude_of_projection_origin={!r}, false_easting={!r}," - " false_northing={!r}, ellipsoid={!r}, datum={!r})" + " false_northing={!r}, ellipsoid={!r})" ).format( self.latitude_of_projection_origin, self.longitude_of_projection_origin, self.false_easting, self.false_northing, self.ellipsoid, - self.datum, ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.LambertAzimuthalEqualArea( central_longitude=self.longitude_of_projection_origin, @@ -1391,7 +1209,6 @@ def as_cartopy_projection(self): class AlbersEqualArea(CoordSystem): """ A coordinate system in the Albers Conical Equal Area projection. - """ grid_mapping_name = "albers_conical_equal_area" @@ -1404,37 +1221,24 @@ def __init__( false_northing=None, standard_parallels=None, ellipsoid=None, - datum=None, ): """ Constructs a Albers Conical Equal Area coord system. - Kwargs: - * latitude_of_projection_origin: True latitude of planar origin in degrees. Defaults to 0.0 . - * longitude_of_central_meridian: True longitude of planar central meridian in degrees. Defaults to 0.0 . - * false_easting: X offset from planar origin in metres. Defaults to 0.0 . - * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . - * standard_parallels (number or iterable of 1 or 2 numbers): The one or two latitudes of correct scale. Defaults to (20.0, 50.0). - * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. - - * datum: - If given, specifies the datum of the coordinate system. Only respected if - iris.FUTURE.daum_support is set. - """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( @@ -1460,14 +1264,12 @@ def __init__( #: Ellipsoid definition (:class:`GeogCS` or None). self.ellipsoid = ellipsoid - self.datum = datum - def __repr__(self): return ( "AlbersEqualArea(latitude_of_projection_origin={!r}," " longitude_of_central_meridian={!r}, false_easting={!r}," " false_northing={!r}, standard_parallels={!r}," - " ellipsoid={!r}, datum={!r})" + " ellipsoid={!r})" ).format( self.latitude_of_projection_origin, self.longitude_of_central_meridian, @@ -1475,16 +1277,10 @@ def __repr__(self): self.false_northing, self.standard_parallels, self.ellipsoid, - self.datum, ) def as_cartopy_crs(self): - if FUTURE.datum_support: - globe = self._ellipsoid_to_globe( - self.ellipsoid, ccrs.Globe(datum=self.datum) - ) - else: - globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) + globe = self._ellipsoid_to_globe(self.ellipsoid, ccrs.Globe()) return ccrs.AlbersEqualArea( central_longitude=self.longitude_of_central_meridian, diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 19f5568944..24f8ccd900 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -20,6 +20,7 @@ import numpy as np import numpy.ma as ma +import iris.FUTURE.datum_support import iris.aux_factory from iris.common.mixin import _get_valid_standard_name import iris.coord_systems @@ -286,7 +287,10 @@ def build_rotated_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None rcs = iris.coord_systems.RotatedGeogCS( north_pole_latitude, @@ -339,7 +343,10 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.TransverseMercator( latitude_of_projection_origin, @@ -383,7 +390,10 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.LambertConformal( latitude_of_projection_origin, @@ -426,7 +436,10 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.Stereographic( latitude_of_projection_origin, @@ -470,7 +483,10 @@ def build_mercator_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.Mercator( longitude_of_projection_origin, @@ -511,7 +527,10 @@ def build_lambert_azimuthal_equal_area_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.LambertAzimuthalEqualArea( latitude_of_projection_origin, @@ -554,7 +573,10 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.AlbersEqualArea( latitude_of_projection_origin, @@ -598,7 +620,10 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.VerticalPerspective( latitude_of_projection_origin, @@ -645,7 +670,10 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): ): ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + else: + datum = None cs = iris.coord_systems.Geostationary( latitude_of_projection_origin, From e74fa20540c16fa828e47f653f5276a990bbdce8 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Wed, 20 Apr 2022 10:05:57 +0100 Subject: [PATCH 05/15] Fixing tests and improving --- lib/iris/__init__.py | 4 +- lib/iris/coord_systems.py | 112 ++++++- .../fileformats/_nc_load_rules/helpers.py | 171 ++--------- lib/iris/fileformats/netcdf.py | 2 + lib/iris/tests/integration/test_netcdf.py | 93 ++++++ .../results/coord_systems/Stereographic.xml | 2 +- .../coord_systems/TransverseMercator_osgb.xml | 2 +- lib/iris/tests/results/netcdf/netcdf_laea.cml | 8 +- lib/iris/tests/results/netcdf/netcdf_lcc.cml | 8 +- lib/iris/tests/results/netcdf/netcdf_merc.cml | 8 +- .../results/netcdf/netcdf_merc_false.cml | 8 +- .../netcdf/netcdf_merc_scale_factor.cml | 8 +- .../tests/results/netcdf/netcdf_stereo.cml | 8 +- .../netcdf/netcdf_tmerc_and_climatology.cml | 8 +- lib/iris/tests/results/nimrod/load_2flds.cml | 8 +- .../results/nimrod/probability_fields.cml | 168 +++++------ .../nimrod/u1096_ng_bmr04_precip_2km.cml | 4 +- .../u1096_ng_bsr05_precip_accum60_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_cloud3d0060_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_cloud_2km.cml | 28 +- .../nimrod/u1096_ng_ek00_convection_2km.cml | 36 +-- .../nimrod/u1096_ng_ek00_convwind_2km.cml | 24 +- .../nimrod/u1096_ng_ek00_frzlev_2km.cml | 32 +- .../nimrod/u1096_ng_ek00_height_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_precip_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_precipaccum_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_preciptype_2km.cml | 36 +-- .../nimrod/u1096_ng_ek00_pressure_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_radiation_2km.cml | 28 +- .../nimrod/u1096_ng_ek00_radiationuv_2km.cml | 12 +- .../results/nimrod/u1096_ng_ek00_refl_2km.cml | 4 +- .../u1096_ng_ek00_relhumidity3d0060_2km.cml | 4 +- .../nimrod/u1096_ng_ek00_relhumidity_2km.cml | 4 +- .../results/nimrod/u1096_ng_ek00_snow_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_soil3d0060_2km.cml | 16 +- .../results/nimrod/u1096_ng_ek00_soil_2km.cml | 12 +- .../nimrod/u1096_ng_ek00_temperature_2km.cml | 16 +- .../nimrod/u1096_ng_ek00_visibility_2km.cml | 20 +- .../results/nimrod/u1096_ng_ek00_wind_2km.cml | 281 ++++++++++++++++++ .../nimrod/u1096_ng_ek00_winduv3d0015_2km.cml | 8 +- .../nimrod/u1096_ng_ek00_winduv_2km.cml | 8 +- .../results/nimrod/u1096_ng_ek01_cape_2km.cml | 24 +- ...u1096_ng_ek07_precip0540_accum180_18km.cml | 43 +++ .../results/nimrod/u1096_ng_umqv_fog_2km.cml | 4 +- lib/iris/tests/test_coordsystem.py | 83 +++--- 45 files changed, 900 insertions(+), 497 deletions(-) create mode 100644 lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml create mode 100644 lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml diff --git a/lib/iris/__init__.py b/lib/iris/__init__.py index 59dc3f74aa..a03abfcdf7 100644 --- a/lib/iris/__init__.py +++ b/lib/iris/__init__.py @@ -165,8 +165,8 @@ def __repr__(self): # msg = ('Future(example_future_flag={})') # return msg.format(self.example_future_flag) - msg = "Future()" - return msg.format() + msg = "Future(datum_support={})" + return msg.format(self.datum_support) # deprecated_options = {'example_future_flag': 'warning',} deprecated_options = {} diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index cb3a9d3599..e6bb1f8416 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -137,7 +137,6 @@ def __init__( semi_minor_axis=None, inverse_flattening=None, longitude_of_prime_meridian=None, - datum=None, ): """ Create a new GeogCS. @@ -246,7 +245,7 @@ def __init__( longitude_of_prime_meridian, 0 ) - self.datum = datum + self._datum = None def _pretty_attrs(self): attrs = [("semi_major_axis", self.semi_major_axis)] @@ -259,11 +258,11 @@ def _pretty_attrs(self): self.longitude_of_prime_meridian, ) ) - if self.datum is not None: + if self._datum is not None: attrs.append( ( "datum", - self.datum, + self._datum, ) ) return attrs @@ -314,12 +313,30 @@ def as_cartopy_projection(self): def as_cartopy_globe(self): # Explicitly set `ellipse` to None as a workaround for # Cartopy setting WGS84 as the default. - return ccrs.Globe( - semimajor_axis=self.semi_major_axis, - semiminor_axis=self.semi_minor_axis, - ellipse=None, - datum=self.datum, + if self._datum is not None: + return ccrs.Globe( + datum=self._datum, + ellipse=None, + ) + else: + return ccrs.Globe( + semimajor_axis=self.semi_major_axis, + semiminor_axis=self.semi_minor_axis, + ellipse=None, + ) + + @classmethod + def from_datum(cls, datum, longitude_of_prime_meridian=None): + # Cartopy doesn't actually enact datums unless they're provided without + # ellipsoid axes, so only provide the datum + ref_crs = ccrs.Geodetic(ccrs.Globe(datum)) + crs = cls( + ref_crs.ellipsoid.semi_major_metre, + ref_crs.ellipsoid.semi_minor_metre, + longitude_of_prime_meridian, ) + crs._datum = datum + return crs class RotatedGeogCS(CoordSystem): @@ -572,6 +589,7 @@ def as_cartopy_projection(self): class Orthographic(CoordSystem): """ An orthographic map projection. + """ grid_mapping_name = "orthographic" @@ -586,18 +604,26 @@ def __init__( ): """ Constructs an Orthographic coord system. + Args: + * latitude_of_projection_origin: True latitude of planar origin in degrees. + * longitude_of_projection_origin: True longitude of planar origin in degrees. + Kwargs: + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -653,6 +679,7 @@ def as_cartopy_projection(self): class VerticalPerspective(CoordSystem): """ A vertical/near-side perspective satellite image map projection. + """ grid_mapping_name = "vertical_perspective" @@ -668,21 +695,30 @@ def __init__( ): """ Constructs a Vertical Perspective coord system. + Args: + * latitude_of_projection_origin: True latitude of planar origin in degrees. + * longitude_of_projection_origin: True longitude of planar origin in degrees. + * perspective_point_height: Altitude of satellite in metres above the surface of the ellipsoid. + Kwargs: + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -742,6 +778,7 @@ def as_cartopy_projection(self): class Geostationary(CoordSystem): """ A geostationary satellite image map projection. + """ grid_mapping_name = "geostationary" @@ -759,22 +796,32 @@ def __init__( """ Constructs a Geostationary coord system. + Args: + * latitude_of_projection_origin: True latitude of planar origin in degrees. + * longitude_of_projection_origin: True longitude of planar origin in degrees. + * perspective_point_height: Altitude of satellite in metres above the surface of the ellipsoid. + * sweep_angle_axis (string): The axis along which the satellite instrument sweeps - 'x' or 'y'. + Kwargs: + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = float( @@ -845,6 +892,7 @@ def as_cartopy_projection(self): class Stereographic(CoordSystem): """ A stereographic map projection. + """ grid_mapping_name = "stereographic" @@ -860,20 +908,29 @@ def __init__( ): """ Constructs a Stereographic coord system. + Args: + * central_lat: The latitude of the pole. + * central_lon: The central longitude, which aligns with the y axis. + Kwargs: + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * true_scale_lat: Latitude of true scale. + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. @@ -932,6 +989,7 @@ def as_cartopy_projection(self): class LambertConformal(CoordSystem): """ A coordinate system in the Lambert Conformal conic projection. + """ grid_mapping_name = "lambert_conformal_conic" @@ -947,25 +1005,35 @@ def __init__( ): """ Constructs a LambertConformal coord system. + Kwargs: + * central_lat: The latitude of "unitary scale". Defaults to 39.0 . + * central_lon: The central longitude. Defaults to -96.0 . + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * secant_latitudes (number or iterable of 1 or 2 numbers): Latitudes of secant intersection. One or two. Defaults to (33.0, 45.0). + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + .. note: + Default arguments are for the familiar USA map: central_lon=-96.0, central_lat=39.0, false_easting=0.0, false_northing=0.0, secant_latitudes=(33, 45) + """ #: True latitude of planar origin in degrees. @@ -1032,6 +1100,7 @@ def as_cartopy_projection(self): class Mercator(CoordSystem): """ A coordinate system in the Mercator projection. + """ grid_mapping_name = "mercator" @@ -1047,19 +1116,27 @@ def __init__( ): """ Constructs a Mercator coord system. + Kwargs: + * longitude_of_projection_origin: True longitude of planar origin in degrees. Defaults to 0.0 . + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + * standard_parallel: The latitude where the scale is 1. Defaults to 0.0 . + * scale_factor_at_projection_origin: Scale factor at natural origin. Defaults to unused. + * false_easting: X offset from the planar origin in metres. Defaults to 0.0. + * false_northing: Y offset from the planar origin in metres. Defaults to 0.0. + * datum: If given, specifies the datumof the coordinate system. Only respected if iris.Future.daum_support is set. @@ -1133,6 +1210,7 @@ def as_cartopy_projection(self): class LambertAzimuthalEqualArea(CoordSystem): """ A coordinate system in the Lambert Azimuthal Equal Area projection. + """ grid_mapping_name = "lambert_azimuthal_equal_area" @@ -1147,17 +1225,24 @@ def __init__( ): """ Constructs a Lambert Azimuthal Equal Area coord system. + Kwargs: + * latitude_of_projection_origin: True latitude of planar origin in degrees. Defaults to 0.0 . + * longitude_of_projection_origin: True longitude of planar origin in degrees. Defaults to 0.0 . + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( @@ -1209,6 +1294,7 @@ def as_cartopy_projection(self): class AlbersEqualArea(CoordSystem): """ A coordinate system in the Albers Conical Equal Area projection. + """ grid_mapping_name = "albers_conical_equal_area" @@ -1224,21 +1310,29 @@ def __init__( ): """ Constructs a Albers Conical Equal Area coord system. + Kwargs: + * latitude_of_projection_origin: True latitude of planar origin in degrees. Defaults to 0.0 . + * longitude_of_central_meridian: True longitude of planar central meridian in degrees. Defaults to 0.0 . + * false_easting: X offset from planar origin in metres. Defaults to 0.0 . + * false_northing: Y offset from planar origin in metres. Defaults to 0.0 . + * standard_parallels (number or iterable of 1 or 2 numbers): The one or two latitudes of correct scale. Defaults to (20.0, 50.0). + * ellipsoid (:class:`GeogCS`): If given, defines the ellipsoid. + """ #: True latitude of planar origin in degrees. self.latitude_of_projection_origin = _arg_default( diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 24f8ccd900..3abb741d6c 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -19,8 +19,9 @@ import cf_units import numpy as np import numpy.ma as ma +import pyproj -import iris.FUTURE.datum_support +import iris import iris.aux_factory from iris.common.mixin import _get_valid_standard_name import iris.coord_systems @@ -132,6 +133,7 @@ CF_ATTR_BOUNDS = "bounds" CF_ATTR_CALENDAR = "calendar" CF_ATTR_CLIMATOLOGY = "climatology" +CF_ATTR_GRID_CRS_WKT = "crs_wkt" CF_ATTR_GRID_DATUM = "horizontal_datum_name" CF_ATTR_GRID_INVERSE_FLATTENING = "inverse_flattening" CF_ATTR_GRID_EARTH_RADIUS = "earth_radius" @@ -250,21 +252,36 @@ def _get_ellipsoid(cf_grid_var): if major is None and minor is None and inverse_flattening is None: major = getattr(cf_grid_var, CF_ATTR_GRID_EARTH_RADIUS, None) - return major, minor, inverse_flattening + if iris.FUTURE.datum_support: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + # Check crs_wkt if no datum + if datum is None: + crs_wkt = getattr(cf_grid_var, CF_ATTR_GRID_CRS_WKT, None) + if crs_wkt is not None: + proj_crs = pyproj.crs.CRS.from_wkt(crs_wkt) + if proj_crs.datum is not None: + datum = proj_crs.datum.name + else: + datum = None + + if datum is not None: + return iris.coord_systems.GeogCS.from_datum(datum) + elif major is None and minor is None and inverse_flattening is None: + return None + else: + return iris.coord_systems.GeogCS(major, minor, inverse_flattening) ################################################################################ def build_coordinate_system(engine, cf_grid_var): """Create a coordinate system from the CF-netCDF grid mapping variable.""" - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) - - return iris.coord_systems.GeogCS(major, minor, inverse_flattening) + return _get_ellipsoid(cf_grid_var) ################################################################################ def build_rotated_coordinate_system(engine, cf_grid_var): """Create a rotated coordinate system from the CF-netCDF grid mapping variable.""" - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) north_pole_latitude = getattr( cf_grid_var, CF_ATTR_GRID_NORTH_POLE_LAT, 90.0 @@ -279,25 +296,11 @@ def build_rotated_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_NORTH_POLE_GRID_LON, 0.0 ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - rcs = iris.coord_systems.RotatedGeogCS( north_pole_latitude, north_pole_longitude, north_pole_grid_lon, ellipsoid, - datum=datum, ) return rcs @@ -310,7 +313,7 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -335,19 +338,6 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_SCALE_FACTOR_AT_PROJ_ORIGIN, None ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.TransverseMercator( latitude_of_projection_origin, longitude_of_central_meridian, @@ -355,7 +345,6 @@ def build_transverse_mercator_coordinate_system(engine, cf_grid_var): false_northing, scale_factor_at_central_meridian, ellipsoid, - datum=datum, ) return cs @@ -368,7 +357,7 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -382,19 +371,6 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_STANDARD_PARALLEL, None ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.LambertConformal( latitude_of_projection_origin, longitude_of_central_meridian, @@ -402,7 +378,6 @@ def build_lambert_conformal_coordinate_system(engine, cf_grid_var): false_northing, standard_parallel, ellipsoid, - datum=datum, ) return cs @@ -415,7 +390,7 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -428,19 +403,6 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): # Iris currently only supports Stereographic projections with a scale # factor of 1.0. This is checked elsewhere. - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.Stereographic( latitude_of_projection_origin, longitude_of_projection_origin, @@ -448,7 +410,6 @@ def build_stereographic_coordinate_system(engine, cf_grid_var): false_northing, true_scale_lat=None, ellipsoid=ellipsoid, - datum=datum, ) return cs @@ -461,7 +422,7 @@ def build_mercator_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) longitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LON_OF_PROJ_ORIGIN, None @@ -475,19 +436,6 @@ def build_mercator_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_SCALE_FACTOR_AT_PROJ_ORIGIN, None ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.Mercator( longitude_of_projection_origin, ellipsoid=ellipsoid, @@ -495,7 +443,6 @@ def build_mercator_coordinate_system(engine, cf_grid_var): scale_factor_at_projection_origin=scale_factor_at_projection_origin, false_easting=false_easting, false_northing=false_northing, - datum=datum, ) return cs @@ -508,7 +455,7 @@ def build_lambert_azimuthal_equal_area_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -519,26 +466,12 @@ def build_lambert_azimuthal_equal_area_coordinate_system(engine, cf_grid_var): false_easting = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_EASTING, None) false_northing = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_NORTHING, None) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.LambertAzimuthalEqualArea( latitude_of_projection_origin, longitude_of_projection_origin, false_easting, false_northing, ellipsoid, - datum=datum, ) return cs @@ -551,7 +484,7 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -565,19 +498,6 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_STANDARD_PARALLEL, None ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.AlbersEqualArea( latitude_of_projection_origin, longitude_of_central_meridian, @@ -585,7 +505,6 @@ def build_albers_equal_area_coordinate_system(engine, cf_grid_var): false_northing, standard_parallels, ellipsoid, - datum=datum, ) return cs @@ -598,7 +517,7 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -612,19 +531,6 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): false_easting = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_EASTING, None) false_northing = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_NORTHING, None) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.VerticalPerspective( latitude_of_projection_origin, longitude_of_projection_origin, @@ -632,7 +538,6 @@ def build_vertical_perspective_coordinate_system(engine, cf_grid_var): false_easting, false_northing, ellipsoid, - datum=datum, ) return cs @@ -645,7 +550,7 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): grid mapping variable. """ - major, minor, inverse_flattening = _get_ellipsoid(cf_grid_var) + ellipsoid = _get_ellipsoid(cf_grid_var) latitude_of_projection_origin = getattr( cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None @@ -662,19 +567,6 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): cf_grid_var, CF_ATTR_GRID_SWEEP_ANGLE_AXIS, None ) - ellipsoid = None - if ( - major is not None - or minor is not None - or inverse_flattening is not None - ): - ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening) - - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - else: - datum = None - cs = iris.coord_systems.Geostationary( latitude_of_projection_origin, longitude_of_projection_origin, @@ -683,7 +575,6 @@ def build_geostationary_coordinate_system(engine, cf_grid_var): false_easting, false_northing, ellipsoid, - datum=datum, ) return cs diff --git a/lib/iris/fileformats/netcdf.py b/lib/iris/fileformats/netcdf.py index 4818f621c8..5fecf2b8ad 100644 --- a/lib/iris/fileformats/netcdf.py +++ b/lib/iris/fileformats/netcdf.py @@ -2519,6 +2519,8 @@ def add_ellipsoid(ellipsoid): else: cf_var_grid.semi_major_axis = semi_major cf_var_grid.semi_minor_axis = semi_minor + if ellipsoid._datum is not None: + cf_var_grid.datum = ellipsoid._datum # latlon if isinstance(cs, iris.coord_systems.GeogCS): diff --git a/lib/iris/tests/integration/test_netcdf.py b/lib/iris/tests/integration/test_netcdf.py index 2a45561e17..4165540f5f 100644 --- a/lib/iris/tests/integration/test_netcdf.py +++ b/lib/iris/tests/integration/test_netcdf.py @@ -24,6 +24,7 @@ import numpy.ma as ma import iris +import iris.coord_systems from iris.coords import CellMethod from iris.cube import Cube, CubeList from iris.fileformats.netcdf import ( @@ -32,6 +33,7 @@ UnknownCellMethodWarning, ) import iris.tests.stock as stock +import iris.tests.unit.fileformats.netcdf.test_load_cubes as tlc @tests.skip_data @@ -484,6 +486,12 @@ def test_unknown_method(self): @tests.skip_data class TestCoordSystem(tests.IrisTest): + def setUp(self): + tlc.setUpModule() + + def tearDown(self): + tlc.tearDownModule() + def test_load_laea_grid(self): cube = iris.load_cube( tests.get_data_path( @@ -492,6 +500,91 @@ def test_load_laea_grid(self): ) self.assertCML(cube, ("netcdf", "netcdf_laea.cml")) + datum_cf_var_cdl = """ + netcdf output { + dimensions: + bar = 3 ; + foo = 4 ; + variables: + int thingness(bar, foo) ; + thingness:long_name = "thingness" ; + thingness:units = "1" ; + thingness:grid_mapping = "latitude_longitude" ; + int latitude_longitude ; + latitude_longitude:grid_mapping_name = "latitude_longitude" ; + latitude_longitude:longitude_of_prime_meridian = 0. ; + latitude_longitude:earth_radius = 6000000. ; + latitude_longitude:datum = "wibble" ; + double bar(bar) ; + bar:units = "1" ; + bar:long_name = "bar" ; + double foo(foo) ; + foo:units = "1" ; + foo:long_name = "foo" ; + + // global attributes: + :Conventions = "CF-1.7" ; + data: + + thingness = + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 ; + + latitude_longitude = _ ; + + bar = 2.5, 7.5, 12.5 ; + + foo = -7.5, 7.5, 22.5, 37.5 ; + } + """ + + def test_load_datum_wkt(self): + expected = "wibble" + nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) + with iris.FUTURE.context(datum_support=True): + cube = iris.load_cube(nc_path) + test_crs = cube.coord("foo").coord_system + actual = test_crs.as_cartopy_crs().ellipsoid.datum + self.assertStringEqual(expected, actual) + + def test_no_load_datum_wkt(self): + nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) + cube = iris.load_cube(nc_path) + test_crs = cube.coord("foo").coord_system + actual = test_crs.as_cartopy_crs().ellipsoid.datum + self.assertIsNone(actual) + + def test_load_datum_cf_var(self): + expected = "wibble" + nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) + with iris.FUTURE.context(datum_support=True): + cube = iris.load_cube(nc_path) + test_crs = cube.coord("foo").coord_system + actual = test_crs.as_cartopy_crs().ellipsoid.datum + self.assertStringEqual(expected, actual) + + def test_no_load_datum_cf_var(self): + nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) + cube = iris.load_cube(nc_path) + test_crs = cube.coord("foo").coord_system + actual = test_crs.as_cartopy_crs().ellipsoid.datum + self.assertIsNone(actual) + + def test_save_datum(self): + expected = "OSGB 1936" + test_cube = stock.realistic_3d() + test_crs = iris.coord_systems.GeogCS.from_datum(datum="OSGB36") + test_cube.coord("grid_latitude").coord_system = test_crs + test_cube.coord("grid_longitude").coord_system = test_crs + with self.temp_filename(suffix=".nc") as filename: + iris.save(test_cube, filename) + with iris.FUTURE.context(datum_support=True): + cube = iris.load_cube(filename) + test_crs = cube.coord("grid_latitude").coord_system + actual = test_crs.as_cartopy_crs().ellipsoid.datum + self.assertStringEqual(expected, actual) + def _get_scale_factor_add_offset(cube, datatype): """Utility function used by netCDF data packing tests.""" diff --git a/lib/iris/tests/results/coord_systems/Stereographic.xml b/lib/iris/tests/results/coord_systems/Stereographic.xml index 7e3437b064..bb12cd94cc 100644 --- a/lib/iris/tests/results/coord_systems/Stereographic.xml +++ b/lib/iris/tests/results/coord_systems/Stereographic.xml @@ -1,2 +1,2 @@ - + diff --git a/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml b/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml index 67a07d9a81..6176d01aa5 100644 --- a/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml +++ b/lib/iris/tests/results/coord_systems/TransverseMercator_osgb.xml @@ -1,2 +1,2 @@ - + diff --git a/lib/iris/tests/results/netcdf/netcdf_laea.cml b/lib/iris/tests/results/netcdf/netcdf_laea.cml index 720d5c51da..ad23114038 100644 --- a/lib/iris/tests/results/netcdf/netcdf_laea.cml +++ b/lib/iris/tests/results/netcdf/netcdf_laea.cml @@ -31,12 +31,12 @@ [5262500.0, 5701785.71429], [5701785.71429, 6141071.42857], [6141071.42857, 6580357.14286], - [6580357.14286, 7019642.85714]]" id="aa58b139" points="[650000.0, 1089285.71429, 1528571.42857, + [6580357.14286, 7019642.85714]]" id="b71cdf0e" points="[650000.0, 1089285.71429, 1528571.42857, 1967857.14286, 2407142.85714, 2846428.57143, 3285714.28571, 3725000.0, 4164285.71429, 4603571.42857, 5042857.14286, 5482142.85714, 5921428.57143, 6360714.28571, 6800000.0]" shape="(15,)" standard_name="projection_x_coordinate" units="Unit('m')" value_type="float64" var_name="projection_x_coordinate"> - + @@ -54,12 +54,12 @@ [4500000.0, 4871428.57143], [4871428.57143, 5242857.14286], [5242857.14286, 5614285.71429], - [5614285.71429, 5985714.28571]]" id="1bc9bec2" points="[600000.0, 971428.571429, 1342857.14286, + [5614285.71429, 5985714.28571]]" id="f1f8b7cb" points="[600000.0, 971428.571429, 1342857.14286, 1714285.71429, 2085714.28571, 2457142.85714, 2828571.42857, 3200000.0, 3571428.57143, 3942857.14286, 4314285.71429, 4685714.28571, 5057142.85714, 5428571.42857, 5800000.0]" shape="(15,)" standard_name="projection_y_coordinate" units="Unit('m')" value_type="float64" var_name="projection_y_coordinate"> - + diff --git a/lib/iris/tests/results/netcdf/netcdf_lcc.cml b/lib/iris/tests/results/netcdf/netcdf_lcc.cml index 070ff2d267..7ea53e6600 100644 --- a/lib/iris/tests/results/netcdf/netcdf_lcc.cml +++ b/lib/iris/tests/results/netcdf/netcdf_lcc.cml @@ -48,7 +48,7 @@ ..., 11.7454500198, 11.7587404251, 11.77202034]]" shape="(60, 60)" standard_name="longitude" units="Unit('degrees')" value_type="float64" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc.cml b/lib/iris/tests/results/netcdf/netcdf_merc.cml index ee5e318b7c..831a8fdaa7 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc.cml @@ -53,15 +53,15 @@ 45.5158, 45.9993]]" shape="(192, 192)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc_false.cml b/lib/iris/tests/results/netcdf/netcdf_merc_false.cml index 9667f35c73..1e50aa6e65 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc_false.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc_false.cml @@ -6,17 +6,17 @@ - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml b/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml index 1a3e4c73b4..c9ad4ca33f 100644 --- a/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml +++ b/lib/iris/tests/results/netcdf/netcdf_merc_scale_factor.cml @@ -6,13 +6,13 @@ - - + + - - + + diff --git a/lib/iris/tests/results/netcdf/netcdf_stereo.cml b/lib/iris/tests/results/netcdf/netcdf_stereo.cml index 343d4e5e2f..b07304cd62 100644 --- a/lib/iris/tests/results/netcdf/netcdf_stereo.cml +++ b/lib/iris/tests/results/netcdf/netcdf_stereo.cml @@ -54,15 +54,15 @@ 10.449, 10.5996]]" shape="(160, 256)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml b/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml index 53503cbc76..2d909ba57e 100644 --- a/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml +++ b/lib/iris/tests/results/netcdf/netcdf_tmerc_and_climatology.cml @@ -50,15 +50,15 @@ 1.99301683617]]" shape="(290, 180)" standard_name="longitude" units="Unit('degrees')" value_type="float64" var_name="lon"/> - - + - - + diff --git a/lib/iris/tests/results/nimrod/load_2flds.cml b/lib/iris/tests/results/nimrod/load_2flds.cml index 40cd824337..b068657d40 100644 --- a/lib/iris/tests/results/nimrod/load_2flds.cml +++ b/lib/iris/tests/results/nimrod/load_2flds.cml @@ -17,15 +17,15 @@ - - + - - + diff --git a/lib/iris/tests/results/nimrod/probability_fields.cml b/lib/iris/tests/results/nimrod/probability_fields.cml index 068e29195d..7add3e75a4 100644 --- a/lib/iris/tests/results/nimrod/probability_fields.cml +++ b/lib/iris/tests/results/nimrod/probability_fields.cml @@ -29,12 +29,12 @@ - + - + @@ -79,12 +79,12 @@ - + - + @@ -122,12 +122,12 @@ - + - + @@ -169,12 +169,12 @@ - + - + @@ -221,12 +221,12 @@ - + - + @@ -282,12 +282,12 @@ - + - + @@ -322,12 +322,12 @@ - + - + @@ -369,12 +369,12 @@ - + - + @@ -413,12 +413,12 @@ - + - + @@ -452,12 +452,12 @@ - + - + @@ -488,12 +488,12 @@ - + - + @@ -528,12 +528,12 @@ - + - + @@ -564,12 +564,12 @@ - + - + @@ -604,12 +604,12 @@ - + - + @@ -648,12 +648,12 @@ - + - + @@ -692,12 +692,12 @@ - + - + @@ -733,12 +733,12 @@ - + - + @@ -770,12 +770,12 @@ - + - + @@ -811,12 +811,12 @@ - + - + @@ -857,12 +857,12 @@ - + - + @@ -905,12 +905,12 @@ - + - + @@ -953,12 +953,12 @@ - + - + @@ -996,12 +996,12 @@ - + - + @@ -1033,12 +1033,12 @@ - + - + @@ -1074,12 +1074,12 @@ - + - + @@ -1114,12 +1114,12 @@ - + - + @@ -1157,12 +1157,12 @@ - + - + @@ -1204,12 +1204,12 @@ - + - + @@ -1247,12 +1247,12 @@ - + - + @@ -1294,12 +1294,12 @@ - + - + @@ -1337,12 +1337,12 @@ - + - + @@ -1380,12 +1380,12 @@ - + - + @@ -1428,12 +1428,12 @@ - + - + @@ -1479,12 +1479,12 @@ - + - + @@ -1529,12 +1529,12 @@ - + - + @@ -1576,12 +1576,12 @@ - + - + @@ -1619,12 +1619,12 @@ - + - + @@ -1667,12 +1667,12 @@ - + - + @@ -1718,12 +1718,12 @@ - + - + @@ -1768,12 +1768,12 @@ - + - + @@ -1811,12 +1811,12 @@ - + - + @@ -1858,12 +1858,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml index 0cea405e39..31518dd321 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_bmr04_precip_2km.cml @@ -23,12 +23,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml index 56f74cebdf..80cb1834c0 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_bsr05_precip_accum60_2km.cml @@ -22,12 +22,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml index a3d83527bd..68ec95555c 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud3d0060_2km.cml @@ -38,12 +38,12 @@ - + - + @@ -88,12 +88,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml index 439d4d0afd..c6bc6f0419 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_cloud_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -100,12 +100,12 @@ - + - + @@ -140,12 +140,12 @@ - + - + @@ -191,12 +191,12 @@ - + - + @@ -230,12 +230,12 @@ - + - + @@ -272,12 +272,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml index a9c5903fe4..e6c99f9e50 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convection_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -67,12 +67,12 @@ - + - + @@ -106,12 +106,12 @@ - + - + @@ -145,12 +145,12 @@ - + - + @@ -189,12 +189,12 @@ - + - + @@ -235,12 +235,12 @@ - + - + @@ -274,12 +274,12 @@ - + - + @@ -313,12 +313,12 @@ - + - + @@ -355,12 +355,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml index e523ff80f7..2f52a93277 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_convwind_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + @@ -212,12 +212,12 @@ - + - + @@ -258,12 +258,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml index a2fca2e87b..b2b47715a2 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_frzlev_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + @@ -139,12 +139,12 @@ - + - + @@ -179,12 +179,12 @@ - + - + @@ -218,12 +218,12 @@ - + - + @@ -258,12 +258,12 @@ - + - + @@ -298,12 +298,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml index 1898df3284..4fb1371250 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_height_2km.cml @@ -21,12 +21,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml index a4389eb974..59776b5b74 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precip_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -101,12 +101,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml index 48d71da6e6..0fa98e3bb6 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_precipaccum_2km.cml @@ -23,12 +23,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml index f293c0403d..3fdf646e70 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_preciptype_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -101,12 +101,12 @@ - + - + @@ -140,12 +140,12 @@ - + - + @@ -180,12 +180,12 @@ - + - + @@ -220,12 +220,12 @@ - + - + @@ -259,12 +259,12 @@ - + - + @@ -299,12 +299,12 @@ - + - + @@ -339,12 +339,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml index 475a97bb48..edb0862676 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_pressure_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml index 01095c7569..38f076f232 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiation_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + @@ -138,12 +138,12 @@ - + - + @@ -177,12 +177,12 @@ - + - + @@ -216,12 +216,12 @@ - + - + @@ -255,12 +255,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml index 98e8861545..35bed38591 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_radiationuv_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -99,12 +99,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml index 32480b454c..4411ff9dd5 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_refl_2km.cml @@ -30,12 +30,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml index f7dc257619..8759dac5c7 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity3d0060_2km.cml @@ -38,12 +38,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml index efeacb86f9..9b7e7582d0 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_relhumidity_2km.cml @@ -28,12 +28,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml index f46bda9802..ce549ab3cd 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_snow_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -61,12 +61,12 @@ - + - + @@ -100,12 +100,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml index afe0b3d1e9..9385bfc9ae 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil3d0060_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml index d7cc29605e..a76971a1ed 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_soil_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -104,12 +104,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml index ac3ab3b008..09677ff57a 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_temperature_2km.cml @@ -29,12 +29,12 @@ - + - + @@ -76,12 +76,12 @@ - + - + @@ -122,12 +122,12 @@ - + - + @@ -168,12 +168,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml index ab44aaaaac..8a0f50700c 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_visibility_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + @@ -120,12 +120,12 @@ - + - + @@ -166,12 +166,12 @@ - + - + @@ -212,12 +212,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml new file mode 100644 index 0000000000..df2054e8af --- /dev/null +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_wind_2km.cml @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml index 923c164b9e..331ff59c74 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv3d0015_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml index bb247871a3..aa14346e2f 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek00_winduv_2km.cml @@ -28,12 +28,12 @@ - + - + @@ -74,12 +74,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml index c8a5d8247f..1756ac0205 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek01_cape_2km.cml @@ -21,12 +21,12 @@ - + - + @@ -60,12 +60,12 @@ - + - + @@ -102,12 +102,12 @@ - + - + @@ -141,12 +141,12 @@ - + - + @@ -180,12 +180,12 @@ - + - + @@ -219,12 +219,12 @@ - + - + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml b/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml new file mode 100644 index 0000000000..f4710dd36d --- /dev/null +++ b/lib/iris/tests/results/nimrod/u1096_ng_ek07_precip0540_accum180_18km.cml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml b/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml index f817152ab5..57756ccc1d 100644 --- a/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml +++ b/lib/iris/tests/results/nimrod/u1096_ng_umqv_fog_2km.cml @@ -28,12 +28,12 @@ - + - + diff --git a/lib/iris/tests/test_coordsystem.py b/lib/iris/tests/test_coordsystem.py index 433dee95c0..dacb3b15f8 100644 --- a/lib/iris/tests/test_coordsystem.py +++ b/lib/iris/tests/test_coordsystem.py @@ -30,7 +30,6 @@ def osgb(): false_northing=100, scale_factor_at_central_meridian=0.9996012717, ellipsoid=GeogCS(6377563.396, 6356256.909), - datum="OSGB_1936", ) @@ -41,7 +40,6 @@ def stereo(): false_easting=100, false_northing=200, ellipsoid=GeogCS(6377563.396, 6356256.909), - datum="WGS_1984", ) @@ -165,24 +163,28 @@ def test_invalid_ellipsoid_params(self): class Test_GeogCS_repr(tests.IrisTest): def test_repr(self): - cs = GeogCS(6543210, 6500000, datum="WGS_1984") - expected = "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0, datum=WGS_1984)" + cs = GeogCS(6543210, 6500000) + expected = ( + "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0)" + ) self.assertEqual(expected, repr(cs)) class Test_GeogCS_str(tests.IrisTest): def test_str(self): - cs = GeogCS(6543210, 6500000, datum="WGS_1984") - expected = "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0, datum=WGS_1984)" + cs = GeogCS(6543210, 6500000) + expected = ( + "GeogCS(semi_major_axis=6543210.0, semi_minor_axis=6500000.0)" + ) self.assertEqual(expected, str(cs)) class Test_GeogCS_as_cartopy_globe(tests.IrisTest): def test_as_cartopy_globe(self): - cs = GeogCS(6543210, 6500000, datum="WGS_1984") + cs = GeogCS(6543210, 6500000) # Can't check equality directly, so use the proj4 params instead. res = cs.as_cartopy_globe().to_proj4_params() - expected = {"a": 6543210, "b": 6500000, "datum": "WGS_1984"} + expected = {"a": 6543210, "b": 6500000} self.assertEqual(res, expected) @@ -192,7 +194,6 @@ def test_as_cartopy_projection(self): "semi_major_axis": 6543210, "semi_minor_axis": 6500000, "longitude_of_prime_meridian": 30, - "datum": "WGS_1984", } cs = GeogCS(**geogcs_args) res = cs.as_cartopy_projection() @@ -201,7 +202,6 @@ def test_as_cartopy_projection(self): semimajor_axis=geogcs_args["semi_major_axis"], semiminor_axis=geogcs_args["semi_minor_axis"], ellipse=None, - datum="WGS_1984", ) expected = ccrs.PlateCarree( globe=globe, @@ -213,13 +213,12 @@ def test_as_cartopy_projection(self): class Test_GeogCS_as_cartopy_crs(tests.IrisTest): def test_as_cartopy_crs(self): - cs = GeogCS(6543210, 6500000, datum="WGS_1984") + cs = GeogCS(6543210, 6500000) res = cs.as_cartopy_crs() globe = ccrs.Globe( semimajor_axis=6543210.0, semiminor_axis=6500000.0, ellipse=None, - datum="WGS_1984", ) expected = ccrs.Geodetic(globe) self.assertEqual(res, expected) @@ -250,22 +249,19 @@ def test_repr(self): 40, north_pole_grid_longitude=50, ellipsoid=GeogCS(6371229), - datum="WGS_1984", ) expected = ( "RotatedGeogCS(30.0, 40.0, " - "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0), datum=WGS_1984)" + "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0))" ) self.assertEqual(expected, repr(rcs)) - rcs = RotatedGeogCS( - 30, 40, north_pole_grid_longitude=50, datum="WGS_1984" - ) - expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0, datum=WGS_1984)" + rcs = RotatedGeogCS(30, 40, north_pole_grid_longitude=50) + expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0)" self.assertEqual(expected, repr(rcs)) - rcs = RotatedGeogCS(30, 40, datum="WGS_1984") - expected = "RotatedGeogCS(30.0, 40.0, datum=WGS_1984)" + rcs = RotatedGeogCS(30, 40) + expected = "RotatedGeogCS(30.0, 40.0)" self.assertEqual(expected, repr(rcs)) @@ -276,22 +272,19 @@ def test_str(self): 40, north_pole_grid_longitude=50, ellipsoid=GeogCS(6371229), - datum="WGS_1984", ) expected = ( "RotatedGeogCS(30.0, 40.0, " - "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0), datum=WGS_1984)" + "north_pole_grid_longitude=50.0, ellipsoid=GeogCS(6371229.0))" ) self.assertEqual(expected, str(rcs)) - rcs = RotatedGeogCS( - 30, 40, north_pole_grid_longitude=50, datum="WGS_1984" - ) - expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0, datum=WGS_1984)" + rcs = RotatedGeogCS(30, 40, north_pole_grid_longitude=50) + expected = "RotatedGeogCS(30.0, 40.0, north_pole_grid_longitude=50.0)" self.assertEqual(expected, str(rcs)) - rcs = RotatedGeogCS(30, 40, datum="WGS_1984") - expected = "RotatedGeogCS(30.0, 40.0, datum=WGS_1984)" + rcs = RotatedGeogCS(30, 40) + expected = "RotatedGeogCS(30.0, 40.0)" self.assertEqual(expected, str(rcs)) @@ -309,7 +302,7 @@ def test_osgb(self): expected = ( "TransverseMercator(latitude_of_projection_origin=49.0, longitude_of_central_meridian=-2.0, " "false_easting=-400.0, false_northing=100.0, scale_factor_at_central_meridian=0.9996012717, " - "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909), datum=WGS_1984)" + "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909))" ) self.assertEqual(expected, repr(tm)) @@ -324,7 +317,6 @@ def test_as_cartopy_crs(self): ellipsoid = GeogCS( semi_major_axis=6377563.396, semi_minor_axis=6356256.909 ) - datum = "WGS_1984" tmerc_cs = TransverseMercator( latitude_of_projection_origin, @@ -333,7 +325,6 @@ def test_as_cartopy_crs(self): false_northing, scale_factor_at_central_meridian, ellipsoid=ellipsoid, - datum=datum, ) expected = ccrs.TransverseMercator( @@ -346,7 +337,6 @@ def test_as_cartopy_crs(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, - datum=datum, ), ) @@ -364,7 +354,6 @@ def test_as_cartopy_projection(self): ellipsoid = GeogCS( semi_major_axis=6377563.396, semi_minor_axis=6356256.909 ) - datum = "WGS_1984" tmerc_cs = TransverseMercator( latitude_of_projection_origin, @@ -373,7 +362,6 @@ def test_as_cartopy_projection(self): false_northing, scale_factor_at_central_meridian, ellipsoid=ellipsoid, - datum=datum, ) expected = ccrs.TransverseMercator( @@ -386,7 +374,6 @@ def test_as_cartopy_projection(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, - datum="WGS_1984", ), ) @@ -406,7 +393,7 @@ def test_stereo(self): expected = ( "Stereographic(central_lat=-90.0, central_lon=-45.0, " "false_easting=100.0, false_northing=200.0, true_scale_lat=None, " - "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909), datum=WGS_1984)" + "ellipsoid=GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909))" ) self.assertEqual(expected, repr(st)) @@ -418,7 +405,6 @@ def test_as_cartopy_crs(self): false_easting = 100.0 false_northing = 200.0 ellipsoid = GeogCS(6377563.396, 6356256.909) - datum = "WGS_1984" st = Stereographic( central_lat=latitude_of_projection_origin, @@ -426,7 +412,6 @@ def test_as_cartopy_crs(self): false_easting=false_easting, false_northing=false_northing, ellipsoid=ellipsoid, - datum=datum, ) expected = ccrs.Stereographic( central_latitude=latitude_of_projection_origin, @@ -437,7 +422,6 @@ def test_as_cartopy_crs(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, - datum=datum, ), ) @@ -452,7 +436,6 @@ def test_as_cartopy_projection(self): false_easting = 100.0 false_northing = 200.0 ellipsoid = GeogCS(6377563.396, 6356256.909) - datum = "WGS_1984" st = Stereographic( central_lat=latitude_of_projection_origin, @@ -460,7 +443,6 @@ def test_as_cartopy_projection(self): false_easting=false_easting, false_northing=false_northing, ellipsoid=ellipsoid, - datum=datum, ) expected = ccrs.Stereographic( central_latitude=latitude_of_projection_origin, @@ -471,7 +453,6 @@ def test_as_cartopy_projection(self): semimajor_axis=6377563.396, semiminor_axis=6356256.909, ellipse=None, - datum=datum, ), ) @@ -515,5 +496,23 @@ def test_south_cutoff(self): self.assertEqual(ccrs.cutoff, 30) +class Test_Datums(tests.IrisTest): + def test_default_none(self): + cs = GeogCS(6543210, 6500000) + cartopy_crs = cs.as_cartopy_crs() + self.assertStringEqual(cartopy_crs.datum.name, "unknown") + + def test_set_persist(self): + cs = GeogCS.from_datum(datum="WGS84") + cartopy_crs = cs.as_cartopy_crs() + self.assertStringEqual( + cartopy_crs.datum.name, "World Geodetic System 1984" + ) + + cs = GeogCS.from_datum(datum="OSGB36") + cartopy_crs = cs.as_cartopy_crs() + self.assertStringEqual(cartopy_crs.datum.name, "OSGB 1936") + + if __name__ == "__main__": tests.main() From 92efb7d9664944efad8f18b6abe4069db4901ffa Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 21 Apr 2022 09:26:31 +0100 Subject: [PATCH 06/15] More additions --- .../fileformats/_nc_load_rules/helpers.py | 13 ++ lib/iris/tests/integration/test_netcdf.py | 164 +++++++++++++----- 2 files changed, 136 insertions(+), 41 deletions(-) diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 3abb741d6c..f4c6049136 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -260,10 +260,23 @@ def _get_ellipsoid(cf_grid_var): if crs_wkt is not None: proj_crs = pyproj.crs.CRS.from_wkt(crs_wkt) if proj_crs.datum is not None: + print(dir(proj_crs.datum)) + print(proj_crs.datum.ellipsoid) + print(proj_crs.datum.type_name) + print(proj_crs.datum.name) + print(proj_crs.datum.to_json_dict()) + print(dir(proj_crs)) + print(proj_crs.to_proj4()) + print(proj_crs.source_crs) datum = proj_crs.datum.name else: datum = None + if datum is not None and ( + datum.startswith("Unknown") or datum.startswith("unknown") + ): + datum = None + if datum is not None: return iris.coord_systems.GeogCS.from_datum(datum) elif major is None and minor is None and inverse_flattening is None: diff --git a/lib/iris/tests/integration/test_netcdf.py b/lib/iris/tests/integration/test_netcdf.py index 4165540f5f..dc1abbd262 100644 --- a/lib/iris/tests/integration/test_netcdf.py +++ b/lib/iris/tests/integration/test_netcdf.py @@ -25,7 +25,7 @@ import iris import iris.coord_systems -from iris.coords import CellMethod +from iris.coords import CellMethod, DimCoord from iris.cube import Cube, CubeList from iris.fileformats.netcdf import ( CF_CONVENTIONS_VERSION, @@ -503,86 +503,168 @@ def test_load_laea_grid(self): datum_cf_var_cdl = """ netcdf output { dimensions: - bar = 3 ; - foo = 4 ; + y = 4 ; + x = 3 ; variables: - int thingness(bar, foo) ; - thingness:long_name = "thingness" ; - thingness:units = "1" ; - thingness:grid_mapping = "latitude_longitude" ; - int latitude_longitude ; - latitude_longitude:grid_mapping_name = "latitude_longitude" ; - latitude_longitude:longitude_of_prime_meridian = 0. ; - latitude_longitude:earth_radius = 6000000. ; - latitude_longitude:datum = "wibble" ; - double bar(bar) ; - bar:units = "1" ; - bar:long_name = "bar" ; - double foo(foo) ; - foo:units = "1" ; - foo:long_name = "foo" ; + float data(y, x) ; + data :standard_name = "toa_brightness_temperature" ; + data :units = "K" ; + data :grid_mapping = "mercator" ; + int mercator ; + mercator:grid_mapping_name = "mercator" ; + mercator:longitude_of_prime_meridian = 0. ; + mercator:earth_radius = 6378169. ; + mercator:datum = "wibble" ; + float y(y) ; + y:axis = "Y" ; + y:units = "m" ; + y:standard_name = "projection_y_coordinate" ; + float x(x) ; + x:axis = "X" ; + x:units = "m" ; + x:standard_name = "projection_x_coordinate" ; // global attributes: :Conventions = "CF-1.7" ; + :standard_name_vocabulary = "CF Standard Name Table v27" ; + data: - thingness = - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11 ; + data = + 0, 1, 2, + 3, 4, 5, + 6, 7, 8, + 9, 10, 11 ; + + mercator = _ ; - latitude_longitude = _ ; + y = 1, 2, 3, 5 ; - bar = 2.5, 7.5, 12.5 ; + x = -6, -4, -2 ; - foo = -7.5, 7.5, 22.5, 37.5 ; } """ + datum_wkt_cdl = """ +netcdf output5 { +dimensions: + y = 4 ; + x = 3 ; +variables: + float data(y, x) ; + data :standard_name = "toa_brightness_temperature" ; + data :units = "K" ; + data :grid_mapping = "mercator" ; + int mercator ; + mercator:grid_mapping_name = "mercator" ; + mercator:longitude_of_prime_meridian = 0. ; + mercator:earth_radius = 6378169. ; + mercator:longitude_of_projection_origin = 0. ; + mercator:false_easting = 0. ; + mercator:false_northing = 0. ; + mercator:scale_factor_at_projection_origin = 1. ; + mercator:crs_wkt = "PROJCRS[\\"unknown\\",BASEGEOGCRS[\\"unknown\\",DATUM[\\"WGS84\\",ELLIPSOID[\\"unknown\\",6378169,0,LENGTHUNIT[\\"metre\\",1,ID[\\"EPSG\\",9001]]]],PRIMEM[\\"Greenwich\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8901]]],CONVERSION[\\"unknown\\",METHOD[\\"Mercator (variant B)\\",ID[\\"EPSG\\",9805]],PARAMETER[\\"Latitude of 1st standard parallel\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8823]],PARAMETER[\\"Longitude of natural origin\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8802]],PARAMETER[\\"False easting\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8806]],PARAMETER[\\"False northing\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8807]]],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]]]]" ; + float y(y) ; + y:axis = "Y" ; + y:units = "m" ; + y:standard_name = "projection_y_coordinate" ; + float x(x) ; + x:axis = "X" ; + x:units = "m" ; + x:standard_name = "projection_x_coordinate" ; + +// global attributes: + :standard_name_vocabulary = "CF Standard Name Table v27" ; + :Conventions = "CF-1.7" ; +data: + + data = + 0, 1, 2, + 3, 4, 5, + 6, 7, 8, + 9, 10, 11 ; + + mercator = _ ; + + y = 1, 2, 3, 5 ; + + x = -6, -4, -2 ; +} + """ + def test_load_datum_wkt(self): expected = "wibble" nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) with iris.FUTURE.context(datum_support=True): cube = iris.load_cube(nc_path) - test_crs = cube.coord("foo").coord_system - actual = test_crs.as_cartopy_crs().ellipsoid.datum + test_crs = cube.coord("projection_y_coordinate").coord_system + actual = str(test_crs.as_cartopy_crs().datum) self.assertStringEqual(expected, actual) def test_no_load_datum_wkt(self): nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) cube = iris.load_cube(nc_path) - test_crs = cube.coord("foo").coord_system - actual = test_crs.as_cartopy_crs().ellipsoid.datum - self.assertIsNone(actual) + test_crs = cube.coord("projection_y_coordinate").coord_system + actual = str(test_crs.as_cartopy_crs().datum) + self.assertStringEqual(actual, "unknown") def test_load_datum_cf_var(self): expected = "wibble" nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) with iris.FUTURE.context(datum_support=True): cube = iris.load_cube(nc_path) - test_crs = cube.coord("foo").coord_system - actual = test_crs.as_cartopy_crs().ellipsoid.datum + test_crs = cube.coord("projection_y_coordinate").coord_system + actual = str(test_crs.as_cartopy_crs().datum) self.assertStringEqual(expected, actual) def test_no_load_datum_cf_var(self): nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) cube = iris.load_cube(nc_path) - test_crs = cube.coord("foo").coord_system - actual = test_crs.as_cartopy_crs().ellipsoid.datum - self.assertIsNone(actual) + test_crs = cube.coord("projection_y_coordinate").coord_system + actual = str(test_crs.as_cartopy_crs().datum) + self.assertStringEqual(actual, "unknown") def test_save_datum(self): expected = "OSGB 1936" - test_cube = stock.realistic_3d() - test_crs = iris.coord_systems.GeogCS.from_datum(datum="OSGB36") - test_cube.coord("grid_latitude").coord_system = test_crs - test_cube.coord("grid_longitude").coord_system = test_crs + # saved_crs = iris.coord_systems.GeogCS.from_datum(datum="OSGB36") + saved_crs = iris.coord_systems.Mercator( + ellipsoid=iris.coord_systems.GeogCS.from_datum(datum="OSGB36") + ) + + base_cube = stock.realistic_3d() + base_lat_coord = base_cube.coord("grid_latitude") + test_lat_coord = DimCoord( + base_lat_coord.points, + standard_name="projection_y_coordinate", + coord_system=saved_crs, + ) + base_lon_coord = base_cube.coord("grid_longitude") + test_lon_coord = DimCoord( + base_lon_coord.points, + standard_name="projection_x_coordinate", + coord_system=saved_crs, + ) + test_cube = Cube( + base_cube.data, + standard_name=base_cube.standard_name, + units=base_cube.units, + dim_coords_and_dims=( + (base_cube.coord("time"), 0), + (test_lat_coord, 1), + (test_lon_coord, 2), + ), + ) + with self.temp_filename(suffix=".nc") as filename: iris.save(test_cube, filename) with iris.FUTURE.context(datum_support=True): cube = iris.load_cube(filename) - test_crs = cube.coord("grid_latitude").coord_system - actual = test_crs.as_cartopy_crs().ellipsoid.datum + print(cube) + for coord in cube.coords(): + print(coord) + + test_crs = cube.coord("projection_y_coordinate").coord_system + actual = str(test_crs.as_cartopy_crs().datum) self.assertStringEqual(expected, actual) From 23a810d45e297e78b07568676cc612ff2babe88a Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Fri, 22 Apr 2022 09:44:49 +0100 Subject: [PATCH 07/15] Tests pass --- lib/iris/coord_systems.py | 107 ++++++++---------- .../fileformats/_nc_load_rules/helpers.py | 10 +- lib/iris/fileformats/netcdf.py | 4 +- lib/iris/tests/integration/test_netcdf.py | 10 +- .../unit/fileformats/netcdf/test_Saver.py | 4 +- 5 files changed, 56 insertions(+), 79 deletions(-) diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index e6bb1f8416..3b79825b48 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -122,6 +122,12 @@ def as_cartopy_projection(self): pass +_short_datum_names = { + "OSGB 1936": "OSGB36", + "WGS 84": "WGS84", +} + + class GeogCS(CoordSystem): """ A geographic (ellipsoidal) coordinate system, defined by the shape of @@ -193,59 +199,33 @@ def __init__( ): raise ValueError("Ellipsoid is overspecified") - # Perfect sphere (semi_major_axis only)? (1 0 0) - elif semi_major_axis is not None and ( - semi_minor_axis is None and not inverse_flattening + # We didn't get enough to specify an ellipse. + if semi_major_axis is None and ( + semi_minor_axis is None or inverse_flattening is None ): - semi_minor_axis = semi_major_axis - inverse_flattening = 0.0 + raise ValueError("Insufficient ellipsoid specification") - # Calculate semi_major_axis? (0 1 1) - elif semi_major_axis is None and ( - semi_minor_axis is not None and inverse_flattening is not None - ): + # Making a globe needs a semi_major_axis and a semi_minor_axis + if semi_major_axis is None: semi_major_axis = -semi_minor_axis / ( (1.0 - inverse_flattening) / inverse_flattening ) - - # Calculate semi_minor_axis? (1 0 1) - elif semi_minor_axis is None and ( - semi_major_axis is not None and inverse_flattening is not None - ): + if semi_minor_axis is None and inverse_flattening: semi_minor_axis = semi_major_axis - ( (1.0 / inverse_flattening) * semi_major_axis ) - # Calculate inverse_flattening? (1 1 0) - elif inverse_flattening is None and ( - semi_major_axis is not None and semi_minor_axis is not None - ): - if semi_major_axis == semi_minor_axis: - inverse_flattening = 0.0 - else: - inverse_flattening = 1.0 / ( - (semi_major_axis - semi_minor_axis) / semi_major_axis - ) - - # We didn't get enough to specify an ellipse. - else: - raise ValueError("Insufficient ellipsoid specification") - - #: Major radius of the ellipsoid in metres. - self.semi_major_axis = float(semi_major_axis) - - #: Minor radius of the ellipsoid in metres. - self.semi_minor_axis = float(semi_minor_axis) - - #: :math:`1/f` where :math:`f = (a-b)/a`. - self.inverse_flattening = float(inverse_flattening) - #: Describes 'zero' on the ellipsoid in degrees. self.longitude_of_prime_meridian = _arg_default( longitude_of_prime_meridian, 0 ) - self._datum = None + globe = ccrs.Globe( + ellipse=None, + semimajor_axis=semi_major_axis, + semiminor_axis=semi_minor_axis, + ) + self._crs = ccrs.Geodetic(globe) def _pretty_attrs(self): attrs = [("semi_major_axis", self.semi_major_axis)] @@ -258,11 +238,11 @@ def _pretty_attrs(self): self.longitude_of_prime_meridian, ) ) - if self._datum is not None: + if self.datum is not None and self.datum != "unknown": attrs.append( ( "datum", - self._datum, + self.datum, ) ) return attrs @@ -302,7 +282,7 @@ def xml_element(self, doc): return CoordSystem.xml_element(self, doc, attrs) def as_cartopy_crs(self): - return ccrs.Geodetic(self.as_cartopy_globe()) + return self._crs def as_cartopy_projection(self): return ccrs.PlateCarree( @@ -311,31 +291,36 @@ def as_cartopy_projection(self): ) def as_cartopy_globe(self): - # Explicitly set `ellipse` to None as a workaround for - # Cartopy setting WGS84 as the default. - if self._datum is not None: - return ccrs.Globe( - datum=self._datum, - ellipse=None, - ) - else: - return ccrs.Globe( - semimajor_axis=self.semi_major_axis, - semiminor_axis=self.semi_minor_axis, - ellipse=None, - ) + return self._crs.globe + + def __getattr__(self, name): + if name == "semi_major_axis": + return self._crs.ellipsoid.semi_major_metre + if name == "semi_minor_axis": + return self._crs.ellipsoid.semi_minor_metre + if name == "inverse_flattening": + return self._crs.ellipsoid.inverse_flattening + if name == "datum": + datum = self._crs.datum.name + if datum.startswith("unknown") or datum.startswith("Unknown"): + return None + return datum + return getattr(super(), name) @classmethod def from_datum(cls, datum, longitude_of_prime_meridian=None): + + short_datum = _short_datum_names.get(datum, datum) + # Cartopy doesn't actually enact datums unless they're provided without # ellipsoid axes, so only provide the datum - ref_crs = ccrs.Geodetic(ccrs.Globe(datum)) - crs = cls( - ref_crs.ellipsoid.semi_major_metre, - ref_crs.ellipsoid.semi_minor_metre, - longitude_of_prime_meridian, + crs = super().__new__(cls) + crs._crs = ccrs.Geodetic(ccrs.Globe(short_datum, ellipse=None)) + + #: Describes 'zero' on the ellipsoid in degrees. + crs.longitude_of_prime_meridian = _arg_default( + longitude_of_prime_meridian, 0 ) - crs._datum = datum return crs diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index f4c6049136..a50b7f2f0c 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -260,19 +260,11 @@ def _get_ellipsoid(cf_grid_var): if crs_wkt is not None: proj_crs = pyproj.crs.CRS.from_wkt(crs_wkt) if proj_crs.datum is not None: - print(dir(proj_crs.datum)) - print(proj_crs.datum.ellipsoid) - print(proj_crs.datum.type_name) - print(proj_crs.datum.name) - print(proj_crs.datum.to_json_dict()) - print(dir(proj_crs)) - print(proj_crs.to_proj4()) - print(proj_crs.source_crs) datum = proj_crs.datum.name else: datum = None - if datum is not None and ( + if isinstance(datum, str) and ( datum.startswith("Unknown") or datum.startswith("unknown") ): datum = None diff --git a/lib/iris/fileformats/netcdf.py b/lib/iris/fileformats/netcdf.py index 5fecf2b8ad..2337403f4c 100644 --- a/lib/iris/fileformats/netcdf.py +++ b/lib/iris/fileformats/netcdf.py @@ -2519,8 +2519,8 @@ def add_ellipsoid(ellipsoid): else: cf_var_grid.semi_major_axis = semi_major cf_var_grid.semi_minor_axis = semi_minor - if ellipsoid._datum is not None: - cf_var_grid.datum = ellipsoid._datum + if ellipsoid.datum is not None: + cf_var_grid.horizontal_datum_name = ellipsoid.datum # latlon if isinstance(cs, iris.coord_systems.GeogCS): diff --git a/lib/iris/tests/integration/test_netcdf.py b/lib/iris/tests/integration/test_netcdf.py index dc1abbd262..eb53b6633c 100644 --- a/lib/iris/tests/integration/test_netcdf.py +++ b/lib/iris/tests/integration/test_netcdf.py @@ -514,7 +514,7 @@ def test_load_laea_grid(self): mercator:grid_mapping_name = "mercator" ; mercator:longitude_of_prime_meridian = 0. ; mercator:earth_radius = 6378169. ; - mercator:datum = "wibble" ; + mercator:horizontal_datum_name = "OSGB36" ; float y(y) ; y:axis = "Y" ; y:units = "m" ; @@ -563,7 +563,7 @@ def test_load_laea_grid(self): mercator:false_easting = 0. ; mercator:false_northing = 0. ; mercator:scale_factor_at_projection_origin = 1. ; - mercator:crs_wkt = "PROJCRS[\\"unknown\\",BASEGEOGCRS[\\"unknown\\",DATUM[\\"WGS84\\",ELLIPSOID[\\"unknown\\",6378169,0,LENGTHUNIT[\\"metre\\",1,ID[\\"EPSG\\",9001]]]],PRIMEM[\\"Greenwich\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8901]]],CONVERSION[\\"unknown\\",METHOD[\\"Mercator (variant B)\\",ID[\\"EPSG\\",9805]],PARAMETER[\\"Latitude of 1st standard parallel\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8823]],PARAMETER[\\"Longitude of natural origin\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8802]],PARAMETER[\\"False easting\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8806]],PARAMETER[\\"False northing\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8807]]],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]]]]" ; + mercator:crs_wkt = "PROJCRS[\\"unknown\\",BASEGEOGCRS[\\"unknown\\",DATUM[\\"OSGB36\\",ELLIPSOID[\\"unknown\\",6378169,0,LENGTHUNIT[\\"metre\\",1,ID[\\"EPSG\\",9001]]]],PRIMEM[\\"Greenwich\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8901]]],CONVERSION[\\"unknown\\",METHOD[\\"Mercator (variant B)\\",ID[\\"EPSG\\",9805]],PARAMETER[\\"Latitude of 1st standard parallel\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8823]],PARAMETER[\\"Longitude of natural origin\\",0,ANGLEUNIT[\\"degree\\",0.0174532925199433],ID[\\"EPSG\\",8802]],PARAMETER[\\"False easting\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8806]],PARAMETER[\\"False northing\\",0,LENGTHUNIT[\\"metre\\",1],ID[\\"EPSG\\",8807]]],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]]]]" ; float y(y) ; y:axis = "Y" ; y:units = "m" ; @@ -593,7 +593,7 @@ def test_load_laea_grid(self): """ def test_load_datum_wkt(self): - expected = "wibble" + expected = "OSGB 1936" nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) with iris.FUTURE.context(datum_support=True): cube = iris.load_cube(nc_path) @@ -609,7 +609,7 @@ def test_no_load_datum_wkt(self): self.assertStringEqual(actual, "unknown") def test_load_datum_cf_var(self): - expected = "wibble" + expected = "OSGB 1936" nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) with iris.FUTURE.context(datum_support=True): cube = iris.load_cube(nc_path) @@ -628,7 +628,7 @@ def test_save_datum(self): expected = "OSGB 1936" # saved_crs = iris.coord_systems.GeogCS.from_datum(datum="OSGB36") saved_crs = iris.coord_systems.Mercator( - ellipsoid=iris.coord_systems.GeogCS.from_datum(datum="OSGB36") + ellipsoid=iris.coord_systems.GeogCS.from_datum("OSGB36") ) base_cube = stock.realistic_3d() diff --git a/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py b/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py index 6b534b52b5..61b37fe477 100644 --- a/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py +++ b/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py @@ -769,7 +769,7 @@ def check_call(self, coord_name, coord_system, units, expected_units): self.assertEqual(result, expected_units) def test_geogcs_latitude(self): - crs = iris.coord_systems.GeogCS(60, 0) + crs = iris.coord_systems.GeogCS(60, 30) self.check_call( "latitude", coord_system=crs, @@ -778,7 +778,7 @@ def test_geogcs_latitude(self): ) def test_geogcs_longitude(self): - crs = iris.coord_systems.GeogCS(60, 0) + crs = iris.coord_systems.GeogCS(60, 30) self.check_call( "longitude", coord_system=crs, From 04e89409e2ce3709fbfb72182ed790acc8651f67 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Fri, 22 Apr 2022 11:50:37 +0100 Subject: [PATCH 08/15] transform_points test --- .../coord_systems/test_LambertConformal.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/iris/tests/unit/coord_systems/test_LambertConformal.py b/lib/iris/tests/unit/coord_systems/test_LambertConformal.py index 7ba89208b1..7a572e3554 100644 --- a/lib/iris/tests/unit/coord_systems/test_LambertConformal.py +++ b/lib/iris/tests/unit/coord_systems/test_LambertConformal.py @@ -9,7 +9,10 @@ # importing anything else. import iris.tests as tests # isort:skip -from iris.coord_systems import LambertConformal +import cartopy.crs as ccrs +import numpy as np + +from iris.coord_systems import GeogCS, LambertConformal class Test_init_defaults(tests.IrisTest): @@ -74,5 +77,43 @@ def test_optional_args_None(self): self._check_crs_defaults(crs) +class TestDatumTransformation(tests.IrisTest): + def setUp(self): + self.x_points = np.array([-1.5]) + self.y_points = np.array([50.5]) + + self.start_crs = ccrs.OSGB(False) + + def test_transform_points_datum(self): + + # Iris version + wgs84 = GeogCS.from_datum("WGS84") + iris_cs = LambertConformal( + central_lat=54, + central_lon=-4, + secant_latitudes=[52, 56], + ellipsoid=wgs84, + ) + iris_cs_as_cartopy = iris_cs.as_cartopy_crs() + + # Cartopy equivalent + cartopy_cs = ccrs.LambertConformal( + central_latitude=54, + central_longitude=-4, + standard_parallels=[52, 56], + globe=ccrs.Globe("WGS84"), + ) + + expected = cartopy_cs.transform_points( + self.start_crs, self.x_points, self.y_points + ) + + actual = iris_cs_as_cartopy.transform_points( + self.start_crs, self.x_points, self.y_points + ) + + self.assertArrayEqual(expected, actual) + + if __name__ == "__main__": tests.main() From 3bc522af88a39db072d2e4d2ab2470d39e36c44d Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Wed, 27 Apr 2022 16:00:45 +0100 Subject: [PATCH 09/15] Review fixes --- .../fileformats/_nc_load_rules/helpers.py | 36 +++++++++++-------- lib/iris/tests/integration/test_netcdf.py | 6 ++-- requirements/ci/py38.yml | 1 + 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index a50b7f2f0c..6fdecc532d 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -237,7 +237,10 @@ def build_cube_metadata(engine): ################################################################################ def _get_ellipsoid(cf_grid_var): - """Return the ellipsoid definition.""" + """ + Return a :class:`iris.coord_systems.GeogCS` using the relevant properties of + `cf_grid_var`. Returns None if no relevant properties are specified. + """ major = getattr(cf_grid_var, CF_ATTR_GRID_SEMI_MAJOR_AXIS, None) minor = getattr(cf_grid_var, CF_ATTR_GRID_SEMI_MINOR_AXIS, None) inverse_flattening = getattr( @@ -252,21 +255,26 @@ def _get_ellipsoid(cf_grid_var): if major is None and minor is None and inverse_flattening is None: major = getattr(cf_grid_var, CF_ATTR_GRID_EARTH_RADIUS, None) - if iris.FUTURE.datum_support: - datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) - # Check crs_wkt if no datum - if datum is None: - crs_wkt = getattr(cf_grid_var, CF_ATTR_GRID_CRS_WKT, None) - if crs_wkt is not None: - proj_crs = pyproj.crs.CRS.from_wkt(crs_wkt) - if proj_crs.datum is not None: - datum = proj_crs.datum.name - else: + datum = getattr(cf_grid_var, CF_ATTR_GRID_DATUM, None) + # Check crs_wkt if no datum + if datum is None: + crs_wkt = getattr(cf_grid_var, CF_ATTR_GRID_CRS_WKT, None) + if crs_wkt is not None: + proj_crs = pyproj.crs.CRS.from_wkt(crs_wkt) + if proj_crs.datum is not None: + datum = proj_crs.datum.name + + if datum == "unknown": datum = None - if isinstance(datum, str) and ( - datum.startswith("Unknown") or datum.startswith("unknown") - ): + if not iris.FUTURE.datum_support: + wmsg = ( + "Ignoring a datum in netCDF load for consistency with existing " + "behaviour. In a future version of Iris, this datum will be " + "applied. To apply the datum when loading, use the " + "iris.FUTURE.datum_support flag." + ) + warnings.warn(wmsg) datum = None if datum is not None: diff --git a/lib/iris/tests/integration/test_netcdf.py b/lib/iris/tests/integration/test_netcdf.py index eb53b6633c..8c913a1043 100644 --- a/lib/iris/tests/integration/test_netcdf.py +++ b/lib/iris/tests/integration/test_netcdf.py @@ -603,7 +603,8 @@ def test_load_datum_wkt(self): def test_no_load_datum_wkt(self): nc_path = tlc.cdl_to_nc(self.datum_wkt_cdl) - cube = iris.load_cube(nc_path) + with self.assertWarnsRegexp("iris.FUTURE.datum_support"): + cube = iris.load_cube(nc_path) test_crs = cube.coord("projection_y_coordinate").coord_system actual = str(test_crs.as_cartopy_crs().datum) self.assertStringEqual(actual, "unknown") @@ -619,7 +620,8 @@ def test_load_datum_cf_var(self): def test_no_load_datum_cf_var(self): nc_path = tlc.cdl_to_nc(self.datum_cf_var_cdl) - cube = iris.load_cube(nc_path) + with self.assertWarnsRegexp("iris.FUTURE.datum_support"): + cube = iris.load_cube(nc_path) test_crs = cube.coord("projection_y_coordinate").coord_system actual = str(test_crs.as_cartopy_crs().datum) self.assertStringEqual(actual, "unknown") diff --git a/requirements/ci/py38.yml b/requirements/ci/py38.yml index 91cd9d3f5f..4382db1f5e 100644 --- a/requirements/ci/py38.yml +++ b/requirements/ci/py38.yml @@ -18,6 +18,7 @@ dependencies: - netcdf4 - numpy >=1.19 - python-xxhash + - pyproj - scipy # Optional dependencies. From 353a5996354c2c26aae6acafaec3d79e9a0a388b Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Wed, 27 Apr 2022 17:03:15 +0100 Subject: [PATCH 10/15] In datums, unknown means None --- lib/iris/coord_systems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index 3b79825b48..9f462d727c 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -302,7 +302,7 @@ def __getattr__(self, name): return self._crs.ellipsoid.inverse_flattening if name == "datum": datum = self._crs.datum.name - if datum.startswith("unknown") or datum.startswith("Unknown"): + if datum == "unknown": return None return datum return getattr(super(), name) From dbbaab771e87bada60bb166c73b754f0856ad863 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 28 Apr 2022 09:39:26 +0100 Subject: [PATCH 11/15] Fix test --- lib/iris/fileformats/_nc_load_rules/helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 6fdecc532d..4d5a1ed62a 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -288,7 +288,11 @@ def _get_ellipsoid(cf_grid_var): ################################################################################ def build_coordinate_system(engine, cf_grid_var): """Create a coordinate system from the CF-netCDF grid mapping variable.""" - return _get_ellipsoid(cf_grid_var) + coord_system = _get_ellipsoid(cf_grid_var) + if coord_system is None: + raise ValueError("No ellipsoid specified") + else: + return coord_system ################################################################################ From 106fafdb48c13b0c7d699c8e269f5f36f2b76d4b Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 28 Apr 2022 11:05:46 +0100 Subject: [PATCH 12/15] Suppress FutureWarning in the tests that were failing due to it --- .../fileformats/_nc_load_rules/helpers.py | 2 +- .../nc_load_rules/actions/__init__.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 4d5a1ed62a..bf42f277b9 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -274,7 +274,7 @@ def _get_ellipsoid(cf_grid_var): "applied. To apply the datum when loading, use the " "iris.FUTURE.datum_support flag." ) - warnings.warn(wmsg) + warnings.warn(wmsg, FutureWarning) datum = None if datum is not None: diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py index 0d3ed932e8..f79aa494f3 100644 --- a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py +++ b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py @@ -11,6 +11,7 @@ import shutil import subprocess import tempfile +import warnings import iris.fileformats._nc_load_rules.engine from iris.fileformats.cf import CFReader @@ -95,10 +96,20 @@ def load_cube_from_cdl(self, cdl_string, cdl_path, nc_path): # Use 'patch' so it is restored after the test. self.patch("iris.fileformats.netcdf.DEBUG", self.debug) - # Call the main translation function to load a single cube. - # _load_cube establishes per-cube facts, activates rules and - # produces an actual cube. - cube = _load_cube(engine, cf, cf_var, nc_path) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message="Ignoring a datum in netCDF load for consistency with existing " + "behaviour. In a future version of Iris, this datum will be " + "applied. To apply the datum when loading, use the " + "iris.FUTURE.datum_support flag.", + category=FutureWarning, + module="iris.fileformats._nc_load_rules.helpers", + ) + # Call the main translation function to load a single cube. + # _load_cube establishes per-cube facts, activates rules and + # produces an actual cube. + cube = _load_cube(engine, cf, cf_var, nc_path) # Also Record, on the cubes, which hybrid coord elements were identified # by the rules operation. From d12f688bfb7008857331f3fa9ab7b022f47ca488 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 28 Apr 2022 12:04:08 +0100 Subject: [PATCH 13/15] Review fixes --- lib/iris/__init__.py | 13 ++--- lib/iris/coord_systems.py | 2 + .../fileformats/_nc_load_rules/helpers.py | 1 + lib/iris/tests/integration/test_Datums.py | 53 +++++++++++++++++++ lib/iris/tests/test_coordsystem.py | 2 +- .../coord_systems/test_LambertConformal.py | 43 +-------------- 6 files changed, 65 insertions(+), 49 deletions(-) create mode 100755 lib/iris/tests/integration/test_Datums.py diff --git a/lib/iris/__init__.py b/lib/iris/__init__.py index a03abfcdf7..7442ff1173 100644 --- a/lib/iris/__init__.py +++ b/lib/iris/__init__.py @@ -151,12 +151,14 @@ def __init__(self, datum_support=False): .. note:: iris.FUTURE.example_future_flag does not exist. It is provided - as an example because there are currently no flags in - iris.Future. + as an example. """ - # The flag 'example_future_flag' is provided as a future reference - # for the structure of this class. + # The flag 'example_future_flag' is provided as a reference for the + # structure of this class. + # + # Note that self.__dict__ is used explicitly due to the manner in which + # __setattr__ is overridden. # # self.__dict__['example_future_flag'] = example_future_flag self.__dict__["datum_support"] = datum_support @@ -211,8 +213,7 @@ def context(self, **kwargs): .. note:: iris.FUTURE.example_future_flag does not exist and is - provided only as an example since there are currently no - flags in Future. + provided only as an example. """ # Save the current context diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index 9f462d727c..1f755998b1 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -238,6 +238,7 @@ def _pretty_attrs(self): self.longitude_of_prime_meridian, ) ) + # An unknown crs datum will be treated as None if self.datum is not None and self.datum != "unknown": attrs.append( ( @@ -302,6 +303,7 @@ def __getattr__(self, name): return self._crs.ellipsoid.inverse_flattening if name == "datum": datum = self._crs.datum.name + # An unknown crs datum will be treated as None if datum == "unknown": return None return datum diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index bf42f277b9..65ab3d8b5b 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -264,6 +264,7 @@ def _get_ellipsoid(cf_grid_var): if proj_crs.datum is not None: datum = proj_crs.datum.name + # An unknown crs datum will be treated as None if datum == "unknown": datum = None diff --git a/lib/iris/tests/integration/test_Datums.py b/lib/iris/tests/integration/test_Datums.py new file mode 100755 index 0000000000..77b7f28249 --- /dev/null +++ b/lib/iris/tests/integration/test_Datums.py @@ -0,0 +1,53 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the LGPL license. +# See COPYING and COPYING.LESSER in the root of the repository for full +# licensing details. +"""Integration tests for :class:`iris.coord_systems` datum suppport.""" + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests # isort:skip + +import cartopy.crs as ccrs +import numpy as np + +from iris.coord_systems import GeogCS, LambertConformal + + +class TestDatumTransformation(tests.IrisTest): + def setUp(self): + self.x_points = np.array([-1.5]) + self.y_points = np.array([50.5]) + + self.start_crs = ccrs.OSGB(False) + + def test_transform_points_datum(self): + + # Iris version + wgs84 = GeogCS.from_datum("WGS84") + iris_cs = LambertConformal( + central_lat=54, + central_lon=-4, + secant_latitudes=[52, 56], + ellipsoid=wgs84, + ) + iris_cs_as_cartopy = iris_cs.as_cartopy_crs() + + # Cartopy equivalent + cartopy_cs = ccrs.LambertConformal( + central_latitude=54, + central_longitude=-4, + standard_parallels=[52, 56], + globe=ccrs.Globe("WGS84"), + ) + + expected = cartopy_cs.transform_points( + self.start_crs, self.x_points, self.y_points + ) + + actual = iris_cs_as_cartopy.transform_points( + self.start_crs, self.x_points, self.y_points + ) + + self.assertArrayEqual(expected, actual) diff --git a/lib/iris/tests/test_coordsystem.py b/lib/iris/tests/test_coordsystem.py index dacb3b15f8..046d76b15a 100644 --- a/lib/iris/tests/test_coordsystem.py +++ b/lib/iris/tests/test_coordsystem.py @@ -498,7 +498,7 @@ def test_south_cutoff(self): class Test_Datums(tests.IrisTest): def test_default_none(self): - cs = GeogCS(6543210, 6500000) + cs = GeogCS(6543210, 6500000) # Arbitrary radii cartopy_crs = cs.as_cartopy_crs() self.assertStringEqual(cartopy_crs.datum.name, "unknown") diff --git a/lib/iris/tests/unit/coord_systems/test_LambertConformal.py b/lib/iris/tests/unit/coord_systems/test_LambertConformal.py index 7a572e3554..7ba89208b1 100644 --- a/lib/iris/tests/unit/coord_systems/test_LambertConformal.py +++ b/lib/iris/tests/unit/coord_systems/test_LambertConformal.py @@ -9,10 +9,7 @@ # importing anything else. import iris.tests as tests # isort:skip -import cartopy.crs as ccrs -import numpy as np - -from iris.coord_systems import GeogCS, LambertConformal +from iris.coord_systems import LambertConformal class Test_init_defaults(tests.IrisTest): @@ -77,43 +74,5 @@ def test_optional_args_None(self): self._check_crs_defaults(crs) -class TestDatumTransformation(tests.IrisTest): - def setUp(self): - self.x_points = np.array([-1.5]) - self.y_points = np.array([50.5]) - - self.start_crs = ccrs.OSGB(False) - - def test_transform_points_datum(self): - - # Iris version - wgs84 = GeogCS.from_datum("WGS84") - iris_cs = LambertConformal( - central_lat=54, - central_lon=-4, - secant_latitudes=[52, 56], - ellipsoid=wgs84, - ) - iris_cs_as_cartopy = iris_cs.as_cartopy_crs() - - # Cartopy equivalent - cartopy_cs = ccrs.LambertConformal( - central_latitude=54, - central_longitude=-4, - standard_parallels=[52, 56], - globe=ccrs.Globe("WGS84"), - ) - - expected = cartopy_cs.transform_points( - self.start_crs, self.x_points, self.y_points - ) - - actual = iris_cs_as_cartopy.transform_points( - self.start_crs, self.x_points, self.y_points - ) - - self.assertArrayEqual(expected, actual) - - if __name__ == "__main__": tests.main() From 317c5ad7a53497c154191da82544ab6b086450a8 Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 28 Apr 2022 15:02:50 +0100 Subject: [PATCH 14/15] What's new --- docs/src/whatsnew/latest.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 1c64610f89..fcb0d05835 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -37,7 +37,7 @@ This document explains the changes made to Iris for this release :pull:`4589`) #. `@wjbenfold`_ added support for ``false_easting`` and ``false_northing`` to - :class:`~iris.coord_system.Mercator`. (:issue:`3107`, :pull:`4524`) + :class:`~iris.coord_systems.Mercator`. (:issue:`3107`, :pull:`4524`) #. `@rcomer`_ and `@wjbenfold`_ (reviewer) implemented lazy aggregation for the :obj:`iris.analysis.PERCENTILE` aggregator. (:pull:`3901`) @@ -47,7 +47,13 @@ This document explains the changes made to Iris for this release #. `@wjbenfold`_ added support for CF-compliant treatment of ``standard_parallel`` and ``scale_factor_at_projection_origin`` to - :class:`~iris.coord_system.Mercator`. (:issue:`3844`, :pull:`4609`) + :class:`~iris.coord_systems.Mercator`. (:issue:`3844`, :pull:`4609`) + +#. `@wjbenfold`_ added support datums associated with coordinate systems (e.g. + :class:`~iris.coord_systems.GeogCS` other subclasses of + :class:`~iris.coord_systems.CoordSystem`). Loading of datum information from + a netCDF file only happens when the :obj:`iris.FUTURE.datum_support` flag is + set. (:issue:`4619`, :pull:`4704`) 🐛 Bugs Fixed From 6d1cbe83259bd243069ab00cf11a17d09144f42a Mon Sep 17 00:00:00 2001 From: Will Benfold Date: Thu, 28 Apr 2022 16:16:45 +0100 Subject: [PATCH 15/15] Extra test result --- lib/iris/tests/results/coord_systems/Mercator.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 lib/iris/tests/results/coord_systems/Mercator.xml diff --git a/lib/iris/tests/results/coord_systems/Mercator.xml b/lib/iris/tests/results/coord_systems/Mercator.xml new file mode 100644 index 0000000000..4ea768b41e --- /dev/null +++ b/lib/iris/tests/results/coord_systems/Mercator.xml @@ -0,0 +1,2 @@ + +