From f4ce7fb4d2389b061a1f5977d081b6d570f52bd2 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 14 Oct 2020 15:11:09 +0100 Subject: [PATCH 1/7] Set 1D plots to put the coordinate on the y-axis where appropriate. --- lib/iris/plot.py | 13 +++++++++++ lib/iris/quickplot.py | 8 ++----- lib/iris/tests/unit/quickplot/test_plot.py | 25 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 349f1fea10..180e0b51e6 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -665,7 +665,20 @@ def _get_plot_objects(args): # single argument v_object = args[0] u_object = _u_object_from_v_object(v_object) + u, v = _uv_from_u_object_v_object(u_object, args[0]) + + # If a single cube argument, and the associated dimension coordinate + # is vertical-like, put the coordinate on the y axis, and the data o + # the x. + if isinstance(v_object, iris.cube.Cube) and iris.util.guess_coord_axis( + u_object + ) in ["Y", "Z"]: + # If we have a single argument, and it is vertical-like, put it on + # the y axis. + u_object, v_object = v_object, u_object + u, v = v, u + args = args[1:] return u_object, v_object, u, v, args diff --git a/lib/iris/quickplot.py b/lib/iris/quickplot.py index 350d61b537..2eec514e9c 100644 --- a/lib/iris/quickplot.py +++ b/lib/iris/quickplot.py @@ -138,12 +138,8 @@ def _get_titles(u_object, v_object): def _label_1d_plot(*args, **kwargs): - if len(args) > 1 and isinstance( - args[1], (iris.cube.Cube, iris.coords.Coord) - ): - xlabel, ylabel, title = _get_titles(*args[:2]) - else: - xlabel, ylabel, title = _get_titles(None, args[0]) + u_obj, v_obj, _, _, _ = iplt._get_plot_objects(args) + xlabel, ylabel, title = _get_titles(u_obj, v_obj) axes = kwargs.pop("axes", None) diff --git a/lib/iris/tests/unit/quickplot/test_plot.py b/lib/iris/tests/unit/quickplot/test_plot.py index 0a88107a6f..f65d73cd12 100644 --- a/lib/iris/tests/unit/quickplot/test_plot.py +++ b/lib/iris/tests/unit/quickplot/test_plot.py @@ -8,6 +8,7 @@ # Import iris.tests first so that some things can be initialised before # importing anything else. import iris.tests as tests +from iris.tests.stock import simple_2d from iris.tests.unit.plot import TestGraphicStringCoord if tests.MPL_AVAILABLE: @@ -29,5 +30,29 @@ def test_xaxis_labels(self): self.assertBoundsTickLabels("xaxis") +class TestAxisLabels(tests.GraphicsTest): + def test_xy_cube(self): + c = simple_2d()[:, 0] + qplt.plot(c) + ax = qplt.plt.gca() + x = ax.xaxis.get_label().get_text() + self.assertEqual(x, "Bar") + y = ax.yaxis.get_label().get_text() + self.assertEqual(y, "Thingness") + + def test_yx_cube(self): + c = simple_2d()[:, 0] + c.transpose() + # Making the cube a vertical coordinate should change the default + # orientation of the plot. + c.coord("bar").attributes["positive"] = "up" + qplt.plot(c) + ax = qplt.plt.gca() + x = ax.xaxis.get_label().get_text() + self.assertEqual(x, "Thingness") + y = ax.yaxis.get_label().get_text() + self.assertEqual(y, "Bar") + + if __name__ == "__main__": tests.main() From a1c329e2b729402ad0a9d3edd0fd38a65e544acb Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 14 Oct 2020 15:49:31 +0100 Subject: [PATCH 2/7] New target image-hashes for 1D y-axis-coordinate plots. --- lib/iris/tests/results/imagerepo.json | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json index a7fd9e1faf..6c2bf66ba6 100644 --- a/lib/iris/tests/results/imagerepo.json +++ b/lib/iris/tests/results/imagerepo.json @@ -288,9 +288,7 @@ "https://scitools.github.io/test-iris-imagehash/images/v4/8ff897066a01f0f2f818ee1eb007ca41853e3b81c57e36a991fe2ca9725e29ed.png" ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4fa6c059d2ef1494e4b90f26304847d78c1872a6cfc938b2e3e.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/8fffc1dc7e019c70f001b70ee4386de1814e7938837b6a7f84d07c9f15b02f21.png" ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube_coord.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7e0098757103a71ce4506dc3d11e7b20d2477ec094857db895217f6a.png", @@ -323,8 +321,7 @@ "https://scitools.github.io/test-iris-imagehash/images/v4/87ffb79e7f0060d8303fcd1eb007d801c52699e18d769e2199e60ce1da5629ed.png" ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1de7e009c7030019786f438cde3810fd93c9b734a778ce47c9799b02731.png" ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube_coord.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f90987720029f1ef458cd43811cdb60d647de609485ddb899215f62.png", @@ -649,15 +646,10 @@ "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe956b7c01c2f26300929dfc1e3c6690736f91817e3b0c84be6be5d1603ed1.png" ], "iris.tests.test_plot.TestPlot.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe896266f068d873b83cb71e435725cd07c607ad07e70fcd0007a7881fe7ab8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe896066f068d873b83cb71e435725cd07c607ad07c70fcd0007af881fe7bb8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe896366f0f8d93398bcb71e435f24ed074646ed07670acf010726d81f2798c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/aff8946c7a14c99fb193d263e42432d8d00c2d27944a3f8dc5223ef703ff6b90.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/8ff99c067e01e7166101c9c6b04396b5cd4e2f0993163de9c4fe7b79207e36a1.png" ], "iris.tests.test_plot.TestPlot.test_z.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4fa6c059d2ef1494e4b90f26304847d78c1872a6cfc938b2e3e.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/8fffc1dc7e019c70f001b70ee4386de1814e7938837b6a7f84d07c9f15b02f21.png" ], "iris.tests.test_plot.TestPlotCitation.test.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png", @@ -836,14 +828,10 @@ "https://scitools.github.io/test-iris-imagehash/images/v4/82ff950b7f81c0d6620199bcfc5e986695734da1816e1b2c85be2b65d96276d1.png" ], "iris.tests.test_plot.TestQuickplotPlot.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ffb6067f008d87339bc973e435d86ef034c87ad07c586cd001da69897e5838.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ffb6067f008d87339bc973e435d86ef034c87ad07cd86cd001da68897e58a8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a7efb6367f008d97338fc973e435d86ef030c86ed070d86cd030d86d89f0d82c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a2fbb46e7f10c99f2013d863e46498dcd06c0d2798421fa5dd221e7789ff6f10.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/a3f9bc067e01c6166009c9c6b5439ee5cd4e0d2993361de9ccf65b79887636a9.png" ], "iris.tests.test_plot.TestQuickplotPlot.test_z.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1de7e009c7030019786f438cde3810fd93c9b734a778ce47c9799b02731.png" ], "iris.tests.test_plot.TestSimple.test_bounds.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85954a957ac17e954ac17a9c3e956ac07e3e80c07f3e857aa5c27d3f80.png" From c6136d3a485c40c6ce2901b4ae79305eaf2e44cd Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 14 Oct 2020 16:02:53 +0100 Subject: [PATCH 3/7] Whatsnew entry for 1D y-axis-coordinate plots. --- docs/iris/src/whatsnew/latest.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/iris/src/whatsnew/latest.rst b/docs/iris/src/whatsnew/latest.rst index e9fb007aca..a4b4c5b001 100644 --- a/docs/iris/src/whatsnew/latest.rst +++ b/docs/iris/src/whatsnew/latest.rst @@ -16,7 +16,11 @@ This document explains the changes made to Iris for this release ✨ Features =========== -* N/A +* `@pelson`_ and `@trexfeathers`_ enhanced :meth:iris.plot.plot and + :meth:iris.quickplot.plot to automatically place the cube on the x axis if + the primary coordinate being plotted against is a vertical coordinate. E.g. + ``iris.plot.plot(z_cube)`` will produce a z-vs-phenomenon plot, where before + it would have produced a phenomenon-vs-z plot. (:pull:`3906`) 🐛 Bugs Fixed @@ -56,5 +60,8 @@ This document explains the changes made to Iris for this release * N/A + +.. _@pelson: https://github.com/pelson +.. _@trexfeathers: https://github.com/trexfeathers .. _@gcaria: https://github.com/gcaria .. _@rcomer: https://github.com/rcomer From 72f381eba34af04e22fa31b46428008d69c0e463 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Mon, 14 Dec 2020 08:31:53 +0000 Subject: [PATCH 4/7] Remove duplicate comment about flipping plot axes. --- lib/iris/plot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 180e0b51e6..8e187b0bc9 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -674,8 +674,6 @@ def _get_plot_objects(args): if isinstance(v_object, iris.cube.Cube) and iris.util.guess_coord_axis( u_object ) in ["Y", "Z"]: - # If we have a single argument, and it is vertical-like, put it on - # the y axis. u_object, v_object = v_object, u_object u, v = v, u From 54f952a4f1af5d5d8e5a9b5cc3c52b01fd83ee10 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Tue, 15 Dec 2020 17:08:59 +0000 Subject: [PATCH 5/7] Use simple_1d stock cube for TestAxisLabels instead of simple_2d. --- lib/iris/tests/unit/quickplot/test_plot.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/iris/tests/unit/quickplot/test_plot.py b/lib/iris/tests/unit/quickplot/test_plot.py index f65d73cd12..9bc4a7dca3 100644 --- a/lib/iris/tests/unit/quickplot/test_plot.py +++ b/lib/iris/tests/unit/quickplot/test_plot.py @@ -8,7 +8,7 @@ # Import iris.tests first so that some things can be initialised before # importing anything else. import iris.tests as tests -from iris.tests.stock import simple_2d +from iris.tests.stock import simple_1d from iris.tests.unit.plot import TestGraphicStringCoord if tests.MPL_AVAILABLE: @@ -32,26 +32,26 @@ def test_xaxis_labels(self): class TestAxisLabels(tests.GraphicsTest): def test_xy_cube(self): - c = simple_2d()[:, 0] + c = simple_1d() qplt.plot(c) ax = qplt.plt.gca() x = ax.xaxis.get_label().get_text() - self.assertEqual(x, "Bar") + self.assertEqual(x, "Foo") y = ax.yaxis.get_label().get_text() self.assertEqual(y, "Thingness") def test_yx_cube(self): - c = simple_2d()[:, 0] + c = simple_1d() c.transpose() # Making the cube a vertical coordinate should change the default # orientation of the plot. - c.coord("bar").attributes["positive"] = "up" + c.coord("foo").attributes["positive"] = "up" qplt.plot(c) ax = qplt.plt.gca() x = ax.xaxis.get_label().get_text() self.assertEqual(x, "Thingness") y = ax.yaxis.get_label().get_text() - self.assertEqual(y, "Bar") + self.assertEqual(y, "Foo") if __name__ == "__main__": From 808d1af6abbaabb887edd88b5d925eb21323ec52 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 16 Dec 2020 11:45:24 +0000 Subject: [PATCH 6/7] Only attempt 1D vertical axis swap if coord can be identified. --- lib/iris/plot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 8e187b0bc9..bda5274cca 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -671,9 +671,11 @@ def _get_plot_objects(args): # If a single cube argument, and the associated dimension coordinate # is vertical-like, put the coordinate on the y axis, and the data o # the x. - if isinstance(v_object, iris.cube.Cube) and iris.util.guess_coord_axis( - u_object - ) in ["Y", "Z"]: + if ( + isinstance(v_object, iris.cube.Cube) + and isinstance(u_object, iris.coords.Coord) + and iris.util.guess_coord_axis(u_object) in ["Y", "Z"] + ): u_object, v_object = v_object, u_object u, v = v, u From fa41a55fc4ac815256a210960207b7fa6d1ce79e Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 16 Dec 2020 11:46:15 +0000 Subject: [PATCH 7/7] Simplify usages of plot() to take advantage of auto 1D vertical axis swap. --- .../oceanography/plot_atlantic_profiles.py | 7 ++----- .../regridding_plots/interpolate_column.py | 13 +++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/iris/gallery_code/oceanography/plot_atlantic_profiles.py b/docs/iris/gallery_code/oceanography/plot_atlantic_profiles.py index a7e82c34f5..89d99c80b4 100644 --- a/docs/iris/gallery_code/oceanography/plot_atlantic_profiles.py +++ b/docs/iris/gallery_code/oceanography/plot_atlantic_profiles.py @@ -39,9 +39,8 @@ def main(): theta_1000m = theta.extract(depth_cons & lon_cons & lat_cons) salinity_1000m = salinity.extract(depth_cons & lon_cons & lat_cons) - # Plot these profiles on the same set of axes. In each case we call plot - # with two arguments, the cube followed by the depth coordinate. Putting - # them in this order places the depth coordinate on the y-axis. + # Plot these profiles on the same set of axes. Depth is automatically + # recognised as a vertical coordinate and placed on the y-axis. # The first plot is in the default axes. We'll use the same color for the # curve and its axes/tick labels. plt.figure(figsize=(5, 6)) @@ -49,7 +48,6 @@ def main(): ax1 = plt.gca() iplt.plot( theta_1000m, - theta_1000m.coord("depth"), linewidth=2, color=temperature_color, alpha=0.75, @@ -65,7 +63,6 @@ def main(): ax2 = plt.gca().twiny() iplt.plot( salinity_1000m, - salinity_1000m.coord("depth"), linewidth=2, color=salinity_color, alpha=0.75, diff --git a/docs/iris/src/userguide/regridding_plots/interpolate_column.py b/docs/iris/src/userguide/regridding_plots/interpolate_column.py index 273ef365cc..4378ec98be 100644 --- a/docs/iris/src/userguide/regridding_plots/interpolate_column.py +++ b/docs/iris/src/userguide/regridding_plots/interpolate_column.py @@ -1,4 +1,3 @@ -import iris import iris.quickplot as qplt import iris.analysis import matplotlib.pyplot as plt @@ -12,8 +11,13 @@ # Interpolate the "perfect" linear interpolation. Really this is just # a high number of interpolation points, in this case 1000 of them. -altitude_points = [("altitude", np.linspace(400, 1250, 1000))] -scheme = iris.analysis.Linear(extrapolation_mode="mask") +altitude_points = [ + ( + "altitude", + np.linspace(min(alt_coord.points), max(alt_coord.points), 1000), + ) +] +scheme = iris.analysis.Linear() linear_column = column.interpolate(altitude_points, scheme) # Now interpolate the data onto 10 evenly spaced altitude levels, @@ -27,7 +31,6 @@ # Plot the black markers for the original data. qplt.plot( column, - column.coord("altitude"), marker="o", color="black", linestyle="", @@ -39,7 +42,6 @@ # Plot the gray line to display the linear interpolation. qplt.plot( linear_column, - linear_column.coord("altitude"), color="gray", label="Linear interpolation", zorder=0, @@ -48,7 +50,6 @@ # Plot the red markers for the new data. qplt.plot( new_column, - new_column.coord("altitude"), marker="D", color="red", linestyle="",