Skip to content

Commit

Permalink
Add barbs plot (#3710)
Browse files Browse the repository at this point in the history
* Add plot and gallery example

* Add a what's new

* Added test and reduced size of gallery example

* Painted it black

* Added test to imagerepo.json but name of file in repo will be wrong

* Correct hash inserted

* Correctly resolved merge conflict

* Hopefully fully compliant with new style gallery now

* Consistency with new style documentation

* Improved language as Lake Victoria is not relevant and shorelines are not included in this simulated data.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Clarify units

* Correct function name.

* Test vector plots

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* isort:skip

Co-authored-by: Martin Yeo <40734014+trexfeathers@users.noreply.github.com>

* Docstring note now visible in Sphinx output

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Martin Yeo <40734014+trexfeathers@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 10, 2021
1 parent d4d0514 commit 619c62c
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 6 deletions.
62 changes: 62 additions & 0 deletions docs/gallery_code/meteorology/plot_wind_barbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
Plotting Wind Direction Using Barbs
===================================
This example demonstrates using barbs to plot wind speed contours and wind
direction barbs from wind vector component input data. The vector components
are co-located in space in this case.
The magnitude of the wind in the original data is low and so doesn't illustrate
the full range of barbs. The wind is scaled to simulate a storm that better
illustrates the range of barbs that are available.
"""

import matplotlib.pyplot as plt

import iris
import iris.plot as iplt
import iris.quickplot as qplt


def main():
# Load the u and v components of wind from a pp file
infile = iris.sample_data_path("wind_speed_lake_victoria.pp")

uwind = iris.load_cube(infile, "x_wind")
vwind = iris.load_cube(infile, "y_wind")

uwind.convert_units("knot")
vwind.convert_units("knot")

# To illustrate the full range of barbs, scale the wind speed up to pretend
# that a storm is passing over
magnitude = (uwind ** 2 + vwind ** 2) ** 0.5
magnitude.convert_units("knot")
max_speed = magnitude.collapsed(
("latitude", "longitude"), iris.analysis.MAX
).data
max_desired = 65

uwind = uwind / max_speed * max_desired
vwind = vwind / max_speed * max_desired

# Create a cube containing the wind speed
windspeed = (uwind ** 2 + vwind ** 2) ** 0.5
windspeed.rename("windspeed")
windspeed.convert_units("knot")

plt.figure()

# Plot the wind speed as a contour plot
qplt.contourf(windspeed)

# Add wind barbs except for the outermost values which overhang the edge
# of the plot if left
iplt.barbs(uwind[1:-1, 1:-1], vwind[1:-1, 1:-1], pivot="middle", length=6)

plt.title("Wind speed during a simulated storm")
qplt.show()


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions docs/gallery_tests/test_plot_wind_barbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the LGPL license.
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.

# Import Iris tests first so that some things can be initialised before
# importing anything else.
import iris.tests as tests # isort:skip

from .gallerytest_util import (
add_gallery_to_path,
fail_any_deprecation_warnings,
show_replaced_by_check_graphic,
)


class TestWindBarbs(tests.GraphicsTest):
"""Test the wind_barbs example code."""

def test_wind_barbs(self):
with fail_any_deprecation_warnings():
with add_gallery_to_path():
import plot_wind_barbs
with show_replaced_by_check_graphic(self):
plot_wind_barbs.main()


if __name__ == "__main__":
tests.main()
3 changes: 3 additions & 0 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ This document explains the changes made to Iris for this release
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`)
#. `@jonseddon`_ added :meth:`iris.plot.barbs` to provide a convenient way to
use :func:`matplotlib.pyplot.barbs` with Iris cubes. A gallery example was
included to illustrate the new method's use. (:pull:`3710`)

#. `@bjlittle`_ introduced :func:`iris.common.metadata.hexdigest` to the
public API. Previously it was a private function introduced in ``v3.0.0``.
Expand Down
53 changes: 51 additions & 2 deletions lib/iris/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,55 @@ def _vector_component_args(x_points, y_points, u_data, *args, **kwargs):
return ((x_points, y_points, u_data, v_data), kwargs)


def barbs(u_cube, v_cube, *args, **kwargs):
"""
Draws a barb plot from two vector component cubes. Triangles, full-lines
and half-lines represent increments of 50, 10 and 5 respectively.
Args:
* u_cube, v_cube : (:class:`~iris.cube.Cube`)
u and v vector components. Must have same shape and units.
If the cubes have geographic coordinates, the values are treated as
true distance differentials, e.g. windspeeds, and *not* map coordinate
vectors. The components are aligned with the North and East of the
cube coordinate system.
.. Note::
At present, if u_cube and v_cube have geographic coordinates, then they
must be in a lat-lon coordinate system, though it may be a rotated one.
To transform wind values between coordinate systems, use
:func:`iris.analysis.cartography.rotate_grid_vectors`.
To transform coordinate grid points, you will need to create
2-dimensional arrays of x and y values. These can be transformed with
:meth:`cartopy.crs.CRS.transform_points`.
Kwargs:
* coords: (list of :class:`~iris.coords.Coord` or string)
Coordinates or coordinate names. Use the given coordinates as the axes
for the plot. The order of the given coordinates indicates which axis
to use for each, where the first element is the horizontal
axis of the plot and the second element is the vertical axis
of the plot.
* axes: the :class:`matplotlib.axes.Axes` to use for drawing.
Defaults to the current axes if none provided.
See :func:`matplotlib.pyplot.barbs` for details of other valid
keyword arguments.
"""
#
# TODO: check u + v cubes for compatibility.
#
kwargs["_v_data"] = v_cube.data
return _draw_2d_from_points(
"barbs", _vector_component_args, u_cube, *args, **kwargs
)


def quiver(u_cube, v_cube, *args, **kwargs):
"""
Draws an arrow plot from two vector component cubes.
Expand All @@ -1432,12 +1481,12 @@ def quiver(u_cube, v_cube, *args, **kwargs):
vectors. The components are aligned with the North and East of the
cube coordinate system.
.. Note:
.. Note::
At present, if u_cube and v_cube have geographic coordinates, then they
must be in a lat-lon coordinate system, though it may be a rotated one.
To transform wind values between coordinate systems, use
:func:`iris.analysis.cartography.rotate_vectors`.
:func:`iris.analysis.cartography.rotate_grid_vectors`.
To transform coordinate grid points, you will need to create
2-dimensional arrays of x and y values. These can be transformed with
:meth:`cartopy.crs.CRS.transform_points`.
Expand Down
36 changes: 32 additions & 4 deletions lib/iris/tests/integration/plot/test_vector_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@
if tests.MPL_AVAILABLE:
import matplotlib.pyplot as plt

from iris.plot import quiver
from iris.plot import barbs, quiver


@tests.skip_plot
class MixinVectorPlotCases:
"""
Test examples mixin, used by separate quiver + streamplot classes.
Test examples mixin, used by separate barb, quiver + streamplot classes.
NOTE: at present for quiver only, as streamplot does not support arbitrary
coordinates.
NOTE: at present for barb and quiver only, as streamplot does not support
arbitrary coordinates.
"""

Expand Down Expand Up @@ -193,6 +193,34 @@ def test_circular_longitude(self):
self.plot("circular", u_cube, v_cube, coords=("longitude", "latitude"))


class TestBarbs(MixinVectorPlotCases, tests.GraphicsTest):
def setUp(self):
super().setUp()

@staticmethod
def _nonlatlon_xyuv():
# Increase the range of wind speeds used in the barbs test to test more
# barbs shapes than just circles
x, y, u, v = MixinVectorPlotCases._nonlatlon_xyuv()
scale_factor = 50
u *= scale_factor
v *= scale_factor
return x, y, u, v

@staticmethod
def _latlon_uv_cubes(grid_cube):
# Increase the range of wind speeds used in the barbs test to test all
# barbs shapes
u_cube, v_cube = MixinVectorPlotCases._latlon_uv_cubes(grid_cube)
scale_factor = 30
u_cube.data *= scale_factor
v_cube.data *= scale_factor
return u_cube, v_cube

def plot_function_to_test(self):
return barbs


class TestQuiver(MixinVectorPlotCases, tests.GraphicsTest):
def setUp(self):
super().setUp()
Expand Down
19 changes: 19 additions & 0 deletions lib/iris/tests/results/imagerepo.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@
"https://scitools.github.io/test-iris-imagehash/images/v4/fa8172d0847ecd2bc913939c36846c714933799cc3cc8727e67639f939996a58.png",
"https://scitools.github.io/test-iris-imagehash/images/v4/fa8172c6857ecd38cb3392ce36c564311931d85ec64e9787719a39993c316e66.png"
],
"gallery_tests.test_plot_wind_barbs.TestWindBarbs.test_wind_barbs.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169316c1fe9e96c29e36739e13c07c3d61c07f39a13921c07f3e21.png",
"https://scitools.github.io/test-iris-imagehash/images/v4/e9e161e996169316c1fe9e96c29e36739e13c07c3d61c07f39813929c07f3f01.png"
],
"gallery_tests.test_plot_wind_speed.TestWindSpeed.test_plot_wind_speed.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cd3e29847b18e639e6c14f1a09.png",
"https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169306c1fe9e96c29e36739e13c06c3d61c07f39a139e1c07f3f01.png"
Expand Down Expand Up @@ -173,6 +177,21 @@
"iris.tests.integration.plot.test_plot_2d_coords.Test2dContour.test_2d_coords_contour.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/b4b2643ecb05cb43b0f23d80c53c4e1d3e5990eb1f81c19f2f983cb1c4ff3e42.png"
],
"iris.tests.integration.plot.test_vector_plots.TestBarbs.test_2d_plain_latlon.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/eb036726c47c9273918e6e2c6f216336787590eb969a165890ee6c676925b3b3.png"
],
"iris.tests.integration.plot.test_vector_plots.TestBarbs.test_2d_plain_latlon_on_polar_map.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/e66d673c999031cd6667663398dc332c676364e798959336636660d933998666.png"
],
"iris.tests.integration.plot.test_vector_plots.TestBarbs.test_2d_rotated_latlon.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/eba037a4c479c273b2963f2c6f6126966865d86f969e33c9b1706c26692793b0.png"
],
"iris.tests.integration.plot.test_vector_plots.TestBarbs.test_non_latlon_1d_coords.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/a7ac334934d2e65c72596325b343338cb41c92d9c5b36f65330d379692ca6d6c.png"
],
"iris.tests.integration.plot.test_vector_plots.TestBarbs.test_non_latlon_2d_coords.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/a7acb36134d2e676627963259343330cb43e92d9c5336e67330d379292ca6d6c.png"
],
"iris.tests.integration.plot.test_vector_plots.TestQuiver.test_2d_plain_latlon.0": [
"https://scitools.github.io/test-iris-imagehash/images/v4/fb8d4f21c472b27e919d2e216f216b3178e69c7e961ab39a84696c616d245b94.png"
],
Expand Down

0 comments on commit 619c62c

Please sign in to comment.