diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index ebab20c806..39bc555043 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -30,13 +30,10 @@ This document explains the changes made to Iris for this release ✨ Features =========== -#. N/A - #. `@pp-mo`_ added a new utility function :func:`~iris.util.equalise_cubes`, to help with aligning cubes so they can merge / concatenate. (:issue:`6248`, :pull:`6257`) - #. `@fnattino`_ added the lazy median aggregator :class:`iris.analysis.MEDIAN` based on the implementation discussed by `@rcomer`_ and `@stefsmeets`_ in :issue:`4039` (:pull:`6167`). @@ -51,6 +48,11 @@ This document explains the changes made to Iris for this release However, :meth:`~iris.cube.Cube.transpose` will work, as will :meth:`~iris.cube.Cube.copy`. Note that, ``cube.copy(data=iris.DATALESS)`` will provide a dataless copy of a cube. (:issue:`4447`, :pull:`6253`) + +#. `@ESadek-MO`_ added the :mod:`iris.quickplot` ``footer`` kwarg to + render text in the bottom right of the plot figure. + (:issue:`6247`, :pull:`6332`) + 🐛 Bugs Fixed ============= diff --git a/lib/iris/quickplot.py b/lib/iris/quickplot.py index c1ca7f733f..431c71cd8e 100644 --- a/lib/iris/quickplot.py +++ b/lib/iris/quickplot.py @@ -7,11 +7,15 @@ These routines work much like their :mod:`iris.plot` counterparts, but they automatically add a plot title, axis titles, and a colour bar when appropriate. +These also have the optional kwarg ``footer``, which adds text to the bottom right of +the plot figure. + See also: :ref:`matplotlib `. """ import cf_units +from matplotlib import patheffects import matplotlib.pyplot as plt import iris.config @@ -139,6 +143,17 @@ def _label_1d_plot(*args, **kwargs): axes.set_ylabel(ylabel) +def _footer(footer_text: str) -> None: + if footer_text: + text = plt.figtext( + x=0.99, y=0.01, s=footer_text, fontsize=12, ha="right", va="bottom" + ) + text.set_path_effects( + [patheffects.Stroke(linewidth=3, foreground="white"), patheffects.Normal()] + ) + plt.subplots_adjust(bottom=0.15, left=0.1) + + def contour(cube, *args, **kwargs): """Draw contour lines on a labelled plot based on the given Cube. @@ -165,8 +180,10 @@ def contour(cube, *args, **kwargs): """ coords = kwargs.get("coords") axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.contour(cube, *args, **kwargs) _label_with_points(cube, coords=coords, axes=axes) + _footer(footer) return result @@ -201,12 +218,14 @@ def contourf(cube, *args, **kwargs): coords = kwargs.get("coords") axes = kwargs.get("axes") colorbar = kwargs.pop("colorbar", True) + footer = kwargs.pop("footer", None) result = iplt.contourf(cube, *args, **kwargs) _label_with_points(cube, result, coords=coords, axes=axes, colorbar=colorbar) + _footer(footer) return result -def outline(cube, coords=None, color="k", linewidth=None, axes=None): +def outline(cube, coords=None, color="k", linewidth=None, axes=None, footer=None): """Draw cell outlines on a labelled plot based on the given Cube. Parameters @@ -234,6 +253,7 @@ def outline(cube, coords=None, color="k", linewidth=None, axes=None): ) _label_with_bounds(cube, coords=coords, axes=axes) + _footer(footer) return result @@ -255,8 +275,10 @@ def pcolor(cube, *args, **kwargs): coords = kwargs.get("coords") axes = kwargs.get("axes") colorbar = kwargs.pop("colorbar", True) + footer = kwargs.pop("footer", None) result = iplt.pcolor(cube, *args, **kwargs) _label_with_bounds(cube, result, coords=coords, axes=axes, colorbar=colorbar) + _footer(footer) return result @@ -279,8 +301,10 @@ def pcolormesh(cube, *args, **kwargs): coords = kwargs.get("coords") axes = kwargs.get("axes") colorbar = kwargs.pop("colorbar", True) + footer = kwargs.pop("footer", None) result = iplt.pcolormesh(cube, *args, **kwargs) _label_with_bounds(cube, result, coords=coords, axes=axes, colorbar=colorbar) + _footer(footer) return result @@ -297,8 +321,10 @@ def points(cube, *args, **kwargs): """ coords = kwargs.get("coords") axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.points(cube, *args, **kwargs) _label_with_points(cube, coords=coords, axes=axes) + _footer(footer) return result @@ -315,8 +341,10 @@ def plot(*args, **kwargs): """ axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.plot(*args, **kwargs) _label_1d_plot(*args, axes=axes) + _footer(footer) return result @@ -333,8 +361,10 @@ def scatter(x, y, *args, **kwargs): """ axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.scatter(x, y, *args, **kwargs) _label_1d_plot(x, y, axes=axes) + _footer(footer) return result @@ -351,8 +381,10 @@ def fill_between(x, y1, y2, *args, **kwargs): """ axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.fill_between(x, y1, y2, *args, **kwargs) _label_1d_plot(x, y1, axes=axes) + _footer(footer) return result @@ -369,6 +401,7 @@ def hist(x, *args, **kwargs): """ axes = kwargs.get("axes") + footer = kwargs.pop("footer", None) result = iplt.hist(x, *args, **kwargs) title = _title(x, with_units=False) label = _title(x, with_units=True) @@ -382,7 +415,7 @@ def hist(x, *args, **kwargs): else: axes.set_xlabel(label) axes.set_title(title) - + _footer(footer) return result diff --git a/lib/iris/tests/test_quickplot.py b/lib/iris/tests/test_quickplot.py index 17ef68e64b..52e0fad18d 100644 --- a/lib/iris/tests/test_quickplot.py +++ b/lib/iris/tests/test_quickplot.py @@ -279,3 +279,13 @@ def test_vertical(self): cube = test_plot.simple_cube()[0] qplt.hist(cube, bins=np.linspace(287.7, 288.2, 11), orientation="horizontal") self.check_graphic() + + +@_shared_utils.skip_plot +class TestFooter: + def test__footer(self): + text = "Example" + qplt._footer(text) + fig = plt.gcf() + footer_text = fig.texts[0].get_text() + assert text == footer_text