diff --git a/.travis.yml b/.travis.yml
index a563ed67c0..b7502bca30 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -105,10 +105,10 @@ install:
script:
- if [[ $TEST_TARGET == 'default' ]]; then
- python -m iris.tests.runner --default-tests --system-tests --print-failed-images;
+ python -m iris.tests.runner --default-tests --system-tests;
fi
- if [[ $TEST_TARGET == 'example' ]]; then
- python -m iris.tests.runner --example-tests --print-failed-images;
+ python -m iris.tests.runner --example-tests;
fi
# "make html" produces an error when run on Travis that does not
# affect any downstream functionality but causes the build to fail
diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py
index 8dcc0e136c..5529990dd0 100644
--- a/lib/iris/tests/__init__.py
+++ b/lib/iris/tests/__init__.py
@@ -139,7 +139,7 @@
plt.switch_backend('tkagg')
_DISPLAY_FIGURES = True
-_DEFAULT_IMAGE_TOLERANCE = 10.0
+_DEFAULT_IMAGE_TOLERANCE = 0.1
def main():
diff --git a/lib/iris/tests/integration/plot/__init__.py b/lib/iris/tests/integration/plot/__init__.py
deleted file mode 100644
index fb405492cf..0000000000
--- a/lib/iris/tests/integration/plot/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# (C) British Crown Copyright 2014 - 2015, Met Office
-#
-# This file is part of Iris.
-#
-# Iris is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Iris is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Iris. If not, see .
-"""Integration tests for the :mod:`iris.plot` package."""
-
-from __future__ import (absolute_import, division, print_function)
-from six.moves import (filter, input, map, range, zip) # noqa
diff --git a/lib/iris/tests/integration/plotting/__init__.py b/lib/iris/tests/integration/plotting/__init__.py
new file mode 100644
index 0000000000..91d6b76be0
--- /dev/null
+++ b/lib/iris/tests/integration/plotting/__init__.py
@@ -0,0 +1,295 @@
+# (C) British Crown Copyright 2014 - 2016, Met Office
+#
+# This file is part of Iris.
+#
+# Iris is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Iris is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Iris. If not, see .
+"""
+Integration tests for the packages :mod:`iris.plot` and :mod:`iris.quickplot`.
+
+"""
+
+from __future__ import (absolute_import, division, print_function)
+from six.moves import (filter, input, map, range, zip) # noqa
+import iris.tests as tests
+
+from functools import wraps
+import types
+import warnings
+
+import cf_units
+import matplotlib.pyplot as plt
+import numpy as np
+
+import iris.coords as coords
+from iris.exceptions import CoordinateNotFoundError
+import iris.tests.stock
+
+
+# Helper functions and classes.
+
+_LOAD_CUBE_ONCE_CACHE = {}
+
+
+def load_cube_once(filename, constraint):
+ """Same syntax as load_cube, but will only load a file once,
+
+ then cache the answer in a dictionary.
+
+ """
+ global _LOAD_CUBE_ONCE_CACHE
+ key = (filename, str(constraint))
+ cube = _LOAD_CUBE_ONCE_CACHE.get(key, None)
+
+ if cube is None:
+ cube = iris.load_cube(filename, constraint)
+ _LOAD_CUBE_ONCE_CACHE[key] = cube
+
+ return cube
+
+
+class LambdaStr(object):
+ """Provides a callable function which has a sensible __repr__."""
+ def __init__(self, repr, lambda_fn):
+ self.repr = repr
+ self.lambda_fn = lambda_fn
+
+ def __call__(self, *args, **kwargs):
+ return self.lambda_fn(*args, **kwargs)
+
+ def __repr__(self):
+ return self.repr
+
+
+def simple_cube():
+ cube = iris.tests.stock.realistic_4d()
+ cube = cube[:, 0, 0, :]
+ cube.coord('time').guess_bounds()
+ return cube
+
+
+def load_theta():
+ path = tests.get_data_path(('PP', 'COLPEX', 'theta_and_orog_subset.pp'))
+ theta = load_cube_once(path, 'air_potential_temperature')
+
+ # Improve the unit
+ theta.units = 'K'
+
+ return theta
+
+
+# Permutations classes.
+
+
+@tests.skip_data
+@tests.skip_plot
+class Test1dScatter(tests.GraphicsTest):
+
+ def test_coord_coord(self):
+ x = self.cube.coord('longitude')
+ y = self.cube.coord('altitude')
+ c = self.cube.data
+ self.draw_method(x, y, c=c, edgecolor='none')
+ self.check_graphic()
+
+ def test_coord_coord_map(self):
+ x = self.cube.coord('longitude')
+ y = self.cube.coord('latitude')
+ c = self.cube.data
+ self.draw_method(x, y, c=c, edgecolor='none')
+ plt.gca().coastlines()
+ self.check_graphic()
+
+ def test_coord_cube(self):
+ x = self.cube.coord('latitude')
+ y = self.cube
+ c = self.cube.coord('Travel Time').points
+ self.draw_method(x, y, c=c, edgecolor='none')
+ self.check_graphic()
+
+ def test_cube_coord(self):
+ x = self.cube
+ y = self.cube.coord('altitude')
+ c = self.cube.coord('Travel Time').points
+ self.draw_method(x, y, c=c, edgecolor='none')
+ self.check_graphic()
+
+ def test_cube_cube(self):
+ x = iris.load_cube(
+ tests.get_data_path(('NAME', 'NAMEIII_trajectory.txt')),
+ 'Rel Humidity')
+ y = self.cube
+ c = self.cube.coord('Travel Time').points
+ self.draw_method(x, y, c=c, edgecolor='none')
+ self.check_graphic()
+
+ def test_incompatible_objects(self):
+ # cubes/coordinates of different sizes cannot be plotted
+ x = self.cube
+ y = self.cube.coord('altitude')[:-1]
+ with self.assertRaises(ValueError):
+ self.draw_method(x, y)
+
+ def test_not_cube_or_coord(self):
+ # inputs must be cubes or coordinates
+ x = np.arange(self.cube.shape[0])
+ y = self.cube
+ with self.assertRaises(TypeError):
+ self.draw_method(x, y)
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestNDCoordinatesGiven(tests.GraphicsTest):
+ def setUp(self):
+ self.results = {'yx': ([self.contourf, ['grid_latitude',
+ 'grid_longitude']],
+ [self.contourf, ['grid_longitude',
+ 'grid_latitude']],
+ [self.contour, ['grid_latitude',
+ 'grid_longitude']],
+ [self.contour, ['grid_longitude',
+ 'grid_latitude']],
+ [self.pcolor, ['grid_latitude',
+ 'grid_longitude']],
+ [self.pcolor, ['grid_longitude',
+ 'grid_latitude']],
+ [self.pcolormesh, ['grid_latitude',
+ 'grid_longitude']],
+ [self.pcolormesh, ['grid_longitude',
+ 'grid_latitude']],
+ [self.points, ['grid_latitude',
+ 'grid_longitude']],
+ [self.points, ['grid_longitude',
+ 'grid_latitude']],),
+ 'zx': ([self.contourf, ['model_level_number',
+ 'grid_longitude']],
+ [self.contourf, ['grid_longitude',
+ 'model_level_number']],
+ [self.contour, ['model_level_number',
+ 'grid_longitude']],
+ [self.contour, ['grid_longitude',
+ 'model_level_number']],
+ [self.pcolor, ['model_level_number',
+ 'grid_longitude']],
+ [self.pcolor, ['grid_longitude',
+ 'model_level_number']],
+ [self.pcolormesh, ['model_level_number',
+ 'grid_longitude']],
+ [self.pcolormesh, ['grid_longitude',
+ 'model_level_number']],
+ [self.points, ['model_level_number',
+ 'grid_longitude']],
+ [self.points, ['grid_longitude',
+ 'model_level_number']],),
+ 'tx': ([self.contourf, ['time', 'grid_longitude']],
+ [self.contourf, ['grid_longitude', 'time']],
+ [self.contour, ['time', 'grid_longitude']],
+ [self.contour, ['grid_longitude', 'time']],
+ [self.pcolor, ['time', 'grid_longitude']],
+ [self.pcolor, ['grid_longitude', 'time']],
+ [self.pcolormesh, ['time', 'grid_longitude']],
+ [self.pcolormesh, ['grid_longitude', 'time']],
+ [self.points, ['time', 'grid_longitude']],
+ [self.points, ['grid_longitude', 'time']],),
+ 'x': ([self.plot, ['grid_longitude']],),
+ 'y': ([self.plot, ['grid_latitude']],),
+ }
+
+ def draw(self, draw_method, *args, **kwargs):
+ draw_fn = getattr(self, draw_method)
+ draw_fn(*args, **kwargs)
+ self.check_graphic()
+
+ def run_tests(self, cube, results):
+ for draw_method, coords in results:
+ draw_method(cube, coords=coords)
+ try:
+ self.check_graphic()
+ except AssertionError as err:
+ self.fail('Draw method %r failed with coords: %r. '
+ 'Assertion message: %s' % (draw_method, coords, err))
+
+ def run_tests_1d(self, cube, results):
+ # there is a different calling convention for 1d plots
+ for draw_method, coords in results:
+ draw_method(cube.coord(coords[0]), cube)
+ try:
+ self.check_graphic()
+ except AssertionError as err:
+ msg = 'Draw method {!r} failed with coords: {!r}. ' \
+ 'Assertion message: {!s}'
+ self.fail(msg.format(draw_method, coords, err))
+
+ def test_yx(self):
+ test_cube = self.cube[0, 0, :, :]
+ self.run_tests(test_cube, self.results['yx'])
+
+ def test_zx(self):
+ test_cube = self.cube[0, :15, 0, :]
+ self.run_tests(test_cube, self.results['zx'])
+
+ def test_tx(self):
+ test_cube = self.cube[:, 0, 0, :]
+ self.run_tests(test_cube, self.results['tx'])
+
+ def test_x(self):
+ test_cube = self.cube[0, 0, 0, :]
+ self.run_tests_1d(test_cube, self.results['x'])
+
+ def test_y(self):
+ test_cube = self.cube[0, 0, :, 0]
+ self.run_tests_1d(test_cube, self.results['y'])
+
+ def test_bad__duplicate_coord(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(ValueError):
+ self.draw('contourf', cube, coords=['grid_longitude',
+ 'grid_longitude'])
+
+ def test_bad__too_many_coords(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(ValueError):
+ self.draw('contourf', cube, coords=['grid_longitude',
+ 'grid_longitude',
+ 'grid_latitude'])
+
+ def test_bad__coord_name(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(CoordinateNotFoundError):
+ self.draw('contourf', cube, coords=['grid_longitude', 'wibble'])
+
+ def test_bad__no_coords_given(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(ValueError):
+ self.draw('contourf', cube, coords=[])
+
+ def test_bad__duplicate_coord_ref(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(ValueError):
+ self.draw('contourf', cube, coords=[cube.coord('grid_longitude'),
+ cube.coord('grid_longitude')])
+
+ def test_bad__too_many_coord_refs(self):
+ cube = self.cube[0, 0, :, :]
+ with self.assertRaises(ValueError):
+ self.draw('contourf', cube, coords=[cube.coord('grid_longitude'),
+ cube.coord('grid_longitude'),
+ cube.coord('grid_longitude')])
+
+ def test_non_cube_coordinate(self):
+ cube = self.cube[0, :, :, 0]
+ pts = -100 + np.arange(cube.shape[1]) * 13
+ x = coords.DimCoord(pts, standard_name='model_level_number',
+ attributes={'positive': 'up'})
+ self.draw('contourf', cube, coords=['grid_latitude', x])
diff --git a/lib/iris/tests/integration/plot/test_colorbar.py b/lib/iris/tests/integration/plotting/test_colorbar.py
similarity index 100%
rename from lib/iris/tests/integration/plot/test_colorbar.py
rename to lib/iris/tests/integration/plotting/test_colorbar.py
diff --git a/lib/iris/tests/integration/plot/test_netcdftime.py b/lib/iris/tests/integration/plotting/test_netcdftime.py
similarity index 100%
rename from lib/iris/tests/integration/plot/test_netcdftime.py
rename to lib/iris/tests/integration/plotting/test_netcdftime.py
diff --git a/lib/iris/tests/integration/plotting/test_plot.py b/lib/iris/tests/integration/plotting/test_plot.py
new file mode 100644
index 0000000000..18d8e88433
--- /dev/null
+++ b/lib/iris/tests/integration/plotting/test_plot.py
@@ -0,0 +1,155 @@
+# (C) British Crown Copyright 2016, Met Office
+#
+# This file is part of Iris.
+#
+# Iris is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Iris is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Iris. If not, see .
+"""Integration tests for the package :mod:`iris.plot`."""
+
+from __future__ import (absolute_import, division, print_function)
+from six.moves import (filter, input, map, range, zip) # noqa
+import six
+
+# import iris tests first so that some things can be initialised before
+# importing anything else
+import iris.tests as tests
+import iris
+
+import iris.tests.integration.plotting as plotting
+
+# Run tests in no graphics mode if matplotlib is not available.
+if tests.MPL_AVAILABLE:
+ import matplotlib.pyplot as plt
+ from iris.plot import (plot, contour, contourf, pcolor, pcolormesh,
+ scatter, points, orography_at_points)
+
+
+@tests.skip_plot
+class TestSimple(tests.GraphicsTest):
+ def test_points(self):
+ cube = plotting.simple_cube()
+ contourf(cube)
+ self.check_graphic()
+
+ def test_bounds(self):
+ cube = plotting.simple_cube()
+ pcolor(cube)
+ self.check_graphic()
+
+
+@tests.skip_plot
+class TestMissingCoord(tests.GraphicsTest):
+ def _check(self, cube):
+ contourf(cube)
+ self.check_graphic()
+ pcolor(cube)
+ self.check_graphic()
+
+ def test_no_u(self):
+ cube = plotting.simple_cube()
+ cube.remove_coord('grid_longitude')
+ self._check(cube)
+
+ def test_no_v(self):
+ cube = plotting.simple_cube()
+ cube.remove_coord('time')
+ self._check(cube)
+
+ def test_none(self):
+ cube = plotting.simple_cube()
+ cube.remove_coord('grid_longitude')
+ cube.remove_coord('time')
+ self._check(cube)
+
+
+@tests.skip_plot
+class TestHybridHeight(tests.GraphicsTest):
+ def setUp(self):
+ self.cube = iris.tests.stock.realistic_4d()[0, :15, 0, :]
+
+ def test_points(self):
+ contourf(self.cube, coords=['level_height', 'grid_longitude'])
+ self.check_graphic()
+ contourf(self.cube, coords=['altitude', 'grid_longitude'])
+ self.check_graphic()
+
+ def test_orography(self):
+ contourf(self.cube)
+ orography_at_points(self.cube)
+ self.check_graphic()
+
+ def test_orography_specific_coords(self):
+ coords = ['altitude', 'grid_longitude']
+ contourf(self.cube, coords=coords)
+ orography_at_points(self.cube, coords=coords)
+ self.check_graphic()
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestMissingCoordSystem(tests.GraphicsTest):
+ def test(self):
+ cube = tests.stock.simple_pp()
+ cube.coord("latitude").coord_system = None
+ cube.coord("longitude").coord_system = None
+ contourf(cube)
+ self.check_graphic()
+
+
+@tests.skip_data
+@tests.skip_plot
+class Test1dPlotScatter(plotting.Test1dScatter):
+
+ def setUp(self):
+ self.cube = iris.load_cube(
+ tests.get_data_path(('NAME', 'NAMEIII_trajectory.txt')),
+ 'Temperature')
+ self.draw_method = scatter
+ self.check_graphic()
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestPlotCoordinatesGiven(plotting.TestNDCoordinatesGiven):
+ def setUp(self):
+ filename = tests.get_data_path(('PP', 'COLPEX',
+ 'theta_and_orog_subset.pp'))
+ self.cube = plotting.load_cube_once(filename,
+ 'air_potential_temperature')
+
+ self.draw_module = iris.plot
+ self.contourf = plotting.LambdaStr('iris.plot.contourf',
+ lambda cube, *args, **kwargs:
+ contourf(cube, *args, **kwargs))
+ self.contour = plotting.LambdaStr('iris.plot.contour',
+ lambda cube, *args, **kwargs:
+ contour(cube, *args, **kwargs))
+ self.pcolor = plotting.LambdaStr('iris.quickplot.pcolor',
+ lambda cube, *args, **kwargs:
+ pcolor(cube, *args, **kwargs))
+ self.pcolormesh = plotting.LambdaStr('iris.quickplot.pcolormesh',
+ lambda cube, *args, **kwargs:
+ pcolormesh(cube, *args, **kwargs))
+ self.points = plotting.LambdaStr('iris.plot.points',
+ lambda cube, *args, **kwargs:
+ points(cube, c=cube.data,
+ *args, **kwargs))
+ self.plot = plotting.LambdaStr('iris.plot.plot',
+ lambda cube, *args, **kwargs:
+ plot(cube, *args, **kwargs))
+
+ super(TestPlotCoordinatesGiven, self).setUp()
+
+
+if __name__ == "__main__":
+ tests.main()
diff --git a/lib/iris/tests/integration/plotting/test_quickplot.py b/lib/iris/tests/integration/plotting/test_quickplot.py
new file mode 100644
index 0000000000..357aac8162
--- /dev/null
+++ b/lib/iris/tests/integration/plotting/test_quickplot.py
@@ -0,0 +1,189 @@
+# (C) British Crown Copyright 2016, Met Office
+#
+# This file is part of Iris.
+#
+# Iris is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Iris is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Iris. If not, see .
+"""Integration tests for the package :mod:`iris.quickplot`."""
+
+from __future__ import (absolute_import, division, print_function)
+from six.moves import (filter, input, map, range, zip) # noqa
+import six
+
+# import iris tests first so that some things can be initialised before
+# importing anything else
+import iris.tests as tests
+import iris
+import iris.tests.integration.plotting as plotting
+
+# Run tests in no graphics mode if matplotlib is not available.
+if tests.MPL_AVAILABLE:
+ import matplotlib.pyplot as plt
+ from iris.plot import (plot, contour, contourf, pcolor, pcolormesh, points)
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestAttributePositive(tests.GraphicsTest):
+ def test_1d_positive_up(self):
+ path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
+ cube = iris.load_cube(path)
+ plot(cube.coord('depth'), cube[0, :, 60, 80])
+ self.check_graphic()
+
+ def test_1d_positive_down(self):
+ path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
+ cube = iris.load_cube(path)
+ plot(cube[0, :, 60, 80], cube.coord('depth'))
+ self.check_graphic()
+
+ def test_2d_positive_up(self):
+ path = tests.get_data_path(('NetCDF', 'testing',
+ 'small_theta_colpex.nc'))
+ cube = iris.load_cube(path)[0, :, 42, :]
+ pcolormesh(cube)
+ self.check_graphic()
+
+ def test_2d_positive_down(self):
+ path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
+ cube = iris.load_cube(path)[0, :, 42, :]
+ pcolormesh(cube)
+ self.check_graphic()
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestQuickplotCoordinatesGiven(plotting.TestNDCoordinatesGiven):
+ def setUp(self):
+ filename = tests.get_data_path(
+ ('PP', 'COLPEX', 'theta_and_orog_subset.pp'))
+ self.cube = plotting.load_cube_once(filename,
+ 'air_potential_temperature')
+
+ self.contourf = plotting.LambdaStr('iris.quickplot.contourf',
+ lambda cube, *args, **kwargs:
+ contourf(cube, *args, **kwargs))
+ self.contour = plotting.LambdaStr('iris.quickplot.contour',
+ lambda cube, *args, **kwargs:
+ contour(cube, *args, **kwargs))
+ self.pcolor = plotting.LambdaStr('iris.quickplot.pcolor',
+ lambda cube, *args, **kwargs:
+ pcolor(cube, *args, **kwargs))
+ self.pcolormesh = plotting.LambdaStr('iris.quickplot.pcolormesh',
+ lambda cube, *args, **kwargs:
+ pcolormesh(cube, *args, **kwargs))
+ self.points = plotting.LambdaStr('iris.quickplot.points',
+ lambda cube, *args, **kwargs:
+ points(cube, c=cube.data,
+ *args, **kwargs))
+ self.plot = plotting.LambdaStr('iris.quickplot.plot',
+ lambda cube, *args, **kwargs:
+ plot(cube, *args, **kwargs))
+
+ super(TestQuickplotCoordinatesGiven, self).setUp()
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestLabels(tests.GraphicsTest):
+ def setUp(self):
+ self.theta = plotting.load_theta()
+
+ def _slice(self, coords):
+ """Returns the first cube containing the requested coordinates."""
+ for cube in self.theta.slices(coords):
+ break
+ return cube
+
+ def _small(self):
+ # Use a restricted size so we can make out the detail.
+ cube = self._slice(['model_level_number', 'grid_longitude'])
+ return cube[:5, :5]
+
+ def test_contour(self):
+ contour(self._small())
+ self.check_graphic()
+
+ contour(self._small(),
+ coords=['model_level_number', 'grid_longitude'])
+ self.check_graphic()
+
+ def test_contourf(self):
+ contourf(self._small())
+ self.check_graphic()
+
+ contourf(self._small(),
+ coords=['model_level_number', 'grid_longitude'])
+ self.check_graphic()
+
+ contourf(self._small(),
+ coords=['grid_longitude', 'model_level_number'])
+ self.check_graphic()
+
+ def test_contourf_nameless(self):
+ cube = self._small()
+ cube.standard_name = None
+ contourf(cube,
+ coords=['grid_longitude', 'model_level_number'])
+ self.check_graphic()
+
+ def test_pcolor(self):
+ pcolor(self._small())
+ self.check_graphic()
+
+ def test_pcolormesh(self):
+ pcolormesh(self._small())
+ self.check_graphic()
+
+ def test_map(self):
+ cube = self._slice(['grid_latitude', 'grid_longitude'])
+ contour(cube)
+ self.check_graphic()
+
+ def test_add_roll(self):
+ # Check that the result of adding 360 to the data is almost identical.
+ cube = self._slice(['grid_latitude', 'grid_longitude'])
+ lon = cube.coord('grid_longitude')
+ lon.points = lon.points + 360
+ contour(cube)
+ self.check_graphic()
+
+ def test_alignment(self):
+ cube = self._small()
+ contourf(cube)
+ points(cube)
+ self.check_graphic()
+
+
+@tests.skip_data
+@tests.skip_plot
+class TestTimeReferenceUnitsLabels(tests.GraphicsTest):
+
+ def setUp(self):
+ path = tests.get_data_path(('PP', 'aPProt1', 'rotatedMHtimecube.pp'))
+ self.cube = iris.load_cube(path)[:, 0, 0]
+
+ def test_reference_time_units(self):
+ # units should not be displayed for a reference time
+ plot(self.cube.coord('time'), self.cube)
+ plt.gcf().autofmt_xdate()
+ self.check_graphic()
+
+ def test_not_reference_time_units(self):
+ # units should be displayed for other time coordinates
+ plot(self.cube.coord('forecast_period'), self.cube)
+ self.check_graphic()
+
+
+if __name__ == "__main__":
+ tests.main()
diff --git a/lib/iris/tests/integration/plotting/test_symbols.py b/lib/iris/tests/integration/plotting/test_symbols.py
new file mode 100644
index 0000000000..9eb1bc09dc
--- /dev/null
+++ b/lib/iris/tests/integration/plotting/test_symbols.py
@@ -0,0 +1,46 @@
+# (C) British Crown Copyright 2016, Met Office
+#
+# This file is part of Iris.
+#
+# Iris is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Iris is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Iris. If not, see .
+"""Integration tests for the package :mod:`iris.symbols`."""
+
+from __future__ import (absolute_import, division, print_function)
+from six.moves import (filter, input, map, range, zip) # noqa
+import six
+
+# import iris tests first so that some things can be initialised before
+# importing anything else
+import iris.tests as tests
+
+# Run tests in no graphics mode if matplotlib is not available.
+if tests.MPL_AVAILABLE:
+ import matplotlib.pyplot as plt
+ from iris.plot import symbols as iplt_symbols
+ import iris.symbols as symbols
+
+
+@tests.skip_plot
+class TestSymbols(tests.GraphicsTest):
+ def test_cloud_cover(self):
+ iplt_symbols(list(range(10)),
+ [0] * 10,
+ [symbols.CLOUD_COVER[i] for i in range(10)],
+ 0.375)
+ plt.axis('off')
+ self.check_graphic()
+
+
+if __name__ == "__main__":
+ tests.main()
diff --git a/lib/iris/tests/results/visual_tests/test_plot.TestHybridHeight.test_orography.0.png b/lib/iris/tests/results/visual_tests/test_plot.TestHybridHeight.test_orography.0.png
index 077d80f4e4..2932aaeae4 100644
Binary files a/lib/iris/tests/results/visual_tests/test_plot.TestHybridHeight.test_orography.0.png and b/lib/iris/tests/results/visual_tests/test_plot.TestHybridHeight.test_orography.0.png differ
diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py
index ec0c19aba5..2ba957c3ea 100644
--- a/lib/iris/tests/test_coding_standards.py
+++ b/lib/iris/tests/test_coding_standards.py
@@ -118,7 +118,6 @@ class StandardReportWithExclusions(pep8.StandardReport):
'*/iris/tests/test_pp_module.py',
'*/iris/tests/test_pp_stash.py',
'*/iris/tests/test_pp_to_cube.py',
- '*/iris/tests/test_quickplot.py',
'*/iris/tests/test_regrid.py',
'*/iris/tests/test_std_names.py',
'*/iris/tests/test_trajectory.py',
diff --git a/lib/iris/tests/test_plot.py b/lib/iris/tests/test_plot.py
deleted file mode 100644
index 026fc59acc..0000000000
--- a/lib/iris/tests/test_plot.py
+++ /dev/null
@@ -1,924 +0,0 @@
-# (C) British Crown Copyright 2010 - 2015, Met Office
-#
-# This file is part of Iris.
-#
-# Iris is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Iris is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Iris. If not, see .
-
-from __future__ import (absolute_import, division, print_function)
-from six.moves import (filter, input, map, range, zip) # noqa
-import six
-
-# import iris tests first so that some things can be initialised before
-# importing anything else
-import iris.tests as tests
-
-from functools import wraps
-import types
-import warnings
-
-import cf_units
-import numpy as np
-
-import iris
-import iris.coords as coords
-import iris.tests.stock
-
-# Run tests in no graphics mode if matplotlib is not available.
-if tests.MPL_AVAILABLE:
- import matplotlib.pyplot as plt
- import iris.plot as iplt
- import iris.quickplot as qplt
- import iris.symbols
-
-
-def simple_cube():
- cube = iris.tests.stock.realistic_4d()
- cube = cube[:, 0, 0, :]
- cube.coord('time').guess_bounds()
- return cube
-
-
-@tests.skip_plot
-class TestSimple(tests.GraphicsTest):
- def test_points(self):
- cube = simple_cube()
- qplt.contourf(cube)
- self.check_graphic()
-
- def test_bounds(self):
- cube = simple_cube()
- qplt.pcolor(cube)
- self.check_graphic()
-
-
-@tests.skip_plot
-class TestMissingCoord(tests.GraphicsTest):
- def _check(self, cube):
- qplt.contourf(cube)
- self.check_graphic()
-
- qplt.pcolor(cube)
- self.check_graphic()
-
- def test_no_u(self):
- cube = simple_cube()
- cube.remove_coord('grid_longitude')
- self._check(cube)
-
- def test_no_v(self):
- cube = simple_cube()
- cube.remove_coord('time')
- self._check(cube)
-
- def test_none(self):
- cube = simple_cube()
- cube.remove_coord('grid_longitude')
- cube.remove_coord('time')
- self._check(cube)
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestMissingCS(tests.GraphicsTest):
- @tests.skip_data
- def test_missing_cs(self):
- cube = tests.stock.simple_pp()
- cube.coord("latitude").coord_system = None
- cube.coord("longitude").coord_system = None
- qplt.contourf(cube)
- qplt.plt.gca().coastlines()
- self.check_graphic()
-
-
-@tests.skip_plot
-class TestHybridHeight(tests.GraphicsTest):
- def setUp(self):
- self.cube = iris.tests.stock.realistic_4d()[0, :15, 0, :]
-
- def _check(self, plt_method, test_altitude=True):
- plt_method(self.cube)
- self.check_graphic()
-
- plt_method(self.cube, coords=['level_height', 'grid_longitude'])
- self.check_graphic()
-
- plt_method(self.cube, coords=['grid_longitude', 'level_height'])
- self.check_graphic()
-
- if test_altitude:
- plt_method(self.cube, coords=['grid_longitude', 'altitude'])
- self.check_graphic()
-
- plt_method(self.cube, coords=['altitude', 'grid_longitude'])
- self.check_graphic()
-
- def test_points(self):
- self._check(qplt.contourf)
-
- def test_bounds(self):
- self._check(qplt.pcolor, test_altitude=False)
-
- def test_orography(self):
- qplt.contourf(self.cube)
- iplt.orography_at_points(self.cube)
- iplt.points(self.cube)
- self.check_graphic()
-
- coords = ['altitude', 'grid_longitude']
- qplt.contourf(self.cube, coords=coords)
- iplt.orography_at_points(self.cube, coords=coords)
- iplt.points(self.cube, coords=coords)
- self.check_graphic()
-
- # TODO: Test bounds once they are supported.
- with self.assertRaises(NotImplementedError):
- qplt.pcolor(self.cube)
- iplt.orography_at_bounds(self.cube)
- iplt.outline(self.cube)
- self.check_graphic()
-
-
-@tests.skip_plot
-class Test1dPlotMultiArgs(tests.GraphicsTest):
- # tests for iris.plot using multi-argument calling convention
-
- def setUp(self):
- self.cube1d = _load_4d_testcube()[0, :, 0, 0]
- self.draw_method = iplt.plot
-
- def test_cube(self):
- # just plot a cube against its dim coord
- self.draw_method(self.cube1d) # altitude vs temp
- self.check_graphic()
-
- def test_coord(self):
- # plot the altitude coordinate
- self.draw_method(self.cube1d.coord('altitude'))
- self.check_graphic()
-
- def test_coord_cube(self):
- # plot temperature against sigma
- self.draw_method(self.cube1d.coord('sigma'), self.cube1d)
- self.check_graphic()
-
- def test_cube_coord(self):
- # plot a vertical profile of temperature
- self.draw_method(self.cube1d, self.cube1d.coord('altitude'))
- self.check_graphic()
-
- def test_coord_coord(self):
- # plot two coordinates that are not mappable
- self.draw_method(self.cube1d.coord('sigma'),
- self.cube1d.coord('altitude'))
- self.check_graphic()
-
- def test_coord_coord_map(self):
- # plot lat-lon aux coordinates of a trajectory, which draws a map
- lon = iris.coords.AuxCoord([0, 5, 10, 15, 20, 25, 30, 35, 40, 45],
- standard_name='longitude',
- units='degrees_north')
- lat = iris.coords.AuxCoord([45, 55, 50, 60, 55, 65, 60, 70, 65, 75],
- standard_name='latitude',
- units='degrees_north')
- self.draw_method(lon, lat)
- plt.gca().coastlines()
- self.check_graphic()
-
- def test_cube_cube(self):
- # plot two phenomena against eachother, in this case just dummy data
- cube1 = self.cube1d.copy()
- cube2 = self.cube1d.copy()
- cube1.rename('some phenomenon')
- cube2.rename('some other phenomenon')
- cube1.units = cf_units.Unit('no_unit')
- cube2.units = cf_units.Unit('no_unit')
- cube1.data[:] = np.linspace(0, 1, 7)
- cube2.data[:] = np.exp(cube1.data)
- self.draw_method(cube1, cube2)
- self.check_graphic()
-
- def test_incompatible_objects(self):
- # incompatible objects (not the same length) should raise an error
- with self.assertRaises(ValueError):
- self.draw_method(self.cube1d.coord('time'), (self.cube1d))
-
- def test_multimidmensional(self):
- # multidimensional cubes are not allowed
- cube = _load_4d_testcube()[0, :, :, 0]
- with self.assertRaises(ValueError):
- self.draw_method(cube)
-
- def test_not_cube_or_coord(self):
- # inputs must be cubes or coordinates, otherwise an error should be
- # raised
- xdim = np.arange(self.cube1d.shape[0])
- with self.assertRaises(TypeError):
- self.draw_method(xdim, self.cube1d)
-
- def test_plot_old_coords_kwarg(self):
- # Coords used to be a valid kwarg to plot, but it was deprecated and
- # we are maintaining a reasonable exception, check that it is raised
- # here.
- with self.assertRaises(TypeError):
- self.draw_method(self.cube1d, coords=None)
-
-
-@tests.skip_plot
-class Test1dQuickplotPlotMultiArgs(Test1dPlotMultiArgs):
- # tests for iris.plot using multi-argument calling convention
-
- def setUp(self):
- self.cube1d = _load_4d_testcube()[0, :, 0, 0]
- self.draw_method = qplt.plot
-
-
-@tests.skip_data
-@tests.skip_plot
-class Test1dScatter(tests.GraphicsTest):
-
- def setUp(self):
- self.cube = iris.load_cube(
- tests.get_data_path(('NAME', 'NAMEIII_trajectory.txt')),
- 'Temperature')
- self.draw_method = iplt.scatter
-
- def test_coord_coord(self):
- x = self.cube.coord('longitude')
- y = self.cube.coord('altitude')
- c = self.cube.data
- self.draw_method(x, y, c=c, edgecolor='none')
- self.check_graphic()
-
- def test_coord_coord_map(self):
- x = self.cube.coord('longitude')
- y = self.cube.coord('latitude')
- c = self.cube.data
- self.draw_method(x, y, c=c, edgecolor='none')
- plt.gca().coastlines()
- self.check_graphic()
-
- def test_coord_cube(self):
- x = self.cube.coord('latitude')
- y = self.cube
- c = self.cube.coord('Travel Time').points
- self.draw_method(x, y, c=c, edgecolor='none')
- self.check_graphic()
-
- def test_cube_coord(self):
- x = self.cube
- y = self.cube.coord('altitude')
- c = self.cube.coord('Travel Time').points
- self.draw_method(x, y, c=c, edgecolor='none')
- self.check_graphic()
-
- def test_cube_cube(self):
- x = iris.load_cube(
- tests.get_data_path(('NAME', 'NAMEIII_trajectory.txt')),
- 'Rel Humidity')
- y = self.cube
- c = self.cube.coord('Travel Time').points
- self.draw_method(x, y, c=c, edgecolor='none')
- self.check_graphic()
-
- def test_incompatible_objects(self):
- # cubes/coordinates of different sizes cannot be plotted
- x = self.cube
- y = self.cube.coord('altitude')[:-1]
- with self.assertRaises(ValueError):
- self.draw_method(x, y)
-
- def test_multidimensional(self):
- # multidimensional cubes/coordinates are not allowed
- x = _load_4d_testcube()[0, :, :, 0]
- y = x.coord('model_level_number')
- with self.assertRaises(ValueError):
- self.draw_method(x, y)
-
- def test_not_cube_or_coord(self):
- # inputs must be cubes or coordinates
- x = np.arange(self.cube.shape[0])
- y = self.cube
- with self.assertRaises(TypeError):
- self.draw_method(x, y)
-
-
-@tests.skip_data
-@tests.skip_plot
-class Test1dQuickplotScatter(Test1dScatter):
-
- def setUp(self):
- self.cube = iris.load_cube(
- tests.get_data_path(('NAME', 'NAMEIII_trajectory.txt')),
- 'Temperature')
- self.draw_method = qplt.scatter
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestAttributePositive(tests.GraphicsTest):
- def test_1d_positive_up(self):
- path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
- cube = iris.load_cube(path)
- qplt.plot(cube.coord('depth'), cube[0, :, 60, 80])
- self.check_graphic()
-
- def test_1d_positive_down(self):
- path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
- cube = iris.load_cube(path)
- qplt.plot(cube[0, :, 60, 80], cube.coord('depth'))
- self.check_graphic()
-
- def test_2d_positive_up(self):
- path = tests.get_data_path(('NetCDF', 'testing',
- 'small_theta_colpex.nc'))
- cube = iris.load_cube(path)[0, :, 42, :]
- qplt.pcolormesh(cube)
- self.check_graphic()
-
- def test_2d_positive_down(self):
- path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc'))
- cube = iris.load_cube(path)[0, :, 42, :]
- qplt.pcolormesh(cube)
- self.check_graphic()
-
-
-# Caches _load_4d_testcube so subsequent calls are faster
-def cache(fn, cache={}):
- def inner(*args, **kwargs):
- key = fn.__name__
- if key not in cache:
- cache[key] = fn(*args, **kwargs)
- return cache[key]
- return inner
-
-
-@cache
-def _load_4d_testcube():
- # Load example 4d data (TZYX).
- test_cube = iris.tests.stock.realistic_4d()
- # Replace forecast_period coord with a multi-valued version.
- time_coord = test_cube.coord('time')
- n_times = len(time_coord.points)
- forecast_dims = test_cube.coord_dims(time_coord)
- test_cube.remove_coord('forecast_period')
- # Make up values (including bounds), to roughly match older testdata.
- point_values = np.linspace((1 + 1.0 / 6), 2.0, n_times)
- point_uppers = point_values + (point_values[1] - point_values[0])
- bound_values = np.column_stack([point_values, point_uppers])
- # NOTE: this must be a DimCoord
- # - an equivalent AuxCoord produces different plots.
- new_forecast_coord = iris.coords.DimCoord(
- points=point_values,
- bounds=bound_values,
- standard_name='forecast_period',
- units=cf_units.Unit('hours')
- )
- test_cube.add_aux_coord(new_forecast_coord, forecast_dims)
- # Heavily reduce dimensions for faster testing.
- # NOTE: this makes ZYX non-contiguous. Doesn't seem to matter for now.
- test_cube = test_cube[:, ::10, ::10, ::10]
- return test_cube
-
-
-@cache
-def _load_wind_no_bounds():
- # Load the COLPEX data => TZYX
- path = tests.get_data_path(('PP', 'COLPEX', 'small_eastward_wind.pp'))
- wind = iris.load_cube(path, 'x_wind')
-
- # Remove bounds from all coords that have them.
- wind.coord('grid_latitude').bounds = None
- wind.coord('grid_longitude').bounds = None
- wind.coord('level_height').bounds = None
- wind.coord('sigma').bounds = None
-
- return wind[:, :, :50, :50]
-
-
-def _time_series(src_cube):
- # Until we have plotting support for multiple axes on the same dimension,
- # remove the time coordinate and its axis.
- cube = src_cube.copy()
- cube.remove_coord('time')
- return cube
-
-
-def _date_series(src_cube):
- # Until we have plotting support for multiple axes on the same dimension,
- # remove the forecast_period coordinate and its axis.
- cube = src_cube.copy()
- cube.remove_coord('forecast_period')
- return cube
-
-
-@tests.skip_plot
-class SliceMixin(object):
- """Mixin class providing tests for each 2-dimensional permutation of axes.
-
- Requires self.draw_method to be the relevant plotting function,
- and self.results to be a dictionary containing the desired test results."""
-
- def test_yx(self):
- cube = self.wind[0, 0, :, :]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_zx(self):
- cube = self.wind[0, :, 0, :]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_tx(self):
- cube = _time_series(self.wind[:, 0, 0, :])
- self.draw_method(cube)
- self.check_graphic()
-
- def test_zy(self):
- cube = self.wind[0, :, :, 0]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_ty(self):
- cube = _time_series(self.wind[:, 0, :, 0])
- self.draw_method(cube)
- self.check_graphic()
-
- def test_tz(self):
- cube = _time_series(self.wind[:, :, 0, 0])
- self.draw_method(cube)
- self.check_graphic()
-
-
-@tests.skip_data
-class TestContour(tests.GraphicsTest, SliceMixin):
- """Test the iris.plot.contour routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = iplt.contour
-
-
-@tests.skip_data
-class TestContourf(tests.GraphicsTest, SliceMixin):
- """Test the iris.plot.contourf routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = iplt.contourf
-
-
-@tests.skip_data
-class TestPcolor(tests.GraphicsTest, SliceMixin):
- """Test the iris.plot.pcolor routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = iplt.pcolor
-
-
-@tests.skip_data
-class TestPcolormesh(tests.GraphicsTest, SliceMixin):
- """Test the iris.plot.pcolormesh routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = iplt.pcolormesh
-
-
-def check_warnings(method):
- """
- Decorator that adds a catch_warnings and filter to assert
- the method being decorated issues a UserWarning.
-
- """
- @wraps(method)
- def decorated_method(self, *args, **kwargs):
- # Force reset of iris.coords warnings registry to avoid suppression of
- # repeated warnings. warnings.resetwarnings() does not do this.
- if hasattr(coords, '__warningregistry__'):
- coords.__warningregistry__.clear()
-
- # Check that method raises warning.
- with warnings.catch_warnings():
- warnings.simplefilter("error")
- with self.assertRaises(UserWarning):
- return method(self, *args, **kwargs)
- return decorated_method
-
-
-def ignore_warnings(method):
- """
- Decorator that adds a catch_warnings and filter to suppress
- any warnings issues by the method being decorated.
-
- """
- @wraps(method)
- def decorated_method(self, *args, **kwargs):
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- return method(self, *args, **kwargs)
- return decorated_method
-
-
-class CheckForWarningsMetaclass(type):
- """
- Metaclass that adds a further test for each base class test
- that checks that each test raises a UserWarning. Each base
- class test is then overriden to ignore warnings in order to
- check the underlying functionality.
-
- """
- def __new__(cls, name, bases, local):
- def add_decorated_methods(attr_dict, target_dict, decorator):
- for key, value in attr_dict.items():
- if (isinstance(value, types.FunctionType) and
- key.startswith('test')):
- new_key = '_'.join((key, decorator.__name__))
- if new_key not in target_dict:
- wrapped = decorator(value)
- wrapped.__name__ = new_key
- target_dict[new_key] = wrapped
- else:
- raise RuntimeError('A attribute called {!r} '
- 'already exists.'.format(new_key))
-
- def override_with_decorated_methods(attr_dict, target_dict,
- decorator):
- for key, value in attr_dict.items():
- if (isinstance(value, types.FunctionType) and
- key.startswith('test')):
- target_dict[key] = decorator(value)
-
- # Add decorated versions of base methods
- # to check for warnings.
- for base in bases:
- add_decorated_methods(base.__dict__, local, check_warnings)
-
- # Override base methods to ignore warnings.
- for base in bases:
- override_with_decorated_methods(base.__dict__, local,
- ignore_warnings)
-
- return type.__new__(cls, name, bases, local)
-
-
-@tests.skip_data
-class TestPcolorNoBounds(six.with_metaclass(CheckForWarningsMetaclass,
- tests.GraphicsTest, SliceMixin)):
- """
- Test the iris.plot.pcolor routine on a cube with coordinates
- that have no bounds.
-
- """
-
- def setUp(self):
- self.wind = _load_wind_no_bounds()
- self.draw_method = iplt.pcolor
-
-
-@tests.skip_data
-class TestPcolormeshNoBounds(six.with_metaclass(CheckForWarningsMetaclass,
- tests.GraphicsTest,
- SliceMixin)):
- """
- Test the iris.plot.pcolormesh routine on a cube with coordinates
- that have no bounds.
-
- """
-
- def setUp(self):
- self.wind = _load_wind_no_bounds()
- self.draw_method = iplt.pcolormesh
-
-
-@tests.skip_plot
-class Slice1dMixin(object):
- """Mixin class providing tests for each 1-dimensional permutation of axes.
-
- Requires self.draw_method to be the relevant plotting function,
- and self.results to be a dictionary containing the desired test results."""
-
- def test_x(self):
- cube = self.wind[0, 0, 0, :]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_y(self):
- cube = self.wind[0, 0, :, 0]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_z(self):
- cube = self.wind[0, :, 0, 0]
- self.draw_method(cube)
- self.check_graphic()
-
- def test_t(self):
- cube = _time_series(self.wind[:, 0, 0, 0])
- self.draw_method(cube)
- self.check_graphic()
-
- def test_t_dates(self):
- cube = _date_series(self.wind[:, 0, 0, 0])
- self.draw_method(cube)
- plt.gcf().autofmt_xdate()
- plt.xlabel('Phenomenon time')
-
- self.check_graphic()
-
-
-@tests.skip_data
-class TestPlot(tests.GraphicsTest, Slice1dMixin):
- """Test the iris.plot.plot routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = iplt.plot
-
-
-@tests.skip_data
-class TestQuickplotPlot(tests.GraphicsTest, Slice1dMixin):
- """Test the iris.quickplot.plot routine."""
- def setUp(self):
- self.wind = _load_4d_testcube()
- self.draw_method = qplt.plot
-
-
-_load_cube_once_cache = {}
-
-
-def load_cube_once(filename, constraint):
- """Same syntax as load_cube, but will only load a file once,
-
- then cache the answer in a dictionary.
-
- """
- global _load_cube_once_cache
- key = (filename, str(constraint))
- cube = _load_cube_once_cache.get(key, None)
-
- if cube is None:
- cube = iris.load_cube(filename, constraint)
- _load_cube_once_cache[key] = cube
-
- return cube
-
-
-class LambdaStr(object):
- """Provides a callable function which has a sensible __repr__."""
- def __init__(self, repr, lambda_fn):
- self.repr = repr
- self.lambda_fn = lambda_fn
-
- def __call__(self, *args, **kwargs):
- return self.lambda_fn(*args, **kwargs)
-
- def __repr__(self):
- return self.repr
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestPlotCoordinatesGiven(tests.GraphicsTest):
- def setUp(self):
- filename = tests.get_data_path(('PP', 'COLPEX',
- 'theta_and_orog_subset.pp'))
- self.cube = load_cube_once(filename, 'air_potential_temperature')
-
- self.draw_module = iris.plot
- self.contourf = LambdaStr('iris.plot.contourf',
- lambda cube, *args, **kwargs:
- iris.plot.contourf(cube, *args, **kwargs))
- self.contour = LambdaStr('iris.plot.contour',
- lambda cube, *args, **kwargs:
- iris.plot.contour(cube, *args, **kwargs))
- self.points = LambdaStr('iris.plot.points',
- lambda cube, *args, **kwargs:
- iris.plot.points(cube, c=cube.data,
- *args, **kwargs))
- self.plot = LambdaStr('iris.plot.plot',
- lambda cube, *args, **kwargs:
- iris.plot.plot(cube, *args, **kwargs))
-
- self.results = {'yx': ([self.contourf, ['grid_latitude',
- 'grid_longitude']],
- [self.contourf, ['grid_longitude',
- 'grid_latitude']],
- [self.contour, ['grid_latitude',
- 'grid_longitude']],
- [self.contour, ['grid_longitude',
- 'grid_latitude']],
- [self.points, ['grid_latitude',
- 'grid_longitude']],
- [self.points, ['grid_longitude',
- 'grid_latitude']],),
- 'zx': ([self.contourf, ['model_level_number',
- 'grid_longitude']],
- [self.contourf, ['grid_longitude',
- 'model_level_number']],
- [self.contour, ['model_level_number',
- 'grid_longitude']],
- [self.contour, ['grid_longitude',
- 'model_level_number']],
- [self.points, ['model_level_number',
- 'grid_longitude']],
- [self.points, ['grid_longitude',
- 'model_level_number']],),
- 'tx': ([self.contourf, ['time', 'grid_longitude']],
- [self.contourf, ['grid_longitude', 'time']],
- [self.contour, ['time', 'grid_longitude']],
- [self.contour, ['grid_longitude', 'time']],
- [self.points, ['time', 'grid_longitude']],
- [self.points, ['grid_longitude', 'time']],),
- 'x': ([self.plot, ['grid_longitude']],),
- 'y': ([self.plot, ['grid_latitude']],)
- }
-
- def draw(self, draw_method, *args, **kwargs):
- draw_fn = getattr(self.draw_module, draw_method)
- draw_fn(*args, **kwargs)
- self.check_graphic()
-
- def run_tests(self, cube, results):
- for draw_method, coords in results:
- draw_method(cube, coords=coords)
- try:
- self.check_graphic()
- except AssertionError as err:
- self.fail('Draw method %r failed with coords: %r. '
- 'Assertion message: %s' % (draw_method, coords, err))
-
- def run_tests_1d(self, cube, results):
- # there is a different calling convention for 1d plots
- for draw_method, coords in results:
- draw_method(cube.coord(coords[0]), cube)
- try:
- self.check_graphic()
- except AssertionError as err:
- msg = 'Draw method {!r} failed with coords: {!r}. ' \
- 'Assertion message: {!s}'
- self.fail(msg.format(draw_method, coords, err))
-
- def test_yx(self):
- test_cube = self.cube[0, 0, :, :]
- self.run_tests(test_cube, self.results['yx'])
-
- def test_zx(self):
- test_cube = self.cube[0, :15, 0, :]
- self.run_tests(test_cube, self.results['zx'])
-
- def test_tx(self):
- test_cube = self.cube[:, 0, 0, :]
- self.run_tests(test_cube, self.results['tx'])
-
- def test_x(self):
- test_cube = self.cube[0, 0, 0, :]
- self.run_tests_1d(test_cube, self.results['x'])
-
- def test_y(self):
- test_cube = self.cube[0, 0, :, 0]
- self.run_tests_1d(test_cube, self.results['y'])
-
- def test_badcoords(self):
- cube = self.cube[0, 0, :, :]
- draw_fn = getattr(self.draw_module, 'contourf')
- self.assertRaises(ValueError, draw_fn, cube,
- coords=['grid_longitude', 'grid_longitude'])
- self.assertRaises(ValueError, draw_fn, cube,
- coords=['grid_longitude', 'grid_longitude',
- 'grid_latitude'])
- self.assertRaises(iris.exceptions.CoordinateNotFoundError, draw_fn,
- cube, coords=['grid_longitude', 'wibble'])
- self.assertRaises(ValueError, draw_fn, cube, coords=[])
- self.assertRaises(ValueError, draw_fn, cube,
- coords=[cube.coord('grid_longitude'),
- cube.coord('grid_longitude')])
- self.assertRaises(ValueError, draw_fn, cube,
- coords=[cube.coord('grid_longitude'),
- cube.coord('grid_longitude'),
- cube.coord('grid_longitude')])
-
- def test_non_cube_coordinate(self):
- cube = self.cube[0, :, :, 0]
- pts = -100 + np.arange(cube.shape[1]) * 13
- x = coords.DimCoord(pts, standard_name='model_level_number',
- attributes={'positive': 'up'})
- self.draw('contourf', cube, coords=['grid_latitude', x])
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestPlotDimAndAuxCoordsKwarg(tests.GraphicsTest):
- def setUp(self):
- filename = tests.get_data_path(('NetCDF', 'rotated', 'xy',
- 'rotPole_landAreaFraction.nc'))
- self.cube = iris.load_cube(filename)
-
- def test_default(self):
- iplt.contourf(self.cube)
- plt.gca().coastlines()
- self.check_graphic()
-
- def test_coords(self):
- # Pass in dimension coords.
- rlat = self.cube.coord('grid_latitude')
- rlon = self.cube.coord('grid_longitude')
- iplt.contourf(self.cube, coords=[rlon, rlat])
- plt.gca().coastlines()
- self.check_graphic()
- # Pass in auxiliary coords.
- lat = self.cube.coord('latitude')
- lon = self.cube.coord('longitude')
- iplt.contourf(self.cube, coords=[lon, lat])
- plt.gca().coastlines()
- self.check_graphic()
-
- def test_coord_names(self):
- # Pass in names of dimension coords.
- iplt.contourf(self.cube, coords=['grid_longitude', 'grid_latitude'])
- plt.gca().coastlines()
- self.check_graphic()
- # Pass in names of auxiliary coords.
- iplt.contourf(self.cube, coords=['longitude', 'latitude'])
- plt.gca().coastlines()
- self.check_graphic()
-
- def test_yx_order(self):
- # Do not attempt to draw coastlines as it is not a map.
- iplt.contourf(self.cube, coords=['grid_latitude', 'grid_longitude'])
- self.check_graphic()
- iplt.contourf(self.cube, coords=['latitude', 'longitude'])
- self.check_graphic()
-
-
-@tests.skip_plot
-class TestSymbols(tests.GraphicsTest):
- def test_cloud_cover(self):
- iplt.symbols(list(range(10)),
- [0] * 10,
- [iris.symbols.CLOUD_COVER[i] for i in range(10)],
- 0.375)
- iplt.plt.axis('off')
- self.check_graphic()
-
-
-@tests.skip_plot
-class TestPlottingExceptions(tests.IrisTest):
- def setUp(self):
- self.bounded_cube = tests.stock.lat_lon_cube()
- self.bounded_cube.coord("latitude").guess_bounds()
- self.bounded_cube.coord("longitude").guess_bounds()
-
- def test_boundmode_multidim(self):
- # Test exception translation.
- # We can't get contiguous bounded grids from multi-d coords.
- cube = self.bounded_cube
- cube.remove_coord("latitude")
- cube.add_aux_coord(coords.AuxCoord(points=cube.data,
- standard_name='latitude',
- units='degrees'), [0, 1])
- with self.assertRaises(ValueError):
- iplt.pcolormesh(cube, coords=['longitude', 'latitude'])
-
- def test_boundmode_4bounds(self):
- # Test exception translation.
- # We can only get contiguous bounded grids with 2 bounds per point.
- cube = self.bounded_cube
- lat = coords.AuxCoord.from_coord(cube.coord("latitude"))
- lat.bounds = np.array([lat.points, lat.points + 1,
- lat.points + 2, lat.points + 3]).transpose()
- cube.remove_coord("latitude")
- cube.add_aux_coord(lat, 0)
- with self.assertRaises(ValueError):
- iplt.pcolormesh(cube, coords=['longitude', 'latitude'])
-
- def test_different_coord_systems(self):
- cube = self.bounded_cube
- lat = cube.coord('latitude')
- lon = cube.coord('longitude')
- lat.coord_system = iris.coord_systems.GeogCS(7000000)
- lon.coord_system = iris.coord_systems.GeogCS(7000001)
- with self.assertRaises(ValueError):
- iplt.pcolormesh(cube, coords=['longitude', 'latitude'])
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestPlotOtherCoordSystems(tests.GraphicsTest):
- def test_plot_tmerc(self):
- filename = tests.get_data_path(('NetCDF', 'transverse_mercator',
- 'tmean_1910_1910.nc'))
- self.cube = iris.load_cube(filename)
- iplt.pcolormesh(self.cube[0])
- plt.gca().coastlines()
- self.check_graphic()
-
-
-if __name__ == "__main__":
- tests.main()
diff --git a/lib/iris/tests/test_quickplot.py b/lib/iris/tests/test_quickplot.py
deleted file mode 100644
index ef26e8b609..0000000000
--- a/lib/iris/tests/test_quickplot.py
+++ /dev/null
@@ -1,205 +0,0 @@
-# (C) British Crown Copyright 2010 - 2015, Met Office
-#
-# This file is part of Iris.
-#
-# Iris is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Iris is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Iris. If not, see .
-"""
-Tests the high-level plotting interface.
-
-"""
-
-from __future__ import (absolute_import, division, print_function)
-from six.moves import (filter, input, map, range, zip) # noqa
-
-# import iris tests first so that some things can be initialised before importing anything else
-import iris.tests as tests
-import iris.tests.test_plot as test_plot
-
-import iris
-
-# Run tests in no graphics mode if matplotlib is not available.
-if tests.MPL_AVAILABLE:
- import matplotlib.pyplot as plt
- import iris.plot as iplt
- import iris.quickplot as qplt
-
-
-# Caches _load_theta so subsequent calls are faster
-def cache(fn, cache={}):
- def inner(*args, **kwargs):
- key = "result"
- if not cache:
- cache[key] = fn(*args, **kwargs)
- return cache[key]
- return inner
-
-
-@cache
-def _load_theta():
- path = tests.get_data_path(('PP', 'COLPEX', 'theta_and_orog_subset.pp'))
- theta = iris.load_cube(path, 'air_potential_temperature')
-
- # Improve the unit
- theta.units = 'K'
-
- return theta
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestQuickplotCoordinatesGiven(test_plot.TestPlotCoordinatesGiven):
- def setUp(self):
- filename = tests.get_data_path(('PP', 'COLPEX', 'theta_and_orog_subset.pp'))
- self.cube = test_plot.load_cube_once(filename, 'air_potential_temperature')
-
- self.draw_module = iris.quickplot
- self.contourf = test_plot.LambdaStr('iris.quickplot.contourf', lambda cube, *args, **kwargs:
- iris.quickplot.contourf(cube, *args, **kwargs))
- self.contour = test_plot.LambdaStr('iris.quickplot.contour', lambda cube, *args, **kwargs:
- iris.quickplot.contour(cube, *args, **kwargs))
- self.points = test_plot.LambdaStr('iris.quickplot.points', lambda cube, *args, **kwargs:
- iris.quickplot.points(cube, c=cube.data, *args, **kwargs))
- self.plot = test_plot.LambdaStr('iris.quickplot.plot', lambda cube, *args, **kwargs:
- iris.quickplot.plot(cube, *args, **kwargs))
-
- self.results = {'yx': (
- [self.contourf, ['grid_latitude', 'grid_longitude']],
- [self.contourf, ['grid_longitude', 'grid_latitude']],
- [self.contour, ['grid_latitude', 'grid_longitude']],
- [self.contour, ['grid_longitude', 'grid_latitude']],
- [self.points, ['grid_latitude', 'grid_longitude']],
- [self.points, ['grid_longitude', 'grid_latitude']],
- ),
- 'zx': (
- [self.contourf, ['model_level_number', 'grid_longitude']],
- [self.contourf, ['grid_longitude', 'model_level_number']],
- [self.contour, ['model_level_number', 'grid_longitude']],
- [self.contour, ['grid_longitude', 'model_level_number']],
- [self.points, ['model_level_number', 'grid_longitude']],
- [self.points, ['grid_longitude', 'model_level_number']],
- ),
- 'tx': (
- [self.contourf, ['time', 'grid_longitude']],
- [self.contourf, ['grid_longitude', 'time']],
- [self.contour, ['time', 'grid_longitude']],
- [self.contour, ['grid_longitude', 'time']],
- [self.points, ['time', 'grid_longitude']],
- [self.points, ['grid_longitude', 'time']],
- ),
- 'x': (
- [self.plot, ['grid_longitude']],
- ),
- 'y': (
- [self.plot, ['grid_latitude']],
- ),
- }
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestLabels(tests.GraphicsTest):
- def setUp(self):
- self.theta = _load_theta()
-
- def _slice(self, coords):
- """Returns the first cube containing the requested coordinates."""
- for cube in self.theta.slices(coords):
- break
- return cube
-
- def _small(self):
- # Use a restricted size so we can make out the detail
- cube = self._slice(['model_level_number', 'grid_longitude'])
- return cube[:5, :5]
-
- def test_contour(self):
- qplt.contour(self._small())
- self.check_graphic()
-
- qplt.contourf(self._small(), coords=['model_level_number', 'grid_longitude'])
- self.check_graphic()
-
- def test_contourf(self):
- qplt.contourf(self._small())
-
- cube = self._small()
- iplt.orography_at_points(cube)
-
- self.check_graphic()
-
- qplt.contourf(self._small(), coords=['model_level_number', 'grid_longitude'])
- self.check_graphic()
-
- qplt.contourf(self._small(), coords=['grid_longitude', 'model_level_number'])
- self.check_graphic()
-
- def test_contourf_nameless(self):
- cube = self._small()
- cube.standard_name = None
- qplt.contourf(cube, coords=['grid_longitude', 'model_level_number'])
- self.check_graphic()
-
- def test_pcolor(self):
- qplt.pcolor(self._small())
- self.check_graphic()
-
- def test_pcolormesh(self):
- qplt.pcolormesh(self._small())
-
- #cube = self._small()
- #iplt.orography_at_bounds(cube)
-
- self.check_graphic()
-
- def test_map(self):
- cube = self._slice(['grid_latitude', 'grid_longitude'])
- qplt.contour(cube)
- self.check_graphic()
-
- # check that the result of adding 360 to the data is *almost* identically the same result
- lon = cube.coord('grid_longitude')
- lon.points = lon.points + 360
- qplt.contour(cube)
- self.check_graphic()
-
- def test_alignment(self):
- cube = self._small()
- qplt.contourf(cube)
- #qplt.outline(cube)
- qplt.points(cube)
- self.check_graphic()
-
-
-@tests.skip_data
-@tests.skip_plot
-class TestTimeReferenceUnitsLabels(tests.GraphicsTest):
-
- def setUp(self):
- path = tests.get_data_path(('PP', 'aPProt1', 'rotatedMHtimecube.pp'))
- self.cube = iris.load_cube(path)[:, 0, 0]
-
- def test_reference_time_units(self):
- # units should not be displayed for a reference time
- qplt.plot(self.cube.coord('time'), self.cube)
- plt.gcf().autofmt_xdate()
- self.check_graphic()
-
- def test_not_reference_time_units(self):
- # units should be displayed for other time coordinates
- qplt.plot(self.cube.coord('forecast_period'), self.cube)
- self.check_graphic()
-
-
-if __name__ == "__main__":
- tests.main()