Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ env:
# Conda packages to be installed.
CONDA_CACHE_PACKAGES: "nox pip"
# Git commit hash for iris test data.
IRIS_TEST_DATA_VERSION: "2.5"
IRIS_TEST_DATA_VERSION: "2.7"
# Base directory for the iris-test-data.
IRIS_TEST_DATA_DIR: ${HOME}/iris-test-data

Expand Down
3 changes: 2 additions & 1 deletion docs/src/whatsnew/dev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ This document explains the changes made to Iris for this release
✨ Features
===========

#. N/A
#. `@wjbenfold`_ added support for ``false_easting`` and ``false_northing`` to
:class:`~iris.coord_system.Mercator`. (:issue:`3107`, :pull:`4524`)


🐛 Bugs Fixed
Expand Down
20 changes: 19 additions & 1 deletion lib/iris/coord_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,8 @@ def __init__(
longitude_of_projection_origin=None,
ellipsoid=None,
standard_parallel=None,
false_easting=None,
false_northing=None,
):
"""
Constructs a Mercator coord system.
Expand All @@ -1098,6 +1100,12 @@ def __init__(
* standard_parallel:
The latitude where the scale is 1. Defaults to 0.0 .

* 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.

"""
#: True longitude of planar origin in degrees.
self.longitude_of_projection_origin = _arg_default(
Expand All @@ -1110,12 +1118,20 @@ def __init__(
#: The latitude where the scale is 1.
self.standard_parallel = _arg_default(standard_parallel, 0)

#: X offset from the planar origin in metres.
self.false_easting = _arg_default(false_easting, 0)

#: Y offset from the planar origin in metres.
self.false_northing = _arg_default(false_northing, 0)

def __repr__(self):
res = (
"Mercator(longitude_of_projection_origin="
"{self.longitude_of_projection_origin!r}, "
"ellipsoid={self.ellipsoid!r}, "
"standard_parallel={self.standard_parallel!r})"
"standard_parallel={self.standard_parallel!r}, "
"false_easting={self.false_easting!r}, "
"false_northing={self.false_northing!r})"
)
return res.format(self=self)

Expand All @@ -1126,6 +1142,8 @@ def as_cartopy_crs(self):
central_longitude=self.longitude_of_projection_origin,
globe=globe,
latitude_true_scale=self.standard_parallel,
false_easting=self.false_easting,
false_northing=self.false_northing,
)

def as_cartopy_projection(self):
Expand Down
38 changes: 11 additions & 27 deletions lib/iris/fileformats/_nc_load_rules/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,13 @@ def build_mercator_coordinate_system(engine, cf_grid_var):
longitude_of_projection_origin = getattr(
cf_grid_var, CF_ATTR_GRID_LON_OF_PROJ_ORIGIN, None
)
standard_parallel = getattr(
cf_grid_var, CF_ATTR_GRID_STANDARD_PARALLEL, None
)
false_easting = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_EASTING, None)
false_northing = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_NORTHING, None)
# Iris currently only supports Mercator projections with specific
# values for false_easting, false_northing,
# scale_factor_at_projection_origin and standard_parallel. These are
# checked elsewhere.
# scale_factor_at_projection_origin. This is checked elsewhere.

ellipsoid = None
if (
Expand All @@ -454,7 +457,11 @@ def build_mercator_coordinate_system(engine, cf_grid_var):
ellipsoid = iris.coord_systems.GeogCS(major, minor, inverse_flattening)

cs = iris.coord_systems.Mercator(
longitude_of_projection_origin, ellipsoid=ellipsoid
longitude_of_projection_origin,
ellipsoid=ellipsoid,
standard_parallel=standard_parallel,
false_easting=false_easting,
false_northing=false_northing,
)

return cs
Expand Down Expand Up @@ -1244,27 +1251,10 @@ def has_supported_mercator_parameters(engine, cf_name):
is_valid = True
cf_grid_var = engine.cf_var.cf_group[cf_name]

false_easting = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_EASTING, None)
false_northing = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_NORTHING, None)
scale_factor_at_projection_origin = getattr(
cf_grid_var, CF_ATTR_GRID_SCALE_FACTOR_AT_PROJ_ORIGIN, None
)
standard_parallel = getattr(
cf_grid_var, CF_ATTR_GRID_STANDARD_PARALLEL, None
)

if false_easting is not None and false_easting != 0:
warnings.warn(
"False eastings other than 0.0 not yet supported "
"for Mercator projections"
)
is_valid = False
if false_northing is not None and false_northing != 0:
warnings.warn(
"False northings other than 0.0 not yet supported "
"for Mercator projections"
)
is_valid = False
if (
scale_factor_at_projection_origin is not None
and scale_factor_at_projection_origin != 1
Expand All @@ -1274,12 +1264,6 @@ def has_supported_mercator_parameters(engine, cf_name):
"Mercator projections"
)
is_valid = False
if standard_parallel is not None and standard_parallel != 0:
warnings.warn(
"Standard parallels other than 0.0 not yet "
"supported for Mercator projections"
)
is_valid = False

return is_valid

Expand Down
6 changes: 2 additions & 4 deletions lib/iris/fileformats/netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2553,10 +2553,8 @@ def add_ellipsoid(ellipsoid):
cf_var_grid.longitude_of_projection_origin = (
cs.longitude_of_projection_origin
)
# The Mercator class has implicit defaults for certain
# parameters
cf_var_grid.false_easting = 0.0
cf_var_grid.false_northing = 0.0
cf_var_grid.false_easting = cs.false_easting
cf_var_grid.false_northing = cs.false_northing
cf_var_grid.scale_factor_at_projection_origin = 1.0

# lcc
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/tests/results/coord_systems/Mercator.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<?xml version="1.0" ?>
<mercator ellipsoid="GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909)" longitude_of_projection_origin="90.0" standard_parallel="0.0"/>
<mercator ellipsoid="GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909)" false_easting="0.0" false_northing="0.0" longitude_of_projection_origin="90.0" standard_parallel="0.0"/>
8 changes: 4 additions & 4 deletions lib/iris/tests/results/netcdf/netcdf_merc.cml
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@
45.5158, 45.9993]]" shape="(192, 192)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/>
</coord>
<coord datadims="[1]">
<dimCoord id="c20d9238" points="[-5.16102e+06, -5.10719e+06, -5.05336e+06, ...,
<dimCoord id="970d5aa6" points="[-5.16102e+06, -5.10719e+06, -5.05336e+06, ...,
5.01299e+06, 5.06682e+06, 5.12065e+06]" shape="(192,)" standard_name="projection_x_coordinate" units="Unit('m')" value_type="float32" var_name="x">
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
<mercator ellipsoid="GeogCS(6378169.0)" false_easting="0.0" false_northing="0.0" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
</dimCoord>
</coord>
<coord datadims="[0]">
<dimCoord id="745a4735" points="[5.16101e+06, 5.10718e+06, 5.05335e+06, ...,
<dimCoord id="072b8f17" points="[5.16101e+06, 5.10718e+06, 5.05335e+06, ...,
-5.01294e+06, -5.06678e+06, -5.12061e+06]" shape="(192,)" standard_name="projection_y_coordinate" units="Unit('m')" value_type="float32" var_name="y">
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
<mercator ellipsoid="GeogCS(6378169.0)" false_easting="0.0" false_northing="0.0" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
</dimCoord>
</coord>
<coord>
Expand Down
33 changes: 33 additions & 0 deletions lib/iris/tests/results/netcdf/netcdf_merc_false.cml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" ?>
<cubes xmlns="urn:x-iris:cubeml-0.2">
<cube dtype="float32" long_name="Sea Level Pressure" standard_name="air_pressure_at_sea_level" units="Pa" var_name="psl">
<attributes>
<attribute name="Conventions" value="CF-1.7"/>
</attributes>
<coords>
<coord datadims="[2]">
<dimCoord id="7979c65f" long_name="x-coordinate in Cartesian system" points="[-5950000.0, -5925000.0, -5900000.0, -5875000.0,
-5850000.0, -5825000.0, -5800000.0, -5775000.0,
-5750000.0, -5725000.0]" shape="(10,)" standard_name="projection_x_coordinate" units="Unit('m')" value_type="float64" var_name="x">
<mercator ellipsoid="GeogCS(6371229.0)" false_easting="-12500.0" false_northing="-12500.0" longitude_of_projection_origin="12.0" standard_parallel="-2.0"/>
</dimCoord>
</coord>
<coord datadims="[1]">
<dimCoord id="f5c6807e" long_name="y-coordinate in Cartesian system" points="[-6200000.0, -6175000.0, -6150000.0, -6125000.0,
-6100000.0, -6075000.0, -6050000.0, -6025000.0,
-6000000.0, -5975000.0]" shape="(10,)" standard_name="projection_y_coordinate" units="Unit('m')" value_type="float64" var_name="y">
<mercator ellipsoid="GeogCS(6371229.0)" false_easting="-12500.0" false_northing="-12500.0" longitude_of_projection_origin="12.0" standard_parallel="-2.0"/>
</dimCoord>
</coord>
<coord datadims="[0]">
<dimCoord bounds="[[10410.54, 10440.92]]" id="74a96651" long_name="time" points="[10425.73]" shape="(1,)" standard_name="time" units="Unit('days since 1949-12-01 00:00:00', calendar='proleptic_gregorian')" value_type="float64" var_name="time"/>
</coord>
</coords>
<cellMethods>
<cellMethod method="mean">
<coord name="time"/>
</cellMethod>
</cellMethods>
<data checksum="0xdf56f817" dtype="float32" mask_checksum="no-masked-elements" shape="(1, 10, 10)"/>
</cube>
</cubes>
10 changes: 10 additions & 0 deletions lib/iris/tests/test_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,16 @@ def test_load_merc_grid(self):
)
self.assertCML(cube, ("netcdf", "netcdf_merc.cml"))

def test_load_merc_false_en_grid(self):
# Test loading a single CF-netCDF file with a Mercator grid_mapping that
# includes false easting and northing
cube = iris.load_cube(
tests.get_data_path(
("NetCDF", "mercator", "false_east_north_merc.nc")
)
)
self.assertCML(cube, ("netcdf", "netcdf_merc_false.cml"))

def test_load_stereographic_grid(self):
# Test loading a single CF-netCDF file with a stereographic
# grid_mapping.
Expand Down
29 changes: 26 additions & 3 deletions lib/iris/tests/unit/coord_systems/test_Mercator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def test_repr(self):
"Mercator(longitude_of_projection_origin=90.0, "
"ellipsoid=GeogCS(semi_major_axis=6377563.396, "
"semi_minor_axis=6356256.909), "
"standard_parallel=0.0)"
"standard_parallel=0.0, "
"false_easting=0.0, false_northing=0.0)"
)
self.assertEqual(expected, repr(self.tm))

Expand All @@ -38,16 +39,23 @@ class Test_init_defaults(tests.IrisTest):
def test_set_optional_args(self):
# Check that setting the optional (non-ellipse) args works.
crs = Mercator(
longitude_of_projection_origin=27, standard_parallel=157.4
longitude_of_projection_origin=27,
standard_parallel=157.4,
false_easting=13,
false_northing=12,
)
self.assertEqualAndKind(crs.longitude_of_projection_origin, 27.0)
self.assertEqualAndKind(crs.standard_parallel, 157.4)
self.assertEqualAndKind(crs.false_easting, 13.0)
self.assertEqualAndKind(crs.false_northing, 12.0)

def _check_crs_defaults(self, crs):
# Check for property defaults when no kwargs options were set.
# NOTE: except ellipsoid, which is done elsewhere.
self.assertEqualAndKind(crs.longitude_of_projection_origin, 0.0)
self.assertEqualAndKind(crs.standard_parallel, 0.0)
self.assertEqualAndKind(crs.false_easting, 0.0)
self.assertEqualAndKind(crs.false_northing, 0.0)

def test_no_optional_args(self):
# Check expected defaults with no optional args.
Expand All @@ -57,7 +65,10 @@ def test_no_optional_args(self):
def test_optional_args_None(self):
# Check expected defaults with optional args=None.
crs = Mercator(
longitude_of_projection_origin=None, standard_parallel=None
longitude_of_projection_origin=None,
standard_parallel=None,
false_easting=None,
false_northing=None,
)
self._check_crs_defaults(crs)

Expand All @@ -77,6 +88,8 @@ def test_extra_kwargs(self):
# converted to a cartopy CRS.
longitude_of_projection_origin = 90.0
true_scale_lat = 14.0
false_easting = 13
false_northing = 12
ellipsoid = GeogCS(
semi_major_axis=6377563.396, semi_minor_axis=6356256.909
)
Expand All @@ -85,6 +98,8 @@ def test_extra_kwargs(self):
longitude_of_projection_origin,
ellipsoid=ellipsoid,
standard_parallel=true_scale_lat,
false_easting=false_easting,
false_northing=false_northing,
)

expected = ccrs.Mercator(
Expand All @@ -95,6 +110,8 @@ def test_extra_kwargs(self):
ellipse=None,
),
latitude_true_scale=true_scale_lat,
false_easting=false_easting,
false_northing=false_northing,
)

res = merc_cs.as_cartopy_crs()
Expand All @@ -113,6 +130,8 @@ def test_simple(self):
def test_extra_kwargs(self):
longitude_of_projection_origin = 90.0
true_scale_lat = 14.0
false_easting = 13
false_northing = 12
ellipsoid = GeogCS(
semi_major_axis=6377563.396, semi_minor_axis=6356256.909
)
Expand All @@ -121,6 +140,8 @@ def test_extra_kwargs(self):
longitude_of_projection_origin,
ellipsoid=ellipsoid,
standard_parallel=true_scale_lat,
false_easting=false_easting,
false_northing=false_northing,
)

expected = ccrs.Mercator(
Expand All @@ -131,6 +152,8 @@ def test_extra_kwargs(self):
ellipse=None,
),
latitude_true_scale=true_scale_lat,
false_easting=false_easting,
false_northing=false_northing,
)

res = merc_cs.as_cartopy_projection()
Expand Down
Loading