Skip to content

Commit 5fa15f1

Browse files
DPeterKpelson
authored andcommitted
Full-featured Mercator coord system (#3041)
Add ``standard_parallel`` to the Mercator Coord System
1 parent 93b11cf commit 5fa15f1

6 files changed

Lines changed: 129 additions & 68 deletions

File tree

docs/iris/src/whatsnew/2.1.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Iris 2.1 Features
5757
* Iris can now represent data on the Albers Equal Area Projection,
5858
and the NetCDF loader and saver were updated to handle this.
5959
https://github.com/SciTools/iris/issues/2943
60+
* The :class:`~iris.coord_systems.Mercator` projection has been updated to accept
61+
the ``standard_parallel`` keyword argument.
6062

6163
Bugs Fixed
6264
==========

lib/iris/coord_systems.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,8 @@ class Mercator(CoordSystem):
831831

832832
grid_mapping_name = "mercator"
833833

834-
def __init__(self, longitude_of_projection_origin=0, ellipsoid=None):
834+
def __init__(self, longitude_of_projection_origin=0.0, ellipsoid=None,
835+
standard_parallel=0.0):
835836
"""
836837
Constructs a Mercator coord system.
837838
@@ -840,17 +841,23 @@ def __init__(self, longitude_of_projection_origin=0, ellipsoid=None):
840841
True longitude of planar origin in degrees.
841842
* ellipsoid
842843
:class:`GeogCS` defining the ellipsoid.
844+
* standard_parallel
845+
the latitude where the scale is 1. Defaults to 0 degrees.
843846
844847
"""
845-
846848
#: True longitude of planar origin in degrees.
847849
self.longitude_of_projection_origin = longitude_of_projection_origin
848850
#: Ellipsoid definition.
849851
self.ellipsoid = ellipsoid
852+
#: The latitude where the scale is 1 (defaults to 0 degrees).
853+
self.standard_parallel = standard_parallel
850854

851855
def __repr__(self):
852-
res = "Mercator(longitude_of_projection_origin={!r}, ellipsoid={!r})"
853-
return res.format(self.longitude_of_projection_origin, self.ellipsoid)
856+
res = ("Mercator(longitude_of_projection_origin="
857+
"{self.longitude_of_projection_origin!r}, "
858+
"ellipsoid={self.ellipsoid!r}, "
859+
"standard_parallel={self.standard_parallel!r})")
860+
return res.format(self=self)
854861

855862
def as_cartopy_crs(self):
856863
if self.ellipsoid is not None:
@@ -860,7 +867,8 @@ def as_cartopy_crs(self):
860867

861868
return ccrs.Mercator(
862869
central_longitude=self.longitude_of_projection_origin,
863-
globe=globe)
870+
globe=globe,
871+
latitude_true_scale=self.standard_parallel)
864872

865873
def as_cartopy_projection(self):
866874
return self.as_cartopy_crs()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<?xml version="1.0" ?>
2-
<mercator ellipsoid="GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909)" longitude_of_projection_origin="90.0"/>
2+
<mercator ellipsoid="GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909)" longitude_of_projection_origin="90.0" standard_parallel="0.0"/>

lib/iris/tests/results/netcdf/netcdf_merc.cml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@
5353
45.5158, 45.9993]]" shape="(192, 192)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="lon"/>
5454
</coord>
5555
<coord datadims="[1]">
56-
<dimCoord id="a2415886" points="[-5.16102e+06, -5.10719e+06, -5.05336e+06, ...,
56+
<dimCoord id="c20d9238" points="[-5.16102e+06, -5.10719e+06, -5.05336e+06, ...,
5757
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">
58-
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0"/>
58+
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
5959
</dimCoord>
6060
</coord>
6161
<coord datadims="[0]">
62-
<dimCoord id="41466ff9" points="[5.16101e+06, 5.10718e+06, 5.05335e+06, ...,
62+
<dimCoord id="745a4735" points="[5.16101e+06, 5.10718e+06, 5.05335e+06, ...,
6363
-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">
64-
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0"/>
64+
<mercator ellipsoid="GeogCS(6378169.0)" longitude_of_projection_origin="0.0" standard_parallel="0.0"/>
6565
</dimCoord>
6666
</coord>
6767
<coord>

lib/iris/tests/test_coordsystem.py

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,12 @@
3333
from iris.coord_systems import *
3434

3535

36-
37-
3836
def osgb():
3937
return TransverseMercator(latitude_of_projection_origin=49, longitude_of_central_meridian=-2,
4038
false_easting=-400, false_northing=100,
4139
scale_factor_at_central_meridian=0.9996012717,
4240
ellipsoid=GeogCS(6377563.396, 6356256.909))
4341

44-
def merc():
45-
return Mercator(longitude_of_projection_origin=90.0,
46-
ellipsoid=GeogCS(6377563.396, 6356256.909))
4742

4843
def stereo():
4944
return Stereographic(central_lat=-90, central_lon=-45,
@@ -391,58 +386,5 @@ def test_south_cutoff(self):
391386
self.assertEqual(ccrs.cutoff, 30)
392387

393388

394-
class Test_Mercator_construction(tests.IrisTest):
395-
def test_merc(self):
396-
tm = merc()
397-
self.assertXMLElement(tm, ("coord_systems", "Mercator.xml"))
398-
399-
400-
class Test_Mercator_repr(tests.IrisTest):
401-
def test_merc(self):
402-
tm = merc()
403-
expected = "Mercator(longitude_of_projection_origin=90.0, "\
404-
"ellipsoid=GeogCS(semi_major_axis=6377563.396, "\
405-
"semi_minor_axis=6356256.909))"
406-
self.assertEqual(expected, repr(tm))
407-
408-
409-
class Test_Mercator_as_cartopy_crs(tests.IrisTest):
410-
def test_as_cartopy_crs(self):
411-
longitude_of_projection_origin = 90.0
412-
ellipsoid = GeogCS(semi_major_axis=6377563.396,
413-
semi_minor_axis=6356256.909)
414-
415-
merc_cs = Mercator(
416-
longitude_of_projection_origin,
417-
ellipsoid=ellipsoid)
418-
419-
expected = ccrs.Mercator(
420-
central_longitude=longitude_of_projection_origin,
421-
globe=ccrs.Globe(semimajor_axis=6377563.396,
422-
semiminor_axis=6356256.909, ellipse=None))
423-
424-
res = merc_cs.as_cartopy_crs()
425-
self.assertEqual(res, expected)
426-
427-
428-
class Test_Mercator_as_cartopy_projection(tests.IrisTest):
429-
def test_as_cartopy_projection(self):
430-
longitude_of_projection_origin = 90.0
431-
ellipsoid = GeogCS(semi_major_axis=6377563.396,
432-
semi_minor_axis=6356256.909)
433-
434-
merc_cs = Mercator(
435-
longitude_of_projection_origin,
436-
ellipsoid=ellipsoid)
437-
438-
expected = ccrs.Mercator(
439-
central_longitude=longitude_of_projection_origin,
440-
globe=ccrs.Globe(semimajor_axis=6377563.396,
441-
semiminor_axis=6356256.909, ellipse=None))
442-
443-
res = merc_cs.as_cartopy_projection()
444-
self.assertEqual(res, expected)
445-
446-
447389
if __name__ == "__main__":
448390
tests.main()
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# (C) British Crown Copyright 2018, Met Office
2+
#
3+
# This file is part of Iris.
4+
#
5+
# Iris is free software: you can redistribute it and/or modify it under
6+
# the terms of the GNU Lesser General Public License as published by the
7+
# Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# Iris is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU Lesser General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Lesser General Public License
16+
# along with Iris. If not, see <http://www.gnu.org/licenses/>.
17+
"""Unit tests for the :class:`iris.coord_systems.Mercator` class."""
18+
19+
from __future__ import (absolute_import, division, print_function)
20+
from six.moves import (filter, input, map, range, zip) # noqa
21+
22+
# Import iris.tests first so that some things can be initialised before
23+
# importing anything else.
24+
import iris.tests as tests
25+
26+
import cartopy.crs as ccrs
27+
from iris.coord_systems import GeogCS, Mercator
28+
29+
30+
class Test_Mercator__basics(tests.IrisTest):
31+
def setUp(self):
32+
self.tm = Mercator(longitude_of_projection_origin=90.0,
33+
ellipsoid=GeogCS(6377563.396, 6356256.909))
34+
35+
def test_construction(self):
36+
self.assertXMLElement(self.tm, ("coord_systems", "Mercator.xml"))
37+
38+
def test_repr(self):
39+
expected = ("Mercator(longitude_of_projection_origin=90.0, "
40+
"ellipsoid=GeogCS(semi_major_axis=6377563.396, "
41+
"semi_minor_axis=6356256.909), "
42+
"standard_parallel=0.0)")
43+
self.assertEqual(expected, repr(self.tm))
44+
45+
46+
class Test_Mercator__as_cartopy_crs(tests.IrisTest):
47+
def test_simple(self):
48+
# Check that a projection set up with all the defaults is correctly
49+
# converted to a cartopy CRS.
50+
merc_cs = Mercator()
51+
res = merc_cs.as_cartopy_crs()
52+
expected = ccrs.Mercator(globe=ccrs.Globe())
53+
self.assertEqual(res, expected)
54+
55+
def test_extra_kwargs(self):
56+
# Check that a projection with non-default values is correctly
57+
# converted to a cartopy CRS.
58+
longitude_of_projection_origin = 90.0
59+
true_scale_lat = 14.0
60+
ellipsoid = GeogCS(semi_major_axis=6377563.396,
61+
semi_minor_axis=6356256.909)
62+
63+
merc_cs = Mercator(
64+
longitude_of_projection_origin,
65+
ellipsoid=ellipsoid,
66+
standard_parallel=true_scale_lat)
67+
68+
expected = ccrs.Mercator(
69+
central_longitude=longitude_of_projection_origin,
70+
globe=ccrs.Globe(semimajor_axis=6377563.396,
71+
semiminor_axis=6356256.909, ellipse=None),
72+
latitude_true_scale=true_scale_lat)
73+
74+
res = merc_cs.as_cartopy_crs()
75+
self.assertEqual(res, expected)
76+
77+
78+
class Test_as_cartopy_projection(tests.IrisTest):
79+
def test_simple(self):
80+
# Check that a projection set up with all the defaults is correctly
81+
# converted to a cartopy projection.
82+
merc_cs = Mercator()
83+
res = merc_cs.as_cartopy_projection()
84+
expected = ccrs.Mercator(globe=ccrs.Globe())
85+
self.assertEqual(res, expected)
86+
87+
def test_extra_kwargs(self):
88+
longitude_of_projection_origin = 90.0
89+
true_scale_lat = 14.0
90+
ellipsoid = GeogCS(semi_major_axis=6377563.396,
91+
semi_minor_axis=6356256.909)
92+
93+
merc_cs = Mercator(
94+
longitude_of_projection_origin,
95+
ellipsoid=ellipsoid,
96+
standard_parallel=true_scale_lat)
97+
98+
expected = ccrs.Mercator(
99+
central_longitude=longitude_of_projection_origin,
100+
globe=ccrs.Globe(semimajor_axis=6377563.396,
101+
semiminor_axis=6356256.909, ellipse=None),
102+
latitude_true_scale=true_scale_lat)
103+
104+
res = merc_cs.as_cartopy_projection()
105+
self.assertEqual(res, expected)
106+
107+
108+
if __name__ == "__main__":
109+
tests.main()

0 commit comments

Comments
 (0)