From ff7ec92cd1294d188bebbe034b1e6b22c57e0ca6 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Mon, 23 Jul 2018 16:10:40 +0100 Subject: [PATCH 01/24] Add gridcell_angles and rotate_grid_vectors to iris.analysis.cartography, with tests: INCOMPLETE WIP --- lib/iris/analysis/_grid_angles.py | 261 ++++++++++++++++++ lib/iris/analysis/cartography.py | 3 + .../cartography/test_gridcell_angles.py | 171 ++++++++++++ 3 files changed, 435 insertions(+) create mode 100644 lib/iris/analysis/_grid_angles.py create mode 100644 lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py new file mode 100644 index 0000000000..6f7dcf15db --- /dev/null +++ b/lib/iris/analysis/_grid_angles.py @@ -0,0 +1,261 @@ +import numpy as np + +import iris + + +def _3d_xyz_from_latlon(lon, lat): + """ + Return locations of (lon, lat) in 3D space. + + Args: + + * lon, lat: (arrays in degrees) + + Returns: + + x, y, z : (array, dtype=float64) + cartesian coordinates on a unit sphere. + + """ + lon1 = np.deg2rad(lon).astype(np.float64) + lat1 = np.deg2rad(lat).astype(np.float64) + + old_way = True + if old_way: + x3 = np.empty((3,) + lon.shape, dtype=float) + x3[0] = np.cos(lat1) * np.cos(lon1) + x3[1] = np.cos(lat1) * np.sin(lon1) + x3[2] = np.sin(lat1) + result = x3 + else: + x = np.cos(lat1) * np.cos(lon1) + y = np.cos(lat1) * np.sin(lon1) + z = np.sin(lat1) + + result = np.concatenate([array[np.newaxis] for array in (x, y, z)]) + + return result + + +def _latlon_from_xyz(xyz): + """ + Return arrays of lons+lats angles from xyz locations. + + Args: + + * xyz: (array) + positions array, of dims (3, ), where index 0 maps x/y/z. + + Returns: + + lonlat : (array) + spherical angles, of dims (2, ), in radians. + Dim 0 maps longitude, latitude. + + """ + lons = np.arctan2(xyz[1], xyz[0]) + axial_radii = np.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1]) + lats = np.arctan2(xyz[2], axial_radii) + return np.array([lons, lats]) + + +def _angle(p, q, r): + """ + Return angle (in _radians_) of grid wrt local east. Anticlockwise +ve, as usual. + {P, Q, R} are consecutve points in the same row, eg {v(i,j),f(i,j),v(i+1,j)}, or {T(i-1,j),T(i,j),T(i+1,j)} + Calculate dot product of PR with lambda_hat at Q. This gives us cos(required angle). + Disciminate between +/- angles by comparing latitudes of P and R. + p, q, r, are all 2-element arrays [lon, lat] of angles in degrees. + + """ + q1 = np.deg2rad(q) + + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + pr_norm = np.sqrt(np.sum(pr**2, axis=0)) + pr_top = pr[1] * np.cos(q1[0]) - pr[0] * np.sin(q1[0]) + + index = np.where(pr_norm == 0) + pr_norm[index] = 1 + + cosine = np.maximum(np.minimum(pr_top / pr_norm, 1), -1) + cosine[index] = 0 + + psi = np.arccos(cosine) * np.sign(r[1] - p[1]) + psi[index] = np.nan + + return psi + + +def gridcell_angles(x, y=None): + """ + Calculate gridcell orientation angles. + + The inputs (x [,y]) can be any of the folliwing : + + * x (:class:`~iris.cube.Cube`): + a grid cube with 2D longitude and latitude coordinates. + + * x, y (:class:`~iris.coords.Coord`): + longitude and latitude coordinates. + + * x, y (2-dimensional arrays of same shape (ny, nx)): + longitude and latitude cell center locations, in degrees. + + * x, y (3-dimensional arrays of same shape (ny, nx, 4)): + longitude and latitude cell bounds, in degrees. + The last index maps cell corners anticlockwise from bottom-left. + + Returns: + + angles : (2-dimensional cube) + + Cube of angles of grid-x vector from true Eastward direction for + each gridcell, in radians. + It also has longitude and latitude coordinates. If coordinates + were input the output has identical ones : If the input was 2d + arrays, the output coords have no bounds; or, if the input was 3d + arrays, the output coords have bounds and centrepoints which are + the average of the 4 bounds. + + """ + if hasattr(x, 'core_data'): + # N.B. only "true" lats + longs will do : Cannot handle rotated ! + x, y = x.coord('longitude'), x.coord('latitude') + + # Now should have either 2 coords or 2 arrays. + if not hasattr(x, 'shape') and hasattr(y, 'shape'): + msg = ('Inputs (x,y) must have array shape property.' + 'Got type(x)={} and type(y)={}.') + raise ValueError(msg.format(type(x), type(y))) + + x_coord, y_coord = None, None + if isinstance(x, iris.coords.Coord) and isinstance(y, iris.coords.Coord): + x_coord, y_coord = x.copy(), y.copy() + x_coord.convert_units('degrees') + y_coord.convert_units('degrees') + if x_coord.ndim != 2 or y_coord.ndim != 2: + msg = ('Coordinate inputs must have 2-dimensional shape. ', + 'Got x-shape of {} and y-shape of {}.') + raise ValueError(msg.format(x_coord.shape, y_coord.shape)) + if x_coord.shape != y_coord.shape: + msg = ('Coordinate inputs must have same shape. ', + 'Got x-shape of {} and y-shape of {}.') + raise ValueError(msg.format(x_coord.shape, y_coord.shape)) +# NOTE: would like to check that dims are in correct order, but can't do that +# if there is no cube. +# TODO: **document** -- another input format requirement +# x_dims, y_dims = (cube.coord_dims(co) for co in (x_coord, y_coord)) +# if x_dims != (0, 1) or y_dims != (0, 1): +# msg = ('Coordinate inputs must map to cube dimensions (0, 1). ', +# 'Got x-dims of {} and y-dims of {}.') +# raise ValueError(msg.format(x_dims, y_dims)) + if x_coord.has_bounds() and y_coord.has_bounds(): + x, y = x_coord.bounds, y_coord.bounds + else: + x, y = x_coord.points, y_coord.points + + elif isinstance(x, iris.coords.Coord) or isinstance(y, iris.coords.Coord): + is_and_not = ('x', 'y') + if isinstance(y, iris.coords.Coord): + is_and_not = reversed(is_and_not) + msg = 'Input {!r} is a Coordinate, but {!r} is not.' + raise ValueError(*is_and_not) + + # Now have either 2 points arrays or 2 bounds arrays. + # Construct (lhs, mid, rhs) where these represent 3 adjacent points with + # increasing longitudes. + if x.ndim == 2: + # PROBLEM: we can't use this if data is not full-longitudes, + # i.e. rhs of array must connect to lhs (aka 'circular' coordinate). + # But we have no means of checking that ? + + # Use previous + subsequent points along longitude-axis as references. + # NOTE: we also have no way to check that dim #2 really is the 'X' dim. + mid = np.array([x, y]) + lhs = np.roll(mid, 1, 2) + rhs = np.roll(mid, -1, 2) + if not x_coord: + # Create coords for result cube : with no bounds. + y_coord = iris.coords.AuxCoord(x, standard_name='latitude', + units='degrees') + x_coord = iris.coords.AuxCoord(y, standard_name='longitude', + units='degrees') + else: + # Get lhs and rhs locations by averaging top+bottom each side. + # NOTE: so with bounds, we *don't* need full circular longitudes. + xyz = _3d_xyz_from_latlon(x, y) + lhs_xyz = 0.5 * (xyz[..., 0] + xyz[..., 3]) + rhs_xyz = 0.5 * (xyz[..., 1] + xyz[..., 2]) + if not x_coord: + # Create bounded coords for result cube. + # Use average lhs+rhs points in 3d to get 'mid' points, as coords + # with no points are not allowed. + mid_xyz = 0.5 * (lhs_xyz + rhs_xyz) + mid_latlons = _latlon_from_xyz(mid_xyz) + # Create coords with given bounds, and averaged centrepoints. + x_coord = iris.coords.AuxCoord( + points=mid_latlons[0], bounds=x, + standard_name='longitude', units='degrees') + y_coord = iris.coords.AuxCoord( + points=mid_latlons[1], bounds=y, + standard_name='latitude', units='degrees') + # Convert lhs and rhs points back to latlon form -- IN DEGREES ! + lhs = np.rad2deg(_latlon_from_xyz(lhs_xyz)) + rhs = np.rad2deg(_latlon_from_xyz(rhs_xyz)) + # mid is coord.points, whether input or made up. + mid = np.array([x_coord.points, y_coord.points]) + + # Do the angle calcs, and return as a suitable cube. + angles = _angle(lhs, mid, rhs) + result = iris.cube.Cube(angles, + long_name='gridcell_angle_from_true_east', + units='radians') + result.add_aux_coord(x_coord, (0, 1)) + result.add_aux_coord(y_coord, (0, 1)) + return result + + +def true_vectors_from_grid_vectors(u_cube, v_cube, grid_angles_cube=None): + """ + Rotate distance vectors from grid-oriented to true-latlon-oriented. + + Args: + + * u_cube, v_cube : (cube) + Cubes of grid-u and grid-v vector components. + Units should be differentials of true-distance, e.g. 'm/s'. + + Optional args: + + * grid_angles_cube : (cube) + gridcell orientation angles. + Units must be angular, i.e. can be converted to 'radians'. + If not provided, grid angles are estimated from 'u_cube' using the + :func:`gridcell_angles` method. + + Returns: + + true_u, true_v : (cube) + Cubes of true-north oriented vector components. + Units are same as inputs. + + """ + u_out, v_out = (cube.copy() for cube in (u_cube, v_cube)) + if not grid_angles_cube: + grid_angles_cube = gridcell_angles(u_cube) + gridangles = grid_angles_cube.copy() + gridangles.convert_units('radians') + uu, vv, aa = (cube.data for cube in (u_out, v_out, gridangles)) + mags = np.sqrt(uu*uu + vv*vv) + angs = np.arctan2(vv, uu) + aa + uu, vv = mags * np.cos(angs), mags * np.sin(angs) + + # Promote all to masked arrays, and also apply mask at bad (NaN) angles. + mask = np.isnan(aa) + for cube in (u_out, v_out, aa): + if hasattr(cube.data, 'mask'): + mask |= cube.data.mask + u_out.data = np.ma.masked_array(uu, mask=mask) + v_out.data = np.ma.masked_array(vv, mask=mask) + + return u_out, v_out diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index 4b68dcc949..1c55d48fd3 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -37,6 +37,9 @@ import iris.coord_systems import iris.exceptions from iris.util import _meshgrid +from ._grid_angles import ( + gridcell_angles, + true_vectors_from_grid_vectors as rotate_grid_vectors) # This value is used as a fall-back if the cube does not define the earth diff --git a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py new file mode 100644 index 0000000000..b3a10a783f --- /dev/null +++ b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py @@ -0,0 +1,171 @@ +# (C) British Crown Copyright 2015 - 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 . +""" +Unit tests for the function +:func:`iris.analysis.cartography.gridcell_angles`. + +""" +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 numpy as np +import numpy.ma as ma + +import cartopy.crs as ccrs +from iris.cube import Cube +from iris.coords import DimCoord, AuxCoord +import iris.coord_systems +from iris.analysis.cartography import unrotate_pole + +from iris.analysis.cartography import (gridcell_angles, + rotate_grid_vectors) + + +def _rotated_grid_sample(pole_lat=15, pole_lon=-180, + lon_bounds=np.linspace(-30, 30, 8, endpoint=True), + lat_bounds=np.linspace(-30, 30, 8, endpoint=True)): + # Calculate *true* lat_bounds+lon_bounds for the rotated grid. + lon_bounds = np.array(lon_bounds, dtype=float) + lat_bounds = np.array(lat_bounds, dtype=float) + # Construct centrepoints. + lons = 0.5 * (lon_bounds[:-1] + lon_bounds[1:]) + lats = 0.5 * (lat_bounds[:-1] + lat_bounds[1:]) + # Convert all to full 2d arrays. + lon_bounds, lat_bounds = np.meshgrid(lon_bounds, lat_bounds) + lons, lats = np.meshgrid(lons, lats) + # Calculate true lats+lons for all points. + lons_true_bds, lats_true_bds = unrotate_pole(lon_bounds, lat_bounds, + pole_lon, pole_lat) + lons_true, lats_true = unrotate_pole(lons, lats, pole_lon, pole_lat) + # Make the 'unified' bounds into contiguous (ny, nx, 4) arrays. + def expand_unified_bds(bds): + ny, nx = bds.shape + bds_4 = np.zeros((ny - 1, nx - 1, 4)) + bds_4[:, :, 0] = bds[:-1, :-1] + bds_4[:, :, 1] = bds[:-1, 1:] + bds_4[:, :, 2] = bds[1:, 1:] + bds_4[:, :, 3] = bds[1:, :-1] + return bds_4 + + lon_true_bds4, lat_true_bds4 = (expand_unified_bds(bds) + for bds in (lons_true_bds, lats_true_bds)) + # Make these into a 2d-latlon grid for a cube + cube = Cube(np.zeros(lon_true_bds4.shape[:-1])) + co_x = AuxCoord(lons_true, bounds=lon_true_bds4, + standard_name='longitude', units='degrees') + co_y = AuxCoord(lats_true, bounds=lat_true_bds4, + standard_name='latitude', units='degrees') + cube.add_aux_coord(co_x, (0, 1)) + cube.add_aux_coord(co_y, (0, 1)) + return cube + + +class TestGridcellAngles(tests.IrisTest): + def test_values(self): + # Construct a rotated-pole grid and check angle calculation. + testcube = _rotated_grid_sample() + angles_cube = gridcell_angles(testcube) + angles_cube.convert_units('radians') + + # testing phase... + print(np.rad2deg(angles_cube.data)) + + import matplotlib.pyplot as plt + plt.switch_backend('tkagg') + ax = plt.axes(projection=ccrs.Orthographic(central_longitude=0.0, + central_latitude=90.0,)) + ax.coastlines() + ax.gridlines() + for i_bnd in range(4): + color = ['black', 'red', 'blue', 'magenta'][i_bnd] + plt.plot(testcube.coord('longitude').bounds[..., i_bnd], + testcube.coord('latitude').bounds[..., i_bnd], + '+', markersize=10., markeredgewidth=2., + markerfacecolor=color, markeredgecolor=color, + transform=ccrs.PlateCarree()) + + + # Show plain 0,1 + 1,0 vectors unrotated at the given points. + pts_shape = testcube.coord('longitude').shape + u0 = np.ones(pts_shape) + v0 = np.zeros(pts_shape) + u1 = v0.copy() + v1 = u0.copy() + +# u0_cube, u1_cube, v0_cube, v1_cube = [testcube.copy(data=aa) +# for aa in (u0, v0, u1, v1)] +# u0r_cube, v0r_cube = rotate_grid_vectors( +# u0_cube, v0_cube, grid_angles_cube=angles_cube) + + scale = 4.0e-6 + plt.quiver(testcube.coord('longitude').points, + testcube.coord('latitude').points, + u0, v0, color='blue', linewidth=0.5, scale_units='xy', scale=scale, + transform=ccrs.PlateCarree()) + plt.quiver(testcube.coord('longitude').points, + testcube.coord('latitude').points, + u1, v1, color='red', linewidth=0.5, scale_units='xy', scale=scale, + transform=ccrs.PlateCarree()) + +# plt.quiver(testcube.coord('longitude').points, +# testcube.coord('latitude').points, +# u0r_cube.data, v0r_cube.data, +# color='red', +# transform=ccrs.PlateCarree()) + + # Also draw small lines pointing at the correct angle. + x0s = testcube.coord('longitude').points + y0s = testcube.coord('latitude').points + ny, nx = x0s.shape + size_degrees = 5.0 + angles = angles_cube.copy() + angles.convert_units('radians') + angles = angles.data + lats = testcube.coord('latitude').copy() + lats.convert_units('radians') + lats = lats.points + dys = size_degrees * np.sin(angles) / np.cos(-lats) + dxs = size_degrees * np.cos(angles) + x1s = x0s + dxs + y1s = y0s + dys + for iy in range(ny): + for ix in range(nx): + plt.plot([x0s[iy, ix], x1s[iy, ix]], + [y0s[iy, ix], y1s[iy, ix]], + 'o-', markersize=4., markeredgewidth=0., + color='green', + transform=ccrs.PlateCarree()) + + + + ax.set_global() + plt.show() + + + self.assertArrayAllClose( + angles_cube.data, + [[100.0, 100.0, 100.0], + [100.0, 100.0, 100.0], + [100.0, 100.0, 100.0]]) + + +if __name__ == "__main__": + tests.main() From 393489eb6f827a4675fb9126bc2d066254b28371 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 25 Jul 2018 20:22:48 +0100 Subject: [PATCH 02/24] Roughly working, snapshotted with complex test plot code, to be reduced. --- lib/iris/analysis/_grid_angles.py | 136 ++++++-- lib/iris/analysis/cartography.py | 2 +- .../cartography/test_gridcell_angles.py | 323 +++++++++++++++--- 3 files changed, 375 insertions(+), 86 deletions(-) diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py index 6f7dcf15db..f5fa94a256 100644 --- a/lib/iris/analysis/_grid_angles.py +++ b/lib/iris/analysis/_grid_angles.py @@ -13,26 +13,18 @@ def _3d_xyz_from_latlon(lon, lat): Returns: - x, y, z : (array, dtype=float64) - cartesian coordinates on a unit sphere. + xyz : (array, dtype=float64) + cartesian coordinates on a unit sphere. Dimension 0 maps x,y,z. """ lon1 = np.deg2rad(lon).astype(np.float64) lat1 = np.deg2rad(lat).astype(np.float64) - old_way = True - if old_way: - x3 = np.empty((3,) + lon.shape, dtype=float) - x3[0] = np.cos(lat1) * np.cos(lon1) - x3[1] = np.cos(lat1) * np.sin(lon1) - x3[2] = np.sin(lat1) - result = x3 - else: - x = np.cos(lat1) * np.cos(lon1) - y = np.cos(lat1) * np.sin(lon1) - z = np.sin(lat1) + x = np.cos(lat1) * np.cos(lon1) + y = np.cos(lat1) * np.sin(lon1) + z = np.sin(lat1) - result = np.concatenate([array[np.newaxis] for array in (x, y, z)]) + result = np.concatenate([array[np.newaxis] for array in (x, y, z)]) return result @@ -61,35 +53,82 @@ def _latlon_from_xyz(xyz): def _angle(p, q, r): """ - Return angle (in _radians_) of grid wrt local east. Anticlockwise +ve, as usual. - {P, Q, R} are consecutve points in the same row, eg {v(i,j),f(i,j),v(i+1,j)}, or {T(i-1,j),T(i,j),T(i+1,j)} - Calculate dot product of PR with lambda_hat at Q. This gives us cos(required angle). - Disciminate between +/- angles by comparing latitudes of P and R. - p, q, r, are all 2-element arrays [lon, lat] of angles in degrees. + Return angle (in _radians_) of grid wrt local east. + Anticlockwise +ve, as usual. + {P, Q, R} are consecutive points in the same row, + eg {v(i,j),f(i,j),v(i+1,j)}, or {T(i-1,j),T(i,j),T(i+1,j)} + Calculate dot product of PR with lambda_hat at Q. + This gives us cos(required angle). + Disciminate between +/- angles by comparing latitudes of P and R. + p, q, r, are all 2-element arrays [lon, lat] of angles in degrees. """ - q1 = np.deg2rad(q) +# old_style = True + old_style = False + if old_style: + mid_lons = np.deg2rad(q[0]) - pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) - pr_norm = np.sqrt(np.sum(pr**2, axis=0)) - pr_top = pr[1] * np.cos(q1[0]) - pr[0] * np.sin(q1[0]) + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + pr_norm = np.sqrt(np.sum(pr**2, axis=0)) + pr_top = pr[1] * np.cos(mid_lons) - pr[0] * np.sin(mid_lons) - index = np.where(pr_norm == 0) - pr_norm[index] = 1 + index = pr_norm == 0 + pr_norm[index] = 1 - cosine = np.maximum(np.minimum(pr_top / pr_norm, 1), -1) - cosine[index] = 0 + cosine = np.maximum(np.minimum(pr_top / pr_norm, 1), -1) + cosine[index] = 0 - psi = np.arccos(cosine) * np.sign(r[1] - p[1]) - psi[index] = np.nan + psi = np.arccos(cosine) * np.sign(r[1] - p[1]) + psi[index] = np.nan + else: + # Calculate unit vectors. + midpt_lons, midpt_lats = q[0], q[1] + lmb_r, phi_r = (np.deg2rad(arr) for arr in (midpt_lons, midpt_lats)) + phi_hatvec_x = -np.sin(phi_r) * np.cos(lmb_r) + phi_hatvec_y = -np.sin(phi_r) * np.sin(lmb_r) + phi_hatvec_z = np.cos(phi_r) + shape_xyz = (1,) + midpt_lons.shape + phi_hatvec = np.concatenate([arr.reshape(shape_xyz) + for arr in (phi_hatvec_x, + phi_hatvec_y, + phi_hatvec_z)]) + lmb_hatvec_z = np.zeros(midpt_lons.shape) + lmb_hatvec_y = np.cos(lmb_r) + lmb_hatvec_x = -np.sin(lmb_r) + lmb_hatvec = np.concatenate([arr.reshape(shape_xyz) + for arr in (lmb_hatvec_x, + lmb_hatvec_y, + lmb_hatvec_z)]) + + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + + # Dot products to form true-northward / true-eastward projections. + pr_cmpt_e = np.sum(pr * lmb_hatvec, axis=0) + pr_cmpt_n = np.sum(pr * phi_hatvec, axis=0) + psi = np.arctan2(pr_cmpt_n, pr_cmpt_e) + + # TEMPORARY CHECKS: + # ensure that the two unit vectors are perpendicular. + dotprod = np.sum(phi_hatvec * lmb_hatvec, axis=0) + assert np.allclose(dotprod, 0.0) + # ensure that the vector components carry the original magnitude. + mag_orig = np.sum(pr * pr) + mag_rot = np.sum(pr_cmpt_e * pr_cmpt_e) + np.sum(pr_cmpt_n * pr_cmpt_n) + rtol = 1.e-3 + check = np.allclose(mag_rot, mag_orig, rtol=rtol) + if not check: + print (mag_rot, mag_orig) + assert np.allclose(mag_rot, mag_orig, rtol=rtol) return psi -def gridcell_angles(x, y=None): +def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): """ Calculate gridcell orientation angles. + Args: + The inputs (x [,y]) can be any of the folliwing : * x (:class:`~iris.cube.Cube`): @@ -105,6 +144,16 @@ def gridcell_angles(x, y=None): longitude and latitude cell bounds, in degrees. The last index maps cell corners anticlockwise from bottom-left. + Optional Args: + + * cell_angle_boundpoints (string): + Controls which gridcell bounds locations are used to calculate angles, + if the inputs are bounds or bounded coordinates. + Valid values are 'lower-left, lower-right', which takes the angle from + the lower left to the lower right corner, and 'mid-lhs, mid-rhs' which + takes an angles between the average of the left-hand and right-hand + pairs of corners. The default is 'mid-lhs, mid-rhs'. + Returns: angles : (2-dimensional cube) @@ -184,8 +233,20 @@ def gridcell_angles(x, y=None): # Get lhs and rhs locations by averaging top+bottom each side. # NOTE: so with bounds, we *don't* need full circular longitudes. xyz = _3d_xyz_from_latlon(x, y) - lhs_xyz = 0.5 * (xyz[..., 0] + xyz[..., 3]) - rhs_xyz = 0.5 * (xyz[..., 1] + xyz[..., 2]) + angle_boundpoints_vals = {'mid-lhs, mid-rhs': '03_to_12', + 'lower-left, lower-right': '0_to_1'} + bounds_pos = angle_boundpoints_vals.get(cell_angle_boundpoints) + if bounds_pos == '0_to_1': + lhs_xyz = xyz[..., 0] + rhs_xyz = xyz[..., 1] + elif bounds_pos == '03_to_12': + lhs_xyz = 0.5 * (xyz[..., 0] + xyz[..., 3]) + rhs_xyz = 0.5 * (xyz[..., 1] + xyz[..., 2]) + else: + msg = ('unrecognised cell_angle_boundpoints of "{}", ' + 'must be one of {}') + raise ValueError(msg.format(cell_angle_boundpoints, + list(angle_boundpoints_vals.keys()))) if not x_coord: # Create bounded coords for result cube. # Use average lhs+rhs points in 3d to get 'mid' points, as coords @@ -215,7 +276,9 @@ def gridcell_angles(x, y=None): return result -def true_vectors_from_grid_vectors(u_cube, v_cube, grid_angles_cube=None): +def true_vectors_from_grid_vectors(u_cube, v_cube, + grid_angles_cube=None, + grid_angles_kwargs=None): """ Rotate distance vectors from grid-oriented to true-latlon-oriented. @@ -233,6 +296,10 @@ def true_vectors_from_grid_vectors(u_cube, v_cube, grid_angles_cube=None): If not provided, grid angles are estimated from 'u_cube' using the :func:`gridcell_angles` method. + * grid_angles_kwargs : (dict or None) + Additional keyword args to be passed to the :func:`gridcell_angles` + method, if it is used. + Returns: true_u, true_v : (cube) @@ -242,7 +309,8 @@ def true_vectors_from_grid_vectors(u_cube, v_cube, grid_angles_cube=None): """ u_out, v_out = (cube.copy() for cube in (u_cube, v_cube)) if not grid_angles_cube: - grid_angles_cube = gridcell_angles(u_cube) + grid_angles_kwargs = grid_angles_kwargs or {} + grid_angles_cube = gridcell_angles(u_cube, **grid_angles_kwargs) gridangles = grid_angles_cube.copy() gridangles.convert_units('radians') uu, vv, aa = (cube.data for cube in (u_out, v_out, gridangles)) diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index 1c55d48fd3..16c99c83a8 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2017, Met Office +# (C) British Crown Copyright 2010 - 2018, Met Office # # This file is part of Iris. # diff --git a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py index b3a10a783f..a73988f668 100644 --- a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py +++ b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015 - 2016, Met Office +# (C) British Crown Copyright 2018, Met Office # # This file is part of Iris. # @@ -38,10 +38,13 @@ from iris.analysis.cartography import (gridcell_angles, rotate_grid_vectors) +import matplotlib.pyplot as plt +from orca_utils.plot_testing.blockplot_from_bounds import blockplot_2dll + def _rotated_grid_sample(pole_lat=15, pole_lon=-180, - lon_bounds=np.linspace(-30, 30, 8, endpoint=True), - lat_bounds=np.linspace(-30, 30, 8, endpoint=True)): + lon_bounds=np.linspace(-30, 30, 6, endpoint=True), + lat_bounds=np.linspace(-30, 30, 6, endpoint=True)): # Calculate *true* lat_bounds+lon_bounds for the rotated grid. lon_bounds = np.array(lon_bounds, dtype=float) lat_bounds = np.array(lat_bounds, dtype=float) @@ -79,19 +82,151 @@ def expand_unified_bds(bds): class TestGridcellAngles(tests.IrisTest): + def _singlecell_30deg_cube(self, x0=90., y0=0., dx=20., dy=10.): + x_pts = np.array([[x0]]) + y_pts = np.array([[y0]]) + x_bds = x0 + dx * np.array([[[-1., 1, 0.5, -1.5]]]) +# self.assertArrayAllClose(x_bds, np.array([[[70., 110, 100, 60]]])) + y_bds = y0 + dy * np.array([[[-1., 1, 3, 1]]]) +# self.assertArrayAllClose(y_bds, np.array([[[-10., 10, 30, 10]]])) + co_x = AuxCoord(points=x_pts, bounds=x_bds, + standard_name='longitude', units='degrees') + co_y = AuxCoord(points=y_pts, bounds=y_bds, + standard_name='latitude', units='degrees') + cube = Cube(np.zeros((1, 1))) + cube.add_aux_coord(co_x, (0, 1)) + cube.add_aux_coord(co_y, (0, 1)) + return cube + + def _singlecell_diamond_cube(self, x0=90., y0=0., dy=10., dx_eq=None): + if dx_eq is None: + dx_eq = dy + x_pts = np.array([[x0]]) + y_pts = np.array([[y0]]) + dx = dx_eq / np.cos(np.deg2rad(y0)) + x_bds = np.array([[[x0, x0 + dx, x0, x0 - dx]]]) + y_bds = np.array([[[y0 - dy, y0, y0 + dy, y0]]]) + co_x = AuxCoord(points=x_pts, bounds=x_bds, + standard_name='longitude', units='degrees') + co_y = AuxCoord(points=y_pts, bounds=y_bds, + standard_name='latitude', units='degrees') + cube = Cube(np.zeros((1, 1))) + cube.add_aux_coord(co_x, (0, 1)) + cube.add_aux_coord(co_y, (0, 1)) + return cube + + def test_single_cell_equatorial(self): + plt.switch_backend('tkagg') + plt.figure(figsize=(10,10)) + ax = plt.axes(projection=ccrs.Mercator()) +# ax = plt.axes(projection=ccrs.NorthPolarStereo()) +# ax = plt.axes(projection=ccrs.Orthographic(central_longitude=90., +# central_latitude=30.)) + + lon0 = 90.0 + dy = 2.0 + y_0, y_n, ny = -80, 80, 9 + angles = [] + for lat in np.linspace(y_0, y_n, ny): + cube = self._singlecell_diamond_cube(x0=lon0, y0=lat, dy=dy) + angles_cube = gridcell_angles(cube, +# cell_angle_boundpoints='mid-lhs, mid-rhs') + cell_angle_boundpoints='lower-left, lower-right') + tmp_cube = angles_cube.copy() + tmp_cube.convert_units('degrees') + print('') + print(lat) + co_x, co_y = (cube.coord(axis=ax) for ax in ('x', 'y')) + print() + print(' at : {}, {}'.format(co_x.points[0, 0], co_y.points[0, 0])) + print(' x-bds:') + print(co_x.bounds) + print(' y-bds:') + print(co_y.bounds) + angle = tmp_cube.data[0, 0] + angles.append(angle) + print(angle) + blockplot_2dll(cube) + + ax.coastlines() + ax.set_global() + + # Plot constant NEly (45deg) arrows. + xx = np.array([lon0] * ny) + yy = np.linspace(y_0, y_n, ny) - dy + uu = np.array([1.0] * ny) + plt.quiver(xx, yy, + uu, np.cos(np.deg2rad(yy)), + zorder=2, color='red', + scale_units='xy', + transform=ccrs.PlateCarree()) + + # Also plot returned angles. + angles_arr_rad = np.deg2rad(angles) + u_arr = uu * np.cos(angles_arr_rad) + v_arr = uu * np.sin(angles_arr_rad) * np.cos(np.deg2rad(yy)) + + plt.quiver(xx, yy, + u_arr, + v_arr, + zorder=2, color='magenta', + scale_units='xy', + width=0.005, + scale=0.2e-6, +# width=0.5, + transform=ccrs.PlateCarree()) + + plt.show() + + def test_values(self): # Construct a rotated-pole grid and check angle calculation. testcube = _rotated_grid_sample() - angles_cube = gridcell_angles(testcube) + + cell_angle_boundpoints = 'mid-lhs, mid-rhs' +# cell_angle_boundpoints = 'lower-left, lower-right' +# cell_angle_boundpoints = 'garble' + angles_cube = gridcell_angles( + testcube, + cell_angle_boundpoints=cell_angle_boundpoints) angles_cube.convert_units('radians') # testing phase... print(np.rad2deg(angles_cube.data)) - + import matplotlib.pyplot as plt plt.switch_backend('tkagg') - ax = plt.axes(projection=ccrs.Orthographic(central_longitude=0.0, - central_latitude=90.0,)) + +# plot_map = 'north_polar_stereographic' +# plot_map = 'plate_carree' +# plot_map = 'mercator' + plot_map = 'north_polar_orthographic' + if plot_map == 'plate_carree': + scale = 0.1 + map_proj = ccrs.PlateCarree() + elif plot_map == 'mercator': + scale = 3.0e-6 + map_proj = ccrs.Mercator() + map_proj._threshold *= 0.01 + elif plot_map == 'north_polar_orthographic': + scale = 3.0e-6 + map_proj = ccrs.Orthographic(central_longitude=0.0, + central_latitude=90.0,) + map_proj._threshold *= 0.01 + elif plot_map == 'north_polar_stereographic': + scale = 3.0e-6 + map_proj = ccrs.NorthPolarStereo() + else: + assert 0 + + ax = plt.axes(projection=map_proj) + data_proj = ccrs.PlateCarree() + + deg_scale = 10.0 + +# angles = 'uv' + angles = 'xy' + ax.coastlines() ax.gridlines() for i_bnd in range(4): @@ -100,71 +235,157 @@ def test_values(self): testcube.coord('latitude').bounds[..., i_bnd], '+', markersize=10., markeredgewidth=2., markerfacecolor=color, markeredgecolor=color, - transform=ccrs.PlateCarree()) + transform=data_proj) - # Show plain 0,1 + 1,0 vectors unrotated at the given points. + # Show plain 0,1 + 1,0 (PlateCarree) vectors unrotated at the given points. pts_shape = testcube.coord('longitude').shape + ny, nx = pts_shape u0 = np.ones(pts_shape) v0 = np.zeros(pts_shape) u1 = v0.copy() v1 = u0.copy() -# u0_cube, u1_cube, v0_cube, v1_cube = [testcube.copy(data=aa) -# for aa in (u0, v0, u1, v1)] -# u0r_cube, v0r_cube = rotate_grid_vectors( -# u0_cube, v0_cube, grid_angles_cube=angles_cube) + x0s = testcube.coord('longitude').points + y0s = testcube.coord('latitude').points + yscale = np.cos(np.deg2rad(y0s)) + plt.quiver(x0s, y0s, u0, v0 * yscale, + color='blue', width=0.005, + headwidth=2., # headlength=1.0, headaxislength=0.7, + angles=angles, + scale_units='xy', scale=scale, + transform=data_proj) + plt.quiver(x0s, y0s, u1, v1 * yscale, + color='red', width=0.005, + headwidth=2., # headlength=1.0, headaxislength=0.7, + angles=angles, + scale_units='xy', scale=scale, + transform=data_proj) - scale = 4.0e-6 - plt.quiver(testcube.coord('longitude').points, - testcube.coord('latitude').points, - u0, v0, color='blue', linewidth=0.5, scale_units='xy', scale=scale, - transform=ccrs.PlateCarree()) - plt.quiver(testcube.coord('longitude').points, - testcube.coord('latitude').points, - u1, v1, color='red', linewidth=0.5, scale_units='xy', scale=scale, - transform=ccrs.PlateCarree()) + # Add 45deg arrows (NEly), still on a PlateCarree map. + plt.quiver(x0s, y0s, v1, v1 * yscale, + color='green', width=0.005, + headwidth=2., # headlength=1.0, headaxislength=0.7, + angles=angles, + scale_units='xy', scale=scale, + transform=data_proj) -# plt.quiver(testcube.coord('longitude').points, -# testcube.coord('latitude').points, -# u0r_cube.data, v0r_cube.data, -# color='red', -# transform=ccrs.PlateCarree()) - # Also draw small lines pointing at the correct angle. - x0s = testcube.coord('longitude').points - y0s = testcube.coord('latitude').points - ny, nx = x0s.shape - size_degrees = 5.0 - angles = angles_cube.copy() - angles.convert_units('radians') - angles = angles.data - lats = testcube.coord('latitude').copy() - lats.convert_units('radians') - lats = lats.points - dys = size_degrees * np.sin(angles) / np.cos(-lats) - dxs = size_degrees * np.cos(angles) - x1s = x0s + dxs - y1s = y0s + dys + + # + # Repeat the above plotting short lines INSTEAD of quiver. + # + u0d = x0s + deg_scale * u0 + v0d = y0s + deg_scale * v0 + u1d = x0s + deg_scale * u1 + v1d = y0s + deg_scale * v1 + u2d = x0s + deg_scale * u0 + v2d = y0s + deg_scale * v1 for iy in range(ny): for ix in range(nx): - plt.plot([x0s[iy, ix], x1s[iy, ix]], - [y0s[iy, ix], y1s[iy, ix]], - 'o-', markersize=4., markeredgewidth=0., - color='green', - transform=ccrs.PlateCarree()) + plt.plot([x0s[iy, ix], u0d[iy, ix]], + [y0s[iy, ix], v0d[iy, ix]], + ':', color='blue', linewidth=0.5, + transform=data_proj) + plt.plot([x0s[iy, ix], u1d[iy, ix]], + [y0s[iy, ix], v1d[iy, ix]], + ':', color='red', linewidth=0.5, + transform=data_proj) + plt.plot([x0s[iy, ix], u2d[iy, ix]], + [y0s[iy, ix], v2d[iy, ix]], + ':', color='green', linewidth=0.5, + transform=data_proj) + + + # Overplot BL-BR and BL-TL lines from the cell bounds. + co_lon, co_lat = [testcube.coord(name).copy() + for name in ('longitude', 'latitude')] + for co in (co_lon, co_lat): + co.convert_units('degrees') + lon_bds, lat_bds = [co.bounds for co in (co_lon, co_lat)] +# ny, nx = lon_bds.shape[:-1] + for iy in range(ny): + for ix in range(nx): + x0, y0 = lon_bds[iy, ix, 0], lat_bds[iy, ix, 0] + x1, y1 = lon_bds[iy, ix, 1], lat_bds[iy, ix, 1] + x2, y2 = lon_bds[iy, ix, 3], lat_bds[iy, ix, 3] + plt.plot([x0, x1], [y0, y1], 'x-', + color='orange', + transform=data_proj) + plt.plot([x0, x2], [y0, y2], 'x-', + color='orange', linestyle='--', + transform=data_proj) + + # Plot U0, rotated by cell angles, also at cell bottom-lefts. + u0_cube, u1_cube, v0_cube, v1_cube = [testcube.copy(data=aa) + for aa in (u0, v0, u1, v1)] + u0r_cube, v0r_cube = rotate_grid_vectors( + u0_cube, v0_cube, grid_angles_cube=angles_cube) + u0r, v0r = [cube.data for cube in (u0r_cube, v0r_cube)] + + xbl, ybl = lon_bds[..., 0], lat_bds[..., 0] + # + # Replace quiver here with delta-based lineplot + # + urd = xbl + deg_scale * u0r + vrd = ybl + deg_scale * v0r * yscale + for iy in range(ny): + for ix in range(nx): + plt.plot([xbl[iy, ix], urd[iy, ix]], + [ybl[iy, ix], vrd[iy, ix]], + ':', color='magenta', linewidth=2.5, + transform=data_proj) + # Show this is the SAME as lineplot + plt.quiver(xbl, ybl, u0r, v0r * yscale, + color='magenta', width=0.01, + headwidth=1.2, # headlength=1.0, headaxislength=0.7, + angles=angles, + scale_units='xy', scale=scale, + transform=data_proj) + + plt.suptitle('angles from "{}"'.format(cell_angle_boundpoints)) + +# # Also draw small lines pointing at the correct (TRUE, not ) angle. +# ny, nx = x0s.shape +# size_degrees = 1.0 +# angles = angles_cube.copy() +# angles.convert_units('radians') +# angles = angles.data +# lats = testcube.coord('latitude').copy() +# lats.convert_units('radians') +# lats = lats.points +# dxs = size_degrees * u0.copy() #* np.cos(angles) +# dys = size_degrees * u0.copy() # / np.sqrt(np.cos(lats)) +# x1s = x0s + dxs +# y1s = y0s + dys +## for iy in range(ny): +## for ix in range(nx): +## plt.plot([x0s[iy, ix], x1s[iy, ix]], +## [y0s[iy, ix], y1s[iy, ix]], +## 'o-', markersize=4., markeredgewidth=0., +## color='green', # scale_units='xy', scale=scale, +## transform=data_proj) +# plt.quiver(x0s, y0s, dxs, dys, +# color='green', linewidth=0.2, +# angles=angles, +# scale_units='xy', scale=scale * 0.6, +# transform=data_proj) ax.set_global() - plt.show() +# plt.show() + angles_cube.convert_units('degrees') self.assertArrayAllClose( angles_cube.data, - [[100.0, 100.0, 100.0], - [100.0, 100.0, 100.0], - [100.0, 100.0, 100.0]]) + [[33.421, 17.928, 0., -17.928, -33.421], + [41.981, 24.069, 0., -24.069, -41.981], + [56.624, 37.809, 0., -37.809, -56.624], + [79.940, 74.227, 0., -74.227, -79.940], + [107.313, 126.361, -180., -126.361, -107.313]], + atol=0.002) if __name__ == "__main__": From d8bb331ca2774f34f538b9746e4cdc43e935dac8 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Thu, 26 Jul 2018 16:14:04 +0100 Subject: [PATCH 03/24] Small improvements. --- lib/iris/analysis/_grid_angles.py | 79 ++++++++++++++++--- .../cartography/test_gridcell_angles.py | 2 +- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py index f5fa94a256..1746834a65 100644 --- a/lib/iris/analysis/_grid_angles.py +++ b/lib/iris/analysis/_grid_angles.py @@ -1,3 +1,27 @@ +# (C) British Crown Copyright 2010 - 2018, 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 . +""" +Code to implement vector rotation by angles, and inferring gridcell angles +from coordinate points and bounds. + +""" +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + import numpy as np import iris @@ -125,23 +149,41 @@ def _angle(p, q, r): def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): """ - Calculate gridcell orientation angles. + Calculate gridcell orientations for an arbitrary 2-dimensional grid. + + The input grid is defined by two 2-dimensional coordinate arrays with the + same dimensions (ny, nx), specifying the geolocations of a 2D mesh. + + Input values may be coordinate points (ny, nx) or bounds (ny, nx, 4). + However, if points, the edges in the X direction are assumed to be + connected by wraparound. + + Input can be either two arrays, two coordinates, or a single cube + containing two suitable coordinates identified with the 'x' and'y' axes. Args: The inputs (x [,y]) can be any of the folliwing : * x (:class:`~iris.cube.Cube`): - a grid cube with 2D longitude and latitude coordinates. + a grid cube with 2D X and Y coordinates, identified by 'axis'. + The coordinates must be 2-dimensional with the same shape. + The two dimensions represent grid dimensions in the order Y, then X. * x, y (:class:`~iris.coords.Coord`): - longitude and latitude coordinates. + X and Y coordinates, specifying grid locations on the globe. + The coordinates must be 2-dimensional with the same shape. + The two dimensions represent grid dimensions in the order Y, then X. + If there is no coordinate system, they are assumed to be true + longitudes and latitudes. Units must convertible to 'degrees'. * x, y (2-dimensional arrays of same shape (ny, nx)): longitude and latitude cell center locations, in degrees. + The two dimensions represent grid dimensions in the order Y, then X. * x, y (3-dimensional arrays of same shape (ny, nx, 4)): longitude and latitude cell bounds, in degrees. + The first two dimensions are grid dimensions in the order Y, then X. The last index maps cell corners anticlockwise from bottom-left. Optional Args: @@ -167,9 +209,11 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): the average of the 4 bounds. """ - if hasattr(x, 'core_data'): - # N.B. only "true" lats + longs will do : Cannot handle rotated ! - x, y = x.coord('longitude'), x.coord('latitude') + cube = None + if hasattr(x, 'add_aux_coord'): + # Passed a cube : extract 'x' and ;'y' axis coordinates. + cube = x # Save for later checking. + x, y = cube.coord(axis='x'), cube.coord(axis='y') # Now should have either 2 coords or 2 arrays. if not hasattr(x, 'shape') and hasattr(y, 'shape'): @@ -178,7 +222,7 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): raise ValueError(msg.format(type(x), type(y))) x_coord, y_coord = None, None - if isinstance(x, iris.coords.Coord) and isinstance(y, iris.coords.Coord): + if hasattr(x, 'bounds') and hasattr(y, 'bounds'): x_coord, y_coord = x.copy(), y.copy() x_coord.convert_units('degrees') y_coord.convert_units('degrees') @@ -203,9 +247,10 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): else: x, y = x_coord.points, y_coord.points - elif isinstance(x, iris.coords.Coord) or isinstance(y, iris.coords.Coord): + elif hasattr(x, 'bounds') or hasattr(y, 'bounds'): + # One was a Coord, and the other not ? is_and_not = ('x', 'y') - if isinstance(y, iris.coords.Coord): + if hasattr(y, 'bounds'): is_and_not = reversed(is_and_not) msg = 'Input {!r} is a Coordinate, but {!r} is not.' raise ValueError(*is_and_not) @@ -282,6 +327,18 @@ def true_vectors_from_grid_vectors(u_cube, v_cube, """ Rotate distance vectors from grid-oriented to true-latlon-oriented. + .. Note:: + + This operation overlaps somewhat in function with + :func:`iris.analysis.cartography.rotate_winds`. + However, that routine only rotates vectors according to transformations + between coordinate systems. + This function, by contrast, can rotate vectors by arbitrary angles. + Most commonly, the angles are estimated solely from grid sampling + points, using :func:`gridcell_angles` : This allows operation on + complex meshes defined by two-dimensional coordinates, such as most + ocean grids. + Args: * u_cube, v_cube : (cube) @@ -306,6 +363,10 @@ def true_vectors_from_grid_vectors(u_cube, v_cube, Cubes of true-north oriented vector components. Units are same as inputs. + .. Note:: + + Vector magnitudes will always be the same as the inputs. + """ u_out, v_out = (cube.copy() for cube in (u_cube, v_cube)) if not grid_angles_cube: diff --git a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py index a73988f668..de00d2bc76 100644 --- a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py +++ b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py @@ -374,7 +374,7 @@ def test_values(self): ax.set_global() -# plt.show() + plt.show() angles_cube.convert_units('degrees') From c902ce3460279588e4d7cf0d9523372479f9d1d2 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Fri, 20 Jul 2018 10:19:20 +0100 Subject: [PATCH 04/24] Support plotting 2D bounded coords --- lib/iris/coords.py | 129 ++++++++++-- lib/iris/plot.py | 160 ++++++++++++--- lib/iris/tests/stock.py | 217 ++++++++++++--------- lib/iris/tests/unit/plot/test_2d_coords.py | 158 +++++++++++++++ 4 files changed, 529 insertions(+), 135 deletions(-) create mode 100644 lib/iris/tests/unit/plot/test_2d_coords.py diff --git a/lib/iris/coords.py b/lib/iris/coords.py index e4a8eb35f2..57ae723205 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -143,6 +143,85 @@ def __new__(cls, name_or_coord, minimum, maximum, 'groupby_point, groupby_slice') +def _discontinuity_in_2d_bounds(bds, abs_tol=1e-4): + """ + Check bounds of a 2-dimensional coordinate are contiguous + Args: + bds: Array of bounds of shape (X,Y,4) + abs_tol: tolerance + + Returns: + Bool, if there are no discontinuities + absolute difference along the x axis + absolute difference along the y axis + + """ + # Check form is (ny, nx, 4) + if not bds.ndim == 3 and bds.shape[2] == 4: + raise ValueError('2D coordinates must have 4 bounds per point ' + 'for 2D coordinate plotting') + + # Check ordering: + # i i+1 + # j @0 @1 + # j+1 @3 @2 + def mod360_diff(x1, x2): + diff = x1 - x2 + diff = (diff + 360.0 + 180.0) % 360.0 - 180.0 + return diff + + # Compare cell with the cell next to it (i+1) + diffs_along_x = mod360_diff(bds[:, :-1, 1], bds[:, 1:, 0]) + # Compare cell with the cell above it (j+1) + diffs_along_y = mod360_diff(bds[:-1, :, 3], bds[1:, :, 0]) + + def eq_diffs(x1): + return np.all(np.abs(x1) < abs_tol) + + match_y0_x1 = eq_diffs(diffs_along_x) + match_y1_x0 = eq_diffs(diffs_along_y) + + all_eq = match_y0_x1 and match_y1_x0 + + return all_eq, np.abs(diffs_along_x), np.abs(diffs_along_y) + + +def _get_2d_coord_bound_grid(bds): + """ + Function used that takes a bounds array for a 2-D coordinate variable with + 4 sides and returns the bounds grid. + + Cf standards requires the four vertices of the cell to be traversed + anti-clockwise if the coordinates are defined in a right handed coordinate + system. + + selects the zeroth vertex of each cell and then adds the column the first + vertex at the end. For the top row it uses the thirs vertex, with the + second added on to the end. + e.g. + # 0-0-0-0-1 + # 0-0-0-0-1 + # 3-3-3-3-2 + + + Args: + bounds: array of shape (X,Y,4) + + Returns: + array of shape (X+1, Y+1) + + """ + bds_shape = bds.shape + result = np.zeros((bds_shape[0] + 1, bds_shape[1] + 1)) + + result[:-1, :-1] = bds[:, :, 0] + result[:-1, -1] = bds[:, -1, 1] + result[-1, :-1] = bds[-1, :, 3] + result[-1, -1] = bds[-1, -1, 2] + + return result + + class Cell(collections.namedtuple('Cell', ['point', 'bound'])): """ An immutable representation of a single cell of a coordinate, including the @@ -951,15 +1030,22 @@ def cells(self): return _CellIterator(self) def _sanity_check_contiguous(self): - if self.ndim != 1: - raise iris.exceptions.CoordinateMultiDimError( - 'Invalid operation for {!r}. Contiguous bounds are not defined' - ' for multi-dimensional coordinates.'.format(self.name())) - if self.nbounds != 2: - raise ValueError( - 'Invalid operation for {!r}, with {} bounds. Contiguous bounds' - ' are only defined for coordinates with 2 bounds.'.format( - self.name(), self.nbounds)) + if self.ndim == 1: + if self.nbounds != 2: + raise ValueError('Invalid operation for {!r}, with {} bounds. ' + 'Contiguous bounds are only defined for 1D ' + 'coordinates with 2 bounds.'.format + (self.name(), self.nbounds)) + elif self.ndim == 2: + if self.nbounds != 4: + raise ValueError('Invalid operation for {!r}, with {} bounds. ' + 'Contiguous bounds are only defined for 2D ' + 'coordinates with 4 bounds.'.format + (self.name(), self.nbounds)) + else: + raise ValueError('Invalid operation for {!r}. Contiguous bounds ' + 'are not defined for coordinates with more than ' + '2 dimensions.'.format(self.name())) def is_contiguous(self, rtol=1e-05, atol=1e-08): """ @@ -979,19 +1065,29 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): """ if self.has_bounds(): self._sanity_check_contiguous() - return np.allclose(self.bounds[1:, 0], self.bounds[:-1, 1], - rtol=rtol, atol=atol) + if self.ndim == 1: + return np.allclose(self.bounds[1:, 0], self.bounds[:-1, 1], + rtol=rtol, atol=atol) + elif self.ndim == 2: + allclose, _, _ = _discontinuity_in_2d_bounds(self.bounds, + abs_tol=atol) + return allclose else: return False def contiguous_bounds(self): """ - Returns the N+1 bound values for a contiguous bounded coordinate + Returns the N+1 bound values for a contiguous bounded 1D coordinate of length N. + Returns the (N+1, M+1) bound values for a contiguous bounded 2D + coordinate of shape (N, M). + + Assumes input is contiguous. + .. note:: - If the coordinate is does not have bounds, this method will + If the coordinate does not have bounds, this method will return bounds positioned halfway between the coordinate's points. """ @@ -1003,8 +1099,11 @@ def contiguous_bounds(self): self._sanity_check_contiguous() bounds = self.bounds - c_bounds = np.resize(bounds[:, 0], bounds.shape[0] + 1) - c_bounds[-1] = bounds[-1, 1] + if self.ndim == 1: + c_bounds = np.resize(bounds[:, 0], bounds.shape[0] + 1) + c_bounds[-1] = bounds[-1, 1] + elif self.ndim == 2: + c_bounds = _get_2d_coord_bound_grid(bounds) return c_bounds def is_monotonic(self): diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 64ce54c04c..5e00d5f713 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -31,16 +31,17 @@ import cartopy.crs as ccrs import cartopy.mpl.geoaxes from cartopy.geodesic import Geodesic +import cftime import matplotlib.axes import matplotlib.collections as mpl_collections import matplotlib.dates as mpl_dates import matplotlib.pyplot as plt from matplotlib.offsetbox import AnchoredText import matplotlib.ticker as mpl_ticker -import cftime import matplotlib.transforms as mpl_transforms import numpy as np import numpy.ma as ma +import warnings import iris.cube import iris.analysis.cartography as cartography @@ -51,7 +52,6 @@ import iris.palette from iris.util import _meshgrid - # Cynthia Brewer citation text. BREWER_CITE = 'Colours based on ColorBrewer.org' @@ -65,7 +65,7 @@ def names(coords): if isinstance(coord, int): result.append('dim={}'.format(coord)) else: - result.append(coord.name()) + result.append(cube.coord.name()) return ', '.join(result) def as_coord(coord): @@ -95,15 +95,17 @@ def get_span(coord): else: span = set(cube.coord_dims(coord)) return span + spans = list(map(get_span, coords)) for span, coord in zip(spans, coords): if not span: msg = 'The coordinate {!r} doesn\'t span a data dimension.' raise ValueError(msg.format(coord.name())) - if mode == iris.coords.BOUND_MODE and len(span) != 1: - raise ValueError('The coordinate {!r} is multi-dimensional and' - ' cannot be used in a cell-based plot.' - .format(coord.name())) + if mode == iris.coords.BOUND_MODE and len(span) not in [1, 2]: + raise ValueError('The coordinate {!r} has {} dimensions.' + 'Cell-based plotting is only supporting for' + 'coordinates with one or two dimensions.' + .format(coord.name()), len(span)) # Check the combination of coordinates spans enough (ndims) data # dimensions. @@ -125,11 +127,13 @@ def get_span(coord): # Note the use of `reversed` to convert from the X-then-Y # convention of the end-user API to the V-then-U convention used by # the plotting routines. + coords = names(coords) + plot_coords = list(reversed(coords)) return PlotDefn(plot_coords, transpose) -def _valid_bound_coord(coord): +def _valid_bound_dim_coord(coord): result = None if coord and coord.ndim == 1 and coord.nbounds: result = coord @@ -154,7 +158,7 @@ def _get_plot_defn(cube, mode, ndims=2): # When appropriate, restrict to 1D with bounds. if mode == iris.coords.BOUND_MODE: - coords = list(map(_valid_bound_coord, coords)) + coords = list(map(_valid_bound_dim_coord, coords)) def guess_axis(coord): axis = None @@ -172,6 +176,19 @@ def guess_axis(coord): aux_coords.sort(key=lambda coord: coord._as_defn()) coords[dim] = aux_coords[0] + # If plotting a 2 dimensional plot, check for 2d coordinates + if ndims == 2: + missing_dims = [dim for dim, coord in enumerate(coords) if + coord is None] + if missing_dims: + # Note that this only picks up coordinates that span the dims + two_dim_coords = cube.coords(dimensions=missing_dims) + two_dim_coords = [coord for coord in two_dim_coords + if coord.ndim == 2] + if len(two_dim_coords) >= 2: + two_dim_coords.sort(key=lambda coord: coord._as_defn()) + coords = two_dim_coords[:2] + if mode == iris.coords.POINT_MODE: # Allow multi-dimensional aux_coords to override the dim_coords # along the Z axis. This results in a preference for using the @@ -195,6 +212,7 @@ def sort_key(coord): return (order.get(axis, 0), coords.index(coord), coord and coord.name()) + sorted_coords = sorted(coords, key=sort_key) transpose = (sorted_coords != coords) @@ -256,6 +274,61 @@ def _invert_yaxis(v_coord, axes=None): axes.invert_yaxis() +def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): + """ + Check that the discontinuous bounds occur where the data is masked. + + If discontinuity occurs but data is masked, raise warning + If discontinuity occurs and data is NOT masked, raise error + + Args: + coords: + Array of the bounds of a 2D coord, of shape (X,Y,4) + data: + data of the the cube we are plotting + abs_tol: + tolerance when checking the contiguity + + """ + if transpose: + bounds = coord.bounds.T + else: + bounds = coord.bounds + + both_dirs_contiguous, diffs_along_x, diffs_along_y = \ + iris.coords._discontinuity_in_2d_bounds(bounds, abs_tol=abs_tol) + + if not both_dirs_contiguous: + + # True where data exists + mask_invert = np.logical_not(data.mask) + + # Where a discontinuity occurs the grid will not be created correctly. + # This does not matter if the data is masked as this is not plotted. + # So for places where data exists (opposite of the mask) AND a + # diff exists. If any exist, raise a warning + not_masked_at_discontinuity_along_x = np.any( + np.logical_and(mask_invert[:, :-1], diffs_along_x)) + + not_masked_at_discontinuity_along_y = np.any( + np.logical_and(mask_invert[:-1, ], diffs_along_y)) + + # If discontinuity occurs but not masked, any grid will be created + # incorrectly, so raise a warning + if not_masked_at_discontinuity_along_x or \ + not_masked_at_discontinuity_along_y: + raise ValueError('The bounds of the {} coordinate are not' + ' contiguous and data is not masked where the' + ' discontiguity occurs. Not able to create a' + ' suitable mesh to give to' + ' Matplotlib'.format(coord.name())) + else: + msg = 'The bounds of the {} coordinate are not contiguous. ' \ + 'However, data is masked where the discontiguity occurs ' \ + 'so plotting anyway.'.format(coord.name()) + warnings.warn(msg) + + def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): # NB. In the interests of clarity we use "u" and "v" to refer to the # horizontal and vertical axes on the matplotlib plot. @@ -263,10 +336,25 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): # Get & remove the coords entry from kwargs. coords = kwargs.pop('coords', None) if coords is not None: - plot_defn = _get_plot_defn_custom_coords_picked(cube, coords, mode) + plot_defn = _get_plot_defn_custom_coords_picked(cube, coords, mode, + ndims=2) else: plot_defn = _get_plot_defn(cube, mode, ndims=2) + two_dim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', + 1e-4) + for coord in plot_defn.coords: + if coord.ndim == 2 and coord.has_bounds(): + try: + _check_contiguity_and_bounds(coord, data=cube.data, + abs_tol=two_dim_contig_atol) + except ValueError: + if _check_contiguity_and_bounds(coord, data=cube.data, + abs_tol=two_dim_contig_atol, + transpose=True)\ + is True: + plot_defn.transpose = True + if _can_draw_map(plot_defn.coords): result = _map_common(draw_method_name, None, iris.coords.BOUND_MODE, cube, plot_defn, *args, **kwargs) @@ -286,7 +374,7 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): string_axes = {} for coord, axis_name, data_dim in zip([u_coord, v_coord], - ['xaxis', 'yaxis'], + ['plotxaxis', 'yaxis'], [1, 0]): if coord is None: values = np.arange(data.shape[data_dim] + 1) @@ -309,7 +397,16 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): plot_arrays.append(values) u, v = plot_arrays - u, v = _broadcast_2d(u, v) + + # If the data is tranposed, 2D coordinates will also need to be + # tranposed. + if u.ndim == v.ndim == 2 and plot_defn.transpose is True: + u = u.T + v = v.T + + if u.ndim == v.ndim == 1: + u, v = _broadcast_2d(u, v) + axes = kwargs.pop('axes', None) draw_method = getattr(axes if axes else plt, draw_method_name) result = draw_method(u, v, data, *args, **kwargs) @@ -434,8 +531,8 @@ def _fixup_dates(coord, values): raise IrisError(msg) r = [nc_time_axis.CalendarDateTime( - cftime.datetime(*date), coord.units.calendar) - for date in dates] + cftime.datetime(*date), coord.units.calendar) + for date in dates] values = np.empty(len(r), dtype=object) values[:] = r return values @@ -675,8 +772,8 @@ def _ensure_cartopy_axes_and_determine_kwargs(x_coord, y_coord, kwargs): axes = kwargs.get('axes') if axes is None: if (isinstance(cs, iris.coord_systems.RotatedGeogCS) and - x_coord.points.max() > 180 and x_coord.points.max() < 360 and - x_coord.points.min() > 0): + x_coord.points.max() > 180 and x_coord.points.max() < 360 and + x_coord.points.min() > 0): # The RotatedGeogCS has 0 - 360 extent, different from the # assumptions made by Cartopy: rebase longitudes for the map axes # to set the datum longitude to the International Date Line. @@ -721,17 +818,22 @@ def _map_common(draw_method_name, arg_func, mode, cube, plot_defn, else: raise ValueError("Expected 1D or 2D XY coords") else: - try: - x, y = _meshgrid(x_coord.contiguous_bounds(), - y_coord.contiguous_bounds()) - # Exception translation. - except iris.exceptions.CoordinateMultiDimError: - raise ValueError("Could not get XY grid from bounds. " - "X or Y coordinate not 1D.") - except ValueError: - raise ValueError("Could not get XY grid from bounds. " - "X or Y coordinate doesn't have 2 bounds " - "per point.") + if not x_coord.ndim == y_coord.ndim == 2: + try: + x, y = _meshgrid(x_coord.contiguous_bounds(), + y_coord.contiguous_bounds()) + # Exception translation. + except iris.exceptions.CoordinateMultiDimError: + raise ValueError("Expected two 1D coords. Could not get XY" + " grid from bounds. X or Y coordinate not" + " 1D.") + except ValueError: + raise ValueError("Could not get XY grid from bounds. " + "X or Y coordinate doesn't have 2 bounds " + "per point.") + else: + x = x_coord.contiguous_bounds() + y = y_coord.contiguous_bounds() # Obtain the data array. data = cube.data @@ -1045,6 +1147,9 @@ def pcolormesh(cube, *args, **kwargs): * axes: the :class:`matplotlib.axes.Axes` to use for drawing. Defaults to the current axes if none provided. + * two_dim_coord_contiguity_atol: absolute tolerance when checking for + contiguity between cells in a two dimensional coordinate. + See :func:`matplotlib.pyplot.pcolormesh` for details of other valid keyword arguments. @@ -1073,6 +1178,7 @@ def points(cube, *args, **kwargs): keyword arguments. """ + def _scatter_args(u, v, data, *args, **kwargs): return ((u, v) + args, kwargs) diff --git a/lib/iris/tests/stock.py b/lib/iris/tests/stock.py index 25df8eda33..1b55510d2b 100644 --- a/lib/iris/tests/stock.py +++ b/lib/iris/tests/stock.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2017, Met Office +# (C) British Crown Copyright 2010 - 2018, Met Office # # This file is part of Iris. # @@ -46,12 +46,12 @@ def lat_lon_cube(): """ cube = Cube(np.arange(12, dtype=np.int32).reshape((3, 4))) cs = GeogCS(6371229) - coord = iris.coords.DimCoord(points=np.array([-1, 0, 1], dtype=np.int32), + coord = DimCoord(points=np.array([-1, 0, 1], dtype=np.int32), standard_name='latitude', units='degrees', coord_system=cs) cube.add_dim_coord(coord, 0) - coord = iris.coords.DimCoord(points=np.array([-1, 0, 1, 2], dtype=np.int32), + coord = DimCoord(points=np.array([-1, 0, 1, 2], dtype=np.int32), standard_name='longitude', units='degrees', coord_system=cs) @@ -99,7 +99,7 @@ def simple_1d(with_bounds=True): cube.units = '1' points = np.arange(11, dtype=np.int32) + 1 bounds = np.column_stack([np.arange(11, dtype=np.int32), np.arange(11, dtype=np.int32) + 1]) - coord = iris.coords.DimCoord(points, long_name='foo', units='1', bounds=bounds) + coord = DimCoord(points, long_name='foo', units='1', bounds=bounds) cube.add_dim_coord(coord, 0) return cube @@ -124,12 +124,15 @@ def simple_2d(with_bounds=True): cube = Cube(np.arange(12, dtype=np.int32).reshape((3, 4))) cube.long_name = 'thingness' cube.units = '1' - y_points = np.array([ 2.5, 7.5, 12.5]) + y_points = np.array([2.5, 7.5, 12.5]) y_bounds = np.array([[0, 5], [5, 10], [10, 15]], dtype=np.int32) - y_coord = iris.coords.DimCoord(y_points, long_name='bar', units='1', bounds=y_bounds if with_bounds else None) + y_coord = DimCoord(y_points, long_name='bar', units='1', + bounds=y_bounds if with_bounds else None) x_points = np.array([ -7.5, 7.5, 22.5, 37.5]) - x_bounds = np.array([[-15, 0], [0, 15], [15, 30], [30, 45]], dtype=np.int32) - x_coord = iris.coords.DimCoord(x_points, long_name='foo', units='1', bounds=x_bounds if with_bounds else None) + x_bounds = np.array([[-15, 0], [0, 15], [15, 30], [30, 45]], + dtype=np.int32) + x_coord = DimCoord(x_points, long_name='foo', units='1', + bounds=x_bounds if with_bounds else None) cube.add_dim_coord(y_coord, 0) cube.add_dim_coord(x_coord, 1) @@ -191,9 +194,8 @@ def simple_3d_w_multidim_coords(with_bounds=True): [[5, 15], [15, 20], [20, 35], [35, 50]], [[10, 20], [20, 25], [25, 40], [40, 60]]], dtype=np.int32) - y_coord = iris.coords.AuxCoord(points=y_points, long_name='bar', - units='1', - bounds=y_bounds if with_bounds else None) + y_coord = AuxCoord(points=y_points, long_name='bar', units='1', + bounds=y_bounds if with_bounds else None) x_points = np.array([[-7.5, 7.5, 22.5, 37.5], [-12.5, 4., 26.5, 47.5], [2.5, 14., 36.5, 44.]]) @@ -201,12 +203,10 @@ def simple_3d_w_multidim_coords(with_bounds=True): [[-25, 0], [0, 8], [8, 45], [45, 50]], [[-5, 10], [10, 18], [18, 55], [18, 70]]], dtype=np.int32) - x_coord = iris.coords.AuxCoord(points=x_points, long_name='foo', - units='1', - bounds=x_bounds if with_bounds else None) - wibble_coord = iris.coords.DimCoord(np.array([10., 30.], - dtype=np.float32), - long_name='wibble', units='1') + x_coord = AuxCoord(points=x_points, long_name='foo', units='1', + bounds=x_bounds if with_bounds else None) + wibble_coord = DimCoord(np.array([10., 30.], dtype=np.float32), + long_name='wibble', units='1') cube.add_dim_coord(wibble_coord, [0]) cube.add_aux_coord(y_coord, [1, 2]) @@ -238,13 +238,13 @@ def simple_3d(): cube = Cube(np.arange(24, dtype=np.int32).reshape((2, 3, 4))) cube.long_name = 'thingness' cube.units = '1' - wibble_coord = iris.coords.DimCoord(np.array([10., 30.], + wibble_coord = DimCoord(np.array([10., 30.], dtype=np.float32), long_name='wibble', units='1') - lon = iris.coords.DimCoord([-180, -90, 0, 90], + lon = DimCoord([-180, -90, 0, 90], standard_name='longitude', units='degrees', circular=True) - lat = iris.coords.DimCoord([90, 0, -90], + lat = DimCoord([90, 0, -90], standard_name='latitude', units='degrees') cube.add_dim_coord(wibble_coord, [0]) cube.add_dim_coord(lat, [1]) @@ -294,39 +294,46 @@ def track_1d(duplicate_x=False): array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) """ - cube = Cube(np.arange(11, dtype=np.int32), standard_name='air_temperature', units='K') - bounds = np.column_stack([np.arange(11, dtype=np.int32), np.arange(11, dtype=np.int32) + 1]) + cube = Cube(np.arange(11, dtype=np.int32), standard_name='air_temperature', + units='K') + bounds = np.column_stack([np.arange(11, dtype=np.int32), + np.arange(11, dtype=np.int32) + 1]) pts = bounds[:, 1] - coord = iris.coords.AuxCoord(pts, 'projection_x_coordinate', units='1', bounds=bounds) + coord = AuxCoord(pts, 'projection_x_coordinate', units='1', bounds=bounds) cube.add_aux_coord(coord, [0]) if duplicate_x: - coord = iris.coords.AuxCoord(pts, 'projection_x_coordinate', units='1', bounds=bounds) + coord = AuxCoord(pts, 'projection_x_coordinate', units='1', + bounds=bounds) cube.add_aux_coord(coord, [0]) - coord = iris.coords.AuxCoord(pts * 2, 'projection_y_coordinate', units='1', bounds=bounds * 2) + coord = AuxCoord(pts * 2, 'projection_y_coordinate', units='1', + bounds=bounds * 2) cube.add_aux_coord(coord, 0) return cube def simple_2d_w_multidim_and_scalars(): data = np.arange(50, dtype=np.int32).reshape((5, 10)) - cube = iris.cube.Cube(data, long_name='test 2d dimensional cube', units='meters') + cube = iris.cube.Cube(data, long_name='test 2d dimensional cube', + units='meters') # DimCoords - dim1 = iris.coords.DimCoord(np.arange(5, dtype=np.float32) * 5.1 + 3.0, long_name='dim1', units='meters') - dim2 = iris.coords.DimCoord(np.arange(10, dtype=np.int32), long_name='dim2', units='meters', - bounds=np.arange(20, dtype=np.int32).reshape(10, 2)) + dim1 = DimCoord(np.arange(5, dtype=np.float32) * 5.1 + 3.0, + long_name='dim1', units='meters') + dim2 = DimCoord(np.arange(10, dtype=np.int32), + long_name='dim2', units='meters', + bounds=np.arange(20, dtype=np.int32).reshape(10, 2)) # Scalars - an_other = iris.coords.AuxCoord(3.0, long_name='an_other', units='meters') - yet_an_other = iris.coords.DimCoord(23.3, standard_name='air_temperature', - long_name='custom long name', - var_name='custom_var_name', - units='K') + an_other = AuxCoord(3.0, long_name='an_other', units='meters') + yet_an_other = DimCoord(23.3, standard_name='air_temperature', + long_name='custom long name', + var_name='custom_var_name', units='K') # Multidim - my_multi_dim_coord = iris.coords.AuxCoord(np.arange(50, dtype=np.int32).reshape(5, 10), - long_name='my_multi_dim_coord', units='1', - bounds=np.arange(200, dtype=np.int32).reshape(5, 10, 4)) + my_multi_dim_coord = AuxCoord(np.arange(50, dtype=np.int32).reshape(5, 10), + long_name='my_multi_dim_coord', units='1', + bounds=np.arange(200, dtype=np.int32). + reshape(5, 10, 4)) cube.add_dim_coord(dim1, 0) cube.add_dim_coord(dim2, 1) @@ -361,18 +368,21 @@ def hybrid_height(): """ data = np.arange(12, dtype='i8').reshape((3, 4)) - orography = icoords.AuxCoord([10, 25, 50, 5], standard_name='surface_altitude', units='m') - model_level = icoords.AuxCoord([2, 1, 0], standard_name='model_level_number') - level_height = icoords.DimCoord([100, 50, 10], long_name='level_height', - units='m', attributes={'positive': 'up'}, - bounds=[[150, 75], [75, 20], [20, 0]]) - sigma = icoords.AuxCoord([0.8, 0.9, 0.95], long_name='sigma', - bounds=[[0.7, 0.85], [0.85, 0.97], [0.97, 1.0]]) - hybrid_height = iris.aux_factory.HybridHeightFactory(level_height, sigma, orography) + orography = AuxCoord([10, 25, 50, 5], standard_name='surface_altitude', + units='m') + model_level = AuxCoord([2, 1, 0], standard_name='model_level_number') + level_height = DimCoord([100, 50, 10], long_name='level_height', + units='m', attributes={'positive': 'up'}, + bounds=[[150, 75], [75, 20], [20, 0]]) + sigma = AuxCoord([0.8, 0.9, 0.95], long_name='sigma', + bounds=[[0.7, 0.85], [0.85, 0.97], [0.97, 1.0]]) + hybrid_height = iris.aux_factory.HybridHeightFactory(level_height, + sigma, orography) cube = iris.cube.Cube(data, standard_name='air_temperature', units='K', dim_coords_and_dims=[(level_height, 0)], - aux_coords_and_dims=[(orography, 1), (model_level, 0), (sigma, 0)], + aux_coords_and_dims=[(orography, 1), + (model_level, 0), (sigma, 0)], aux_factories=[hybrid_height]) return cube @@ -381,25 +391,25 @@ def simple_4d_with_hybrid_height(): cube = iris.cube.Cube(np.arange(3*4*5*6, dtype='i8').reshape(3, 4, 5, 6), "air_temperature", units="K") - cube.add_dim_coord(iris.coords.DimCoord(np.arange(3, dtype='i8'), "time", - units="hours since epoch"), 0) - cube.add_dim_coord(iris.coords.DimCoord(np.arange(4, dtype='i8')+10, - "model_level_number", units="1"), 1) - cube.add_dim_coord(iris.coords.DimCoord(np.arange(5, dtype='i8')+20, - "grid_latitude", - units="degrees"), 2) - cube.add_dim_coord(iris.coords.DimCoord(np.arange(6, dtype='i8')+30, - "grid_longitude", - units="degrees"), 3) - - cube.add_aux_coord(iris.coords.AuxCoord(np.arange(4, dtype='i8')+40, - long_name="level_height", - units="m"), 1) - cube.add_aux_coord(iris.coords.AuxCoord(np.arange(4, dtype='i8')+50, - long_name="sigma", units="1"), 1) - cube.add_aux_coord(iris.coords.AuxCoord(np.arange(5*6, dtype='i8').reshape(5, 6)+100, - long_name="surface_altitude", - units="m"), [2, 3]) + cube.add_dim_coord(DimCoord(np.arange(3, dtype='i8'), "time", + units="hours since epoch"), 0) + cube.add_dim_coord(DimCoord(np.arange(4, dtype='i8')+10, + "model_level_number", units="1"), 1) + cube.add_dim_coord(DimCoord(np.arange(5, dtype='i8')+20, + "grid_latitude", + units="degrees"), 2) + cube.add_dim_coord(DimCoord(np.arange(6, dtype='i8')+30, + "grid_longitude", + units="degrees"), 3) + + cube.add_aux_coord(AuxCoord(np.arange(4, dtype='i8')+40, + long_name="level_height", + units="m"), 1) + cube.add_aux_coord(AuxCoord(np.arange(4, dtype='i8')+50, + long_name="sigma", units="1"), 1) + cube.add_aux_coord(AuxCoord(np.arange(5*6, dtype='i8').reshape(5, 6)+100, + long_name="surface_altitude", + units="m"), [2, 3]) cube.add_aux_factory(iris.aux_factory.HybridHeightFactory( delta=cube.coord("level_height"), @@ -449,7 +459,8 @@ def realistic_4d(): Returns a realistic 4d cube. >>> print(repr(realistic_4d())) - + """ # the stock arrays were created in Iris 0.8 with: @@ -477,7 +488,8 @@ def realistic_4d(): if not os.path.isfile(data_path): raise IOError('Test data is not available at {}.'.format(data_path)) r = np.load(data_path) - # sort the arrays based on the order they were originally given. The names given are of the form 'arr_1' or 'arr_10' + # sort the arrays based on the order they were originally given. + # The names given are of the form 'arr_1' or 'arr_10' _, arrays = zip(*sorted(six.iteritems(r), key=lambda item: int(item[0][4:]))) lat_pts, lat_bnds, lon_pts, lon_bnds, level_height_pts, \ @@ -486,25 +498,37 @@ def realistic_4d(): ll_cs = RotatedGeogCS(37.5, 177.5, ellipsoid=GeogCS(6371229.0)) - lat = icoords.DimCoord(lat_pts, standard_name='grid_latitude', units='degrees', - bounds=lat_bnds, coord_system=ll_cs) - lon = icoords.DimCoord(lon_pts, standard_name='grid_longitude', units='degrees', - bounds=lon_bnds, coord_system=ll_cs) + lat = icoords.DimCoord(lat_pts, standard_name='grid_latitude', + units='degrees', bounds=lat_bnds, + coord_system=ll_cs) + lon = icoords.DimCoord(lon_pts, standard_name='grid_longitude', + units='degrees', bounds=lon_bnds, + coord_system=ll_cs) level_height = icoords.DimCoord(level_height_pts, long_name='level_height', units='m', bounds=level_height_bnds, attributes={'positive': 'up'}) - model_level = icoords.DimCoord(model_level_pts, standard_name='model_level_number', + model_level = icoords.DimCoord(model_level_pts, + standard_name='model_level_number', units='1', attributes={'positive': 'up'}) - sigma = icoords.AuxCoord(sigma_pts, long_name='sigma', units='1', bounds=sigma_bnds) - orography = icoords.AuxCoord(orography, standard_name='surface_altitude', units='m') - time = icoords.DimCoord(time_pts, standard_name='time', units='hours since 1970-01-01 00:00:00') - forecast_period = icoords.DimCoord(forecast_period_pts, standard_name='forecast_period', units='hours') + sigma = icoords.AuxCoord(sigma_pts, long_name='sigma', units='1', + bounds=sigma_bnds) + orography = icoords.AuxCoord(orography, standard_name='surface_altitude', + units='m') + time = icoords.DimCoord(time_pts, standard_name='time', + units='hours since 1970-01-01 00:00:00') + forecast_period = icoords.DimCoord(forecast_period_pts, + standard_name='forecast_period', + units='hours') - hybrid_height = iris.aux_factory.HybridHeightFactory(level_height, sigma, orography) + hybrid_height = iris.aux_factory.HybridHeightFactory(level_height, + sigma, orography) - cube = iris.cube.Cube(data, standard_name='air_potential_temperature', units='K', - dim_coords_and_dims=[(time, 0), (model_level, 1), (lat, 2), (lon, 3)], - aux_coords_and_dims=[(orography, (2, 3)), (level_height, 1), (sigma, 1), + cube = iris.cube.Cube(data, standard_name='air_potential_temperature', + units='K', + dim_coords_and_dims=[(time, 0), (model_level, 1), + (lat, 2), (lon, 3)], + aux_coords_and_dims=[(orography, (2, 3)), + (level_height, 1), (sigma, 1), (forecast_period, None)], attributes={'source': 'Iris test case'}, aux_factories=[hybrid_height]) @@ -516,7 +540,8 @@ def realistic_4d_no_derived(): Returns a realistic 4d cube without hybrid height >>> print(repr(realistic_4d())) - + """ cube = realistic_4d() @@ -534,24 +559,30 @@ def realistic_4d_w_missing_data(): data_archive = np.load(data_path) data = ma.masked_array(data_archive['arr_0'], mask=data_archive['arr_1']) - # sort the arrays based on the order they were originally given. The names given are of the form 'arr_1' or 'arr_10' + # sort the arrays based on the order they were originally given. + # The names given are of the form 'arr_1' or 'arr_10' ll_cs = GeogCS(6371229) - lat = iris.coords.DimCoord(np.arange(20, dtype=np.float32), standard_name='grid_latitude', - units='degrees', coord_system=ll_cs) - lon = iris.coords.DimCoord(np.arange(20, dtype=np.float32), standard_name='grid_longitude', - units='degrees', coord_system=ll_cs) - time = iris.coords.DimCoord([1000., 1003., 1006.], standard_name='time', - units='hours since 1970-01-01 00:00:00') - forecast_period = iris.coords.DimCoord([0.0, 3.0, 6.0], standard_name='forecast_period', units='hours') - pressure = iris.coords.DimCoord(np.array([ 800., 900., 1000.], dtype=np.float32), - long_name='pressure', units='hPa') + lat = DimCoord(np.arange(20, dtype=np.float32), + standard_name='grid_latitude', + units='degrees', coord_system=ll_cs) + lon = DimCoord(np.arange(20, dtype=np.float32), + standard_name='grid_longitude', + units='degrees', coord_system=ll_cs) + time = DimCoord([1000., 1003., 1006.], standard_name='time', + units='hours since 1970-01-01 00:00:00') + forecast_period = DimCoord([0.0, 3.0, 6.0], + standard_name='forecast_period', + units='hours') + pressure = DimCoord(np.array([800., 900., 1000.], dtype=np.float32), + long_name='pressure', units='hPa') cube = iris.cube.Cube(data, long_name='missing data test data', units='K', - dim_coords_and_dims=[(time, 0), (pressure, 1), (lat, 2), (lon, 3)], + dim_coords_and_dims=[(time, 0), (pressure, 1), + (lat, 2), (lon, 3)], aux_coords_and_dims=[(forecast_period, 0)], - attributes={'source':'Iris test case'}) + attributes={'source': 'Iris test case'}) return cube diff --git a/lib/iris/tests/unit/plot/test_2d_coords.py b/lib/iris/tests/unit/plot/test_2d_coords.py new file mode 100644 index 0000000000..3b8ca91652 --- /dev/null +++ b/lib/iris/tests/unit/plot/test_2d_coords.py @@ -0,0 +1,158 @@ +# (C) British Crown Copyright 2018, 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 . +"""Unit tests for handling and plotting of 2-dimensional coordinates""" + +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.coords as coords + +import numpy.ma as ma + +from iris.tests.stock import simple_2d_w_multidim_coords as cube_2dcoords +from iris.tests.stock import simple_3d_w_multidim_coords as cube3d_2dcoords +from iris.tests.stock import lat_lon_cube + +from orca_utils.plot_testing import testdata_2d_coords + +if tests.MPL_AVAILABLE: + import iris.plot as iplt + + +class Test_2d_coords_plot_defn_bound_mode(tests.IrisTest): + def setUp(self): + self.multidim_cube = cube_2dcoords() + self.overspan_cube = cube3d_2dcoords() + + # latlon_2d is a cube with 2d coords, 4 bounds per point, + # discontiguities in the bounds but masked data at the discontiguities. + self.latlon_2d = testdata_2d_coords.full2d_global() + testdata_2d_coords.make_bounds_discontiguous_at_point\ + (self.latlon_2d, 2, 2) + + # Take a latlon cube with 1D coords, broadcast the coords into 2D + # ones, then add ONE of them back into the cube in place of original: + single_dims = lat_lon_cube() + lon = single_dims.coord('longitude') + lat = single_dims.coord('latitude') + big_lon, big_lat = testdata_2d_coords.grid_coords_2d_from_1d(lon, lat) + mixed_dims = single_dims.copy() + mixed_dims.remove_coord(lon) + # TODO Fix this coord addition: + # When adding an aux_coord, the function '_check_multidim_metadata' + # throws an error as it requires coord.shape to be (1, ) instead of + # (3, 4) or whatever. + mixed_dims.add_aux_coord(big_lon) + + # mixed_dims is now a cube with 2 1D dim coords and an additional + # 2D aux coord. + self.mixed_dims = mixed_dims + + self.mode = coords.BOUND_MODE + + def test_2d_coords_identified(self): + # Test that 2d coords are identified in the plot definition without + # having to be specified in the args without any errors. + cube = self.multidim_cube + defn = iplt._get_plot_defn(cube, mode=self.mode) + self.assertEqual([coord.name() for coord in defn.coords], + ['bar', 'foo']) + + def test_2d_coords_custom_picked(self): + # Test that 2d coords which are specified in the args will be + # accepted without any errors. + cube = self.multidim_cube + defn = iplt._get_plot_defn_custom_coords_picked(cube, ('foo', 'bar'), + self.mode) + self.assertEqual([coord.name() for coord in defn.coords], + ['bar', 'foo']) + + def test_2d_coords_as_integers(self): + # Test that if you pass in 2d coords as args in the form of integers, + # they will still be correctly identified without any errors. + cube = self.multidim_cube + defn = iplt._get_plot_defn_custom_coords_picked(cube, (0, 1), + self.mode) + self.assertEqual([coord for coord in defn.coords], + [1, 0]) + + def test_total_span_check(self): + # Test that an error is raised if a user tries to plot a 2d coord + # against a different coord, making total number of dimensions 3. + cube = self.overspan_cube + with self.assertRaises(ValueError): + iplt._get_plot_defn_custom_coords_picked(cube, ('wibble', 'foo'), + self.mode) + + def test_2dcoord_with_1dcoord(self): + # TODO Generate a cube with one 2d coord and one 1d coord + # TODO Try and plot them against each other + # TODO Find out where I can put a catch for this (if necessary) + cube = self.mixed_dims + with self.assertRaises(ValueError): + iplt._get_plot_defn_custom_coords_picked(cube, + ('latitude', 'longitude'), + self.mode) + + def test_map_common_not_enough_bounds(self): + # Test that a lat-lon cube with 2d coords and 2 bounds per point + # throws an error in contiguity checks. + cube = self.multidim_cube + cube.coord('foo').rename('longitude') + cube.coord('bar').rename('latitude') + with self.assertRaises(ValueError): + plot_defn = iplt._get_plot_defn(cube, self.mode) + iplt._map_common('pcolor', None, self.mode, cube, plot_defn) + + def test_map_common_2d(self): + # Test that a cube with 2d coords can be plotted as a map. + cube = self.latlon_2d + # Get necessary variables from _get_plot_defn to check that the test + # case will be accepted by _map_common. + plot_defn = iplt._get_plot_defn(cube, self.mode) + result = iplt._map_common('pcolor', None, self.mode, cube, plot_defn) + self.assertTrue(result) + + def test_discontiguous_masked(self): + # Test that a contiguity check will raise a warning (not an error) for + # discontiguous bounds but appropriately masked data. + cube = self.latlon_2d + coord = cube.coord('longitude') + msg = 'The bounds of the longitude coordinate are not contiguous. ' \ + 'However, data is masked where the discontiguity occurs so ' \ + 'plotting anyway.' + with self.assertWarnsRegexp(msg): + iplt._check_contiguity_and_bounds(coord, cube.data) + + def test_discontiguous_unmasked(self): + # Check that an error occurs when the contiguity check finds + # discontiguous bounds but unmasked data. + cube = self.latlon_2d + cube.data.mask = ma.nomask + coord = cube.coord('longitude') + with self.assertRaises(ValueError): + iplt._check_contiguity_and_bounds(coord, cube.data) + + def test_draw_2d_from_bounds(self): + # Test this function will not raise an error even with our most + # awkward but supported cube. + cube = self.latlon_2d + result = iplt._draw_2d_from_bounds('pcolormesh', cube) + self.assertTrue(result) From 723d45b1fe40afabcae54ff1d2868805e77da58a Mon Sep 17 00:00:00 2001 From: Corinne Bosley Date: Tue, 31 Jul 2018 09:44:38 +0100 Subject: [PATCH 05/24] some words for docs, WIP probably --- docs/iris/src/whatsnew/2.2.rst | 26 ++++++++++++++++++++++++++ lib/iris/plot.py | 15 +++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 docs/iris/src/whatsnew/2.2.rst diff --git a/docs/iris/src/whatsnew/2.2.rst b/docs/iris/src/whatsnew/2.2.rst new file mode 100644 index 0000000000..4165ebf43d --- /dev/null +++ b/docs/iris/src/whatsnew/2.2.rst @@ -0,0 +1,26 @@ +What's New in Iris 2.2.0 +************************ + +:Release: 2.2.0a0 +:Date: + + +This document explains the new/changed features of Iris in the alpha release + of version 2.2.0 +(:doc:`View all changes `). + + +Iris 2.2.0 Features +=================== +.. _showcase: + +.. admonition:: 2-Dimensional Coordinate Plotting + + The iris plot functions :func:`~iris.plot.pcolor` and + :func:`~iris.plot.pcolormesh` now accommodate the plotting of 2-dimensional + coordinates as well as 1-dimensional coordinates. + + To enable this feature, each coordinate passed in for plotting will be + automatically checked for contiguity. Coordinate bounds must either be + contiguous, or the cube's data must be masked at the discontiguities in + order to avoid plotting errors. diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 5e00d5f713..a464f5393b 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -1107,7 +1107,10 @@ def outline(cube, coords=None, color='k', linewidth=None, axes=None): def pcolor(cube, *args, **kwargs): """ - Draws a pseudocolor plot based on the given Cube. + Draws a pseudocolor plot based on the given 2-dimensional Cube. + + The cube must have either two 1-dimensional coordinates or two + 2-dimensional coordinates with contiguous bounds to plot against each other. Kwargs: @@ -1121,6 +1124,11 @@ def pcolor(cube, *args, **kwargs): * axes: the :class:`matplotlib.axes.Axes` to use for drawing. Defaults to the current axes if none provided. + * two_dim_coord_contiguity_atol: absolute tolerance when checking for + contiguity between cells in a two dimensional coordinate. + + + See :func:`matplotlib.pyplot.pcolor` for details of other valid keyword arguments. @@ -1133,7 +1141,10 @@ def pcolor(cube, *args, **kwargs): def pcolormesh(cube, *args, **kwargs): """ - Draws a pseudocolor plot based on the given Cube. + Draws a pseudocolor plot based on the given 2-dimensional Cube. + + The cube must have either two 1-dimensional coordinates or two + 2-dimensional coordinates with contiguous bounds to plot against each other. Kwargs: From 0b8baf55437f2dd7882cddc1c96e93e415ac1a23 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Mon, 30 Jul 2018 18:54:27 +0100 Subject: [PATCH 06/24] Handle custom coords correctly --- lib/iris/coords.py | 15 ++++++++------- lib/iris/plot.py | 33 +++++++++++++-------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 57ae723205..8f1e48fd18 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -143,7 +143,7 @@ def __new__(cls, name_or_coord, minimum, maximum, 'groupby_point, groupby_slice') -def _discontinuity_in_2d_bounds(bds, abs_tol=1e-4): +def _discontiguity_in_2d_bounds(bds, abs_tol=1e-4): """ Check bounds of a 2-dimensional coordinate are contiguous Args: @@ -156,7 +156,7 @@ def _discontinuity_in_2d_bounds(bds, abs_tol=1e-4): absolute difference along the y axis """ - # Check form is (ny, nx, 4) + # Check bds has the shape (ny, nx, 4) if not bds.ndim == 3 and bds.shape[2] == 4: raise ValueError('2D coordinates must have 4 bounds per point ' 'for 2D coordinate plotting') @@ -1066,14 +1066,15 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): if self.has_bounds(): self._sanity_check_contiguous() if self.ndim == 1: - return np.allclose(self.bounds[1:, 0], self.bounds[:-1, 1], - rtol=rtol, atol=atol) + contiguous = np.allclose(self.bounds[1:, 0], + self.bounds[:-1, 1], + rtol=rtol, atol=atol) elif self.ndim == 2: - allclose, _, _ = _discontinuity_in_2d_bounds(self.bounds, + contiguous, _, _ = _discontiguity_in_2d_bounds(self.bounds, abs_tol=atol) - return allclose else: - return False + contiguous = False + return contiguous def contiguous_bounds(self): """ diff --git a/lib/iris/plot.py b/lib/iris/plot.py index a464f5393b..6ede9e1735 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -41,7 +41,6 @@ import matplotlib.transforms as mpl_transforms import numpy as np import numpy.ma as ma -import warnings import iris.cube import iris.analysis.cartography as cartography @@ -65,7 +64,7 @@ def names(coords): if isinstance(coord, int): result.append('dim={}'.format(coord)) else: - result.append(cube.coord.name()) + result.append(coord.name()) return ', '.join(result) def as_coord(coord): @@ -127,8 +126,6 @@ def get_span(coord): # Note the use of `reversed` to convert from the X-then-Y # convention of the end-user API to the V-then-U convention used by # the plotting routines. - coords = names(coords) - plot_coords = list(reversed(coords)) return PlotDefn(plot_coords, transpose) @@ -296,7 +293,7 @@ def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): bounds = coord.bounds both_dirs_contiguous, diffs_along_x, diffs_along_y = \ - iris.coords._discontinuity_in_2d_bounds(bounds, abs_tol=abs_tol) + iris.coords._discontiguity_in_2d_bounds(bounds, abs_tol=abs_tol) if not both_dirs_contiguous: @@ -322,11 +319,6 @@ def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): ' discontiguity occurs. Not able to create a' ' suitable mesh to give to' ' Matplotlib'.format(coord.name())) - else: - msg = 'The bounds of the {} coordinate are not contiguous. ' \ - 'However, data is masked where the discontiguity occurs ' \ - 'so plotting anyway.'.format(coord.name()) - warnings.warn(msg) def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): @@ -344,16 +336,17 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): two_dim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', 1e-4) for coord in plot_defn.coords: - if coord.ndim == 2 and coord.has_bounds(): - try: - _check_contiguity_and_bounds(coord, data=cube.data, - abs_tol=two_dim_contig_atol) - except ValueError: - if _check_contiguity_and_bounds(coord, data=cube.data, - abs_tol=two_dim_contig_atol, - transpose=True)\ + if hasattr(coord, 'has_bounds'): + if coord.ndim == 2 and coord.has_bounds(): + try: + _check_contiguity_and_bounds(coord, data=cube.data, + abs_tol=two_dim_contig_atol) + except ValueError: + if _check_contiguity_and_bounds( + coord, data=cube.data, + abs_tol=two_dim_contig_atol, transpose=True) \ is True: - plot_defn.transpose = True + plot_defn.transpose = True if _can_draw_map(plot_defn.coords): result = _map_common(draw_method_name, None, iris.coords.BOUND_MODE, @@ -374,7 +367,7 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): string_axes = {} for coord, axis_name, data_dim in zip([u_coord, v_coord], - ['plotxaxis', 'yaxis'], + ['xaxis', 'yaxis'], [1, 0]): if coord is None: values = np.arange(data.shape[data_dim] + 1) From 759e357134d980c314a5da45e53d861cfbb6f2bc Mon Sep 17 00:00:00 2001 From: Corinne Bosley Date: Tue, 31 Jul 2018 11:15:12 +0100 Subject: [PATCH 07/24] suggested tweaks and corrections --- lib/iris/plot.py | 21 ++++++++++----------- lib/iris/tests/unit/plot/test_2d_coords.py | 9 ++++----- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 6ede9e1735..d81354fe4c 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -288,9 +288,7 @@ def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): """ if transpose: - bounds = coord.bounds.T - else: - bounds = coord.bounds + data = data.T both_dirs_contiguous, diffs_along_x, diffs_along_y = \ iris.coords._discontiguity_in_2d_bounds(bounds, abs_tol=abs_tol) @@ -333,19 +331,18 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): else: plot_defn = _get_plot_defn(cube, mode, ndims=2) - two_dim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', + twodim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', 1e-4) for coord in plot_defn.coords: if hasattr(coord, 'has_bounds'): if coord.ndim == 2 and coord.has_bounds(): try: _check_contiguity_and_bounds(coord, data=cube.data, - abs_tol=two_dim_contig_atol) + abs_tol=twodim_contig_atol) except ValueError: - if _check_contiguity_and_bounds( - coord, data=cube.data, - abs_tol=two_dim_contig_atol, transpose=True) \ - is True: + if _check_contiguity_and_bounds(coord, data=cube.data, + abs_tol=twodim_contig_atol, + transpose=True) is True: plot_defn.transpose = True if _can_draw_map(plot_defn.coords): @@ -1103,7 +1100,8 @@ def pcolor(cube, *args, **kwargs): Draws a pseudocolor plot based on the given 2-dimensional Cube. The cube must have either two 1-dimensional coordinates or two - 2-dimensional coordinates with contiguous bounds to plot against each other. + 2-dimensional coordinates with contiguous bounds to plot against each + other. Kwargs: @@ -1137,7 +1135,8 @@ def pcolormesh(cube, *args, **kwargs): Draws a pseudocolor plot based on the given 2-dimensional Cube. The cube must have either two 1-dimensional coordinates or two - 2-dimensional coordinates with contiguous bounds to plot against each other. + 2-dimensional coordinates with contiguous bounds to plot against each + other. Kwargs: diff --git a/lib/iris/tests/unit/plot/test_2d_coords.py b/lib/iris/tests/unit/plot/test_2d_coords.py index 3b8ca91652..ce96157bf4 100644 --- a/lib/iris/tests/unit/plot/test_2d_coords.py +++ b/lib/iris/tests/unit/plot/test_2d_coords.py @@ -30,7 +30,7 @@ from iris.tests.stock import simple_3d_w_multidim_coords as cube3d_2dcoords from iris.tests.stock import lat_lon_cube -from orca_utils.plot_testing import testdata_2d_coords +from orca_utils.plot_testing import testdata_2d_coords as testdata if tests.MPL_AVAILABLE: import iris.plot as iplt @@ -43,16 +43,15 @@ def setUp(self): # latlon_2d is a cube with 2d coords, 4 bounds per point, # discontiguities in the bounds but masked data at the discontiguities. - self.latlon_2d = testdata_2d_coords.full2d_global() - testdata_2d_coords.make_bounds_discontiguous_at_point\ - (self.latlon_2d, 2, 2) + self.latlon_2d = testdata.full2d_global() + testdata.make_bounds_discontiguous_at_point(self.latlon_2d, 2, 2) # Take a latlon cube with 1D coords, broadcast the coords into 2D # ones, then add ONE of them back into the cube in place of original: single_dims = lat_lon_cube() lon = single_dims.coord('longitude') lat = single_dims.coord('latitude') - big_lon, big_lat = testdata_2d_coords.grid_coords_2d_from_1d(lon, lat) + big_lon, big_lat = testdata.grid_coords_2d_from_1d(lon, lat) mixed_dims = single_dims.copy() mixed_dims.remove_coord(lon) # TODO Fix this coord addition: From ade18ad70cbd9fba6fd21802136e5456b143f49d Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Tue, 31 Jul 2018 11:24:30 +0100 Subject: [PATCH 08/24] Update docs to included 2.2 versions (#3110) --- docs/iris/src/_templates/index.html | 2 +- docs/iris/src/whatsnew/index.rst | 1 + lib/iris/__init__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/iris/src/_templates/index.html b/docs/iris/src/_templates/index.html index 0c4b5d958f..31acded447 100644 --- a/docs/iris/src/_templates/index.html +++ b/docs/iris/src/_templates/index.html @@ -134,7 +134,7 @@ extra information on specific technical issues

  • -
  • diff --git a/docs/iris/src/whatsnew/index.rst b/docs/iris/src/whatsnew/index.rst index 104c5074ca..c3a34303f0 100644 --- a/docs/iris/src/whatsnew/index.rst +++ b/docs/iris/src/whatsnew/index.rst @@ -9,6 +9,7 @@ Iris versions. .. toctree:: :maxdepth: 2 + 2.2.rst 2.1.rst 2.0.rst 1.13.rst diff --git a/lib/iris/__init__.py b/lib/iris/__init__.py index 2e7eba5892..882b52aaea 100644 --- a/lib/iris/__init__.py +++ b/lib/iris/__init__.py @@ -121,7 +121,7 @@ def callback(cube, field, filename): # Iris revision. -__version__ = '2.2.0dev0' +__version__ = '2.2.0a0' # Restrict the names imported when using "from iris import *" __all__ = ['load', 'load_cube', 'load_cubes', 'load_raw', From 91d69fa660d7fd1b340e5a2dbdad7ce69082dc97 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Jul 2018 11:36:09 +0100 Subject: [PATCH 09/24] Provide a test skipper for 2d coords WIP. (#3099) --- lib/iris/tests/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index 8c64dc15b1..710a3fbac6 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -1196,6 +1196,12 @@ class MyPlotTests(test.GraphicsTest): 'Test(s) require "python-stratify", which is not available.') +SKIP_2D_TESTS = True +skip_2d = unittest.skipIf( + SKIP_2D_TESTS, + 'Test(s) broken by WIP on 2d coords support -- temporarily disabled.') + + def no_warnings(func): """ Provides a decorator to ensure that there are no warnings raised From b6b305f5a17a5a8d658e71835b0a91aba2bdf36f Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Mon, 30 Jul 2018 14:43:17 +0100 Subject: [PATCH 10/24] First working quiver+streamplot. --- lib/iris/plot.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index d81354fe4c..ac8da822cc 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -1189,6 +1189,139 @@ def _scatter_args(u, v, data, *args, **kwargs): *args, **kwargs) +def _vector_component_args(x_points, y_points, u_data, *args, **kwargs): + """ + Callback from _draw_2d_from_points for 'quiver' and 'streamlines'. + + Returns arguments (x, y, u, v), to be passed to the underlying matplotlib + call. + + "u_data" will always be "u_cube.data". + The matching "v_cube.data" component is stored in kwargs['_v_data']. + + """ + v_data = kwargs.pop('_v_data') + + # Rescale u+v values for plot distortion. + crs = kwargs.get('transform', None) + if crs: + if not isinstance(crs, (ccrs.PlateCarree, ccrs.RotatedPole)): + msg = ('Can only plot vectors provided in a lat-lon ' + 'projection, i.e. "cartopy.crs.PlateCarree" or ' + '"cartopy.crs.RotatedPole". This ' + "cubes coordinate system is {}.") + raise ValueError(msg.format(crs)) + # Given the above check, the Y points must be latitudes. + # We therefore **assume** they are in degrees : I'm not sure this + # is wise, but all the rest of this plot code does that, e.g. in + # _map_common. + # TODO: investigate degree units assumptions, here + elsewhere. + + # Implement a latitude scaling, but preserve the given magnitudes. + v_data = v_data.copy() + mags = np.sqrt(u_data * u_data + v_data * v_data) + v_data *= np.cos(np.deg2rad(y_points)) + scales = mags / np.sqrt(u_data * u_data + v_data * v_data) + u_data *= scales + v_data *= scales + + return ((x_points, y_points, u_data, v_data), kwargs) + + +def quiver(u_cube, v_cube, *args, **kwargs): + """ + Draws an arrow plot from two vector component cubes. + + 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_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.quiver` 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('quiver', _vector_component_args, u_cube, + *args, **kwargs) + + +def streamplot(u_cube, v_cube, *args, **kwargs): + """ + Draws a streamline plot from two vector component cubes. + + 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_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.quiver` 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('streamplot', _vector_component_args, u_cube, + *args, **kwargs) + + def plot(*args, **kwargs): """ Draws a line plot based on the given cube(s) or coordinate(s). From aee7f561d9e6dcc0cf5a8829d2f6db4bc8c17b25 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Tue, 31 Jul 2018 14:05:40 +0100 Subject: [PATCH 11/24] Change version number (#3118) --- docs/iris/src/_templates/layout.html | 2 +- docs/iris/src/whatsnew/2.2.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/iris/src/_templates/layout.html b/docs/iris/src/_templates/layout.html index 53ae5105cb..8ecc35bade 100644 --- a/docs/iris/src/_templates/layout.html +++ b/docs/iris/src/_templates/layout.html @@ -37,7 +37,7 @@

    - Iris v2.1 + Iris v2.2

    A powerful, format-agnostic, community-driven Python library for analysing and diff --git a/docs/iris/src/whatsnew/2.2.rst b/docs/iris/src/whatsnew/2.2.rst index 4165ebf43d..97eba3dee6 100644 --- a/docs/iris/src/whatsnew/2.2.rst +++ b/docs/iris/src/whatsnew/2.2.rst @@ -6,7 +6,7 @@ What's New in Iris 2.2.0 This document explains the new/changed features of Iris in the alpha release - of version 2.2.0 +of version 2.2.0 (:doc:`View all changes `). From 297becf2ddb6b74128f700cffd65876cdeac002b Mon Sep 17 00:00:00 2001 From: Corinne Bosley Date: Tue, 31 Jul 2018 14:22:55 +0100 Subject: [PATCH 12/24] re-added bounds definition for contiguity check, removed unnecessary test --- lib/iris/plot.py | 2 + lib/iris/tests/unit/plot/test_2d_coords.py | 72 +++++++++++----------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index ac8da822cc..8f11c8d116 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -290,6 +290,8 @@ def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): if transpose: data = data.T + bounds = coord.bounds + both_dirs_contiguous, diffs_along_x, diffs_along_y = \ iris.coords._discontiguity_in_2d_bounds(bounds, abs_tol=abs_tol) diff --git a/lib/iris/tests/unit/plot/test_2d_coords.py b/lib/iris/tests/unit/plot/test_2d_coords.py index ce96157bf4..bb27341baa 100644 --- a/lib/iris/tests/unit/plot/test_2d_coords.py +++ b/lib/iris/tests/unit/plot/test_2d_coords.py @@ -46,23 +46,23 @@ def setUp(self): self.latlon_2d = testdata.full2d_global() testdata.make_bounds_discontiguous_at_point(self.latlon_2d, 2, 2) - # Take a latlon cube with 1D coords, broadcast the coords into 2D - # ones, then add ONE of them back into the cube in place of original: - single_dims = lat_lon_cube() - lon = single_dims.coord('longitude') - lat = single_dims.coord('latitude') - big_lon, big_lat = testdata.grid_coords_2d_from_1d(lon, lat) - mixed_dims = single_dims.copy() - mixed_dims.remove_coord(lon) - # TODO Fix this coord addition: - # When adding an aux_coord, the function '_check_multidim_metadata' - # throws an error as it requires coord.shape to be (1, ) instead of - # (3, 4) or whatever. - mixed_dims.add_aux_coord(big_lon) - - # mixed_dims is now a cube with 2 1D dim coords and an additional - # 2D aux coord. - self.mixed_dims = mixed_dims + # # Take a latlon cube with 1D coords, broadcast the coords into 2D + # # ones, then add ONE of them back into the cube in place of original: + # single_dims = lat_lon_cube() + # lon = single_dims.coord('longitude') + # lat = single_dims.coord('latitude') + # big_lon, big_lat = testdata.grid_coords_2d_from_1d(lon, lat) + # mixed_dims = single_dims.copy() + # mixed_dims.remove_coord(lon) + # # TODO Fix this coord addition: + # # When adding an aux_coord, the function '_check_multidim_metadata' + # # throws an error as it requires coord.shape to be (1, ) instead of + # # (3, 4) or whatever. + # mixed_dims.add_aux_coord(big_lon) + # + # # mixed_dims is now a cube with 2 1D dim coords and an additional + # # 2D aux coord. + # self.mixed_dims = mixed_dims self.mode = coords.BOUND_MODE @@ -100,15 +100,15 @@ def test_total_span_check(self): iplt._get_plot_defn_custom_coords_picked(cube, ('wibble', 'foo'), self.mode) - def test_2dcoord_with_1dcoord(self): - # TODO Generate a cube with one 2d coord and one 1d coord - # TODO Try and plot them against each other - # TODO Find out where I can put a catch for this (if necessary) - cube = self.mixed_dims - with self.assertRaises(ValueError): - iplt._get_plot_defn_custom_coords_picked(cube, - ('latitude', 'longitude'), - self.mode) + # def test_2dcoord_with_1dcoord(self): + # # TODO Generate a cube with one 2d coord and one 1d coord + # # TODO Try and plot them against each other + # # TODO Find out where I can put a catch for this (if necessary) + # cube = self.mixed_dims + # with self.assertRaises(ValueError): + # iplt._get_plot_defn_custom_coords_picked(cube, + # ('latitude', 'longitude'), + # self.mode) def test_map_common_not_enough_bounds(self): # Test that a lat-lon cube with 2d coords and 2 bounds per point @@ -129,16 +129,16 @@ def test_map_common_2d(self): result = iplt._map_common('pcolor', None, self.mode, cube, plot_defn) self.assertTrue(result) - def test_discontiguous_masked(self): - # Test that a contiguity check will raise a warning (not an error) for - # discontiguous bounds but appropriately masked data. - cube = self.latlon_2d - coord = cube.coord('longitude') - msg = 'The bounds of the longitude coordinate are not contiguous. ' \ - 'However, data is masked where the discontiguity occurs so ' \ - 'plotting anyway.' - with self.assertWarnsRegexp(msg): - iplt._check_contiguity_and_bounds(coord, cube.data) + # def test_discontiguous_masked(self): + # # Test that a contiguity check will raise a warning (not an error) for + # # discontiguous bounds but appropriately masked data. + # cube = self.latlon_2d + # coord = cube.coord('longitude') + # msg = 'The bounds of the longitude coordinate are not contiguous. ' \ + # 'However, data is masked where the discontiguity occurs so ' \ + # 'plotting anyway.' + # with self.assertWarnsRegexp(msg): + # iplt._check_contiguity_and_bounds(coord, cube.data) def test_discontiguous_unmasked(self): # Check that an error occurs when the contiguity check finds From a281805785e212f2f23cdb8a1ecc372978feca39 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Thu, 2 Aug 2018 12:32:50 +0100 Subject: [PATCH 13/24] Ensure Sphinx autodocs for grid_angles routines. --- lib/iris/analysis/cartography.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index 16c99c83a8..0c19b84815 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -42,6 +42,23 @@ true_vectors_from_grid_vectors as rotate_grid_vectors) +# List of contents to control Sphinx autodocs. +# Unfortunately essential to get docs for the grid_angles functions. +__all__ = [ + 'area_weights', + 'cosine_latitude_weights', + 'get_xy_contiguous_bounded_grids', + 'get_xy_grids', + 'gridcell_angles', + 'project', + 'rotate_grid_vectors', + 'rotate_pole', + 'rotate_winds', + 'unrotate_pole', + 'wrap_lons', + 'DistanceDifferential', + 'PartialDifferential'] + # This value is used as a fall-back if the cube does not define the earth DEFAULT_SPHERICAL_EARTH_RADIUS = 6367470 # TODO: This should not be necessary, as CF is always in meters From e024f5019702013c228844da410af8bc014f30ea Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Thu, 2 Aug 2018 12:34:54 +0100 Subject: [PATCH 14/24] Codestyle fixes. --- lib/iris/analysis/cartography.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index 0c19b84815..dadcdedc86 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -352,7 +352,7 @@ def _quadrant_area(radian_lat_bounds, radian_lon_bounds, radius_of_earth): def area_weights(cube, normalize=False): - """ + r""" Returns an array of area weights, with the same dimensions as the cube. This is a 2D lat/lon area weights array, repeated over the non lat/lon @@ -460,7 +460,7 @@ def area_weights(cube, normalize=False): def cosine_latitude_weights(cube): - """ + r""" Returns an array of latitude weights, with the same dimensions as the cube. The weights are the cosine of latitude. @@ -970,7 +970,7 @@ def _transform_distance_vectors_tolerance_mask(src_crs, x, y, tgt_crs, def rotate_winds(u_cube, v_cube, target_cs): - """ + r""" Transform wind vectors to a different coordinate system. The input cubes contain U and V components parallel to the local X and Y From e3e82e458f68ebbc008f43eb5ce91bced038d445 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Thu, 2 Aug 2018 12:53:08 +0100 Subject: [PATCH 15/24] Whatsnew entries for 2d vector support. --- docs/iris/src/whatsnew/2.2.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/iris/src/whatsnew/2.2.rst b/docs/iris/src/whatsnew/2.2.rst index 97eba3dee6..4edc305025 100644 --- a/docs/iris/src/whatsnew/2.2.rst +++ b/docs/iris/src/whatsnew/2.2.rst @@ -24,3 +24,13 @@ Iris 2.2.0 Features automatically checked for contiguity. Coordinate bounds must either be contiguous, or the cube's data must be masked at the discontiguities in order to avoid plotting errors. + + The iris plot functions :func:`iris.plot.quiver` and + :func:`iris.plot.streamplot` have been added, and these also work with + 2-dimensional plot coordinates. + +.. admonition:: 2-Dimensional Grid Vectors + + The iris functions :func:`iris.analysis.cartography.gridcell_angles` and + :func:`iris.analysis.cartography.rotate_grid_vectors` have been added, + allowing you to convert gridcell-oriented vectors to true-North/East ones. From b041c50ca371dacbbfc14489b35aa9a9dca44b50 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 14 Aug 2018 15:55:29 +0100 Subject: [PATCH 16/24] Pin Dask to avoid 0.18.2 bug with masked arrays. (#3127) * Put orca_util routines in subpackage of iris.test.stock, to get existing iris.tests.unit.plot.test_2d_coords working. * Disable broken misused testcode in test_gridcell_angles. * Ditch test_gridcell_angles, none of it is functional. * Further style fixes. * Skip tests using iris-test-data, for Travis TEST_MINIMAL phases. * Codestyle fix (though this code obsolete anyway). * Fix unused imports. * Made-up test cube replaces use of iris-test-data. * Renamed keyword; improved docstring. * Change 'co' to 'coord' for clarity. * Review changes. --- lib/iris/analysis/_grid_angles.py | 6 +- lib/iris/coords.py | 2 +- lib/iris/plot.py | 2 +- .../tests/{stock.py => stock/__init__.py} | 3 +- lib/iris/tests/stock/_stock_2d_latlons.py | 346 ++++++++++++++++ lib/iris/tests/test_coding_standards.py | 2 +- .../cartography/test_gridcell_angles.py | 392 ------------------ lib/iris/tests/unit/plot/test_2d_coords.py | 46 +- 8 files changed, 382 insertions(+), 417 deletions(-) rename lib/iris/tests/{stock.py => stock/__init__.py} (99%) create mode 100644 lib/iris/tests/stock/_stock_2d_latlons.py delete mode 100644 lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py index 1746834a65..ffe6d51c7f 100644 --- a/lib/iris/analysis/_grid_angles.py +++ b/lib/iris/analysis/_grid_angles.py @@ -92,7 +92,7 @@ def _angle(p, q, r): if old_style: mid_lons = np.deg2rad(q[0]) - pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) pr_norm = np.sqrt(np.sum(pr**2, axis=0)) pr_top = pr[1] * np.cos(mid_lons) - pr[0] * np.sin(mid_lons) @@ -124,7 +124,7 @@ def _angle(p, q, r): lmb_hatvec_y, lmb_hatvec_z)]) - pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) # Dot products to form true-northward / true-eastward projections. pr_cmpt_e = np.sum(pr * lmb_hatvec, axis=0) @@ -141,7 +141,7 @@ def _angle(p, q, r): rtol = 1.e-3 check = np.allclose(mag_rot, mag_orig, rtol=rtol) if not check: - print (mag_rot, mag_orig) + print(mag_rot, mag_orig) assert np.allclose(mag_rot, mag_orig, rtol=rtol) return psi diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 8f1e48fd18..ff2f170a2f 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -1071,7 +1071,7 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): rtol=rtol, atol=atol) elif self.ndim == 2: contiguous, _, _ = _discontiguity_in_2d_bounds(self.bounds, - abs_tol=atol) + abs_tol=atol) else: contiguous = False return contiguous diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 8f11c8d116..7bf8ea07f3 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -334,7 +334,7 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): plot_defn = _get_plot_defn(cube, mode, ndims=2) twodim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', - 1e-4) + 1e-4) for coord in plot_defn.coords: if hasattr(coord, 'has_bounds'): if coord.ndim == 2 and coord.has_bounds(): diff --git a/lib/iris/tests/stock.py b/lib/iris/tests/stock/__init__.py similarity index 99% rename from lib/iris/tests/stock.py rename to lib/iris/tests/stock/__init__.py index 1b55510d2b..5965d5a208 100644 --- a/lib/iris/tests/stock.py +++ b/lib/iris/tests/stock/__init__.py @@ -36,7 +36,8 @@ from iris.coords import DimCoord, AuxCoord import iris.tests as tests from iris.coord_systems import GeogCS, RotatedGeogCS - +from ._stock_2d_latlons import (sample_2d_latlons, + make_bounds_discontiguous_at_point) def lat_lon_cube(): """ diff --git a/lib/iris/tests/stock/_stock_2d_latlons.py b/lib/iris/tests/stock/_stock_2d_latlons.py new file mode 100644 index 0000000000..75b62f31ba --- /dev/null +++ b/lib/iris/tests/stock/_stock_2d_latlons.py @@ -0,0 +1,346 @@ +# (C) British Crown Copyright 2018, 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 . +""" +Extra stock routines for making and manipulating cubes with 2d coordinates, +to mimic ocean grid data. + +""" +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +import numpy as np +import numpy.ma as ma + +from iris.analysis.cartography import unrotate_pole +from iris.cube import Cube +from iris.coords import AuxCoord, DimCoord +from iris.coord_systems import RotatedGeogCS + + +def expand_1d_x_and_y_bounds_to_2d_xy(x_bounds_1d, y_bounds_1d): + """ + Convert bounds for separate 1-D X and Y coords into bounds for the + equivalent 2D coordinates. + + The output arrays have 4 points per cell, for 4 'corners' of a gridcell, + in the usual anticlockwise order + (bottom-left, bottom-right, top-right, top-left). + + If 1-dimensional X and Y coords have shapes nx and ny, then their bounds + have shapes (nx, 2) and (ny, 2). + The equivalent 2d coordinates would have values which are a "meshgrid" of + the original 1-D points, both having the shape (ny, ny). + The outputs are 2d bounds arrays suitable for such 2d coordinates. + + Args: + + * x_bounds_1d, y_bounds_1d : (array) + Coordinate bounds arrays, with shapes (nx, 2) and (ny, 2). + + Result: + + * x_bounds_2d, y_bounds_2d : (array) + Expanded 2d bounds arrays, both of shape (ny, nx, 4). + + """ + shapes = [bds.shape for bds in (x_bounds_1d, y_bounds_1d)] + for shape in shapes: + if len(shape) != 2 or shape[1] != 2: + msg = ('One-dimensional bounds arrays must have shapes (ny, 2) ' + 'and (nx, 2). Got {} and {}.') + raise ValueError(msg.format(*shapes)) + + # Construct output arrays, which are both (ny, nx, 4). + nx, ny = [shape[0] for shape in shapes] + bds_2d_x = np.zeros((ny, nx, 4)) + bds_2d_y = bds_2d_x.copy() + + # Expand x bounds to 2D array (ny, nx, 4) : the same over all 'Y'. + # Bottom left+right corners are the original 1-D x bounds. + bds_2d_x[:, :, 0] = x_bounds_1d[:, 0].reshape((1, nx)) + bds_2d_x[:, :, 1] = x_bounds_1d[:, 1].reshape((1, nx)) + # Top left+right corners are the same as bottom left+right. + bds_2d_x[:, :, 2] = bds_2d_x[:, :, 1].copy() + bds_2d_x[:, :, 3] = bds_2d_x[:, :, 0].copy() + + # Expand y bounds to 2D array (ny, nx, 4) : the same over all 'X'. + # Left-hand lower+upper corners are the original 1-D y bounds. + bds_2d_y[:, :, 0] = y_bounds_1d[:, 0].reshape((ny, 1)) + bds_2d_y[:, :, 3] = y_bounds_1d[:, 1].reshape((ny, 1)) + # Right-hand lower+upper corners are the same as the left-hand ones. + bds_2d_y[:, :, 1] = bds_2d_y[:, :, 0].copy() + bds_2d_y[:, :, 2] = bds_2d_y[:, :, 3].copy() + + return bds_2d_x, bds_2d_y + + +def grid_coords_2d_from_1d(x_coord_1d, y_coord_1d): + """ + Calculate a pair of 2d X+Y coordinates from 1d ones, in a "meshgrid" style. + If the inputs are bounded, the outputs have 4 points per bounds in the + usual way, i.e. points 0,1,2,3 are the gridcell corners anticlockwise from + the bottom left. + + """ + for coord in (x_coord_1d, y_coord_1d): + if coord.ndim != 1: + msg = ('Input coords must be one-dimensional. ' + 'Coordinate "{}" has shape {}.') + raise ValueError(msg.format(coord.name(), coord.shape)) + + # Calculate centre-points as a mesh of the 2 inputs. + pts_2d_x, pts_2d_y = np.meshgrid(x_coord_1d.points, y_coord_1d.points) + if not x_coord_1d.has_bounds() or not y_coord_1d.has_bounds(): + bds_2d_x = None + bds_2d_y = None + else: + bds_2d_x, bds_2d_y = expand_1d_x_and_y_bounds_to_2d_xy( + x_coord_1d.bounds, y_coord_1d.bounds) + + # Make two new coords + return them. + result = [] + for pts, bds, name in zip((pts_2d_x, pts_2d_y), + (bds_2d_x, bds_2d_y), + ('longitude', 'latitude')): + coord = AuxCoord(pts, bounds=bds, standard_name=name, units='degrees') + result.append(coord) + + return result + + +def sample_2d_latlons(regional=False, rotated=False, transformed=False): + """ + Construct small 2d cubes with 2d X and Y coordinates. + + This makes cubes with 'expanded' coordinates (4 bounds per cell), analagous + to ORCA data. + The coordinates are always geographical, so either it has a coord system + or they are "true" lats + lons. + ( At present, they are always latitudes and longitudes, but maybe in a + rotated system. ) + The results always have fully contiguous bounds. + + Kwargs: + * regional (bool): + If False (default), results cover the whole globe, and there is + implicit connectivity between rhs + lhs of the array. + If True, coverage is regional and edges do not connect. + * rotated (bool): + If False, X and Y coordinates are true-latitudes and longitudes, with + an implicit coordinate system (i.e. None). + If True, the X and Y coordinates are lats+lons in a selected + rotated-latlon coordinate system. + * transformed (bool): + Build coords from rotated coords as for 'rotated', but then replace + their values with the equivalent "true" lats + lons, and no + coord-system (defaults to true-latlon). + In this case, the X and Y coords are no longer 'meshgrid' style, + i.e. the points + bounds values vary in *both* dimensions. + + .. note:: + + 'transformed' is an alternative to 'rotated' : when 'transformed' is + set, then 'rotated' has no effect. + + .. Some sample results printouts :: + + >>> print(sample_2d_latlons()) + test_data / (unknown) (-- : 5; -- : 6) + Auxiliary coordinates: + latitude x x + longitude x x + >>> + >>> print(sample_2d_latlons().coord(axis='x')[0, :2]) + AuxCoord(array([ 37.5 , 93.75]), + bounds=array([[ 0. , 65.625, 65.625, 0. ], + [ 65.625, 121.875, 121.875, 65.625]]), + standard_name='longitude', units=Unit('degrees')) + >>> print(np.round(sample_2d_latlons().coord(axis='x').points, 3)) + [[ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75]] + >>> print(np.round(sample_2d_latlons().coord(axis='y').points, 3)) + [[-85. -85. -85. -85. -85. -85. ] + [-47.5 -47.5 -47.5 -47.5 -47.5 -47.5] + [-10. -10. -10. -10. -10. -10. ] + [ 27.5 27.5 27.5 27.5 27.5 27.5] + [ 65. 65. 65. 65. 65. 65. ]] + + + >>> print(np.round( + sample_2d_latlons(rotated=True).coord(axis='x').points, 3)) + [[ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75] + [ 37.5 93.75 150. 206.25 262.5 318.75]] + >>> print(sample_2d_latlons(rotated=True).coord(axis='y').coord_system) + RotatedGeogCS(75.0, 120.0) + + + >>> print( + sample_2d_latlons(transformed=True).coord(axis='y').coord_system) + None + >>> print(np.round( + sample_2d_latlons(transformed=True).coord(axis='x').points, 3)) + [[ -50.718 -40.983 -46.74 -71.938 -79.293 -70.146] + [ -29.867 17.606 77.936 157.145 -141.037 -93.172] + [ -23.139 31.007 87.699 148.322 -154.639 -100.505] + [ -16.054 41.218 92.761 143.837 -164.738 -108.105] + [ 10.86 61.78 100.236 137.285 175.511 -135.446]] + >>> print(np.round( + sample_2d_latlons(transformed=True).coord(axis='y').points, 3)) + [[-70.796 -74.52 -79.048 -79.26 -74.839 -70.96 ] + [-34.99 -46.352 -59.721 -60.34 -47.305 -35.499] + [ 1.976 -10.626 -22.859 -23.349 -11.595 1.37 ] + [ 38.914 25.531 14.312 13.893 24.585 38.215] + [ 74.197 60.258 51.325 51.016 59.446 73.268]] + >>> + + """ + def sample_cube(xargs, yargs): + # Make a test cube with given latitude + longitude coordinates. + # xargs/yargs are args for np.linspace (start, stop, N), to make the X + # and Y coordinate points. + x0, x1, nx = xargs + y0, y1, ny = yargs + # Data has cycling values, staggered a bit in successive rows. + data = np.zeros((ny, nx)) + data.flat[:] = np.arange(ny * nx) % (nx + 2) + # Build a 2d cube with longitude + latitude coordinates. + cube = Cube(data, long_name='test_data') + x_pts = np.linspace(x0, x1, nx, endpoint=True) + y_pts = np.linspace(y0, y1, ny, endpoint=True) + co_x = DimCoord(x_pts, standard_name='longitude', units='degrees') + co_y = DimCoord(y_pts, standard_name='latitude', units='degrees') + cube.add_dim_coord(co_y, 0) + cube.add_dim_coord(co_x, 1) + return cube + + if regional: + # Extract small region. + cube = sample_cube(xargs=(150., 243.75, 6), yargs=(-10., 40., 5)) + + # Make contiguous bounds. + for ax in ('x', 'y'): + cube.coord(axis=ax).guess_bounds() + else: + # Global data, but drastically reduced resolution. + cube = sample_cube(xargs=(37.5, 318.75, 6), yargs=(-85., 65., 5)) + + # Patch bounds to ensure it is still contiguous + global. + for name in ('longitude', 'latitude'): + coord = cube.coord(name) + coord.guess_bounds() + bds = coord.bounds.copy() + # Make bounds global, by fixing lowest and uppermost values. + if name == 'longitude': + bds[0, 0] = 0.0 + bds[-1, 1] = 360.0 + else: + bds[0, 0] = -90.0 + bds[-1, 1] = 90.0 + coord.bounds = bds + + # Get 1d coordinate points + bounds + calculate 2d equivalents. + co_1d_x, co_1d_y = [cube.coord(axis=ax).copy() for ax in ('x', 'y')] + co_2d_x, co_2d_y = grid_coords_2d_from_1d(co_1d_x, co_1d_y) + + # Remove the old grid coords. + for coord in (co_1d_x, co_1d_y): + cube.remove_coord(coord) + + # Add the new grid coords. + for coord in (co_2d_x, co_2d_y): + cube.add_aux_coord(coord, (0, 1)) + + if transformed or rotated: + # Take the lats + lons as being in a rotated coord system. + pole_lat, pole_lon = 75.0, 120.0 + if transformed: + # Reproject coordinate values from rotated to true lat-lons. + co_x, co_y = [cube.coord(axis=ax) for ax in ('x', 'y')] + # Unrotate points. + lons, lats = co_x.points, co_y.points + lons, lats = unrotate_pole(lons, lats, pole_lon, pole_lat) + co_x.points, co_y.points = lons, lats + # Unrotate bounds. + lons, lats = co_x.bounds, co_y.bounds + # Note: save the shape, flatten + then re-apply the shape, because + # "unrotate_pole" uses "cartopy.crs.CRS.transform_points", which + # only works on arrays of 1 or 2 dimensions. + shape = lons.shape + lons, lats = unrotate_pole(lons.flatten(), lats.flatten(), + pole_lon, pole_lat) + co_x.bounds, co_y.bounds = lons.reshape(shape), lats.reshape(shape) + else: + # "Just" rotate operation : add a coord-system to each coord. + cs = RotatedGeogCS(pole_lat, pole_lon) + for coord in cube.coords(): + coord.coord_system = cs + + return cube + + +def make_bounds_discontiguous_at_point(cube, at_iy, at_ix): + """ + Meddle with the XY grid bounds of a cube to make the grid discontiguous. + + Changes the points and bounds of a single gridcell, so that it becomes + discontinuous with the next gridcell to its right. + Also masks the cube data at the given point. + + The cube must have bounded 2d 'x' and 'y' coordinates. + + TODO: add a switch to make a discontiguity in the *y*-direction instead ? + + """ + x_coord = cube.coord(axis='x') + y_coord = cube.coord(axis='y') + assert x_coord.shape == y_coord.shape + assert (coord.bounds.ndim == 3 and coord.shape[-1] == 4 + for coord in (x_coord, y_coord)) + + # For both X and Y coord, move points + bounds to create a discontinuity. + def adjust_coord(coord): + pts, bds = coord.points, coord.bounds + # Fetch the 4 bounds (bottom-left, bottom-right, top-right, top-left) + bds_bl, bds_br, bds_tr, bds_tl = bds[at_iy, at_ix] + # Make a discontinuity "at" (iy, ix), by moving the right-hand edge of + # the cell to the midpoint of the existing left+right bounds. + new_bds_br = 0.5 * (bds_bl + bds_br) + new_bds_tr = 0.5 * (bds_tl + bds_tr) + bds_br, bds_tr = new_bds_br, new_bds_tr + bds[at_iy, at_ix] = [bds_bl, bds_br, bds_tr, bds_tl] + # Also reset the cell midpoint to the middle of the 4 new corners, + # in case having a midpoint outside the corners might cause a problem. + new_pt = 0.25 * sum([bds_bl, bds_br, bds_tr, bds_tl]) + pts[at_iy, at_ix] = new_pt + # Write back the coord points+bounds (can only assign whole arrays). + coord.points, coord.bounds = pts, bds + + adjust_coord(x_coord) + adjust_coord(y_coord) + # Also mask the relevant data point. + data = cube.data # N.B. fetch all the data. + if not ma.isMaskedArray(data): + # Promote to masked array, to avoid converting mask to NaN. + data = ma.masked_array(data) + data[at_iy, at_ix] = ma.masked + cube.data = data diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py index 39c73e8285..c24ac46cb3 100644 --- a/lib/iris/tests/test_coding_standards.py +++ b/lib/iris/tests/test_coding_standards.py @@ -93,7 +93,7 @@ class StandardReportWithExclusions(pep8.StandardReport): '*/iris/io/format_picker.py', '*/iris/tests/__init__.py', '*/iris/tests/pp.py', - '*/iris/tests/stock.py', + '*/iris/tests/stock/__init__.py', '*/iris/tests/system_test.py', '*/iris/tests/test_analysis.py', '*/iris/tests/test_analysis_calculus.py', diff --git a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py deleted file mode 100644 index de00d2bc76..0000000000 --- a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py +++ /dev/null @@ -1,392 +0,0 @@ -# (C) British Crown Copyright 2018, 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 . -""" -Unit tests for the function -:func:`iris.analysis.cartography.gridcell_angles`. - -""" -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 numpy as np -import numpy.ma as ma - -import cartopy.crs as ccrs -from iris.cube import Cube -from iris.coords import DimCoord, AuxCoord -import iris.coord_systems -from iris.analysis.cartography import unrotate_pole - -from iris.analysis.cartography import (gridcell_angles, - rotate_grid_vectors) - -import matplotlib.pyplot as plt -from orca_utils.plot_testing.blockplot_from_bounds import blockplot_2dll - - -def _rotated_grid_sample(pole_lat=15, pole_lon=-180, - lon_bounds=np.linspace(-30, 30, 6, endpoint=True), - lat_bounds=np.linspace(-30, 30, 6, endpoint=True)): - # Calculate *true* lat_bounds+lon_bounds for the rotated grid. - lon_bounds = np.array(lon_bounds, dtype=float) - lat_bounds = np.array(lat_bounds, dtype=float) - # Construct centrepoints. - lons = 0.5 * (lon_bounds[:-1] + lon_bounds[1:]) - lats = 0.5 * (lat_bounds[:-1] + lat_bounds[1:]) - # Convert all to full 2d arrays. - lon_bounds, lat_bounds = np.meshgrid(lon_bounds, lat_bounds) - lons, lats = np.meshgrid(lons, lats) - # Calculate true lats+lons for all points. - lons_true_bds, lats_true_bds = unrotate_pole(lon_bounds, lat_bounds, - pole_lon, pole_lat) - lons_true, lats_true = unrotate_pole(lons, lats, pole_lon, pole_lat) - # Make the 'unified' bounds into contiguous (ny, nx, 4) arrays. - def expand_unified_bds(bds): - ny, nx = bds.shape - bds_4 = np.zeros((ny - 1, nx - 1, 4)) - bds_4[:, :, 0] = bds[:-1, :-1] - bds_4[:, :, 1] = bds[:-1, 1:] - bds_4[:, :, 2] = bds[1:, 1:] - bds_4[:, :, 3] = bds[1:, :-1] - return bds_4 - - lon_true_bds4, lat_true_bds4 = (expand_unified_bds(bds) - for bds in (lons_true_bds, lats_true_bds)) - # Make these into a 2d-latlon grid for a cube - cube = Cube(np.zeros(lon_true_bds4.shape[:-1])) - co_x = AuxCoord(lons_true, bounds=lon_true_bds4, - standard_name='longitude', units='degrees') - co_y = AuxCoord(lats_true, bounds=lat_true_bds4, - standard_name='latitude', units='degrees') - cube.add_aux_coord(co_x, (0, 1)) - cube.add_aux_coord(co_y, (0, 1)) - return cube - - -class TestGridcellAngles(tests.IrisTest): - def _singlecell_30deg_cube(self, x0=90., y0=0., dx=20., dy=10.): - x_pts = np.array([[x0]]) - y_pts = np.array([[y0]]) - x_bds = x0 + dx * np.array([[[-1., 1, 0.5, -1.5]]]) -# self.assertArrayAllClose(x_bds, np.array([[[70., 110, 100, 60]]])) - y_bds = y0 + dy * np.array([[[-1., 1, 3, 1]]]) -# self.assertArrayAllClose(y_bds, np.array([[[-10., 10, 30, 10]]])) - co_x = AuxCoord(points=x_pts, bounds=x_bds, - standard_name='longitude', units='degrees') - co_y = AuxCoord(points=y_pts, bounds=y_bds, - standard_name='latitude', units='degrees') - cube = Cube(np.zeros((1, 1))) - cube.add_aux_coord(co_x, (0, 1)) - cube.add_aux_coord(co_y, (0, 1)) - return cube - - def _singlecell_diamond_cube(self, x0=90., y0=0., dy=10., dx_eq=None): - if dx_eq is None: - dx_eq = dy - x_pts = np.array([[x0]]) - y_pts = np.array([[y0]]) - dx = dx_eq / np.cos(np.deg2rad(y0)) - x_bds = np.array([[[x0, x0 + dx, x0, x0 - dx]]]) - y_bds = np.array([[[y0 - dy, y0, y0 + dy, y0]]]) - co_x = AuxCoord(points=x_pts, bounds=x_bds, - standard_name='longitude', units='degrees') - co_y = AuxCoord(points=y_pts, bounds=y_bds, - standard_name='latitude', units='degrees') - cube = Cube(np.zeros((1, 1))) - cube.add_aux_coord(co_x, (0, 1)) - cube.add_aux_coord(co_y, (0, 1)) - return cube - - def test_single_cell_equatorial(self): - plt.switch_backend('tkagg') - plt.figure(figsize=(10,10)) - ax = plt.axes(projection=ccrs.Mercator()) -# ax = plt.axes(projection=ccrs.NorthPolarStereo()) -# ax = plt.axes(projection=ccrs.Orthographic(central_longitude=90., -# central_latitude=30.)) - - lon0 = 90.0 - dy = 2.0 - y_0, y_n, ny = -80, 80, 9 - angles = [] - for lat in np.linspace(y_0, y_n, ny): - cube = self._singlecell_diamond_cube(x0=lon0, y0=lat, dy=dy) - angles_cube = gridcell_angles(cube, -# cell_angle_boundpoints='mid-lhs, mid-rhs') - cell_angle_boundpoints='lower-left, lower-right') - tmp_cube = angles_cube.copy() - tmp_cube.convert_units('degrees') - print('') - print(lat) - co_x, co_y = (cube.coord(axis=ax) for ax in ('x', 'y')) - print() - print(' at : {}, {}'.format(co_x.points[0, 0], co_y.points[0, 0])) - print(' x-bds:') - print(co_x.bounds) - print(' y-bds:') - print(co_y.bounds) - angle = tmp_cube.data[0, 0] - angles.append(angle) - print(angle) - blockplot_2dll(cube) - - ax.coastlines() - ax.set_global() - - # Plot constant NEly (45deg) arrows. - xx = np.array([lon0] * ny) - yy = np.linspace(y_0, y_n, ny) - dy - uu = np.array([1.0] * ny) - plt.quiver(xx, yy, - uu, np.cos(np.deg2rad(yy)), - zorder=2, color='red', - scale_units='xy', - transform=ccrs.PlateCarree()) - - # Also plot returned angles. - angles_arr_rad = np.deg2rad(angles) - u_arr = uu * np.cos(angles_arr_rad) - v_arr = uu * np.sin(angles_arr_rad) * np.cos(np.deg2rad(yy)) - - plt.quiver(xx, yy, - u_arr, - v_arr, - zorder=2, color='magenta', - scale_units='xy', - width=0.005, - scale=0.2e-6, -# width=0.5, - transform=ccrs.PlateCarree()) - - plt.show() - - - def test_values(self): - # Construct a rotated-pole grid and check angle calculation. - testcube = _rotated_grid_sample() - - cell_angle_boundpoints = 'mid-lhs, mid-rhs' -# cell_angle_boundpoints = 'lower-left, lower-right' -# cell_angle_boundpoints = 'garble' - angles_cube = gridcell_angles( - testcube, - cell_angle_boundpoints=cell_angle_boundpoints) - angles_cube.convert_units('radians') - - # testing phase... - print(np.rad2deg(angles_cube.data)) - - import matplotlib.pyplot as plt - plt.switch_backend('tkagg') - -# plot_map = 'north_polar_stereographic' -# plot_map = 'plate_carree' -# plot_map = 'mercator' - plot_map = 'north_polar_orthographic' - if plot_map == 'plate_carree': - scale = 0.1 - map_proj = ccrs.PlateCarree() - elif plot_map == 'mercator': - scale = 3.0e-6 - map_proj = ccrs.Mercator() - map_proj._threshold *= 0.01 - elif plot_map == 'north_polar_orthographic': - scale = 3.0e-6 - map_proj = ccrs.Orthographic(central_longitude=0.0, - central_latitude=90.0,) - map_proj._threshold *= 0.01 - elif plot_map == 'north_polar_stereographic': - scale = 3.0e-6 - map_proj = ccrs.NorthPolarStereo() - else: - assert 0 - - ax = plt.axes(projection=map_proj) - data_proj = ccrs.PlateCarree() - - deg_scale = 10.0 - -# angles = 'uv' - angles = 'xy' - - ax.coastlines() - ax.gridlines() - for i_bnd in range(4): - color = ['black', 'red', 'blue', 'magenta'][i_bnd] - plt.plot(testcube.coord('longitude').bounds[..., i_bnd], - testcube.coord('latitude').bounds[..., i_bnd], - '+', markersize=10., markeredgewidth=2., - markerfacecolor=color, markeredgecolor=color, - transform=data_proj) - - - # Show plain 0,1 + 1,0 (PlateCarree) vectors unrotated at the given points. - pts_shape = testcube.coord('longitude').shape - ny, nx = pts_shape - u0 = np.ones(pts_shape) - v0 = np.zeros(pts_shape) - u1 = v0.copy() - v1 = u0.copy() - - x0s = testcube.coord('longitude').points - y0s = testcube.coord('latitude').points - yscale = np.cos(np.deg2rad(y0s)) - plt.quiver(x0s, y0s, u0, v0 * yscale, - color='blue', width=0.005, - headwidth=2., # headlength=1.0, headaxislength=0.7, - angles=angles, - scale_units='xy', scale=scale, - transform=data_proj) - plt.quiver(x0s, y0s, u1, v1 * yscale, - color='red', width=0.005, - headwidth=2., # headlength=1.0, headaxislength=0.7, - angles=angles, - scale_units='xy', scale=scale, - transform=data_proj) - - # Add 45deg arrows (NEly), still on a PlateCarree map. - plt.quiver(x0s, y0s, v1, v1 * yscale, - color='green', width=0.005, - headwidth=2., # headlength=1.0, headaxislength=0.7, - angles=angles, - scale_units='xy', scale=scale, - transform=data_proj) - - - - # - # Repeat the above plotting short lines INSTEAD of quiver. - # - u0d = x0s + deg_scale * u0 - v0d = y0s + deg_scale * v0 - u1d = x0s + deg_scale * u1 - v1d = y0s + deg_scale * v1 - u2d = x0s + deg_scale * u0 - v2d = y0s + deg_scale * v1 - for iy in range(ny): - for ix in range(nx): - plt.plot([x0s[iy, ix], u0d[iy, ix]], - [y0s[iy, ix], v0d[iy, ix]], - ':', color='blue', linewidth=0.5, - transform=data_proj) - plt.plot([x0s[iy, ix], u1d[iy, ix]], - [y0s[iy, ix], v1d[iy, ix]], - ':', color='red', linewidth=0.5, - transform=data_proj) - plt.plot([x0s[iy, ix], u2d[iy, ix]], - [y0s[iy, ix], v2d[iy, ix]], - ':', color='green', linewidth=0.5, - transform=data_proj) - - - # Overplot BL-BR and BL-TL lines from the cell bounds. - co_lon, co_lat = [testcube.coord(name).copy() - for name in ('longitude', 'latitude')] - for co in (co_lon, co_lat): - co.convert_units('degrees') - lon_bds, lat_bds = [co.bounds for co in (co_lon, co_lat)] -# ny, nx = lon_bds.shape[:-1] - for iy in range(ny): - for ix in range(nx): - x0, y0 = lon_bds[iy, ix, 0], lat_bds[iy, ix, 0] - x1, y1 = lon_bds[iy, ix, 1], lat_bds[iy, ix, 1] - x2, y2 = lon_bds[iy, ix, 3], lat_bds[iy, ix, 3] - plt.plot([x0, x1], [y0, y1], 'x-', - color='orange', - transform=data_proj) - plt.plot([x0, x2], [y0, y2], 'x-', - color='orange', linestyle='--', - transform=data_proj) - - # Plot U0, rotated by cell angles, also at cell bottom-lefts. - u0_cube, u1_cube, v0_cube, v1_cube = [testcube.copy(data=aa) - for aa in (u0, v0, u1, v1)] - u0r_cube, v0r_cube = rotate_grid_vectors( - u0_cube, v0_cube, grid_angles_cube=angles_cube) - u0r, v0r = [cube.data for cube in (u0r_cube, v0r_cube)] - - xbl, ybl = lon_bds[..., 0], lat_bds[..., 0] - # - # Replace quiver here with delta-based lineplot - # - urd = xbl + deg_scale * u0r - vrd = ybl + deg_scale * v0r * yscale - for iy in range(ny): - for ix in range(nx): - plt.plot([xbl[iy, ix], urd[iy, ix]], - [ybl[iy, ix], vrd[iy, ix]], - ':', color='magenta', linewidth=2.5, - transform=data_proj) - # Show this is the SAME as lineplot - plt.quiver(xbl, ybl, u0r, v0r * yscale, - color='magenta', width=0.01, - headwidth=1.2, # headlength=1.0, headaxislength=0.7, - angles=angles, - scale_units='xy', scale=scale, - transform=data_proj) - - plt.suptitle('angles from "{}"'.format(cell_angle_boundpoints)) - -# # Also draw small lines pointing at the correct (TRUE, not ) angle. -# ny, nx = x0s.shape -# size_degrees = 1.0 -# angles = angles_cube.copy() -# angles.convert_units('radians') -# angles = angles.data -# lats = testcube.coord('latitude').copy() -# lats.convert_units('radians') -# lats = lats.points -# dxs = size_degrees * u0.copy() #* np.cos(angles) -# dys = size_degrees * u0.copy() # / np.sqrt(np.cos(lats)) -# x1s = x0s + dxs -# y1s = y0s + dys -## for iy in range(ny): -## for ix in range(nx): -## plt.plot([x0s[iy, ix], x1s[iy, ix]], -## [y0s[iy, ix], y1s[iy, ix]], -## 'o-', markersize=4., markeredgewidth=0., -## color='green', # scale_units='xy', scale=scale, -## transform=data_proj) -# plt.quiver(x0s, y0s, dxs, dys, -# color='green', linewidth=0.2, -# angles=angles, -# scale_units='xy', scale=scale * 0.6, -# transform=data_proj) - - - - ax.set_global() - plt.show() - - angles_cube.convert_units('degrees') - - self.assertArrayAllClose( - angles_cube.data, - [[33.421, 17.928, 0., -17.928, -33.421], - [41.981, 24.069, 0., -24.069, -41.981], - [56.624, 37.809, 0., -37.809, -56.624], - [79.940, 74.227, 0., -74.227, -79.940], - [107.313, 126.361, -180., -126.361, -107.313]], - atol=0.002) - - -if __name__ == "__main__": - tests.main() diff --git a/lib/iris/tests/unit/plot/test_2d_coords.py b/lib/iris/tests/unit/plot/test_2d_coords.py index bb27341baa..7397921861 100644 --- a/lib/iris/tests/unit/plot/test_2d_coords.py +++ b/lib/iris/tests/unit/plot/test_2d_coords.py @@ -22,20 +22,25 @@ # Import iris.tests first so that some things can be initialised before # importing anything else. import iris.tests as tests -import iris.coords as coords import numpy.ma as ma +import iris.coords as coords from iris.tests.stock import simple_2d_w_multidim_coords as cube_2dcoords from iris.tests.stock import simple_3d_w_multidim_coords as cube3d_2dcoords -from iris.tests.stock import lat_lon_cube +from iris.tests.stock import sample_2d_latlons +from iris.tests.stock import make_bounds_discontiguous_at_point -from orca_utils.plot_testing import testdata_2d_coords as testdata if tests.MPL_AVAILABLE: import iris.plot as iplt +def full2d_global(): + return sample_2d_latlons(transformed=True) + + +@tests.skip_data class Test_2d_coords_plot_defn_bound_mode(tests.IrisTest): def setUp(self): self.multidim_cube = cube_2dcoords() @@ -43,8 +48,8 @@ def setUp(self): # latlon_2d is a cube with 2d coords, 4 bounds per point, # discontiguities in the bounds but masked data at the discontiguities. - self.latlon_2d = testdata.full2d_global() - testdata.make_bounds_discontiguous_at_point(self.latlon_2d, 2, 2) + self.latlon_2d = full2d_global() + make_bounds_discontiguous_at_point(self.latlon_2d, 2, 2) # # Take a latlon cube with 1D coords, broadcast the coords into 2D # # ones, then add ONE of them back into the cube in place of original: @@ -106,9 +111,10 @@ def test_total_span_check(self): # # TODO Find out where I can put a catch for this (if necessary) # cube = self.mixed_dims # with self.assertRaises(ValueError): - # iplt._get_plot_defn_custom_coords_picked(cube, - # ('latitude', 'longitude'), - # self.mode) + # iplt._get_plot_defn_custom_coords_picked( + # cube, + # ('latitude', 'longitude'), + # self.mode) def test_map_common_not_enough_bounds(self): # Test that a lat-lon cube with 2d coords and 2 bounds per point @@ -129,16 +135,16 @@ def test_map_common_2d(self): result = iplt._map_common('pcolor', None, self.mode, cube, plot_defn) self.assertTrue(result) - # def test_discontiguous_masked(self): - # # Test that a contiguity check will raise a warning (not an error) for - # # discontiguous bounds but appropriately masked data. - # cube = self.latlon_2d - # coord = cube.coord('longitude') - # msg = 'The bounds of the longitude coordinate are not contiguous. ' \ - # 'However, data is masked where the discontiguity occurs so ' \ - # 'plotting anyway.' - # with self.assertWarnsRegexp(msg): - # iplt._check_contiguity_and_bounds(coord, cube.data) +# def test_discontiguous_masked(self): +# # Test that a contiguity check will raise a warning (not an error) for +# # discontiguous bounds but appropriately masked data. +# cube = self.latlon_2d +# coord = cube.coord('longitude') +# msg = 'The bounds of the longitude coordinate are not contiguous. ' \ +# 'However, data is masked where the discontiguity occurs so ' \ +# 'plotting anyway.' +# with self.assertWarnsRegexp(msg): +# iplt._check_contiguity_and_bounds(coord, cube.data) def test_discontiguous_unmasked(self): # Check that an error occurs when the contiguity check finds @@ -155,3 +161,7 @@ def test_draw_2d_from_bounds(self): cube = self.latlon_2d result = iplt._draw_2d_from_bounds('pcolormesh', cube) self.assertTrue(result) + + +if __name__ == '__main__': + tests.main() From 5de040fc60c2cad69ce8e8c9cc3d7bd4c1569bfb Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 29 Aug 2018 10:35:06 +0100 Subject: [PATCH 17/24] Vector plots 2 (#3120) * Small improvements; first sensible tests. * Enhanced testing; better checking and crs awareness in grid_angles routine. * Remove crud from test_gridcell_angles. * Use degree units for everything in _grid_angles. * Make assertArrayAllClose print details when it fails. * Rework and extend testing for gridcell_angles. * Fix assertArrayAllClose; remove debug code from test_gridcell_angles. * Remove obsolete assignments. * Remove obsolete code. * Small comment improvements. * Attempt to clarify docstrings of low-level routines. * More tests, and some functional fixes. * Codestyle fixes. * Review changes + fixes. * Avoid using sample data. --- lib/iris/analysis/_grid_angles.py | 281 +++++++++------ lib/iris/analysis/cartography.py | 5 +- lib/iris/tests/__init__.py | 18 +- lib/iris/tests/stock/_stock_2d_latlons.py | 16 +- .../cartography/test_gridcell_angles.py | 327 ++++++++++++++++++ 5 files changed, 526 insertions(+), 121 deletions(-) create mode 100644 lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py index ffe6d51c7f..90876840af 100644 --- a/lib/iris/analysis/_grid_angles.py +++ b/lib/iris/analysis/_grid_angles.py @@ -24,6 +24,7 @@ import numpy as np +import cartopy.crs as ccrs import iris @@ -33,12 +34,16 @@ def _3d_xyz_from_latlon(lon, lat): Args: - * lon, lat: (arrays in degrees) + * lon, lat: (float array) + Arrays of longitudes and latitudes, in degrees. + Both the same shape. Returns: - xyz : (array, dtype=float64) - cartesian coordinates on a unit sphere. Dimension 0 maps x,y,z. + * xyz : (array, dtype=float64) + Cartesian coordinates on a unit sphere. + Shape is (3, ). + The x / y / z coordinates are in xyz[0 / 1 / 2]. """ lon1 = np.deg2rad(lon).astype(np.float64) @@ -60,91 +65,87 @@ def _latlon_from_xyz(xyz): Args: * xyz: (array) - positions array, of dims (3, ), where index 0 maps x/y/z. + Array of 3-D cartesian coordinates. + Shape (3, ). + x / y / z values are in xyz[0 / 1 / 2], Returns: - lonlat : (array) - spherical angles, of dims (2, ), in radians. - Dim 0 maps longitude, latitude. + * lonlat : (array) + longitude and latitude position angles, in degrees. + Shape (2, ). + The longitudes / latitudes are in lonlat[0 / 1]. """ - lons = np.arctan2(xyz[1], xyz[0]) - axial_radii = np.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1]) - lats = np.arctan2(xyz[2], axial_radii) + lons = np.rad2deg(np.arctan2(xyz[1], xyz[0])) + radii = np.sqrt(np.sum(xyz * xyz, axis=0)) + lats = np.rad2deg(np.arcsin(xyz[2] / radii)) return np.array([lons, lats]) def _angle(p, q, r): """ - Return angle (in _radians_) of grid wrt local east. - Anticlockwise +ve, as usual. - {P, Q, R} are consecutive points in the same row, - eg {v(i,j),f(i,j),v(i+1,j)}, or {T(i-1,j),T(i,j),T(i+1,j)} - Calculate dot product of PR with lambda_hat at Q. - This gives us cos(required angle). - Disciminate between +/- angles by comparing latitudes of P and R. - p, q, r, are all 2-element arrays [lon, lat] of angles in degrees. + Estimate grid-angles to true-Eastward direction from positions in the same + grid row, but at increasing column (grid-Eastward) positions. + + {P, Q, R} are locations of consecutive points in the same grid row. + These could be successive points in a single grid, + e.g. {T(i-1,j), T(i,j), T(i+1,j)} + or a mixture of U/V and T gridpoints if row positions are aligned, + e.g. {v(i,j), f(i,j), v(i+1,j)}. + + Method: + + Calculate dot product of a unit-vector parallel to P-->R, unit-scaled, + with the unit eastward (true longitude) vector at Q. + This value is cos(required angle). + Discriminate between +/- angles by comparing latitudes of P and R. + Return NaN where any P-->R are zero. + + .. NOTE:: + + This method assumes that the vector PR is parallel to the surface + at the longitude of Q, as it uses the length of PR as the basis for + the cosine ratio. + That is only exact when Q is at the same longitude as the midpoint + of PR, and this typically causes errors which grow with increasing + gridcell angle. + However, we retain this method because it reproduces the "standard" + gridcell-orientation-angle arrays found in files output by the CICE + model, which presumably uses an equivalent calculation. + + Args: + + * p, q, r : (float array) + Arrays of angles, in degrees. + All the same shape. + Shape is (2, ). + Longitudes / latitudes are in array[0 / 1]. + + Returns: + + * angle : (float array) + Grid angles relative to true-East, in degrees. + Positive when grid-East is anticlockwise from true-East. + Shape is same as . """ -# old_style = True - old_style = False - if old_style: - mid_lons = np.deg2rad(q[0]) + mid_lons = np.deg2rad(q[0]) - pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) - pr_norm = np.sqrt(np.sum(pr**2, axis=0)) - pr_top = pr[1] * np.cos(mid_lons) - pr[0] * np.sin(mid_lons) + pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) + pr_norm = np.sqrt(np.sum(pr**2, axis=0)) + pr_top = pr[1] * np.cos(mid_lons) - pr[0] * np.sin(mid_lons) - index = pr_norm == 0 - pr_norm[index] = 1 + index = pr_norm == 0 + pr_norm[index] = 1 - cosine = np.maximum(np.minimum(pr_top / pr_norm, 1), -1) - cosine[index] = 0 + cosine = np.maximum(np.minimum(pr_top / pr_norm, 1), -1) + cosine[index] = 0 - psi = np.arccos(cosine) * np.sign(r[1] - p[1]) - psi[index] = np.nan - else: - # Calculate unit vectors. - midpt_lons, midpt_lats = q[0], q[1] - lmb_r, phi_r = (np.deg2rad(arr) for arr in (midpt_lons, midpt_lats)) - phi_hatvec_x = -np.sin(phi_r) * np.cos(lmb_r) - phi_hatvec_y = -np.sin(phi_r) * np.sin(lmb_r) - phi_hatvec_z = np.cos(phi_r) - shape_xyz = (1,) + midpt_lons.shape - phi_hatvec = np.concatenate([arr.reshape(shape_xyz) - for arr in (phi_hatvec_x, - phi_hatvec_y, - phi_hatvec_z)]) - lmb_hatvec_z = np.zeros(midpt_lons.shape) - lmb_hatvec_y = np.cos(lmb_r) - lmb_hatvec_x = -np.sin(lmb_r) - lmb_hatvec = np.concatenate([arr.reshape(shape_xyz) - for arr in (lmb_hatvec_x, - lmb_hatvec_y, - lmb_hatvec_z)]) - - pr = _3d_xyz_from_latlon(r[0], r[1]) - _3d_xyz_from_latlon(p[0], p[1]) - - # Dot products to form true-northward / true-eastward projections. - pr_cmpt_e = np.sum(pr * lmb_hatvec, axis=0) - pr_cmpt_n = np.sum(pr * phi_hatvec, axis=0) - psi = np.arctan2(pr_cmpt_n, pr_cmpt_e) - - # TEMPORARY CHECKS: - # ensure that the two unit vectors are perpendicular. - dotprod = np.sum(phi_hatvec * lmb_hatvec, axis=0) - assert np.allclose(dotprod, 0.0) - # ensure that the vector components carry the original magnitude. - mag_orig = np.sum(pr * pr) - mag_rot = np.sum(pr_cmpt_e * pr_cmpt_e) + np.sum(pr_cmpt_n * pr_cmpt_n) - rtol = 1.e-3 - check = np.allclose(mag_rot, mag_orig, rtol=rtol) - if not check: - print(mag_rot, mag_orig) - assert np.allclose(mag_rot, mag_orig, rtol=rtol) - - return psi + psi = np.arccos(cosine) * np.sign(r[1] - p[1]) + psi[index] = np.nan + + return np.rad2deg(psi) def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): @@ -201,12 +202,18 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): angles : (2-dimensional cube) Cube of angles of grid-x vector from true Eastward direction for - each gridcell, in radians. - It also has longitude and latitude coordinates. If coordinates - were input the output has identical ones : If the input was 2d - arrays, the output coords have no bounds; or, if the input was 3d - arrays, the output coords have bounds and centrepoints which are - the average of the 4 bounds. + each gridcell, in degrees. + It also has "true" longitude and latitude coordinates, with no + coordinate system. + When the input has coords, then the output ones are identical if + the inputs are true-latlons, otherwise they are transformed + true-latlon versions. + When the input has bounded coords, then the output coords have + matching bounds and centrepoints (possibly transformed). + When the input is 2d arrays, or has unbounded coords, then the + output coords have matching points and no bounds. + When the input is 3d arrays, then the output coords have matching + bounds, and the centrepoints are an average of the 4 boundpoints. """ cube = None @@ -216,55 +223,105 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): x, y = cube.coord(axis='x'), cube.coord(axis='y') # Now should have either 2 coords or 2 arrays. - if not hasattr(x, 'shape') and hasattr(y, 'shape'): + if not hasattr(x, 'shape') or not hasattr(y, 'shape'): msg = ('Inputs (x,y) must have array shape property.' 'Got type(x)={} and type(y)={}.') raise ValueError(msg.format(type(x), type(y))) x_coord, y_coord = None, None if hasattr(x, 'bounds') and hasattr(y, 'bounds'): + # x and y are Coords. x_coord, y_coord = x.copy(), y.copy() - x_coord.convert_units('degrees') - y_coord.convert_units('degrees') + + # They must be angles : convert into degrees + for coord in (x_coord, y_coord): + if not coord.units.is_convertible('degrees'): + msg = ('Input X and Y coordinates must have angular ' + 'units. Got units of "{!s}" and "{!s}".') + raise ValueError(msg.format(x_coord.units, y_coord.units)) + coord.convert_units('degrees') + if x_coord.ndim != 2 or y_coord.ndim != 2: - msg = ('Coordinate inputs must have 2-dimensional shape. ', + msg = ('Coordinate inputs must have 2-dimensional shape. ' 'Got x-shape of {} and y-shape of {}.') raise ValueError(msg.format(x_coord.shape, y_coord.shape)) if x_coord.shape != y_coord.shape: - msg = ('Coordinate inputs must have same shape. ', + msg = ('Coordinate inputs must have same shape. ' 'Got x-shape of {} and y-shape of {}.') raise ValueError(msg.format(x_coord.shape, y_coord.shape)) -# NOTE: would like to check that dims are in correct order, but can't do that -# if there is no cube. -# TODO: **document** -- another input format requirement -# x_dims, y_dims = (cube.coord_dims(co) for co in (x_coord, y_coord)) -# if x_dims != (0, 1) or y_dims != (0, 1): -# msg = ('Coordinate inputs must map to cube dimensions (0, 1). ', -# 'Got x-dims of {} and y-dims of {}.') -# raise ValueError(msg.format(x_dims, y_dims)) + if cube: + x_dims, y_dims = (cube.coord_dims(co) for co in (x, y)) + if x_dims != y_dims: + msg = ('X and Y coordinates must have the same cube ' + 'dimensions. Got x-dims = {} and y-dims = {}.') + raise ValueError(msg.format(x_dims, y_dims)) + cs = x_coord.coord_system + if y_coord.coord_system != cs: + msg = ('Coordinate inputs must have same coordinate system. ' + 'Got x of {} and y of {}.') + raise ValueError(msg.format(cs, y_coord.coord_system)) + + # Base calculation on bounds if we have them, or points as a fallback. if x_coord.has_bounds() and y_coord.has_bounds(): x, y = x_coord.bounds, y_coord.bounds else: x, y = x_coord.points, y_coord.points + # Make sure these arrays are ordinary lats+lons, in degrees. + if cs is not None: + # Transform points into true lats + lons. + crs_src = cs.as_cartopy_crs() + crs_pc = ccrs.PlateCarree() + + def transform_xy_arrays(x, y): + # Note: flatten, as transform_points is limited to 2D arrays. + shape = x.shape + x, y = (arr.flatten() for arr in (x, y)) + pts = crs_pc.transform_points(crs_src, x, y) + x = pts[..., 0].reshape(shape) + y = pts[..., 1].reshape(shape) + return x, y + + # Transform the main reference points into standard lats+lons. + x, y = transform_xy_arrays(x, y) + + # Likewise replace the original coordinates with transformed ones, + # because the calculation also needs the centrepoint values. + xpts, ypts = (coord.points for coord in (x_coord, y_coord)) + xbds, ybds = (coord.bounds for coord in (x_coord, y_coord)) + xpts, ypts = transform_xy_arrays(xpts, ypts) + xbds, ybds = transform_xy_arrays(xbds, ybds) + x_coord = iris.coords.AuxCoord( + points=xpts, bounds=xbds, + standard_name='longitude', units='degrees') + y_coord = iris.coords.AuxCoord( + points=ypts, bounds=ybds, + standard_name='latitude', units='degrees') + elif hasattr(x, 'bounds') or hasattr(y, 'bounds'): # One was a Coord, and the other not ? is_and_not = ('x', 'y') if hasattr(y, 'bounds'): is_and_not = reversed(is_and_not) msg = 'Input {!r} is a Coordinate, but {!r} is not.' - raise ValueError(*is_and_not) + raise ValueError(msg.format(*is_and_not)) - # Now have either 2 points arrays or 2 bounds arrays. - # Construct (lhs, mid, rhs) where these represent 3 adjacent points with - # increasing longitudes. + # Now have either 2 points arrays (ny, nx) or 2 bounds arrays (ny, nx, 4). + # Construct (lhs, mid, rhs) where these represent 3 points at increasing + # grid-x indices (columns). + # Also make suitable X and Y coordinates for the result cube. if x.ndim == 2: - # PROBLEM: we can't use this if data is not full-longitudes, - # i.e. rhs of array must connect to lhs (aka 'circular' coordinate). - # But we have no means of checking that ? + # Data is points arrays. + # Use previous + subsequent points along grid-x-axis as references. + + # PROBLEM: we assume that the rhs connects to the lhs, so we should + # really only use this if data is full-longitudes (as a 'circular' + # coordinate). + # This is mentioned in the docstring, but we have no general means of + # checking it. - # Use previous + subsequent points along longitude-axis as references. - # NOTE: we also have no way to check that dim #2 really is the 'X' dim. + # NOTE: we take the 2d grid as presented, so the second dimension is + # the 'X' dim. Again, that is implicit + can't be checked. mid = np.array([x, y]) lhs = np.roll(mid, 1, 2) rhs = np.roll(mid, -1, 2) @@ -275,9 +332,11 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): x_coord = iris.coords.AuxCoord(y, standard_name='longitude', units='degrees') else: - # Get lhs and rhs locations by averaging top+bottom each side. + # Data is bounds arrays. + # Use gridcell corners at different grid-x positions as references. # NOTE: so with bounds, we *don't* need full circular longitudes. xyz = _3d_xyz_from_latlon(x, y) + # Support two different choices of reference points locations. angle_boundpoints_vals = {'mid-lhs, mid-rhs': '03_to_12', 'lower-left, lower-right': '0_to_1'} bounds_pos = angle_boundpoints_vals.get(cell_angle_boundpoints) @@ -294,8 +353,8 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): list(angle_boundpoints_vals.keys()))) if not x_coord: # Create bounded coords for result cube. - # Use average lhs+rhs points in 3d to get 'mid' points, as coords - # with no points are not allowed. + # Use average of lhs+rhs points in 3d to get 'mid' points, + # as coords without points are not allowed. mid_xyz = 0.5 * (lhs_xyz + rhs_xyz) mid_latlons = _latlon_from_xyz(mid_xyz) # Create coords with given bounds, and averaged centrepoints. @@ -305,28 +364,30 @@ def gridcell_angles(x, y=None, cell_angle_boundpoints='mid-lhs, mid-rhs'): y_coord = iris.coords.AuxCoord( points=mid_latlons[1], bounds=y, standard_name='latitude', units='degrees') + # Convert lhs and rhs points back to latlon form -- IN DEGREES ! - lhs = np.rad2deg(_latlon_from_xyz(lhs_xyz)) - rhs = np.rad2deg(_latlon_from_xyz(rhs_xyz)) - # mid is coord.points, whether input or made up. + lhs = _latlon_from_xyz(lhs_xyz) + rhs = _latlon_from_xyz(rhs_xyz) + # 'mid' is coord.points, whether from input or just made up. mid = np.array([x_coord.points, y_coord.points]) # Do the angle calcs, and return as a suitable cube. angles = _angle(lhs, mid, rhs) result = iris.cube.Cube(angles, long_name='gridcell_angle_from_true_east', - units='radians') + units='degrees') result.add_aux_coord(x_coord, (0, 1)) result.add_aux_coord(y_coord, (0, 1)) return result -def true_vectors_from_grid_vectors(u_cube, v_cube, - grid_angles_cube=None, - grid_angles_kwargs=None): +def rotate_grid_vectors(u_cube, v_cube, grid_angles_cube=None, + grid_angles_kwargs=None): """ Rotate distance vectors from grid-oriented to true-latlon-oriented. + Can also rotate by arbitrary angles, if they are passed in. + .. Note:: This operation overlaps somewhat in function with diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index dadcdedc86..a778f5f846 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -37,10 +37,7 @@ import iris.coord_systems import iris.exceptions from iris.util import _meshgrid -from ._grid_angles import ( - gridcell_angles, - true_vectors_from_grid_vectors as rotate_grid_vectors) - +from ._grid_angles import gridcell_angles, rotate_grid_vectors # List of contents to control Sphinx autodocs. # Unfortunately essential to get docs for the grid_angles functions. diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index 710a3fbac6..7550fce1ca 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -663,7 +663,23 @@ def assertArrayAllClose(self, a, b, rtol=1.0e-7, atol=0.0, **kwargs): For full details see underlying routine numpy.testing.assert_allclose. """ - np.testing.assert_allclose(a, b, rtol=rtol, atol=atol, **kwargs) + ok = np.allclose(a, b, atol=atol, rtol=rtol, equal_nan=True) + if not ok: + # Calculate errors above a pointwise tolerance : The method is + # taken from "numpy.core.numeric.isclose". + errors = (np.abs(a-b) - atol + rtol * np.abs(b)) + worst_inds = np.unravel_index(np.argmax(errors.flat), errors.shape) + # Build a more useful message than from np.testing.assert_allclose. + msg = ('\nARRAY CHECK FAILED "assertArrayAllClose" :' + '\n with shapes={} {}, atol={}, rtol={}' + '\n worst at element {} : a={} b={}' + '\n absolute error ~{:.3g}, equivalent to rtol ~{:.3e}') + aval, bval = a[worst_inds], b[worst_inds] + absdiff = np.abs(aval - bval) + equiv_rtol = absdiff / bval + raise AssertionError(msg.format( + a.shape, b.shape, atol, rtol, worst_inds, aval, bval, absdiff, + equiv_rtol)) @contextlib.contextmanager def temp_filename(self, suffix=''): diff --git a/lib/iris/tests/stock/_stock_2d_latlons.py b/lib/iris/tests/stock/_stock_2d_latlons.py index 75b62f31ba..395a50d8ea 100644 --- a/lib/iris/tests/stock/_stock_2d_latlons.py +++ b/lib/iris/tests/stock/_stock_2d_latlons.py @@ -233,18 +233,19 @@ def sample_cube(xargs, yargs): cube.add_dim_coord(co_x, 1) return cube + # Start by making a "normal" cube with separate 1-D X and Y coords. if regional: - # Extract small region. + # Make a small regional cube. cube = sample_cube(xargs=(150., 243.75, 6), yargs=(-10., 40., 5)) - # Make contiguous bounds. + # Add contiguous bounds. for ax in ('x', 'y'): cube.coord(axis=ax).guess_bounds() else: - # Global data, but drastically reduced resolution. + # Global data, but at a drastically reduced resolution. cube = sample_cube(xargs=(37.5, 318.75, 6), yargs=(-85., 65., 5)) - # Patch bounds to ensure it is still contiguous + global. + # Make contiguous bounds and adjust outer edges to ensure it is global. for name in ('longitude', 'latitude'): coord = cube.coord(name) coord.guess_bounds() @@ -258,8 +259,11 @@ def sample_cube(xargs, yargs): bds[-1, 1] = 90.0 coord.bounds = bds - # Get 1d coordinate points + bounds + calculate 2d equivalents. + # Now convert the 1-d coords to 2-d equivalents. + # Get original 1-d coords. co_1d_x, co_1d_y = [cube.coord(axis=ax).copy() for ax in ('x', 'y')] + + # Calculate 2-d equivalents. co_2d_x, co_2d_y = grid_coords_2d_from_1d(co_1d_x, co_1d_y) # Remove the old grid coords. @@ -271,7 +275,7 @@ def sample_cube(xargs, yargs): cube.add_aux_coord(coord, (0, 1)) if transformed or rotated: - # Take the lats + lons as being in a rotated coord system. + # Put the cube locations into a rotated coord system. pole_lat, pole_lon = 75.0, 120.0 if transformed: # Reproject coordinate values from rotated to true lat-lons. diff --git a/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py new file mode 100644 index 0000000000..4a1344e83d --- /dev/null +++ b/lib/iris/tests/unit/analysis/cartography/test_gridcell_angles.py @@ -0,0 +1,327 @@ +# (C) British Crown Copyright 2018, 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 . +""" +Unit tests for the function +:func:`iris.analysis.cartography.gridcell_angles`. + +""" +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 numpy as np + +from cf_units import Unit +from iris.cube import Cube +from iris.coords import AuxCoord + +from iris.analysis.cartography import gridcell_angles +from iris.tests.stock import sample_2d_latlons, lat_lon_cube + + +def _2d_multicells_testcube(cellsize_degrees=1.0): + """ + Create a test cube with a grid of X and Y points, where each gridcell + is independent (disjoint), arranged at an angle == the x-coord point. + + """ + # Setup np.linspace arguments to make the coordinate points. + x0, x1, nx = -164, 164, 9 + y0, y1, ny = -75, 75, 7 + + lats = np.linspace(y0, y1, ny, endpoint=True) + lons_angles = np.linspace(x0, x1, nx, endpoint=True) + x_pts_2d, y_pts_2d = np.meshgrid(lons_angles, lats) + + # Make gridcells rectangles surrounding these centrepoints, but also + # tilted at various angles (= same as x-point lons, as that's easy). + + # Calculate centrepoint lons+lats : in radians, and shape (ny, nx, 1). + xangs, yangs = np.deg2rad(x_pts_2d), np.deg2rad(y_pts_2d) + xangs, yangs = [arr[..., None] for arr in (xangs, yangs)] + # Program which corners are up+down on each gridcell axis. + dx_corners = [[[-1, 1, 1, -1]]] + dy_corners = [[[-1, -1, 1, 1]]] + # Calculate the relative offsets in x+y at the 4 corners. + x_ofs_2d = cellsize_degrees * np.cos(xangs) * dx_corners + x_ofs_2d -= cellsize_degrees * np.sin(xangs) * dy_corners + y_ofs_2d = cellsize_degrees * np.cos(xangs) * dy_corners + y_ofs_2d += cellsize_degrees * np.sin(xangs) * dx_corners + # Apply a latitude stretch to make correct angles on the globe. + y_ofs_2d *= np.cos(yangs) + # Make bounds arrays by adding the corner offsets to the centrepoints. + x_bds_2d = x_pts_2d[..., None] + x_ofs_2d + y_bds_2d = y_pts_2d[..., None] + y_ofs_2d + + # Create a cube with these points + bounds in its 'X' and 'Y' coords. + co_x = AuxCoord(points=x_pts_2d, bounds=x_bds_2d, + standard_name='longitude', units='degrees') + co_y = AuxCoord(points=y_pts_2d, bounds=y_bds_2d, + standard_name='latitude', units='degrees') + cube = Cube(np.zeros((ny, nx))) + cube.add_aux_coord(co_x, (0, 1)) + cube.add_aux_coord(co_y, (0, 1)) + return cube + + +class TestGridcellAngles(tests.IrisTest): + def setUp(self): + # Make a small "normal" contiguous-bounded cube to test on. + # This one is regional. + self.standard_regional_cube = sample_2d_latlons( + regional=True, transformed=True) + # Record the standard correct angle answers. + result_cube = gridcell_angles(self.standard_regional_cube) + result_cube.convert_units('degrees') + self.standard_result_cube = result_cube + self.standard_small_cube_results = result_cube.data + + def _check_multiple_orientations_and_latitudes(self, + method='mid-lhs, mid-rhs', + atol_degrees=0.005, + cellsize_degrees=1.0): + + cube = _2d_multicells_testcube(cellsize_degrees=cellsize_degrees) + + # Calculate gridcell angles at each point. + angles_cube = gridcell_angles(cube, cell_angle_boundpoints=method) + + # Check that the results are a close match to the original intended + # gridcell orientation angles. + # NOTE: neither the above gridcell construction nor the calculation + # itself are exact : Errors scale as the square of gridcell sizes. + angles_cube.convert_units('degrees') + angles_calculated = angles_cube.data + + # Note: the gridcell angles **should** just match the longitudes at + # each point + angles_expected = cube.coord('longitude').points + + # Wrap both into standard range for comparison. + angles_calculated = (angles_calculated + 360.) % 360. + angles_expected = (angles_expected + 360.) % 360. + + # Assert (toleranced) equality, and return results. + self.assertArrayAllClose(angles_calculated, angles_expected, + atol=atol_degrees) + + return angles_calculated, angles_expected + + def test_various_orientations_and_locations(self): + self._check_multiple_orientations_and_latitudes() + + def test_result_form(self): + # Check properties of the result cube *other than* the data values. + test_cube = self.standard_regional_cube + result_cube = self.standard_result_cube + self.assertEqual(result_cube.long_name, + 'gridcell_angle_from_true_east') + self.assertEqual(result_cube.units, Unit('degrees')) + self.assertEqual(len(result_cube.coords()), 2) + self.assertEqual(result_cube.coord(axis='x'), + test_cube.coord(axis='x')) + self.assertEqual(result_cube.coord(axis='y'), + test_cube.coord(axis='y')) + + def test_bottom_edge_method(self): + # Get results with the "other" calculation method + check to tolerance. + # A smallish cellsize should yield similar results in both cases. + r1, _ = self._check_multiple_orientations_and_latitudes() + r2, _ = self._check_multiple_orientations_and_latitudes( + method='lower-left, lower-right', + cellsize_degrees=0.1, atol_degrees=0.1) + + # Not *exactly* the same : this checks we tested the 'other' method ! + self.assertFalse(np.allclose(r1, r2)) + # Note: results are a bit different in places. This is acceptable. + self.assertArrayAllClose(r1, r2, atol=0.1) + + def test_bounded_coord_args(self): + # Check that passing the coords gives the same result as the cube. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + result = gridcell_angles(co_x, co_y) + self.assertArrayAllClose(result.data, + self.standard_small_cube_results) + + def test_coords_radians_args(self): + # Check it still works with coords converted to radians. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + for coord in (co_x, co_y): + coord.convert_units('radians') + result = gridcell_angles(co_x, co_y) + self.assertArrayAllClose(result.data, + self.standard_small_cube_results) + + def test_bounds_array_args(self): + # Check we can calculate from bounds values alone. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + # Results drawn from coord bounds should be nearly the same, + # but not exactly, because of the different 'midpoint' values. + result = gridcell_angles(co_x.bounds, co_y.bounds) + self.assertArrayAllClose(result.data, + self.standard_small_cube_results, atol=0.1) + + def test_unbounded_regional_coord_args(self): + # Remove the coord bounds to check points-based calculation. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + for coord in (co_x, co_y): + coord.bounds = None + result = gridcell_angles(co_x, co_y) + # Note: in this case, we can expect the leftmost and rightmost columns + # to be rubbish, because the data is not global. + # But the rest should match okay. + self.assertArrayAllClose(result.data[:, 1:-1], + self.standard_small_cube_results[:, 1:-1]) + + def test_points_array_args(self): + # Check we can calculate from points arrays alone (no coords). + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + # As previous, the leftmost and rightmost columns are not good. + result = gridcell_angles(co_x.points, co_y.points) + self.assertArrayAllClose(result.data[:, 1:-1], + self.standard_small_cube_results[:, 1:-1]) + + def test_unbounded_global(self): + # For a contiguous global grid, a result based on points, i.e. with the + # bounds removed, should be a reasonable match for the 'ideal' one + # based on the bounds. + + # Make a global cube + calculate ideal bounds-based results. + global_cube = sample_2d_latlons(transformed=True) + result_cube = gridcell_angles(global_cube) + result_cube.convert_units('degrees') + global_cube_results = result_cube.data + + # Check a points-based calculation on the same basic grid. + co_x, co_y = (global_cube.coord(axis=ax) + for ax in ('x', 'y')) + for coord in (co_x, co_y): + coord.bounds = None + result = gridcell_angles(co_x, co_y) + # In this case, the match is actually rather poor (!). + self.assertArrayAllClose(result.data, + global_cube_results, + atol=7.5) + # Leaving off first + last columns again gives a decent result. + self.assertArrayAllClose(result.data[:, 1:-1], + global_cube_results[:, 1:-1]) + + # NOTE: although this looks just as bad as 'test_points_array_args', + # maximum errors there in the end columns are actually > 100 degrees ! + + def test_nonlatlon_coord_system(self): + # Check with points specified in an unexpected coord system. + cube = sample_2d_latlons(regional=True, rotated=True) + result = gridcell_angles(cube) + self.assertArrayAllClose(result.data, + self.standard_small_cube_results) + # Check that the result has transformed (true-latlon) coordinates. + self.assertEqual(len(result.coords()), 2) + x_coord = result.coord(axis='x') + y_coord = result.coord(axis='y') + self.assertEqual(x_coord.shape, cube.shape) + self.assertEqual(y_coord.shape, cube.shape) + self.assertIsNotNone(cube.coord_system) + self.assertIsNone(x_coord.coord_system) + self.assertIsNone(y_coord.coord_system) + + def test_fail_coords_bad_units(self): + # Check error with bad coords units. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + co_y.units = 'm' + with self.assertRaisesRegexp(ValueError, 'must have angular units'): + gridcell_angles(co_x, co_y) + + def test_fail_nonarraylike(self): + # Check error with bad args. + co_x, co_y = 1, 2 + with self.assertRaisesRegexp(ValueError, + 'must have array shape property'): + gridcell_angles(co_x, co_y) + + def test_fail_non2d_coords(self): + # Check error with bad args. + cube = lat_lon_cube() + with self.assertRaisesRegexp(ValueError, + 'inputs must have 2-dimensional shape'): + gridcell_angles(cube) + + def test_fail_different_shapes(self): + # Check error with mismatched shapes. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + co_y = co_y[1:] + with self.assertRaisesRegexp(ValueError, 'must have same shape'): + gridcell_angles(co_x, co_y) + + def test_fail_different_coord_system(self): + # Check error with mismatched coord systems. + cube = sample_2d_latlons(regional=True, rotated=True) + cube.coord(axis='x').coord_system = None + with self.assertRaisesRegexp(ValueError, + 'must have same coordinate system'): + gridcell_angles(cube) + + def test_fail_cube_dims(self): + # Check error with mismatched cube dims. + cube = self.standard_regional_cube + # Make 5x6 into 5x5. + cube = cube[:, :-1] + co_x = cube.coord(axis='x') + pts, bds = co_x.points, co_x.bounds + co_new_x = co_x.copy(points=pts.transpose((1, 0)), + bounds=bds.transpose((1, 0, 2))) + cube.remove_coord(co_x) + cube.add_aux_coord(co_new_x, (1, 0)) + with self.assertRaisesRegexp(ValueError, + 'must have the same cube dimensions'): + gridcell_angles(cube) + + def test_fail_coord_noncoord(self): + # Check that passing a coord + an array gives an error. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + with self.assertRaisesRegexp(ValueError, + 'is a Coordinate, but .* is not'): + gridcell_angles(co_x, co_y.bounds) + + def test_fail_noncoord_coord(self): + # Check that passing an array + a coord gives an error. + co_x, co_y = (self.standard_regional_cube.coord(axis=ax) + for ax in ('x', 'y')) + with self.assertRaisesRegexp(ValueError, + 'is a Coordinate, but .* is not'): + gridcell_angles(co_x.points, co_y) + + def test_fail_bad_method(self): + with self.assertRaisesRegexp(ValueError, + 'unrecognised cell_angle_boundpoints'): + self._check_multiple_orientations_and_latitudes( + method='something_unknown') + + +if __name__ == "__main__": + tests.main() From 2b6b7870e346d3eb2fecd89ae1d5d742d6e42338 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Fri, 31 Aug 2018 16:28:59 +0100 Subject: [PATCH 18/24] Temporary pin to avoid problems caused by Proj4 v5.1. (#3150) --- requirements/core.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/core.txt b/requirements/core.txt index 997c91c665..e9abec5732 100644 --- a/requirements/core.txt +++ b/requirements/core.txt @@ -4,6 +4,7 @@ # Without these, iris won't even import. cartopy +#conda: proj4<5 cf_units>=2 cftime dask[array]==0.18.1 #conda: dask==0.18.1 From cb647615968b0bb07013543745205330af90210a Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Fri, 31 Aug 2018 16:35:22 +0100 Subject: [PATCH 19/24] Extending ORCA blockplotting (w/ tests) (#3135) --- lib/iris/coords.py | 210 +++++++------ lib/iris/plot.py | 145 +++++---- lib/iris/tests/unit/coords/test_Coord.py | 293 ++++++++++++++++++ lib/iris/tests/unit/plot/test_2d_coords.py | 167 ---------- .../test__check_bounds_contiguity_and_mask.py | 96 ++++++ .../tests/unit/plot/test__get_plot_defn.py | 10 +- ...est__get_plot_defn_custom_coords_picked.py | 93 ++++++ 7 files changed, 694 insertions(+), 320 deletions(-) delete mode 100644 lib/iris/tests/unit/plot/test_2d_coords.py create mode 100644 lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py create mode 100644 lib/iris/tests/unit/plot/test__get_plot_defn_custom_coords_picked.py diff --git a/lib/iris/coords.py b/lib/iris/coords.py index ff2f170a2f..42882fbc2c 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -143,81 +143,43 @@ def __new__(cls, name_or_coord, minimum, maximum, 'groupby_point, groupby_slice') -def _discontiguity_in_2d_bounds(bds, abs_tol=1e-4): +def _get_2d_coord_bound_grid(bounds): """ - Check bounds of a 2-dimensional coordinate are contiguous - Args: - bds: Array of bounds of shape (X,Y,4) - abs_tol: tolerance - - Returns: - Bool, if there are no discontinuities - absolute difference along the x axis - absolute difference along the y axis - - """ - # Check bds has the shape (ny, nx, 4) - if not bds.ndim == 3 and bds.shape[2] == 4: - raise ValueError('2D coordinates must have 4 bounds per point ' - 'for 2D coordinate plotting') - - # Check ordering: - # i i+1 - # j @0 @1 - # j+1 @3 @2 - def mod360_diff(x1, x2): - diff = x1 - x2 - diff = (diff + 360.0 + 180.0) % 360.0 - 180.0 - return diff - - # Compare cell with the cell next to it (i+1) - diffs_along_x = mod360_diff(bds[:, :-1, 1], bds[:, 1:, 0]) - # Compare cell with the cell above it (j+1) - diffs_along_y = mod360_diff(bds[:-1, :, 3], bds[1:, :, 0]) - - def eq_diffs(x1): - return np.all(np.abs(x1) < abs_tol) - - match_y0_x1 = eq_diffs(diffs_along_x) - match_y1_x0 = eq_diffs(diffs_along_y) + Creates a grid using the bounds of a 2D coordinate with 4 sided cells. - all_eq = match_y0_x1 and match_y1_x0 + Assumes that the four vertices of the cells are in an anti-clockwise order + (bottom-left, bottom-right, top-right, top-left). - return all_eq, np.abs(diffs_along_x), np.abs(diffs_along_y) - - -def _get_2d_coord_bound_grid(bds): - """ - Function used that takes a bounds array for a 2-D coordinate variable with - 4 sides and returns the bounds grid. - - Cf standards requires the four vertices of the cell to be traversed - anti-clockwise if the coordinates are defined in a right handed coordinate - system. - - selects the zeroth vertex of each cell and then adds the column the first - vertex at the end. For the top row it uses the thirs vertex, with the - second added on to the end. + Selects the zeroth vertex of each cell. A final column is added, which + contains the first vertex of the cells in the final column. A final row + is added, which contains the third vertex of all the cells in the final + row, except for in the final column where it uses the second vertex. e.g. # 0-0-0-0-1 # 0-0-0-0-1 # 3-3-3-3-2 - Args: - bounds: array of shape (X,Y,4) + * bounds: (array) + Coordinate bounds array of shape (Y, X, 4) Returns: - array of shape (X+1, Y+1) + * grid: (array) + Grid of shape (Y+1, X+1) """ - bds_shape = bds.shape - result = np.zeros((bds_shape[0] + 1, bds_shape[1] + 1)) + # Check bds has the shape (ny, nx, 4) + if not (bounds.ndim == 3 and bounds.shape[-1] == 4): + raise ValueError('Bounds for 2D coordinates must be 3-dimensional and ' + 'have 4 bounds per point.') + + bounds_shape = bounds.shape + result = np.zeros((bounds_shape[0] + 1, bounds_shape[1] + 1)) - result[:-1, :-1] = bds[:, :, 0] - result[:-1, -1] = bds[:, -1, 1] - result[-1, :-1] = bds[-1, :, 3] - result[-1, -1] = bds[-1, -1, 2] + result[:-1, :-1] = bounds[:, :, 0] + result[:-1, -1] = bounds[:, -1, 1] + result[-1, :-1] = bounds[-1, :, 3] + result[-1, -1] = bounds[-1, -1, 2] return result @@ -1029,29 +991,101 @@ def cells(self): """ return _CellIterator(self) - def _sanity_check_contiguous(self): + def _sanity_check_bounds(self): if self.ndim == 1: if self.nbounds != 2: - raise ValueError('Invalid operation for {!r}, with {} bounds. ' - 'Contiguous bounds are only defined for 1D ' - 'coordinates with 2 bounds.'.format - (self.name(), self.nbounds)) + raise ValueError('Invalid operation for {!r}, with {} ' + 'bound(s). Contiguous bounds are only ' + 'defined for 1D coordinates with 2 ' + 'bounds.'.format(self.name(), self.nbounds)) elif self.ndim == 2: if self.nbounds != 4: - raise ValueError('Invalid operation for {!r}, with {} bounds. ' - 'Contiguous bounds are only defined for 2D ' - 'coordinates with 4 bounds.'.format - (self.name(), self.nbounds)) + raise ValueError('Invalid operation for {!r}, with {} ' + 'bound(s). Contiguous bounds are only ' + 'defined for 2D coordinates with 4 ' + 'bounds.'.format(self.name(), self.nbounds)) else: raise ValueError('Invalid operation for {!r}. Contiguous bounds ' 'are not defined for coordinates with more than ' '2 dimensions.'.format(self.name())) + def _discontiguity_in_bounds(self, rtol=1e-05, atol=1e-08): + """ + Checks that the bounds of the coordinate are contiguous. + + Kwargs: + * rtol: (float) + Relative tolerance that is used when checking contiguity. Defaults + to 1e-5. + * atol: (float) + Absolute tolerance that is used when checking contiguity. Defaults + to 1e-8. + + Returns: + * contiguous: (boolean) + True if there are no discontiguities. + * diffs: (array or tuple of arrays) + The diffs along the bounds of the coordinate. If self is a 2D + coord of shape (Y, X), a tuple of arrays is returned, where the + first is an array of differences along the x-axis, of the shape + (Y, X-1) and the second is an array of differences along the + y-axis, of the shape (Y-1, X). + + """ + self._sanity_check_bounds() + + if self.ndim == 1: + contiguous = np.allclose(self.bounds[1:, 0], + self.bounds[:-1, 1], + rtol=rtol, atol=atol) + diffs = np.abs(self.bounds[:-1, 1] - self.bounds[1:, 0]) + + elif self.ndim == 2: + def mod360_adjust(compare_axis): + bounds = self.bounds.copy() + + if compare_axis == 'x': + upper_bounds = bounds[:, :-1, 1] + lower_bounds = bounds[:, 1:, 0] + elif compare_axis == 'y': + upper_bounds = bounds[:-1, :, 3] + lower_bounds = bounds[1:, :, 0] + + if self.name() in ['longitude', 'grid_longitude']: + # If longitude, adjust for longitude wrapping + diffs = upper_bounds - lower_bounds + index = diffs > 180 + if index.any(): + sign = np.sign(diffs) + modification = (index.astype(int) * 360) * sign + upper_bounds -= modification + + diffs_along_axis = np.abs(upper_bounds - lower_bounds) + contiguous_along_axis = np.allclose(upper_bounds, lower_bounds, + rtol=rtol, atol=atol) + return diffs_along_axis, contiguous_along_axis + + diffs_along_x, match_cell_x1 = mod360_adjust(compare_axis='x') + diffs_along_y, match_cell_y1 = mod360_adjust(compare_axis='y') + + contiguous = match_cell_x1 and match_cell_y1 + diffs = (diffs_along_x, diffs_along_y) + + return contiguous, diffs + def is_contiguous(self, rtol=1e-05, atol=1e-08): """ Return True if, and only if, this Coord is bounded with contiguous bounds to within the specified relative and absolute tolerances. + 1D coords are contiguous if the upper bound of a cell aligns, + within a tolerance, to the lower bound of the next cell along. + + 2D coords, with 4 bounds, are contiguous if the lower right corner of + each cell aligns with the lower left corner of the cell to the right of + it, and the upper left corner of each cell aligns with the lower left + corner of the cell above it. + Args: * rtol: @@ -1064,14 +1098,7 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): """ if self.has_bounds(): - self._sanity_check_contiguous() - if self.ndim == 1: - contiguous = np.allclose(self.bounds[1:, 0], - self.bounds[:-1, 1], - rtol=rtol, atol=atol) - elif self.ndim == 2: - contiguous, _, _ = _discontiguity_in_2d_bounds(self.bounds, - abs_tol=atol) + contiguous, _ = self._discontiguity_in_bounds(rtol=rtol, atol=atol) else: contiguous = False return contiguous @@ -1079,25 +1106,34 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): def contiguous_bounds(self): """ Returns the N+1 bound values for a contiguous bounded 1D coordinate - of length N. - - Returns the (N+1, M+1) bound values for a contiguous bounded 2D + of length N, or the (N+1, M+1) bound values for a contiguous bounded 2D coordinate of shape (N, M). - Assumes input is contiguous. + Only 1D or 2D coordinates are supported. .. note:: - If the coordinate does not have bounds, this method will + If the coordinate has bounds, this method assumes they are + contiguous. + + If the coordinate is 1D and does not have bounds, this method will return bounds positioned halfway between the coordinate's points. + If the coordinate is 2D and does not have bounds, an error will be + raised. + """ if not self.has_bounds(): - warnings.warn('Coordinate {!r} is not bounded, guessing ' - 'contiguous bounds.'.format(self.name())) - bounds = self._guess_bounds() + if self.ndim == 1: + warnings.warn('Coordinate {!r} is not bounded, guessing ' + 'contiguous bounds.'.format(self.name())) + bounds = self._guess_bounds() + elif self.ndim == 2: + raise ValueError('2D coordinate {!r} is not bounded. Guessing ' + 'bounds of 2D coords is not currently ' + 'supported.'.format(self.name())) else: - self._sanity_check_contiguous() + self._sanity_check_bounds() bounds = self.bounds if self.ndim == 1: diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 7bf8ea07f3..0b9b7c54be 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -102,7 +102,7 @@ def get_span(coord): raise ValueError(msg.format(coord.name())) if mode == iris.coords.BOUND_MODE and len(span) not in [1, 2]: raise ValueError('The coordinate {!r} has {} dimensions.' - 'Cell-based plotting is only supporting for' + 'Cell-based plotting is only supported for' 'coordinates with one or two dimensions.' .format(coord.name()), len(span)) @@ -271,54 +271,82 @@ def _invert_yaxis(v_coord, axes=None): axes.invert_yaxis() -def _check_contiguity_and_bounds(coord, data, abs_tol=1e-4, transpose=False): +def _check_bounds_contiguity_and_mask(coord, data, atol=None): """ - Check that the discontinuous bounds occur where the data is masked. + Checks that any discontiguities in the bounds of the given coordinate only + occur where the data is masked. - If discontinuity occurs but data is masked, raise warning - If discontinuity occurs and data is NOT masked, raise error + Where a discontinuity occurs the grid created for plotting will not be + correct. This does not matter if the data is masked in that location as + this is not plotted. + + If a discontiguity occurs where the data is *not* masked, an error is + raised. Args: - coords: - Array of the bounds of a 2D coord, of shape (X,Y,4) - data: - data of the the cube we are plotting - abs_tol: - tolerance when checking the contiguity + coord: (iris.coord.Coord) + Coordinate the bounds of which will be checked for contiguity + data: (array) + Data of the the cube we are plotting + atol: + Absolute tolerance when checking the contiguity. Defaults to None. + If an absolute tolerance is not set, 1D coords are not checked (so + as to not introduce a breaking change without a major release) but + 2D coords are always checked, by calling + :meth:`iris.coords.Coord._discontiguity_in_bounds` with its default + tolerance. """ - if transpose: - data = data.T - - bounds = coord.bounds - - both_dirs_contiguous, diffs_along_x, diffs_along_y = \ - iris.coords._discontiguity_in_2d_bounds(bounds, abs_tol=abs_tol) - - if not both_dirs_contiguous: - - # True where data exists + data_is_masked = hasattr(data, 'mask') + if data_is_masked: + # When checking the location of the discontiguities, we check against + # the opposite of the mask, which is True where data exists. mask_invert = np.logical_not(data.mask) - # Where a discontinuity occurs the grid will not be created correctly. - # This does not matter if the data is masked as this is not plotted. - # So for places where data exists (opposite of the mask) AND a - # diff exists. If any exist, raise a warning - not_masked_at_discontinuity_along_x = np.any( - np.logical_and(mask_invert[:, :-1], diffs_along_x)) + if coord.ndim == 1: + # 1D coords are only checked if an absolute tolerance is set, to avoid + # introducing a breaking change. + if atol: + contiguous, diffs = coord._discontiguity_in_bounds(atol=atol) - not_masked_at_discontinuity_along_y = np.any( - np.logical_and(mask_invert[:-1, ], diffs_along_y)) + if not contiguous and data_is_masked: + not_masked_at_discontiguity = np.any( + np.logical_and(mask_invert[:-1], diffs)) + else: + return - # If discontinuity occurs but not masked, any grid will be created - # incorrectly, so raise a warning - if not_masked_at_discontinuity_along_x or \ - not_masked_at_discontinuity_along_y: - raise ValueError('The bounds of the {} coordinate are not' - ' contiguous and data is not masked where the' - ' discontiguity occurs. Not able to create a' - ' suitable mesh to give to' - ' Matplotlib'.format(coord.name())) + elif coord.ndim == 2: + if atol: + args = {'atol': atol} + else: + args = {} + contiguous, diffs = coord._discontiguity_in_bounds(*args) + diffs_along_x, diffs_along_y = diffs + + if not contiguous and data_is_masked: + # Check along both dimensions. + not_masked_at_discontiguity_along_x = np.any( + np.logical_and(mask_invert[:, :-1], diffs_along_x)) + + not_masked_at_discontiguity_along_y = np.any( + np.logical_and(mask_invert[:-1, ], diffs_along_y)) + + not_masked_at_discontiguity = not_masked_at_discontiguity_along_x \ + or not_masked_at_discontiguity_along_y + + # If any discontiguity occurs where the data is not masked the grid will be + # created incorrectly, so raise an error. + if not contiguous: + if not data_is_masked: + raise ValueError('The bounds of the {} coordinate are not ' + 'contiguous. Not able to create a suitable grid' + 'to plot.'.format(coord.name())) + if not_masked_at_discontiguity: + raise ValueError('The bounds of the {} coordinate are not ' + 'contiguous and data is not masked where the ' + 'discontiguity occurs. Not able to create a ' + 'suitable grid to plot.'.format( + coord.name())) def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): @@ -333,19 +361,12 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): else: plot_defn = _get_plot_defn(cube, mode, ndims=2) - twodim_contig_atol = kwargs.pop('two_dim_coord_contiguity_atol', - 1e-4) + contig_tol = kwargs.pop('contiguity_tolerance', None) + for coord in plot_defn.coords: - if hasattr(coord, 'has_bounds'): - if coord.ndim == 2 and coord.has_bounds(): - try: - _check_contiguity_and_bounds(coord, data=cube.data, - abs_tol=twodim_contig_atol) - except ValueError: - if _check_contiguity_and_bounds(coord, data=cube.data, - abs_tol=twodim_contig_atol, - transpose=True) is True: - plot_defn.transpose = True + if hasattr(coord, 'has_bounds') and coord.has_bounds(): + _check_bounds_contiguity_and_mask(coord, data=cube.data, + atol=contig_tol) if _can_draw_map(plot_defn.coords): result = _map_common(draw_method_name, None, iris.coords.BOUND_MODE, @@ -390,11 +411,10 @@ def _draw_2d_from_bounds(draw_method_name, cube, *args, **kwargs): u, v = plot_arrays - # If the data is tranposed, 2D coordinates will also need to be - # tranposed. - if u.ndim == v.ndim == 2 and plot_defn.transpose is True: - u = u.T - v = v.T + # If the data is transposed, 2D coordinates will also need to be + # transposed. + if plot_defn.transpose is True: + u, v = [coord.T if coord.ndim == 2 else coord for coord in [u, v]] if u.ndim == v.ndim == 1: u, v = _broadcast_2d(u, v) @@ -1102,8 +1122,7 @@ def pcolor(cube, *args, **kwargs): Draws a pseudocolor plot based on the given 2-dimensional Cube. The cube must have either two 1-dimensional coordinates or two - 2-dimensional coordinates with contiguous bounds to plot against each - other. + 2-dimensional coordinates with contiguous bounds to plot the cube against. Kwargs: @@ -1117,10 +1136,8 @@ def pcolor(cube, *args, **kwargs): * axes: the :class:`matplotlib.axes.Axes` to use for drawing. Defaults to the current axes if none provided. - * two_dim_coord_contiguity_atol: absolute tolerance when checking for - contiguity between cells in a two dimensional coordinate. - - + * contiguity_tolerance: The absolute tolerance used when checking for + contiguity between the bounds of the cells. Defaults to None. See :func:`matplotlib.pyplot.pcolor` for details of other valid keyword arguments. @@ -1152,8 +1169,8 @@ def pcolormesh(cube, *args, **kwargs): * axes: the :class:`matplotlib.axes.Axes` to use for drawing. Defaults to the current axes if none provided. - * two_dim_coord_contiguity_atol: absolute tolerance when checking for - contiguity between cells in a two dimensional coordinate. + * contiguity_tolerance: The absolute tolerance used when checking for + contiguity between the bounds of the cells. Defaults to None. See :func:`matplotlib.pyplot.pcolormesh` for details of other valid keyword arguments. diff --git a/lib/iris/tests/unit/coords/test_Coord.py b/lib/iris/tests/unit/coords/test_Coord.py index b43df9447a..58fc3e1f9e 100644 --- a/lib/iris/tests/unit/coords/test_Coord.py +++ b/lib/iris/tests/unit/coords/test_Coord.py @@ -24,6 +24,8 @@ import iris.tests as tests import collections +import mock +import warnings import numpy as np @@ -355,6 +357,297 @@ def test_different_array_attrs_incompatible(self): self.assertFalse(self.test_coord.is_compatible(self.other_coord)) +class Test_contiguous_bounds(tests.IrisTest): + def test_1d_coord_no_bounds_warning(self): + coord = DimCoord([0, 1, 2], standard_name='latitude') + msg = "Coordinate 'latitude' is not bounded, guessing contiguous " \ + "bounds." + with warnings.catch_warnings(): + # Cause all warnings to raise Exceptions + warnings.simplefilter("error") + with self.assertRaisesRegexp(Warning, msg): + coord.contiguous_bounds() + + def test_2d_coord_no_bounds_error(self): + coord = AuxCoord(np.array([[0, 0], [5, 5]]), standard_name='latitude') + emsg = 'Guessing bounds of 2D coords is not currently supported' + with self.assertRaisesRegexp(ValueError, emsg): + coord.contiguous_bounds() + + def test__sanity_check_bounds_call(self): + coord = DimCoord([5, 15, 25], bounds=[[0, 10], [10, 20], [20, 30]]) + with mock.patch('iris.coords.Coord._sanity_check_bounds' + ) as bounds_check: + coord.contiguous_bounds() + bounds_check.assert_called_once() + + def test_1d_coord(self): + coord = DimCoord([2, 4, 6], standard_name='latitude', + bounds=[[1, 3], [3, 5], [5, 7]]) + expected = np.array([1, 3, 5, 7]) + result = coord.contiguous_bounds() + self.assertArrayEqual(result, expected) + + def test_1d_coord_discontiguous(self): + coord = DimCoord([2, 4, 6], standard_name='latitude', + bounds=[[1, 3], [4, 5], [5, 7]]) + expected = np.array([1, 4, 5, 7]) + result = coord.contiguous_bounds() + self.assertArrayEqual(result, expected) + + def test_2d_lon_bounds(self): + coord = AuxCoord(np.array([[1, 3], [1, 3]]), + bounds=np.array([[[0, 2, 2, 0], [2, 4, 4, 2]], + [[0, 2, 2, 0], [2, 4, 4, 2]]])) + expected = np.array([[0, 2, 4], [0, 2, 4], [0, 2, 4]]) + result = coord.contiguous_bounds() + self.assertArrayEqual(result, expected) + + def test_2d_lat_bounds(self): + coord = AuxCoord(np.array([[1, 1], [3, 3]]), + bounds=np.array([[[0, 0, 2, 2], [0, 0, 2, 2]], + [[2, 2, 4, 4], [2, 2, 4, 4]]])) + expected = np.array([[0, 0, 0], [2, 2, 2], [4, 4, 4]]) + result = coord.contiguous_bounds() + self.assertArrayEqual(result, expected) + + +class Test_is_contiguous(tests.IrisTest): + def test_no_bounds(self): + coord = DimCoord([1, 3]) + result = coord.is_contiguous() + self.assertFalse(result) + + def test__discontiguity_in_bounds_call(self): + # Check that :meth:`iris.coords.Coord._discontiguity_in_bounds` is + # called. + coord = DimCoord([1, 3], bounds=[[0, 2], [2, 4]]) + with mock.patch('iris.coords.Coord._discontiguity_in_bounds' + ) as discontiguity_check: + # Discontiguity returns two objects that are unpacked in + # `coord.is_contiguous`. + discontiguity_check.return_value = [None, None] + coord.is_contiguous(rtol=1e-1, atol=1e-3) + discontiguity_check.assert_called_with(rtol=1e-1, atol=1e-3) + + +class Test__discontiguity_in_bounds(tests.IrisTest): + def setUp(self): + self.points_3by3 = np.array([[1, 2, 3], + [1, 2, 3], + [1, 2, 3]]) + self.lon_bounds_3by3 = np.array( + [[[0, 2, 2, 0], [2, 4, 4, 2], [4, 6, 6, 4]], + [[0, 2, 2, 0], [2, 4, 4, 2], [4, 6, 6, 4]], + [[0, 2, 2, 0], [2, 4, 4, 2], [4, 6, 6, 4]]]) + self.lat_bounds_3by3 = np.array( + [[[0, 0, 2, 2], [0, 0, 2, 2], [0, 0, 2, 2]], + [[2, 2, 4, 4], [2, 2, 4, 4], [2, 2, 4, 4]], + [[4, 4, 6, 6], [4, 4, 6, 6], [4, 4, 6, 6]]]) + + def test_1d_contiguous(self): + coord = DimCoord([-20, 0, 20], + bounds=[[-30, -10], [-10, 10], [10, 30]]) + contiguous, diffs = coord._discontiguity_in_bounds() + self.assertTrue(contiguous) + self.assertArrayEqual(diffs, np.zeros(2)) + + def test_1d_discontiguous(self): + coord = DimCoord([10, 20, 40], + bounds=[[5, 15], [15, 25], [35, 45]]) + contiguous, diffs = coord._discontiguity_in_bounds() + self.assertFalse(contiguous) + self.assertArrayEqual(diffs, np.array([0, 10])) + + def test_1d_one_cell(self): + # Test a 1D coord with a single cell. + coord = DimCoord(20, bounds=[[10, 30]]) + contiguous, diffs = coord._discontiguity_in_bounds() + self.assertTrue(contiguous) + self.assertArrayEqual(diffs, np.array([])) + + def test_2d_contiguous_both_dirs(self): + coord = AuxCoord(self.points_3by3, bounds=self.lon_bounds_3by3) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_discontiguous_along_x(self): + coord = AuxCoord(self.points_3by3[:, ::2], + bounds=self.lon_bounds_3by3[:, ::2, :]) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertFalse(contiguous) + self.assertArrayEqual(diffs_along_x, np.array([2, 2, 2]).reshape(3, 1)) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_discontiguous_along_y(self): + coord = AuxCoord(self.points_3by3[::2, :], + bounds=self.lat_bounds_3by3[::2, :, :]) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertFalse(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertArrayEqual(diffs_along_y, np.array([[2, 2, 2]])) + + def test_2d_discontiguous_along_x_and_y(self): + coord = AuxCoord(np.array([[1, 5], [3, 5]]), + bounds=np.array([[[0, 2, 2, 0], [4, 6, 6, 4]], + [[2, 4, 4, 2], [4, 6, 6, 4]]])) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + exp_x_diffs = np.array([2, 0]).reshape(2, 1) + exp_y_diffs = np.array([2, 0]).reshape(1, 2) + self.assertFalse(contiguous) + self.assertArrayEqual(diffs_along_x, exp_x_diffs) + self.assertArrayEqual(diffs_along_y, exp_y_diffs) + + def test_2d_one_cell(self): + # Test a 2D coord with a single cell, where the coord has shape (1, 1). + coord = AuxCoord(self.points_3by3[:1, :1], + bounds=self.lon_bounds_3by3[:1, :1, :]) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + expected_diffs = np.array([], dtype=np.int64) + self.assertTrue(contiguous) + self.assertArrayEqual(diffs_along_x, expected_diffs.reshape(1, 0)) + self.assertArrayEqual(diffs_along_y, expected_diffs.reshape(0, 1)) + + def test_2d_one_cell_along_x(self): + # Test a 2D coord with a single cell along the x axis, where the coord + # has shape (2, 1). + coord = AuxCoord(self.points_3by3[:, :1], + bounds=self.lat_bounds_3by3[:, :1, :]) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertArrayEqual(diffs_along_y, np.array([0, 0]).reshape(2, 1)) + + def test_2d_one_cell_along_y(self): + # Test a 2D coord with a single cell along the y axis, where the coord + # has shape (1, 2). + coord = AuxCoord(self.points_3by3[:1, :], + bounds=self.lon_bounds_3by3[:1, :, :]) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_contiguous_mod_360(self): + # Test that longitude coordinates are adjusted by the 360 modulus when + # calculating the discontiguities in contiguous bounds. + coord = AuxCoord( + [[175, -175], [175, -175]], standard_name='longitude', + bounds=np.array([[[170, 180, 180, 170], [-180, -170, -170, -180]], + [[170, 180, 180, 170], [-180, -170, -170, -180]]]) + ) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_discontiguous_mod_360(self): + # Test that longitude coordinates are adjusted by the 360 modulus when + # calculating the discontiguities in contiguous bounds. + coord = AuxCoord( + [[175, -175], [175, -175]], standard_name='longitude', + bounds=np.array([[[170, 180, 180, 170], [10, 20, 20, 10]], + [[170, 180, 180, 170], [10, 20, 20, 10]]])) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertFalse(contiguous) + self.assertArrayEqual(diffs_along_x, np.array([[170], [170]])) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_contiguous_mod_360_not_longitude(self): + # Test that non-longitude coordinates are not adjusted by the 360 + # modulus when calculating the discontiguities in contiguous bounds. + coord = AuxCoord( + [[-150, 350], [-150, 350]], standard_name='height', + bounds=np.array([[[-400, 100, 100, -400], [100, 600, 600, 100]], + [[-400, 100, 100, -400], [100, 600, 600, 100]]]) + ) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertTrue(not diffs_along_x.any()) + self.assertTrue(not diffs_along_y.any()) + + def test_2d_discontiguous_mod_360_not_longitude(self): + # Test that non-longitude coordinates are not adjusted by the 360 + # modulus when calculating the discontiguities in discontiguous bounds. + coord = AuxCoord( + [[-150, 350], [-150, 350]], standard_name='height', + bounds=np.array([[[-400, 100, 100, -400], [200, 600, 600, 200]], + [[-400, 100, 100, -400], [200, 600, 600, 200]]]) + ) + contiguous, diffs = coord._discontiguity_in_bounds() + diffs_along_x, diffs_along_y = diffs + self.assertFalse(contiguous) + self.assertArrayEqual(diffs_along_x, np.array([[100], [100]])) + self.assertTrue(not diffs_along_y.any()) + + +class Test__sanity_check_bounds(tests.IrisTest): + def test_coord_1d_2_bounds(self): + # Check that a 1d coord with 2 bounds does not raise an error. + coord = iris.coords.DimCoord([0, 1], standard_name='latitude', + bounds=[[0, 1], [1, 2]]) + coord._sanity_check_bounds() + + def test_coord_1d_no_bounds(self): + coord = iris.coords.DimCoord([0, 1], standard_name='latitude') + emsg = "Contiguous bounds are only defined for 1D coordinates with " \ + "2 bounds." + with self.assertRaisesRegexp(ValueError, emsg): + coord._sanity_check_bounds() + + def test_coord_1d_1_bounds(self): + coord = iris.coords.DimCoord([0, 1], standard_name='latitude', + bounds=np.array([[0], [1]])) + emsg = "Contiguous bounds are only defined for 1D coordinates with " \ + "2 bounds." + with self.assertRaisesRegexp(ValueError, emsg): + coord._sanity_check_bounds() + + def test_coord_2d_4_bounds(self): + coord = iris.coords.AuxCoord( + [[0, 0], [1, 1]], standard_name='latitude', + bounds=np.array([[[0, 0, 1, 1], [0, 0, 1, 1]], + [[1, 1, 2, 2], [1, 1, 2, 2]]])) + coord._sanity_check_bounds() + + def test_coord_2d_no_bounds(self): + coord = iris.coords.AuxCoord([[0, 0], [1, 1]], + standard_name='latitude') + emsg = "Contiguous bounds are only defined for 2D coordinates with " \ + "4 bounds." + with self.assertRaisesRegexp(ValueError, emsg): + coord._sanity_check_bounds() + + def test_coord_2d_2_bounds(self): + coord = iris.coords.AuxCoord( + [[0, 0], [1, 1]], standard_name='latitude', + bounds=np.array([[[0, 1], [0, 1]], [[1, 2], [1, 2]]])) + emsg = "Contiguous bounds are only defined for 2D coordinates with " \ + "4 bounds." + with self.assertRaisesRegexp(ValueError, emsg): + coord._sanity_check_bounds() + + def test_coord_3d(self): + coord = iris.coords.AuxCoord(np.zeros((2, 2, 2)), + standard_name='height') + emsg = "Contiguous bounds are not defined for coordinates with more " \ + "than 2 dimensions." + with self.assertRaisesRegexp(ValueError, emsg): + coord._sanity_check_bounds() + + class Test_convert_units(tests.IrisTest): def test_convert_unknown_units(self): coord = iris.coords.AuxCoord(1, units='unknown') diff --git a/lib/iris/tests/unit/plot/test_2d_coords.py b/lib/iris/tests/unit/plot/test_2d_coords.py deleted file mode 100644 index 7397921861..0000000000 --- a/lib/iris/tests/unit/plot/test_2d_coords.py +++ /dev/null @@ -1,167 +0,0 @@ -# (C) British Crown Copyright 2018, 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 . -"""Unit tests for handling and plotting of 2-dimensional coordinates""" - -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 numpy.ma as ma - -import iris.coords as coords -from iris.tests.stock import simple_2d_w_multidim_coords as cube_2dcoords -from iris.tests.stock import simple_3d_w_multidim_coords as cube3d_2dcoords -from iris.tests.stock import sample_2d_latlons -from iris.tests.stock import make_bounds_discontiguous_at_point - - -if tests.MPL_AVAILABLE: - import iris.plot as iplt - - -def full2d_global(): - return sample_2d_latlons(transformed=True) - - -@tests.skip_data -class Test_2d_coords_plot_defn_bound_mode(tests.IrisTest): - def setUp(self): - self.multidim_cube = cube_2dcoords() - self.overspan_cube = cube3d_2dcoords() - - # latlon_2d is a cube with 2d coords, 4 bounds per point, - # discontiguities in the bounds but masked data at the discontiguities. - self.latlon_2d = full2d_global() - make_bounds_discontiguous_at_point(self.latlon_2d, 2, 2) - - # # Take a latlon cube with 1D coords, broadcast the coords into 2D - # # ones, then add ONE of them back into the cube in place of original: - # single_dims = lat_lon_cube() - # lon = single_dims.coord('longitude') - # lat = single_dims.coord('latitude') - # big_lon, big_lat = testdata.grid_coords_2d_from_1d(lon, lat) - # mixed_dims = single_dims.copy() - # mixed_dims.remove_coord(lon) - # # TODO Fix this coord addition: - # # When adding an aux_coord, the function '_check_multidim_metadata' - # # throws an error as it requires coord.shape to be (1, ) instead of - # # (3, 4) or whatever. - # mixed_dims.add_aux_coord(big_lon) - # - # # mixed_dims is now a cube with 2 1D dim coords and an additional - # # 2D aux coord. - # self.mixed_dims = mixed_dims - - self.mode = coords.BOUND_MODE - - def test_2d_coords_identified(self): - # Test that 2d coords are identified in the plot definition without - # having to be specified in the args without any errors. - cube = self.multidim_cube - defn = iplt._get_plot_defn(cube, mode=self.mode) - self.assertEqual([coord.name() for coord in defn.coords], - ['bar', 'foo']) - - def test_2d_coords_custom_picked(self): - # Test that 2d coords which are specified in the args will be - # accepted without any errors. - cube = self.multidim_cube - defn = iplt._get_plot_defn_custom_coords_picked(cube, ('foo', 'bar'), - self.mode) - self.assertEqual([coord.name() for coord in defn.coords], - ['bar', 'foo']) - - def test_2d_coords_as_integers(self): - # Test that if you pass in 2d coords as args in the form of integers, - # they will still be correctly identified without any errors. - cube = self.multidim_cube - defn = iplt._get_plot_defn_custom_coords_picked(cube, (0, 1), - self.mode) - self.assertEqual([coord for coord in defn.coords], - [1, 0]) - - def test_total_span_check(self): - # Test that an error is raised if a user tries to plot a 2d coord - # against a different coord, making total number of dimensions 3. - cube = self.overspan_cube - with self.assertRaises(ValueError): - iplt._get_plot_defn_custom_coords_picked(cube, ('wibble', 'foo'), - self.mode) - - # def test_2dcoord_with_1dcoord(self): - # # TODO Generate a cube with one 2d coord and one 1d coord - # # TODO Try and plot them against each other - # # TODO Find out where I can put a catch for this (if necessary) - # cube = self.mixed_dims - # with self.assertRaises(ValueError): - # iplt._get_plot_defn_custom_coords_picked( - # cube, - # ('latitude', 'longitude'), - # self.mode) - - def test_map_common_not_enough_bounds(self): - # Test that a lat-lon cube with 2d coords and 2 bounds per point - # throws an error in contiguity checks. - cube = self.multidim_cube - cube.coord('foo').rename('longitude') - cube.coord('bar').rename('latitude') - with self.assertRaises(ValueError): - plot_defn = iplt._get_plot_defn(cube, self.mode) - iplt._map_common('pcolor', None, self.mode, cube, plot_defn) - - def test_map_common_2d(self): - # Test that a cube with 2d coords can be plotted as a map. - cube = self.latlon_2d - # Get necessary variables from _get_plot_defn to check that the test - # case will be accepted by _map_common. - plot_defn = iplt._get_plot_defn(cube, self.mode) - result = iplt._map_common('pcolor', None, self.mode, cube, plot_defn) - self.assertTrue(result) - -# def test_discontiguous_masked(self): -# # Test that a contiguity check will raise a warning (not an error) for -# # discontiguous bounds but appropriately masked data. -# cube = self.latlon_2d -# coord = cube.coord('longitude') -# msg = 'The bounds of the longitude coordinate are not contiguous. ' \ -# 'However, data is masked where the discontiguity occurs so ' \ -# 'plotting anyway.' -# with self.assertWarnsRegexp(msg): -# iplt._check_contiguity_and_bounds(coord, cube.data) - - def test_discontiguous_unmasked(self): - # Check that an error occurs when the contiguity check finds - # discontiguous bounds but unmasked data. - cube = self.latlon_2d - cube.data.mask = ma.nomask - coord = cube.coord('longitude') - with self.assertRaises(ValueError): - iplt._check_contiguity_and_bounds(coord, cube.data) - - def test_draw_2d_from_bounds(self): - # Test this function will not raise an error even with our most - # awkward but supported cube. - cube = self.latlon_2d - result = iplt._draw_2d_from_bounds('pcolormesh', cube) - self.assertTrue(result) - - -if __name__ == '__main__': - tests.main() diff --git a/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py b/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py new file mode 100644 index 0000000000..b067b3f387 --- /dev/null +++ b/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py @@ -0,0 +1,96 @@ +# (C) British Crown Copyright 2018, 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 . +"""Unit tests for the `iris.plot._check_bounds_contiguity_and_mask` +function.""" + +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 numpy as np +import numpy.ma as ma + +from iris.coords import DimCoord +from iris.tests.stock import (sample_2d_latlons, + make_bounds_discontiguous_at_point) + +if tests.MPL_AVAILABLE: + import iris.plot as iplt + + +@tests.skip_plot +class Test_check_bounds_contiguity_and_mask(tests.IrisTest): + def test_1d_not_checked(self): + # Test a 1D coordinate, which is not checked as atol is not set. + coord = DimCoord([1, 3, 5], bounds=[[0, 2], [2, 4], [5, 6]]) + data = np.array([278, 300, 282]) + iplt._check_bounds_contiguity_and_mask(coord, data) + + def test_1d_contiguous(self): + # Test a 1D coordinate which is contiguous. + coord = DimCoord([1, 3, 5], bounds=[[0, 2], [2, 4], [4, 6]]) + data = np.array([278, 300, 282]) + iplt._check_bounds_contiguity_and_mask(coord, data, atol=1e-3) + + def test_1d_discontigous_masked(self): + # Test a 1D coordinate which is discontiguous but masked at + # discontiguities. + coord = DimCoord([1, 3, 5], bounds=[[0, 2], [2, 4], [5, 6]]) + data = ma.array(np.array([278, 300, 282]), mask=[0, 1, 0]) + iplt._check_bounds_contiguity_and_mask(coord, data, atol=1e-3) + + def test_1d_discontigous_unmasked(self): + # Test a 1D coordinate which is discontiguous and unmasked at + # discontiguities. + coord = DimCoord([1, 3, 5], bounds=[[0, 2], [2, 4], [5, 6]]) + data = ma.array(np.array([278, 300, 282]), mask=[1, 0, 0]) + msg = 'coordinate are not contiguous and data is not masked where ' \ + 'the discontiguity occurs' + with self.assertRaisesRegexp(ValueError, msg): + iplt._check_bounds_contiguity_and_mask(coord, data, atol=1e-3) + + def test_2d_contiguous(self): + # Test a 2D coordinate which is contiguous. + cube = sample_2d_latlons() + iplt._check_bounds_contiguity_and_mask(cube.coord('longitude'), + cube.data) + + def test_2d_discontigous_masked(self): + # Test a 2D coordinate which is discontiguous but masked at + # discontiguities. + cube = sample_2d_latlons() + make_bounds_discontiguous_at_point(cube, 3, 4) + iplt._check_bounds_contiguity_and_mask(cube.coord('longitude'), + cube.data) + + def test_2d_discontigous_unmasked(self): + # Test a 2D coordinate which is discontiguous and unmasked at + # discontiguities. + cube = sample_2d_latlons() + make_bounds_discontiguous_at_point(cube, 3, 4) + msg = 'coordinate are not contiguous' + cube.data[3, 4] = ma.nomask + with self.assertRaisesRegexp(ValueError, msg): + iplt._check_bounds_contiguity_and_mask(cube.coord('longitude'), + cube.data) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/plot/test__get_plot_defn.py b/lib/iris/tests/unit/plot/test__get_plot_defn.py index 6dfb6f9f11..2947933c7d 100644 --- a/lib/iris/tests/unit/plot/test__get_plot_defn.py +++ b/lib/iris/tests/unit/plot/test__get_plot_defn.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2017, Met Office +# (C) British Crown Copyright 2017 - 2018, Met Office # # This file is part of Iris. # @@ -24,7 +24,7 @@ import iris.tests as tests import iris.coords -from iris.tests.stock import simple_2d +from iris.tests.stock import simple_2d, simple_2d_w_multidim_coords if tests.MPL_AVAILABLE: import iris.plot as iplt @@ -45,6 +45,12 @@ def test_axis_order_yx(self): self.assertEqual([coord.name() for coord in defn.coords], ['foo', 'bar']) + def test_2d_coords(self): + cube = simple_2d_w_multidim_coords() + defn = iplt._get_plot_defn(cube, iris.coords.BOUND_MODE) + self.assertEqual([coord.name() for coord in defn.coords], + ['bar', 'foo']) + if __name__ == "__main__": tests.main() diff --git a/lib/iris/tests/unit/plot/test__get_plot_defn_custom_coords_picked.py b/lib/iris/tests/unit/plot/test__get_plot_defn_custom_coords_picked.py new file mode 100644 index 0000000000..d0ce55a389 --- /dev/null +++ b/lib/iris/tests/unit/plot/test__get_plot_defn_custom_coords_picked.py @@ -0,0 +1,93 @@ +# (C) British Crown Copyright 2018, 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 . +"""Unit tests for the `iris.plot._get_plot_defn_custom_coords_picked` +function.""" + + +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 + +from iris.coords import BOUND_MODE, POINT_MODE +from iris.tests.stock import (simple_2d, simple_2d_w_multidim_coords, + hybrid_height) + + +if tests.MPL_AVAILABLE: + import iris.plot as iplt + + +@tests.skip_plot +class Test_get_plot_defn_custom_coords_picked(tests.IrisTest): + def test_1d_coords(self): + cube = simple_2d() + defn = iplt._get_plot_defn_custom_coords_picked(cube, ('foo', 'bar'), + POINT_MODE) + self.assertEqual([coord.name() for coord in defn.coords], + ['bar', 'foo']) + self.assertFalse(defn.transpose) + + def test_1d_coords_swapped(self): + cube = simple_2d() + defn = iplt._get_plot_defn_custom_coords_picked(cube, ('bar', 'foo'), + POINT_MODE) + self.assertEqual([coord.name() for coord in defn.coords], + ['foo', 'bar']) + self.assertTrue(defn.transpose) + + def test_1d_coords_as_integers(self): + cube = simple_2d() + defn = iplt._get_plot_defn_custom_coords_picked(cube, (1, 0), + POINT_MODE) + self.assertEqual([coord for coord in defn.coords], [0, 1]) + self.assertFalse(defn.transpose) + + def test_1d_coords_as_integers_swapped(self): + cube = simple_2d() + defn = iplt._get_plot_defn_custom_coords_picked(cube, (0, 1), + POINT_MODE) + self.assertEqual([coord for coord in defn.coords], [1, 0]) + self.assertTrue(defn.transpose) + + def test_2d_coords(self): + cube = simple_2d_w_multidim_coords() + defn = iplt._get_plot_defn_custom_coords_picked(cube, ('foo', 'bar'), + BOUND_MODE) + self.assertEqual([coord.name() for coord in defn.coords], + ['bar', 'foo']) + self.assertFalse(defn.transpose) + + def test_2d_coords_as_integers(self): + cube = simple_2d_w_multidim_coords() + defn = iplt._get_plot_defn_custom_coords_picked(cube, (0, 1), + BOUND_MODE) + self.assertEqual([coord for coord in defn.coords], [1, 0]) + self.assertTrue(defn.transpose) + + def test_span_check(self): + cube = hybrid_height() + emsg = 'don\'t span the 2 data dimensions' + with self.assertRaisesRegexp(ValueError, emsg): + iplt._get_plot_defn_custom_coords_picked( + cube, ('sigma', 'level_height'), POINT_MODE) + + +if __name__ == "__main__": + tests.main() From 7455bb1aa2a12080e46501317ae7b54a89946d61 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Mon, 3 Sep 2018 12:47:45 +0100 Subject: [PATCH 20/24] Tests for rotate_grid_vectors (#3148) * Tests for rotate_grid_vectors. * Small fix to assertArrayAllClose for values near 0. * Small tweaks. * Fix test method. * Fix 1 test for 'equal_nans' no longer the default. * Review changes. --- lib/iris/tests/__init__.py | 37 +++-- .../cartography/test_rotate_grid_vectors.py | 148 ++++++++++++++++++ .../regrid/test_RectilinearRegridder.py | 3 +- 3 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 lib/iris/tests/unit/analysis/cartography/test_rotate_grid_vectors.py diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index 7550fce1ca..b7806b455b 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -642,7 +642,7 @@ def assertMaskedArrayAlmostEqual(self, a, b, decimal=6, strict=False): self._assertMaskedArray(np.testing.assert_array_almost_equal, a, b, strict, decimal=decimal) - def assertArrayAllClose(self, a, b, rtol=1.0e-7, atol=0.0, **kwargs): + def assertArrayAllClose(self, a, b, rtol=1.0e-7, atol=1.0e-8, **kwargs): """ Check arrays are equal, within given relative + absolute tolerances. @@ -660,26 +660,35 @@ def assertArrayAllClose(self, a, b, rtol=1.0e-7, atol=0.0, **kwargs): Performs pointwise toleranced comparison, and raises an assertion if the two are not equal 'near enough'. - For full details see underlying routine numpy.testing.assert_allclose. + For full details see underlying routine numpy.allclose. """ - ok = np.allclose(a, b, atol=atol, rtol=rtol, equal_nan=True) + # Handle the 'err_msg' kwarg, which is the only API difference + # between np.allclose and np.testing_assert_allclose. + msg = kwargs.pop('err_msg', None) + ok = np.allclose(a, b, rtol=rtol, atol=atol, **kwargs) if not ok: # Calculate errors above a pointwise tolerance : The method is # taken from "numpy.core.numeric.isclose". + a, b = np.broadcast_arrays(a, b) errors = (np.abs(a-b) - atol + rtol * np.abs(b)) worst_inds = np.unravel_index(np.argmax(errors.flat), errors.shape) - # Build a more useful message than from np.testing.assert_allclose. - msg = ('\nARRAY CHECK FAILED "assertArrayAllClose" :' - '\n with shapes={} {}, atol={}, rtol={}' - '\n worst at element {} : a={} b={}' - '\n absolute error ~{:.3g}, equivalent to rtol ~{:.3e}') - aval, bval = a[worst_inds], b[worst_inds] - absdiff = np.abs(aval - bval) - equiv_rtol = absdiff / bval - raise AssertionError(msg.format( - a.shape, b.shape, atol, rtol, worst_inds, aval, bval, absdiff, - equiv_rtol)) + + if msg is None: + # Build a more useful message than np.testing.assert_allclose. + msg = ( + '\nARRAY CHECK FAILED "assertArrayAllClose" :' + '\n with shapes={} {}, atol={}, rtol={}' + '\n worst at element {} : a={} b={}' + '\n absolute error ~{:.3g}, equivalent to rtol ~{:.3e}') + aval, bval = a[worst_inds], b[worst_inds] + absdiff = np.abs(aval - bval) + equiv_rtol = absdiff / bval + msg = msg.format( + a.shape, b.shape, atol, rtol, worst_inds, aval, bval, + absdiff, equiv_rtol) + + raise AssertionError(msg) @contextlib.contextmanager def temp_filename(self, suffix=''): diff --git a/lib/iris/tests/unit/analysis/cartography/test_rotate_grid_vectors.py b/lib/iris/tests/unit/analysis/cartography/test_rotate_grid_vectors.py new file mode 100644 index 0000000000..90ec37e162 --- /dev/null +++ b/lib/iris/tests/unit/analysis/cartography/test_rotate_grid_vectors.py @@ -0,0 +1,148 @@ +# (C) British Crown Copyright 2018, 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 . +""" +Unit tests for the function +:func:`iris.analysis.cartography.rotate_grid_vectors`. + +""" +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 + +from mock import Mock, call as mock_call +import numpy as np + +from iris.cube import Cube +from iris.tests.stock import sample_2d_latlons + +from iris.analysis.cartography import rotate_grid_vectors + + +class TestRotateGridVectors(tests.IrisTest): + def _check_angles_calculation(self, angles_in_degrees=True, + nan_angles_mask=None): + # Check basic maths on a 2d latlon grid. + u_cube = sample_2d_latlons(regional=True, transformed=True) + u_cube.units = 'ms-1' + u_cube.rename('dx') + u_cube.data[...] = 0 + v_cube = u_cube.copy() + v_cube.name('dy') + + # Define 6 different vectors, repeated in each data row. + in_vu = np.array([(0, 1), (2, -1), (-1, -1), (-3, 1), (2, 0), (0, 0)]) + in_angs = np.rad2deg(np.arctan2(in_vu[..., 0], in_vu[..., 1])) + in_mags = np.sqrt(np.sum(in_vu * in_vu, axis=1)) + v_cube.data[...] = in_vu[..., 0] + u_cube.data[...] = in_vu[..., 1] + + # Define 5 different test rotation angles, one for each data row. + rotation_angles = np.array([0., -45., 135, -140., 90.]) + ang_cube_data = np.broadcast_to(rotation_angles[:, None], + u_cube.shape) + ang_cube = u_cube.copy() + if angles_in_degrees: + ang_cube.units = 'degrees' + else: + ang_cube.units = 'radians' + ang_cube_data = np.deg2rad(ang_cube_data) + ang_cube.data[:] = ang_cube_data + + if nan_angles_mask is not None: + ang_cube.data[nan_angles_mask] = np.nan + + # Rotate all vectors by all the given angles. + result = rotate_grid_vectors(u_cube, v_cube, ang_cube) + out_u, out_v = [cube.data for cube in result] + + # Check that vector magnitudes were unchanged. + out_mags = np.sqrt(out_u * out_u + out_v * out_v) + expect_mags = in_mags[None, :] + self.assertArrayAllClose(out_mags, expect_mags) + + # Check that vector angles are all as expected. + out_angs = np.rad2deg(np.arctan2(out_v, out_u)) + expect_angs = in_angs[None, :] + rotation_angles[:, None] + ang_diffs = out_angs - expect_angs + # Fix for null vectors, and +/-360 differences. + ang_diffs[np.abs(out_mags) < 0.001] = 0.0 + ang_diffs = ang_diffs % 360.0 + # Check that any differences are very small. + self.assertArrayAllClose(ang_diffs, 0.0) + + # Check that results are always masked arrays, masked at NaN angles. + self.assertTrue(np.ma.isMaskedArray(out_u)) + self.assertTrue(np.ma.isMaskedArray(out_v)) + if nan_angles_mask is not None: + self.assertArrayEqual(out_u.mask, nan_angles_mask) + self.assertArrayEqual(out_v.mask, nan_angles_mask) + + def test_angles_calculation(self): + self._check_angles_calculation() + + def test_angles_in_radians(self): + self._check_angles_calculation(angles_in_degrees=False) + + def test_angles_from_grid(self): + # Check it will gets angles from 'u_cube', and pass any kwargs on to + # the angles routine. + u_cube = sample_2d_latlons(regional=True, transformed=True) + u_cube = u_cube[:2, :3] + u_cube.units = 'ms-1' + u_cube.rename('dx') + u_cube.data[...] = 1.0 + v_cube = u_cube.copy() + v_cube.name('dy') + v_cube.data[...] = 0.0 + + # Setup a fake angles result from the inner call to 'gridcell_angles'. + angles_result_data = np.array([[0.0, 90.0, 180.0], + [-180.0, -90.0, 270.0]]) + angles_result_cube = Cube(angles_result_data, units='degrees') + angles_kwargs = {'this': 2} + angles_call_patch = self.patch( + 'iris.analysis._grid_angles.gridcell_angles', + Mock(return_value=angles_result_cube)) + + # Call the routine. + result = rotate_grid_vectors(u_cube, v_cube, + grid_angles_kwargs=angles_kwargs) + + self.assertEqual(angles_call_patch.call_args_list, + [mock_call(u_cube, this=2)]) + + out_u, out_v = [cube.data for cube in result] + # Records what results should be for the various n*90deg rotations. + expect_u = np.array([[1.0, 0.0, -1.0], + [-1.0, 0.0, 0.0]]) + expect_v = np.array([[0.0, 1.0, 0.0], + [0.0, -1.0, -1.0]]) + # Check results are as expected. + self.assertArrayAllClose(out_u, expect_u) + self.assertArrayAllClose(out_v, expect_v) + + def test_nan_vectors(self): + bad_angle_points = np.zeros((5, 6), dtype=bool) + bad_angle_points[2, 3] = True + self._check_angles_calculation(nan_angles_mask=bad_angle_points) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py b/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py index ca86e5cdd7..6035630aae 100644 --- a/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py +++ b/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py @@ -86,7 +86,8 @@ def assert_values(self, values): result = regrid(self.data, self.x_dim, self.y_dim, self.x, self.y, np.array([xs]), np.array([ys])) - self.assertArrayAllClose(result, expecteds, rtol=1e-04) + self.assertArrayAllClose(result, expecteds, rtol=1e-04, + equal_nan=True) # Check that transposing the input data results in the same values ndim = self.data.ndim From 42a90a260d217f59080825e0900e1f1c9c1cc366 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Mon, 3 Sep 2018 14:17:00 +0100 Subject: [PATCH 21/24] Remove 'skip_2d' test decorator. (#3156) --- lib/iris/tests/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index b7806b455b..84d4614c0e 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -1221,12 +1221,6 @@ class MyPlotTests(test.GraphicsTest): 'Test(s) require "python-stratify", which is not available.') -SKIP_2D_TESTS = True -skip_2d = unittest.skipIf( - SKIP_2D_TESTS, - 'Test(s) broken by WIP on 2d coords support -- temporarily disabled.') - - def no_warnings(func): """ Provides a decorator to ensure that there are no warnings raised From 2d6c6a4a66ca6ae9d7cf910e53bbcd197ac590f4 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Mon, 3 Sep 2018 14:52:55 +0100 Subject: [PATCH 22/24] Add integration graphics tests for pcolormesh with 2d coords (#3149) --- .../integration/plot/test_plot_2d_coords.py | 67 ++ lib/iris/tests/results/imagerepo.json | 1036 +++++++++-------- 2 files changed, 588 insertions(+), 515 deletions(-) create mode 100644 lib/iris/tests/integration/plot/test_plot_2d_coords.py diff --git a/lib/iris/tests/integration/plot/test_plot_2d_coords.py b/lib/iris/tests/integration/plot/test_plot_2d_coords.py new file mode 100644 index 0000000000..5b63b10959 --- /dev/null +++ b/lib/iris/tests/integration/plot/test_plot_2d_coords.py @@ -0,0 +1,67 @@ +# (C) British Crown Copyright 2018, 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 . +""" +Test plots with two dimensional coordinates. + +""" + +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 cartopy.crs as ccrs +import matplotlib.pyplot as plt + +import iris + +# Run tests in no graphics mode if matplotlib is not available. +if tests.MPL_AVAILABLE: + import iris.quickplot as qplt + + +@tests.skip_data +def simple_cube_w_2d_coords(): + path = tests.get_data_path(('NetCDF', 'ORCA2', 'votemper.nc')) + cube = iris.load_cube(path) + return cube + + +@tests.skip_plot +@tests.skip_data +class Test(tests.GraphicsTest): + def test_2d_coord_bounds_platecarree(self): + # To avoid a problem with Cartopy smearing the data where the + # longitude wraps, we set the central_longitude + cube = simple_cube_w_2d_coords()[0, 0] + ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180)) + qplt.pcolormesh(cube) + ax.coastlines(color='red') + self.check_graphic() + + def test_2d_coord_bounds_northpolarstereo(self): + cube = simple_cube_w_2d_coords()[0, 0] + ax = plt.axes(projection=ccrs.NorthPolarStereo()) + qplt.pcolormesh(cube) + ax.coastlines(color='red') + self.check_graphic() + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json index 51d37daf3c..e7614cae19 100644 --- a/lib/iris/tests/results/imagerepo.json +++ b/lib/iris/tests/results/imagerepo.json @@ -1,946 +1,952 @@ { "example_tests.test_COP_1d_plot.TestCOP1DPlot.test_COP_1d_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/baff589936602d8ec977334ae4dac9b61a6dc4d99532c86cc2913e36c4cc0f61.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/baff589936602d8ec977334ae4dac9b61a6dc4d99532c86cc2913e36c4cc0f61.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aefec91c3601249cc9b3336dc4c8cdb31a64c6d997b3c0eccb5932d285e42f33.png" - ], + ], "example_tests.test_COP_maps.TestCOPMaps.test_cop_maps.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9138db95668524913e6ac168997e85957e917e876396b96a81b5ce3c496935.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9138db95668524913e6ac168997e85957e917e876396b96a81b5ce3c496935.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea9130db95668524913c6ac178995b0d956e917ec76396b96a853dcf94696935.png" - ], + ], "example_tests.test_SOI_filtering.TestSOIFiltering.test_soi_filtering.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fac460b9c17b78723e05a5a9954edaf062332799954e9ca5c63b9a52d24e5a95.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8460b9c17b78723e05a5a9954edaf062333799954e9ca5c63b9a52d24e4a9d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac460b9c17b78723e05a5a9954edaf062332799954e9ca5c63b9a52d24e5a95.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8460b9c17b78723e05a5a9954edaf062333799954e9ca5c63b9a52d24e4a9d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa167295c5e0696a3c17a58c9568da536233da19994cdab487739b4b9b444eb5.png" - ], + ], "example_tests.test_TEC.TestTEC.test_TEC.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9a42846e9a49c7596e3cce6c907b3a83c17e1b8239b3e4f33bc4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9e43846e9a49c7596e2cce6c907b3a83c16e1b9231b3e4f33b8c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9a42846e9a49c7596e3cce6c907b3a83c17e1b8239b3e4f33bc4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9e43846e9a49c7596e2cce6c907b3a83c16e1b9231b3e4f33b8c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e5a761b69a589a4bc46f9e48c65c6631ce61d1ce3982c13739b33193c0ee3f8c.png" - ], + ], "example_tests.test_anomaly_log_colouring.TestAnomalyLogColouring.test_anomaly_log_colouring.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ec4464e185a39f93931e9b1e91696d2949dde6e63e26a47a5ad391938d9a5a0c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ec4464e185a39f93931e9b1e91696d2949dde6e63e26a47a5ad391938d9a5a0c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ecc164e78e979b19b3789b0885a564a56cc2c65e3ec69469db1bdb9a853c1e24.png" - ], + ], "example_tests.test_atlantic_profiles.TestAtlanticProfiles.test_atlantic_profiles.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc5d08fcd00fdb1c93fcb21c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc7c09f4d00fdb1c93fcb21c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8a60536bd28e1320739437b5f437b0a53d66f4cc5c08f4d00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc5d08fcd00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc7c09f4d00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8a60536bd28e1320739437b5f437b0a53d66f4cc5c08f4d00fdb1c93fcb21c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/9fc060f462a08f07203ebc77a1f36707e61f4e38d8f7d08a910197fc877cec58.png" - ], + ], "example_tests.test_atlantic_profiles.TestAtlanticProfiles.test_atlantic_profiles.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a6eaa57e6e81ddf999311ba3b3775e20845d5889c199673b4e22a4675e8ca11c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6eaa57e6e81ddf999311ba3b3775e20845d5889c199673b4e22a4675e8ca11c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eeea64dd6ea8cd99991f1322b3761e06845718d89995b3131f32a4765ec2a1cd.png" - ], + ], "example_tests.test_coriolis_plot.TestCoriolisPlot.test_coriolis_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e78665de9a699659e55e9965886979966986c5e63e98c19e3a256679e1981a24.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e78665de9a699659e55e9965886979966986c5e63e98c19e3a256679e1981a24.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e68665de9a699659c1fe99a5896965966996c46e3e19c1da3a652669c51e1a26.png" - ], + ], "example_tests.test_cross_section.TestCrossSection.test_cross_section.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95317b9562e4d1649f5a05856e4ca4da52947e4ea5f13f1b499d42f13b1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95317b9562e4d1649f5a05856e4ca4da52947e4ea5f13f1b499d42f13b1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea91b17b9562e4d1609f5a05856e4ca45a52957e5ea5f13b1bca9dc0b17b1ac1.png" - ], + ], "example_tests.test_cross_section.TestCrossSection.test_cross_section.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394069921e93f07f4aad856cc47e4e95857a1ea5da3591ba1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394069921e93f07f4aad856cc47e4e95857a1ea5da3591ba1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394068931e9be07e4aa5856cc47e4a91957a1ba55bb5b17a3b81.png" - ], + ], "example_tests.test_custom_aggregation.TestCustomAggregation.test_custom_aggregation.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81917e907eb43e873f85677ac190f0703c6a95811f1ac33ce1a57a6f18.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81917e907eb43e873f85677ac190f0703c6a95811f1ac33ce1a57a6f18.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81817e907eb43e873f85637ac198d8703c6a94811f1ac73ee1a57a6f90.png" - ], + ], "example_tests.test_custom_file_loading.TestCustomFileLoading.test_custom_file_loading.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa0cbf1845e34be913787416edcc8bc3bc81f9b63332662a4ed30cdc1b2cd21.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fba0cbf1845e34be912787416edcc8bc3b881f9b62332762a5ad32cdc1b2cd21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa0cbf1845e34be913787416edcc8bc3bc81f9b63332662a4ed30cdc1b2cd21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fba0cbf1845e34be912787416edcc8bc3b881f9b62332762a5ad32cdc1b2cd21.png", "https://scitools.github.io/test-iris-imagehash/images/v4/faa1cb47845e34bc912797436cccc8343f11359b73523746c48c72d9d9b34da5.png" - ], + ], "example_tests.test_deriving_phenomena.TestDerivingPhenomena.test_deriving_phenomena.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/b9993986866952e6c9464639c4766bd9c669916e7b99c1663f99768990763e81.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/b99139de866952e6c946c639c47e6bd18769d16e7a9981662e813699d0763e89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/b9993986866952e6c9464639c4766bd9c669916e7b99c1663f99768990763e81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/b99139de866952e6c946c639c47e6bd18769d16e7a9981662e813699d0763e89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ec97681793689768943c97e8926669d186e8c33f6c99c32e6b936c83d33e2c98.png" - ], + ], "example_tests.test_global_map.TestGlobalMap.test_global_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9979468566857ef07e3e8978566b91cb0179883c89946686a96b9d83766f81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9979468566857ef07e3e8978566b91cb0179883c89946686a96b9d83766f81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa997b958466846ed13e87467a997a898d66d17e2cc9906684696f99d3162f81.png" - ], + ], "example_tests.test_hovmoller.TestGlobalMap.test_hovmoller.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bab430b4ce4bce43c5becf89c54b1a63c543c56e1e64907e3bb469b490de1ac1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bab430b4ce4bce43c5becf89c54b1a63c543c56e1e64907e3bb469b490de1ac1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eeb46cb4934b934bc07e974bc14b38949943c0fe3e94c17f6ea46cb4c07b3f00.png" - ], + ], "example_tests.test_inset_plot.TestInsetPlot.test_inset_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992f50096a5b245dac4f6559496b49248dbc95dcb699529912dcf244a54.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e9ff6992b50096a5b245dac4f64594b6b49248dbc95dcb699529952dcf244a56.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992f50096a5b245dac4f6559496b49248dbc95dcb699529912dcf244a54.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9ff6992b50096a5b245dac4f64594b6b49248dbc95dcb699529952dcf244a56.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992b50096ad9267dac4d64094b294924cdbc95d4b699d29952dcda46e94.png" - ], + ], "example_tests.test_lagged_ensemble.TestLaggedEnsemble.test_lagged_ensemble.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31e1c44e64e4b0459b5bb1716ecac464f496ce34618eb1079b39b193ce25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31e1c44e64e4b0459b5bb1716ecac464f496ce34618eb1079b39b193ce25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31b1c44e64e4b1579b5b917133cecc61f146c414668eb1119b1bb197ce34.png" - ], + ], "example_tests.test_lagged_ensemble.TestLaggedEnsemble.test_lagged_ensemble.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abfef958fd462c993a07d87960464b81d1009687c139d3b594e9cf87c6b89687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abfef958fd462c993a07d87960464b81d1009687c139d3b594e9cf87c6b89687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aafec5e9e5e03e099a07e0f86542db879438261ec3b13ce78d8dc65a92d83d89.png" - ], + ], "example_tests.test_lineplot_with_legend.TestLineplotWithLegend.test_lineplot_with_legend.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eae942526540b869961f8da694589da69543cc9af1014afbc3fd596b84fe19a7.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/eae942146540b869961f8de694589da69543cc9af1014afbc3fd596b84fe19a7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae942526540b869961f8da694589da69543cc9af1014afbc3fd596b84fe19a7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae942146540b869961f8de694589da69543cc9af1014afbc3fd596b84fe19a7.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafd9e12a5a061e9925ec716de489e9685078ec981b229e70ddb79219cc3768d.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fb11731a94cea4ee64b35e91d1d2304e9e5ac7397b20e1fe12852487e666ce46.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fb11731a94cea4ee64b35e91d1d2304e9e5ac7397b20e1fe12852487e666ce46.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bb11721a87cce5e4cce79e81d19b3b5e1e1cd3783168e07835853485e65e2e1e.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e5a665a69a599659e5db1865c2653b869996cce63e99e19a1a912639e7181e65.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e5a665a69a599659e5db1865c2653b869996cce63e99e19a1a912639e7181e65.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e58661969e799659c1f719a6c867359a1996c0773649c09c3e612679c07b3f66.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f2c464ce9e399332e1b74ce1cc79338c6586e5b33b31b37a66c9664cc06e1a64.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f2c464ce9e399332e1b74ce1cc79338c6586e5b33b31b37a66c9664cc06e1a64.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a58660ce9e739b31c93d1cc9c8df33863383e33b3f11c03f2664366cc8ee3cc1.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a83846ea46ce539c93391de32cc86cf87a33fa168721cdb3e896e374b04.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a83846ea46ce539c93391de32cc86cf87a33fa168721cdb3e896e374b04.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be817a87845ea56cec79817a919e338436a5c1e73fa16c736c4a3e816a1e6b1c.png" - ], + ], "example_tests.test_polar_stereo.TestPolarStereo.test_polar_stereo.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e168317a92d36d89c5bb9e94c55e6f0c9a93c15a6ec584763b21716791de3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e168317a92d36d89c5bb9e94c55e6f0c9a93c15a6ec584763b21716791de3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b9e16079971e9e93c8ce0f84c31e3b929f92c0ff3ca1c17e39e03961c07e3f80.png" - ], + ], "example_tests.test_polynomial_fit.TestPolynomialFit.test_polynomial_fit.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abff4a9df26435886520c97f12414695c4b69d23934bc86adc969237d68ccc6f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/aaff4a9df26435886520c97f12414695c4b69d23934bc86adc969a17d69ccc6f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abff4a9df26435886520c97f12414695c4b69d23934bc86adc969237d68ccc6f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aaff4a9df26435886520c97f12414695c4b69d23934bc86adc969a17d69ccc6f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aeffcb34d244348be5a2c96c3a4fc6d0c4b69f2d87294ccb9f1a125684cd7c11.png" - ], + ], "example_tests.test_projections_and_annotations.TestProjectionsAndAnnotations.test_projections_and_annotations.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb179325ca7c665b0c938cb4b4e719e9cb727b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fac54f19851a30e4cc76cd0bb179325cb78665b0c938cb4b4e719e9c9727b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb7c664b0c938cb4bce739e9c37a3b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb78665b1c938c94bce739e9c3727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb179325ca7c665b0c938cb4b4e719e9cb727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac54f19851a30e4cc76cd0bb179325cb78665b0c938cb4b4e719e9c9727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb7c664b0c938cb4bce739e9c37a3b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb78665b1c938c94bce739e9c3727b5c0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb0f932dca7c665b1c92ccb4b4ed19e9c3721b5c8.png" - ], + ], "example_tests.test_projections_and_annotations.TestProjectionsAndAnnotations.test_projections_and_annotations.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492e9b69964936dc21974b18592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896727243318f8dad5a7dc65ba492b93699649b6dc25b64938592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492b93699649b6dc25964938592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75965ba492f9b69964db4cc65b64918592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75865ba492e9b69964db6cc65b74918592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492e9b69964936dc21974b18592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896727243318f8dad5a7dc65ba492b93699649b6dc25b64938592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492b93699649b6dc25964938592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75965ba492f9b69964db4cc65b64918592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75865ba492e9b69964db6cc65b74918592.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e3856d999c389662734731afcdad5a7384daa592b1b69b64d26dc29974b18590.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa15615e97a193adc15e1e81c4fa3eb49d30817e3e05c17e7ba59927817e1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa15615e97a193adc15e1e81c4fa3eb49d30817e3e05c17e7ba59927817e1e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee46607e97a19781c0df1f81d0bb3e241f20c16f3fc0c1fe39263d33d06f3e80.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba056717c3e099e9b90f8e81c4da589499b696763e45e56b3b893929c17b7e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba056717c3e099e9b90f8e81c4da589499b696763e45e56b3b893929c17b7e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea57685f95a886a1c0de9da090be3e2697e1c0ff3f00c17e6b266c17c07f3f00.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a191a1b85e9e81c4da58909996b37e3a65e16f7b817939e57a1e01.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a193a1b85e9e81c4da58909996b3763a65e16f7b816939ed7a1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a191a1b85e9e81c4da58909996b37e3a65e16f7b817939e57a1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a193a1b85e9e81c4da58909996b3763a65e16f7b816939ed7a1e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a697e97a18681c6da9f8190bf3e263624c1ef3b48c17a2b223c47c0ff3f81.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172d0847ecd2bc913939c36846c714933799cc3cc8727e67639f939996a58.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172d0847ecd2bc913939c36846c714933799cc3cc8727e67639f939996a58.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172c6857ecd38cb3392ce36c564311931d85ec64e9787719a39993c316e66.png" - ], + ], "example_tests.test_wind_speed.TestWindSpeed.test_wind_speed.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cd3e29847b18e639e6c14f1a09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cd3e29847b18e639e6c14f1a09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169306c1ee9e96c29e36739e13c07d3d61c07f39a139a1c07f3f01.png" - ], + ], "example_tests.test_wind_speed.TestWindSpeed.test_wind_speed.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cc3e29847b38e639e6c14f1a09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cc3e29847b38e639e6c14f1a09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169306c1ee9e86c29e36739e13c07d3d61c07f39a139a1c17f3f01.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe81957ac17e6a85817e6a85857e942a3e81957a7e81917a7a81d95ec17e2ca1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe81957ac17e6a85817e6a85857e942a3e81957a7e81917a7a81d95ec17e2ca1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe81c17e817e3e81817e7e81857e7e817e81c07e7e81c17e7a81817e817e8c2a.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be81c17ec17e7e81c17e3e81c57ea55a3e80c17e3e81c1fe7a81c285c95f2c03.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be81c17ec17e7e81c17e3e81c57ea55a3e80c17e3e81c1fe7a81c285c95f2c03.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe81857e817e6a85817e7a81857e7e817e81957a7e81817e7a81817e817e843e.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957a857e957ec17e817e6a816a853e817a853e816e818d3a862ad3fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957a857e957ec17e817e6a816a853e817a853e816e818d3a862ad3fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be81857ec17e7a81c17e7e81857e3e803e81817a3e81c17e7a81c17ec97e2c2b.png" - ], + ], + "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_northpolarstereo.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/e59661969e699659c0f719a6c967339a1992c07f3649c09c3f612669c07b3f66.png" + ], + "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_platecarree.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/ee816299954a1da699b6915ec25b6e419729c42c3f84bd9fe6d262d1d1dac076.png" + ], "iris.tests.test_analysis.TestProject.test_cartopy_projection.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/9e1952c9c165b4fc668a9d47c1461d7a60fb2e853eb426bd62fd229c9f04c16d.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_contourf.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a346c9685cb899685c9c39695c79396ec634969ce2c74697a3864697b3c8c.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_pcolor.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a69c896a592e59bc99e3384929636c32d98cde6d964ce7e666332386465b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a69c896a592e59bc99e3384929636c32d98cde6d964ce7e666332386465b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a347c96858b8d9685c9c39696c393966c634969ce3c64697a3864697b3c9c.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_unmappable.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5684eb54a947ad09eb731c521978dc2fb1cc0e4966ce26e2c6b2d3a6e691a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5684eb54a947ad09eb731c521978dc2fb1cc0e4966ce26e2c6b2d3a6e691a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853e48957ac1df957ac8be852bc1b1944e7a9878e03f4c6a253e6c7a912dc2.png" - ], + ], "iris.tests.test_mapping.TestBoundedCube.test_grid.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81917e857e6e81857e7a857a81917a7a81857e857e7e81857e7a817a81852e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81817e7a81857e857e7a81857e7a817a81857e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81917e857e6e81857e7a857a81917a7a81857e857e7e81857e7a817a81852e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81817e7a81857e857e7a81857e7a817a81857e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81857a7a81857e857e7a85857e7a817a81857a.png" - ], + ], "iris.tests.test_mapping.TestBoundedCube.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81e535857e92ca8ec23d21b13ce15e7a811ea5c47e1a5ac17b652d3b05e4f2.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81e535857e92ca8ec23d21b13ce15e7a811ea5c47e1a5ac17b652d3b05e4f2.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81c17a857e1ea5857e634a7a81cd257e8584da857e3b29817e68f47a81c791.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_grid.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2b1c17f1d0ac4f7c8d739a637202749699b6bb3ce3666e4b048944d9d89.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2f1c17f1d0ac457c8d619a637213749699b6bb34e3666e4b04e944d9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2b1c17f1d0ac4f7c8d739a637202749699b6bb3ce3666e4b048944d9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2f1c17f1d0ac457c8d619a637213749699b6bb34e3666e4b04e944d9d89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea05392995bac6d691ce3f21666569d86a96c6360ee195cb91e8ce54953b313b.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_outline.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e80857e7a817a817a817a81817f7a81857e857e857e857e7a81.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e21857e7a817a817a857a81857a7a81857a857e857a857e7a84.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e80857e7a817a817a817a81817f7a81857e857e857e857e7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e21857e7a817a817a857a81857a7a81857a857e857a857e7a84.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa1585e885e87a1785fa7a177a177e807a1585e85fa0857a85e86817857f6a16.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf81e6b1c17e1d4884bfc8df39a43720374969db69b34e26c4e4b0ca904f9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf81e6b1c17e1d4884bfc8df39a43720374969db69b34e26c4e4b0ca904f9d89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea57396995a8c6d691ea3f25664569d86b16c63686ed958991ea4a549531393b.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_scatter.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d894346b24f3477acf68ad39329ed8c696e136c1ab9a71.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d895346b2473477acf68ad39329ed8c69ee126c1ab9a71.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d894346b24f3477acf68ad39329ed8c696e136c1ab9a71.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d895346b2473477acf68ad39329ed8c69ee126c1ab9a71.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea05bd2e916ac2d984983d346b2473477acf69ad3d3296d8c696e126c1ab1e71.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_keywords.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a63a71b3e016061c1fe9b8c3e01a473847e5b94d1fb9ac3.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_keywords.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957fe3cea68c6ce0d9f29b9b6a816463953e61cc917f1ae36ac09d38.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957fe3cea68c6ce0d9f29b9b6a816463953e61cc917f1ae36ac09d38.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa819097857e6560957e7bcc7a819c316e81951e857e62c281fe79a17aa19637.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee819cb7913b63c8846e64737bb1999c6ec52633953a69c8916f6c636e92911c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee819cb7913b63c8846e64737bb1999c6ec52633953a69c8916f6c636e92911c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa8190be857e6739917a7bc47a8594337bb1911c857e6ec3913279007e819637.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a63a71b3e016061c1fe9b8c3e01a473847e5b94d1fb9ac3.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957ae3cea68c6ce0c9f39b9b6a816473953e63cc917f1ae36ac09d38.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957ae3cea68c6ce0c9f39b9b6a816473953e63cc917f1ae36ac09d38.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81909f857e6520957e5bcc7a8194716e31851e857e6ac281fe3f817a81963f.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eae0943295154bcc844e6c314fb093ce7bc7c4b3a4307bc4916f3f316ed2b4ce.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae0943295154bcc844e6c314fb093ce7bc7c4b3a4307bc4916f3f316ed2b4ce.png", "https://scitools.github.io/test-iris-imagehash/images/v4/faa0e55c855fdce7857a1ab16a85a50c3ea1e55e856658a5c11837096e8fe17a.png" - ], + ], "iris.tests.test_mapping.TestMappingSubRegion.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bd913e01d07ee07e926e87876f8196c1e0d36967393c1f181e2c3cb8b0f960d7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bd913e01d07ee07e926e87876f8196c1e0d36967393c1f181e2c3cb8b0f960d7.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b9913d90c66eca6ec66ec2f3689195b6cf5b2f00392cb3496695621d34db6c92.png" - ], + ], "iris.tests.test_mapping.TestUnmappable.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe818d6ac17e5a958d7ab12b9d677615986e666dc4f20dea7281d98833889b22.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe818d6ac17e5a958d7ab12b9d677615986e666dc4f20dea7281d98833889b22.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81b54a817eca35817ec701857e3e64943e7bb41b846f996e817e006ee1b19b.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2ff7c00a56de9023b52e4143da5d16d7ecad1b76f2094c963929c6471c8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2ff7c00a56de9023b52e4143da5d16d7ecad1b76f2094c963929c6471c8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bfec2d77e01a5a5ed013b4ac4521c94817d4e6d91ff63349c6d61991e3278cc.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff95776a01e1f67801cc36f4075b81c5437668c1167c88d2676d39d6867b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff95776a01e1f67801cc36f4075b81c5437668c1167c88d2676d39d6867b68.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8fff941e7e01e1c2f801c878a41e5b0d85cf36e1837e2d9992c62f21769e6a4d.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbe0c21ccd179dc3b05e4b689b0771b48698961b7962da446e8ca5bb36716c6e.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff897066b41f076f81dce1fb007da79c50633e9c40626b8d1066df9d6067969.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ff897066b41f076f81dce1fb007da79c50633e9c40626b8d1066df9d6067969.png", "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/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7e0098757103a71ce4506dc3d11e7b20d2477ec094857db895217f6a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7e0098757103a71ce4506dc3d11e7b20d2477ec094857db895217f6a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8fffc1dc7e019c70f001b70ee4386de1814e7938837b6a7f84d07c9f15b02f21.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c2d73a09b4a76c099d26f14b0e5ad0d643b0d42763e9d51378f895867c39.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe8c0173a19b4066d599946f35f0ed5d0b74729d40369d8953678e897877879.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c2d73a09b4a76c099d26f14b0e5ad0d643b0d42763e9d51378f895867c39.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe8c0173a19b4066d599946f35f0ed5d0b74729d40369d8953678e897877879.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c0567a01b096e4019daff10b464bd4da6391943678e5879f7e3103e67f1c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e04256f68023352f6d61da5c109dec8d19bcf089cc9d99a9c85d999.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e06256f68023352f6d61da5c009decad19bcf089cc9d99a9c85d989.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e04256f68023352f6d61da5c109dec8d19bcf089cc9d99a9c85d999.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e06256f68023352f6d61da5c009decad19bcf089cc9d99a9c85d989.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e002427e801bb4ae65a1c94813dcec999db4bbc9ccd79991f3238cc.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9dd77f00e1d73000cc1df707db8184427ef8d1367c88d2667d39d0866b68.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9d977f41e1d73000cc1df707d98184427ef8d1367c88d2667d39d0866b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9dd77f00e1d73000cc1df707db8184427ef8d1367c88d2667d39d0866b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9d977f41e1d73000cc1df707d98184427ef8d1367c88d2667d39d0866b68.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ff9d9f7e01e1c2b001c8f8f63e1b1d81cf36e1837e259982ce2f215c9a626c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbe0c21ccd179dc3b05e4b689b0771b48698961b7962da446e8ca5bb36716c6e.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ffb5867f0060d4301f6d9fb007d899c50699e9c8668e78d8678d69de069969.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ffb5867f0060d4301f6d9fb007d899c50699e9c8668e78d8678d69de069969.png", "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/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f90987720029f1ef458cd43811cdb60d647de609485ddb899215f62.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f94987720009f1ef458cd43810cdb60d647de609485ddb89921df62.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f90987720029f1ef458cd43811cdb60d647de609485ddb899215f62.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f94987720009f1ef458cd43810cdb60d647de609485ddb89921df62.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1de7e009c7030019786f438cde3810fd97c93734a778ce07c9f99b02731.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc8967e0098a6241f9d26e34b8e42f4d20bb4942759e9941f78f8d7867a39.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83f9c8967e009da6245f9946e25f9ed6f0940f29f40749d8853678e8d7857879.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc8967e0098a6241f9d26e34b8e42f4d20bb4942759e9941f78f8d7867a39.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83f9c8967e009da6245f9946e25f9ed6f0940f29f40749d8853678e8d7857879.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc9d67e00909624079daef160cf4bd45a439184367ae5979f7e3119e6261c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d94ac5a1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7c248399cc7917662d84ac5a1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1b47c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d84acda0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d94ac5a1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7c248399cc7917662d84ac5a1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1b47c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d84acda0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b2ecc1a8b9994a16e666b5e3ce151969a5fb4ed49909653990d46b9bfc097684.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bea06899c14eb16e9895ce46c74a396a74ed64b13390b3c61b439f1b4d2ccde6.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0211b9e066e074d83926ed8f8cd3792dad1964db0d80e9b09.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0311b9e066e074d839266d8e8cd379adad1964db0d80e9b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0211b9e066e074d83926ed8f8cd3792dad1964db0d80e9b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0311b9e066e074d839266d8e8cd379adad1964db0d80e9b09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be852fc1e078c83eb30e3607672149c098d95c5b9e4636f2c1fc299d999f7e03.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621deda3f69392cccd246db39018989ec4836de9ed249292.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a66b94c621deda3f69392cccd646db3901898dec4836de9cd249292.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621ceda3f6d392cccd246db3901898dec4836de9cd249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621deda3f69392cccd246db39018989ec4836de9ed249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a66b94c621deda3f69392cccd646db3901898dec4836de9cd249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621ceda3f6d392cccd246db3901898dec4836de9cd249292.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edfa96cb9a256b4f65466d9892d9c865693a1a9c94b39ed8484b35ad9a864c32.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a4fb19b3db04c6cd6307b98678601c738c39d71cf3866186d8616e69bd191b9e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a4fb19b3db04c6cd6307b98678601c738c39d71cf3866186d8616e69bd191b9e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e8b33c129649c78de3a773e578650c728e92279be12de1edc4f246b2939c3b01.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bbfac39d9899384a6f6694a7b613cb489c95b7b7c24a399cc5913262d84acda0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bbfac39d9899384a6f6694a7b613cb489c95b7b7c24a399cc5913262d84acda0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b2ecc12999994e16e666b5e3ce171969a5fb4ed49909e53990c44b9b7c09f684.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bea06899c14eb16e9895ce46c74a396a74ed64b13390b3c61b439f1b4d2ccde6.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af7e1f0ff1e1e0f0d918960e6c076d8bd266d868c537365a90966db0de0e1b09.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7e1f0ff1e1e0f0d918960e6c076d83d266d868c537365ad0966db0de4e1b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af7e1f0ff1e1e0f0d918960e6c076d8bd266d868c537365a90966db0de0e1b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7e1f0ff1e1e0f0d918960e6c076d83d266d868c537365ad0966db0de4e1b09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be812fc1c078c03e930e3627672369c1d8d85c5b96463662e1fc699d9b9f7e03.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/edf896d79a67b94c651ced23d29392cccd646d33901912fcc4836d69ed249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/edf896d79a67b94c651ced23d29392cccd646d33901912fcc4836d69ed249292.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edda96cb9a256b4765c26d9892dbc665693a1a9494b796c86c4b37ad92864c32.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/acf939339a16c64de306318638673c738c19d71cf3866186d8636e69bd191b9e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/acf939339a16c64de306318638673c738c19d71cf3866186d8636e69bd191b9e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edb23c529649c78de38773e538650c729e92279be12de1edc4f246b2139c3b01.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_1d_positive_down.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87fef8117980c7c160078f1ffc049e7e90159a7a95419a7e910dcf1ece19ce3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87fef8117980c7c160078f1ffc049e7e90159a7a95419a7e910dcf1ece19ce3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7fe781b708487c360079e3bb4789869816bdb64c76b4a3cce7b4e749a6130c5.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_1d_positive_up.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47800bd9f660779d0863f49c9947f4e1e9141de38d700da28ce1d9a2b.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47a00bc9f660779d8863f49c9907f4e1e9141de38d708da28ce1d9a0b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47800bd9f660779d0863f49c9947f4e1e9141de38d700da28ce1d9a2b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47a00bc9f660779d8863f49c9907f4e1e9141de38d708da28ce1d9a0b.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff958b7a00b09c661761c9907fcb0d9163ce7895289a618f381bffccf97200.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_2d_positive_down.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba684e194fb901b3a0587641ad03b1ae7674e64c15a5b99c767c47e3a98.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba684e194fb901b3a0587641ad03b1ae7674e64c15a5b99c767c47e3a98.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba484e194dbc01f3665c0e4399a3f0fc2653f90c99e3f613e64c81e3f81.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_2d_positive_up.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee176c7f93e093a0c50f9383815e6e156859e17e6e15e17a9be08e2d851a9b83.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee176c7f93e093a0c50f9383815e6e156859e17e6e15e17a9be08e2d851a9b83.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebc06be1941e941ec07f941f907f6fa0950fc07e6f80c07f6b806be1c07f3f80.png" - ], + ], "iris.tests.test_plot.TestContour.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/cff8a55f7a15b55a7817854ad007a5e8c04f3ce8c04f3e2ac4706ab295b37a96.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/cff8a55f7a15b55a7817854ad007a5e8c04f3ce8c04f3e2ac4706ab295b37a96.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eaece0173d17951fbd03974a914964e8c04a72e8c1531ee1cc746bb293973ecd.png" - ], + ], "iris.tests.test_plot.TestContour.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfc815e78018597fc019b65b425d121955e7eda854b7d6a80db7eb481b72b61.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfc815e78018597fc019b65b425d121955e7eda854b7d6a80db7eb481b72b61.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa8553fc01b15ab4044a269546caa5956b7e9bc0b97f2cc2d62d360b363b49.png" - ], + ], "iris.tests.test_plot.TestContour.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780185fff800955ad4027e00d517d400855f7e0085ff7e8085ff6aed.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780085fff800855fd4027e00d517d400855f7e0085ff7e8085ff6aed.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780185fff800955ad4027e00d517d400855f7e0085ff7e8085ff6aed.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780085fff800855fd4027e00d517d400855f7e0085ff7e8085ff6aed.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe817ffc00855ef0007e81d4027e80815fd56a03ff7a8085ff3aa883ff6aa5.png" - ], + ], "iris.tests.test_plot.TestContour.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa56c3cc34e891b1c9a91c36c5a170e3c71b3e5993a784e492c49b4ecec76393.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa56c3cc34e891b1c9a91c36c5a170e3c71b3e5993a784e492c49b4ecec76393.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e36cb95b199999765cd3694b06478c7396329958434c2cecb6c6d69ce1b92.png" - ], + ], "iris.tests.test_plot.TestContour.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe857f7a01a56afa05854ad015bd00d015d50a90577e80857f7ea0857f7abf.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe857f7a01a56afa05854ad015bd00d015d50a90577e80857f7ea0857f7abf.png", "https://scitools.github.io/test-iris-imagehash/images/v4/affe815ffc008554f8007e01d0027e808557d5ea815f7ea0817f2fea817d2aff.png" - ], + ], "iris.tests.test_plot.TestContour.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bff81ff7a0195fcf8019578d4027e00d550d402857c7e0185fe7a8385fe6aaf.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bff81ff7a0195fcf8019578d4027e00d550d402857c7e0185fe7a8385fe6aaf.png", "https://scitools.github.io/test-iris-imagehash/images/v4/abff857ff8018578f8017a80d4027e00855ec42a81fe7a8185fe6a8f85fe6ab7.png" - ], + ], "iris.tests.test_plot.TestContourf.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa562ed68569d52857abd12953a8f12951f64e0d30f3ac96a4d6a696ee06a32.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa562ed68569d52857abd12953a8f12951f64e0d30f3ac96a4d6a696ee06a32.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957ac57e957a857a957a958ac5723b0d6ac56b833e856e606a923e90.png" - ], + ], "iris.tests.test_plot.TestContourf.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5e03f957a4f80954a9e41e16e9c60970fb5b24ada634e6e93692d4ba562d8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5e03f957a4f80954a9e41e16e9c60970fb5b24ada634e6e93692d4ba562d8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea851f00957ac0f7957ac07f957a628d815e7b126ab13e816a953ae46a859ed3.png" - ], + ], "iris.tests.test_plot.TestContourf.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e954a7a81857e957e857efc00857e7e007a85c02a7e859f287a85c1fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e954a7a81857e957e857efc00857e7e007a85c02a7e859f287a85c1fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a81857e7a81857e7a806a85857a7a85857e7a85817e.png" - ], + ], "iris.tests.test_plot.TestContourf.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a6938b6b5969193901a4fc1e594a7c69999cbce33639879526e72330e65e4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a6938b6b5969193901a4fc1e594a7c69999cbce33639879526e72330e65e4.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a3c7e968597b19685c9c696a7c79491c16e59691a387f6978396e68683184.png" - ], + ], "iris.tests.test_plot.TestContourf.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa85857ec45a7a81857e854a857ee56a917ec56a3a85c56a3a85c4ea7a8112fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa85857ec45a7a81857e854a857ee56a917ec56a3a85c56a3a85c4ea7a8112fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817e857e7a81857a7a81957a6e81917a6caa3a85c57a3a8585fa6a8591fe.png" - ], + ], "iris.tests.test_plot.TestContourf.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817ec40a7a81857e957e857ef40a857ef60b7a81c40a7b81e60f7a814aff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817ec40a7a81857e957e857ef40a857ef60b7a81c40a7b81e60f7a814aff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817e857e7a81857e7a81817a7e81817a668f7a91857e7a81857e7a85817e.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee856aa5957a955ac0bf954bc17e3b819548c07f3e81c07e2ec46ea4c07f3e84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be853f80854ac17ec0bdc2f5c17a0d09cc1fc07f5ab5e1fe3f409d7a38743e00.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be853f80854ac17ec0bdc2f5c17a0d09cc1fc07f5ab5e1fe3f409d7a38743e00.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf813e85c07ec57ec17e9073c07e3f81856ec17a3f80c0fe3e813f84c2733e80.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee856aa5957a955ac0bf954bc17e3b819548c07f3e81c07e2ec46ea4c07f3e84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_orography.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a95c17a6eece4b4e1333b01c07e1bb13909914b9ec1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a91c17a6ee464f4e1333b01c17e1bb1390d914b9ec1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a95c17a6eece4b4e1333b01c07e1bb13909914b9ec1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a91c17a6ee464f4e1333b01c17e1bb1390d914b9ec1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a91957a857ac4fe268cc07f6e846e05d9373b81d17b1b6a1b41c4fa2cc4.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_orography.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc4e0c6b4c31e9ee1847939a1c116c15e7b94e57e1ea9391de16e1ac3.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc6e1c6b4c31e9ee1846939a1c116c15e7b14e17e1ea9393de16e1ac3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc4e0c6b4c31e9ee1847939a1c116c15e7b94e57e1ea9391de16e1ac3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc6e1c6b4c31e9ee1846939a1c116c15e7b14e17e1ea9393de16e1ac3.png", "https://scitools.github.io/test-iris-imagehash/images/v4/af0b690f96f0d2d4c25e94a194ad3da19a52c25e3f02c07f3fa52d03c16a3fcb.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe857b91917a847ec0bd3f01c47e6ca43b11915a3ea4db3b1b4a84c4c03f3fc1.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be813fc0c15ac13dc1bfc27dc17e1d93c51fc43f1ea1c17a3ec138e4b1721a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be813fc0c15ac13dc1bfc27dc17e1d93c51fc43f1ea1c17a3ec138e4b1721a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be813a81c17ec57ec17e952ac07f3f808556c17e3f80c07f3e813f80c27e3f81.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9561ef956a7b92609b922dc16e6ec6845ac47e5aa5c57e5ec04861957b1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9561ef956a7b92609b922dc16e6ec6845ac47e5aa5c57e5ec04861957b1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe856a85957a955ac03f956ac17f3f809552c07f3e81c07e3e806e85c07e3f84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe857b91917a847ec0bd3f01c47e6ca43b11915a3ea4db3b1b4a84c4c03f3fc1.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/baf5347ecf0ac3f1c1f68f83850b1f83cc11c0fc7ad0c17a1be138e4b07e1a0d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/baf5347ecf0ac3f1c1f68f83850b1f83cc11c0fc7ad0c17a1be138e4b07e1a0d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b878387e978ec2f0c0f09f83878f3f81c070c0fe78d0c1763fa13856d03e3f0f.png" - ], + ], "iris.tests.test_plot.TestMissingCS.test_missing_cs.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fac16ee0953b911bc15e9648e56ec4e691be7bcc7a8184733ea16a90c17e930d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac16ee0953b911bc15e9648e56ec4e691be7bcc7a8184733ea16a90c17e930d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816ac1857e853cc17f957ac15f3e849486c8f43e81c13b3f813e91c07e3f46.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_u.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a95c17fb51e953e9485857a1f409552856a1f81c17e5ab94e15c0ff5a85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a95c17fb51e953e9485857a1f409552856a1f81c17e5ab94e15c0ff5a85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95955a954ac17f954ac07e3f48951ec07e3e81c0ff7ea16a81c0bf3e81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_u.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be0e695ac3f096b5943fd2a185fc1e8590e594ee1e05c17a4f403d0fe1fe4b42.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be0e695ac3f096b5943fd2a185fc1e8590e594ee1e05c17a4f403d0fe1fe4b42.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea956ab5954a954ac17f954a817e3f40950ac07f3e81c0ff7a856aa1c0ff3f80.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_v.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0773d09956a955a857a1d88845ec57e3f81c07e4ae56b21d0ff5a85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0773d09956a955a857a1d88845ec57e3f81c07e4ae56b21d0ff5a85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a857ac17e954ac17e1fa2950bc07e3e81c07f3e807a85c17f3f81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_v.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562d4c7c43d0bb57b97e0857a3f1995d284763a05c17a7b856a2dc0f45a84.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562d4c7c43d0bb57b97e0857a3f1995d284763a05c17a7b856a2dc0f45a84.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a85957a857ac17e954ac17e9d02954ac07e3e81c07f3e857a85c2fd3f80.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_none.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0763d09b54a955a857a3f88845ec57a3e85c07e6a616b25d0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0763d09b54a955a857a3f88845ec57a3e85c07e6a616b25d0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a857ac17e954ac17e3fa29506c07e3e81c07f3e807a84c1ff3f81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_none.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562f6c0773d09b54a955a857a3f81955ac47e3e85c17e7aa16a25c0765aa1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562f6c0773d09b54a955a857a3f81955ac47e3e85c17e7aa16a25c0765aa1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a85957a957ac17e954ac17a1f06954ac07e3e81c07f3e817a85c0ff3f80.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4f63066318695ec5c9695e6c49c6a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4f63066318695ec5c9695e6c49c6a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea817a81957e857e957e953e957e857e857e6aa06a816ac16a017a816a9585fa.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eab478972a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eab478972a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea953f83954ac2bc956ac07e956a3509c0de61796ab57a816a856ad16ab590fb.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7a84857a7a85857e7a813a2f7a817a85857a7a85857a7a85857a.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b793b19a1ec3c191c5c6e596191e4e693269336c36391a6e3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b793b19a1ec3c191c5c6e596191e4e693269336c36391a6e3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a387e968596319697c3c19284a62c93a560c36933393a6c7e793b6c6b31cd.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a1695a97a17d5a97a17906785a7816685a7e86685ad687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a1695a97a17d5a97a17906785a7816685a7e86685ad687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7e01857e7a81857e7a81e8177a816a8585fa7a85857e7a81857e.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f003d527f002f427ea82f42d6a8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527e0d2f52d4ad.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f003d527f002f427ea82f42d6a8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527e0d2f52d4ad.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e80857e7a81857e7a812d557a817a85857e7a81857e7a80857e.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1f94e2c119938e9463678c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1f94e2c119938e9463678c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea858782957a703f957a3878957a7a65957a6bc06ae76f806ad50fd06a859c50.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a5695a96a53c0f711b3c0f7d1b97b46943c3e0cc416b5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a5695a96a53c0f711b3c0f7d1b97b46943c3e0cc416b5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85857a857e7e81957a7a81957a6a85857acac6c1fb6aa67a81956e6a81b506.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a5eb40c3f0ebe0c1c0c3f07c0b3e3e96a13c1e6d5b694a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e381f96a096a5d6a5eb40c3f0ebf0c1e0c3f07c0a3e3e96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a5eb40c3f0ebe0c1c0c3f07c0b3e3e96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e381f96a096a5d6a5eb40c3f0ebf0c1e0c3f07c0a3e3e96a13c1e6d5b694a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817e81857e857a857e7a81857e6a85817b81e63a913e857e81c17e7a81956e.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc585c39986cdc79ec792e3a6960d584939793c3438743873.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc585c39986cdc79ec792e3a6960d584939793c3438743873.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e96ac78796953c4c9685383996c538e69692637263696b49693ac796693ac71b.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e885e0954295e195ea95a085e87a153e7f95e06a1778557a1f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e885e0954295e195ea95a085e87a153e7f95e06a1778557a1f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a81857e857e857e7a81857e6a81c17f95786aa77a807e81c17c7e819558.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba173a1795e895e8c5e8f400c1f8c1f895a8c5e87a077a5ec5e83e173e177e02.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba173a1795e895e8c5e8f400c1f8c1f895a8c5e87a077a5ec5e83e173e177e02.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a80857e857e857e7a81817e3e81817e857f6aa07a857e80c17f7e80c15f.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4fe3026318695ec5c9695e6c49c6a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4fe3026318695ec5c9695e6c49c6a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea817a81957e857e957e953e957e857e857e6aa06a816ac16a017a816a9585fa.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eabc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eabc789722.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea953f83954ac2fc956ac07e956a3509c0de61796ab57a816a854ad16ab590fb.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7a84857a7a85857e7a813a2f7a817a85857a7a85857a7a85857a.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b593b19a1ec3c591c5c6e596191e4e693269336c36391a6e3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b593b19a1ec3c591c5c6e596191e4e693269336c36391a6e3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9693878969696139296c38f9bcc3474692169cb6c7339393c6cc387c78796cc.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a16d5a97a17d5a97a17806785a7816685a7e86685ad687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a16d5a97a17d5a97a17806785a7816685a7e86685ad687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e6e05857e7a81857e7a81a0577a816a8585fa7a85857e7a81857e.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f002d527f002f427fa82f42d6a8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/af4280bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527f0d2f52d4ad.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f002d527f002f427fa82f42d6a8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af4280bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527f0d2f52d4ad.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e80857e7a81857e7a8129577a817a85857e7a81857e7a80857e.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d84f17a1d94e2c11d938e9463678e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1d94e2c11993ae9463678c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d84f17a1d94e2c11d938e9463678e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1d94e2c11993ae9463678c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea858782957a703f957a3878957a7a65957e6bc06ae56f806ad50fd06a859c50.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a1695a96a53c1f711b3c0f791b97b46943c3e06c436b5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a1695a96a53c1f711b3c0f791b97b46943c3e06c436b5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85857a857e7e81957a7a81957a6a85857acae6c1fb6aa47a81956e6a81b506.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a56b40c3f06be2c1c0c3f07c0b3ebe96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a56b40c3f06be2c1c0c3f07c0b3ebe96a13c1e6d5b694a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa813e81857e857a857e7a81857e6a85817b00e63eb93e857e81c17e7a81956e.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc785c39986cdc78ec792e7a6960d584939793c3438703873.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc785c39986cdc78ec792e7a6960d584939793c3438703873.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e129c7169ed638ec9ed6387196c761c665396724612dcf0d693896929ed698c9.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e895e0fd4295e095ea95a085e87a153e7e95e06a1778157a17.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e895e0fd4295e095ea95a085e87a153e7e95e06a1778157a17.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a85857a857e857e7a81857e7a81817f95506aaf7a807e81c17c7a81957a.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba176a1795e895e8c5e87c00c1f8c1f894a8c5e87a077adec5e83e173e177a06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba176a1795e895e8c5e87c00c1f8c1f894a8c5e87a077adec5e83e173e177a06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a80857a857e857e7a81857e3e81817e2fc56aa07a857e80c17f7e80c17f.png" - ], + ], "iris.tests.test_plot.TestPlot.test_t.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe955f6a05e5137305d9c4f443127195187e9cd5467fa3d4917b68fc007a1a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95027e05e7007305d9c4a447127f853f069f814f2fa7d4d12b6cfc007e5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe955f6a05e5137305d9c4f443127195187e9cd5467fa3d4917b68fc007a1a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95027e05e7007305d9c4a447127f853f069f814f2fa7d4d12b6cfc007e5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe9c1a7e05e718f305d9d2e46312718138049e824e2fa783db2bed76b4fe00.png" - ], + ], "iris.tests.test_plot.TestPlot.test_t_dates.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15cdb6b10178d7d4082e57d7290906f685814277b1dc88724cfd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15c9b6a10178d7d4082c57d7290906f6c58942f7b1dc88724cfd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd4a02a01cc84f10078d7d4082c77d73909ded6ef816273bd9c98725cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15cdb6b10178d7d4082e57d7290906f685814277b1dc88724cfd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15c9b6a10178d7d4082c57d7290906f6c58942f7b1dc88724cfd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd4a02a01cc84f10078d7d4082c77d73909ded6ef816273bd9c98725cdd26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/87fc9d8a7e054d83f5067bc1c1423471927ba73c8d9f864e09a1a7b358c8276f.png" - ], + ], "iris.tests.test_plot.TestPlot.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95297e87c74a6a059158f89c3d6ed0536597c0387836d0f87866d0697097.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95297e87c74a6a059158f89c3d6ed0536597c0387836d0f87866d0697097.png", "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/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" - ], + ], "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/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test_axes.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test_figure.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_non_cube_coordinate.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e81857e7a81857e7a81857e7a818576c02a7e95856a7e81c17a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e81857e7a81857e7a81857e7a818576c02a7e95856a7e81c17a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e85857e7a81857e7a81857e7a817e81780b7a81c56a7a81857e.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe8142f5c17ebd2cc16eb548954a9542916a347a915e60bd4afd68793f916296.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe8142f5c17ebd2cc16eb548954a9542916a347a915e60bd4afd68793f916296.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853f10956ac5e1957a854e957a203e955e6aa76ae17aa16a856aaf6ab19e12.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6b91d968c161b972d76aa462b5.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6a91d96ac163b972d36aa462b5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6b91d968c161b972d76aa462b5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6a91d96ac163b972d36aa462b5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853a85857a857a957a857a957ad05a857b3e946a606b917a816f647a853af4.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bf88f457a03b5307e16b561f007b53ed067217ac1786afec0f570bf8178681a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8bf98f057a03b5307e16b561f007b53ad067217ac1786afec0f570bf8178685a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bf88f457a03b5307e16b561f007b53ed067217ac1786afec0f570bf8178681a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bf98f057a03b5307e16b561f007b53ad067217ac1786afec0f570bf8178685a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafdcec9bc219530b696a56694c2852a95656b7b81986acdc0e516adad186eda.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe8f367e05952afe05a50b980ded4bd05d69c2c1fb71c1c06272f4d0a06af4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe8f367e05952afe05a50b980ded4bd05d69c2c1fb71c1c06272f4d0a06af4.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aff24ab7bd05952fbd0f950f914fcd48c47860f3e1b9329094266e345a850f6c.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aa953d0f85fab50fd0f2956a7a1785fafa176877d00f68f1d02c60f2f008d0f0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e94b5019e97950d685395bee05361fad05560fad01570fef001dabe.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e95b5419e97950d6853953ee053617ad05560fad01570fef001dabe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aa953d0f85fab50fd0f2956a7a1785fafa176877d00f68f1d02c60f2f008d0f0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e94b5019e97950d685395bee05361fad05560fad01570fef001dabe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e95b5419e97950d6853953ee053617ad05560fad01570fef001dabe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaa56f96a1856cd681a56ee8162d52e8467e12c50c7e8095ad7e0095ad03ff.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaaf439e87b5019687b5019687b56ac05561fae07103fe6079687a607178f8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa2d4b968795059e87970f6854697ae055697ac08561fad041d7aef001d6ae.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaaf439e87b5019687b5019687b56ac05561fae07103fe6079687a607178f8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa2d4b968795059e87970f6854697ae055697ac08561fad041d7aef001d6ae.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eb7a3e0c978187a4950190bc6856687a607e687bc0fcc1e394acfc0197fc2bfb.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aeb8b5095a87cd60386592d9ec97ad6dd23ca4f6d0797827f0096216c1f878e6.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aeb8b5095a87cd60386592d9ec97ad6dd23ca4f6d0797827f0096216c1f878e6.png", "https://scitools.github.io/test-iris-imagehash/images/v4/affa950ddb13c03634359ad8a4c80f26911f26f3c06e0ff3f4007b4285fd6e72.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8fea97194f07c9c830d79169ce16269f91097af6c47861f6d0796076d0797a16.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fee970b4f07c9c930d79129ce16269f91097af6c4f861f4d0786076d0797a16.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/afea97094f07c9c870d79129ce16269f91096af6c4f861f6c07960f6d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fea97194f07c9c830d79169ce16269f91097af6c47861f6d0796076d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fee970b4f07c9c930d79129ce16269f91097af6c4f861f4d0786076d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/afea97094f07c9c870d79129ce16269f91096af6c4f861f6c07960f6d0797a16.png", "https://scitools.github.io/test-iris-imagehash/images/v4/afee9632de05c9d9f180d168c454a53e931b3e84956a3b8c85d94ce703ff7284.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea85603f956a9741951e9d83c1fa8d2fd0a55af0d25f345ae5f062c72d68612d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea85603f956a9741951e9d83c1fa8d2fd0a55af0d25f345ae5f062c72d68612d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853f00957ac07c957ac0be951a69f3c47c7a5f3a6127816b953e646b813761.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a346c9685cb899685c9c39695c79396ec634969ce2c74697a3864697b3c8c.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65767039740bc069d9ad00b8dadd03f52f181dd347a847a62ff81e8626c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65777039740bc069d9ad00b8dadd03d52f181dd707a847a62ff81e8626c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65767039740bc069d9ad00b8dadd03f52f181dd347a847a62ff81e8626c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65777039740bc069d9ad00b8dadd03d52f181dd707a847a62ff81e8626c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebffca44f502b36498309c9b940999add1bb62bba784374acc5a6a246acc6b65.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5649c434ac92e5d9c9361b95b39c38c3835a5ec6d966ced34c633099ace5a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5649c434ac92e5d9c9361b95b39c38c3835a5ec6d966ced34c633099ace5a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a6b6c96a597a591c9949b94b61b69c7926b5bccce66646b3869b831a52c26.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2dd2d09295c3c0c7d13c1bc6d23d2c696de0e53c3ac393daf6d205c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c3c0c7d13c1bc6d23d2c696ce0e53c3ac393dbf6d205c2c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2f92d09295c3d0c7d13c1bc6d23d2c696cf0e53c3ac2b3d9f6d201c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26c1e360f684a3c2c6913dca497b9d38097a903ff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2dd2d09295c3c0c7d13c1bc6d23d2c696de0e53c3ac393daf6d205c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c3c0c7d13c1bc6d23d2c696ce0e53c3ac393dbf6d205c2c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2f92d09295c3d0c7d13c1bc6d23d2c696cf0e53c3ac2b3d9f6d201c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26c1e360f684a3c2c6913dca497b9d38097a903ff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e3f96a1c3e197a169f1785e3b0e68523e1c398bc58687b1d86096e1039f.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9686d8c9696924797879e3b86929e58696d69cc6869659379626133398d9ccd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e961658f961e92469e1e1c7966f36cd86165618c70e166b39b9698719e1e9ec8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9686d8c9696924797879e3b86929e58696d69cc6869659379626133398d9ccd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e961658f961e92469e1e1c7966f36cd86165618c70e166b39b9698719e1e9ec8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e1a530e29e5ecf199a5acd8f64f1326161a530e265999cd29e52cf199a5e6669.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf803f00c05fc4bfc07ec15dc05fd8bbc07cc96c333a32113bd02dd27ced3ec0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf803f00c05fc4bfc07ec15dc05fd8bbc07cc96c333a32113bd02dd27ced3ec0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be813ea0c17ec55ac17ed23dc07e295ac57e3b653f803f813e816e853e81b542.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95956a95626993941a6a2d956e6ed6845a6e65c4bec7b64a9594686ea19578.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95956a95626993941a6a2d956e6ed6845a6e65c4bec7b64a9594686ea19578.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85856e857e4893957a7aa1956a7b81954b3b817a856fd46a85846e6e85857e.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a327c847860fdc57a69beb0be68bd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a32fd847860fdc57269beb0be689d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a327c847860fdc57a69beb0be68bd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a32fd847860fdc57269beb0be689d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bedcf25bc03a4929c103a5bf03fdbbc81cb364d86e46da70f86899b3a0f6cc0.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/cee8953a7a15856978579696d03d672cc49a6e5a842d3d2cc0b66bd1c2ea39f1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/cee8953a7a15856978579696d03d672cc49a6e5a842d3d2cc0b66bd1c2ea39f1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aee1f93a63168569b852d697913d632485ca2e43952d3bbcc2b66bd1426b3c71.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a3dd038c0fef000d0fa.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a7dd068c0fef000d0fa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a3dd038c0fef000d0fa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a7dd068c0fef000d0fa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bec11ab5c1be857ac13e7ae53c422d423e017a85b542fc00c1fefe0091fe03ff.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a973d96a56953968769439685a54ae05117eae0511fba60513bba69717aba.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96a56953968769439685a54ae85197eae0511fba60513bba69717aba.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a973d96a56953968769439685a54ae05117eae0511fba60513bba69717aba.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96a56953968769439685a54ae85197eae0511fba60513bba69717aba.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a96ac97a16c5897a1791e95a53b0b913c6953687c4ec3685cc6c36e7c87c3.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coord_names.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coord_names.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b8a53b59c71ac5a6b8791c1867876b63d9e0e65c96199d871cc23339633664ce.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coords.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coords.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b8a53b59c71ac5a6b8791c1867876b63d9e0e65c96199d871cc23339633664ce.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_default.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_yx_order.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81948e857e4971907ea72e95fa66b2952e4ead6d429b527ac7a5286e981836.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81948e857e4971907ea72e95fa66b2952e4ead6d429b527ac7a5286e981836.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa85978e837e68f094d3673089626ad792073985659a9b1a7a15b52869f19f56.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_yx_order.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea159694856a6b5096afa53a36941da1e4f5c369cd1ae6d69b6a1c80625af2f6.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea159694856a6b5096afa53a36941da1e4f5c369cd1ae6d69b6a1c80625af2f6.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea95969c874a63d39ca3ad2a231cdbc9c4973631cd6336c633182cbc61c3d3f2.png" - ], + ], "iris.tests.test_plot.TestPlotOtherCoordSystems.test_plot_tmerc.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e63399cd99cd64b29999335965369b262649c98c9b3966c6998d3319ccd69333.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e63399cd99cd64b29999335965369b262649c98c9b3966c6998d3319ccd69333.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e665326d999ecc9b3319b3246666cce69b496cccccc9669923193336666699a6.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_t.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5d67fd4e5962211d9c6a443da77d5389c8ed346d923d011d968dc00da48.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82ffb5d67fdde5962211d9c6a441da77d5389c8cd346d927d011d968dc00da48.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fabd867fd5e5822201d9c6a4539a77953d8cbf834f99e7d051996cdc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5d67fd4e5962211d9c6a443da77d5389c8ed346d923d011d968dc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82ffb5d67fdde5962211d9c6a441da77d5389c8cd346d927d011d968dc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fabd867fd5e5822201d9c6a4539a77953d8cbf834f99e7d051996cdc00da48.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb59a7f00e59a2205d9d6e4619a74d9388c8e884e8da799d30b6dddb47e00.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_t_dates.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd5ae7f51efb6200378d7d4082c17d7280906d6e58962db31d800da6cdd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4ae7f55efbe200178d7d4082c17d7280906d6e58962df319800da6cdd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4827f51ef94200078d7c4082c57d739095ed6ed8962db759808da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd5ae7f51efb6200378d7d4082c17d7280906d6e58962db31d800da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4ae7f55efbe200178d7d4082c17d7280906d6e58962df319800da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4827f51ef94200078d7c4082c57d739095ed6ed8962db759808da6cdd26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82fd958a7e006f9ba0077bc5c9462c759873dd3c8d8f826699a187b358c82f67.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5097e84c54a621799d8601d9966d213cd67c039d876d078d866d869d8f7.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffbd097e84c54a621799d8601d9966d253cc27c039d876d078d866d869d8f7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5097e84c54a621799d8601d9966d213cd67c039d876d078d866d869d8f7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffbd097e84c54a621799d8601d9966d253cc27c039d876d078d866d869d8f7.png", "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/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" - ], + ], "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/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" - ], + ], "iris.tests.test_plot.TestSimple.test_bounds.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562fcc7c0b50bb53b9f8085727a157a95c0f67a85e07e0be08069e07d9fa0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562fcc7c0b50bb53b9f8085727a157a95c0f67a85e07e0be08069e07d9fa0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85954a957ac17e954ac17a9c3e956ac07e3e81c07f3e857aa5c2753f80.png" - ], + ], "iris.tests.test_plot.TestSimple.test_points.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85957a957ac17e954ac17e1ca2954ac07e3e81c07f3e807a85c1ff3f81.png" - ], + ], "iris.tests.test_plot.TestSymbols.test_cloud_cover.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a330c96a5ccf2695a330c96a5ccf2695a330c96b5ccf3694a330c96b5ccf3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a330c96a5ccf2695a330c96a5ccf2695a330c96b5ccf3694a330c96b5ccf3.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eb52916494ad6e1b6b5291e494ad6e1b6b5291e494ad6e1b6b5291e494ad6e1b.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_alignment.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad2f0c1f66ac1c55a4af4e550a52b3e05905e1e419e6f937e3b21.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad3f0c1f66a81e55a4af4e550a52b3e05905e1e419e6f937e1b21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad2f0c1f66ac1c55a4af4e550a52b3e05905e1e419e6f937e3b21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad3f0c1f66a81e55a4af4e550a52b3e05905e1e419e6f937e1b21.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be8137f4954ac03fc0ff3e81d03f496a6d00b4af3ea0c07f6fa232c0db7f2d00.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contour.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee321fc96666919b6ec15fdca593600d2586785a259dfa5a01.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee3217c9e66691996ec15fdca593680d2586785a259dfa5a01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee321fc96666919b6ec15fdca593600d2586785a259dfa5a01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee3217c9e66691996ec15fdca593680d2586785a259dfa5a01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7fd95da7a01654a3217c962e4819a56c96f3c8593624da584da3b658db662db.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contour.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf802f85c17fc17fc07eb42ac07f3f929130c07e3f80c07f7aa02e85c07f3e81.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe812f88957a955a857a9257c17f7aa5c03dc0bf5a85c07e7f402d40a57a3f01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe812f88957a955a857a9257c17f7aa5c03dc0bf5a85c07e7f402d40a57a3f01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a95957a957ac0fe1e8bc07f7f806e01c07f3f80c07f3fa23f00c07f3d00.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf802f85c17fc17fc07eb42ac07f3f929130c07e3f80c07f7aa02e85c07f3e81.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa852f81955ac532c0bf9e89c57edae69357e13f4ea0c05a3f8561a4935a3e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa852f81955ac532c0bf9e89c57edae69357e13f4ea0c05a3f8561a4935a3e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a95907ae508c17e955ac07f3fa0945bc07f3f80c07f3aa36f01c0ff3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf_nameless.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa52ec1955ac536c0bf9e09c57edae69357e13f4e80c0da2f81618493da3f01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa52ec1955ac536c0bf9e09c57edae69357e13f4e80c0da2f81618493da3f01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816af5907ee508c17e955ac03f3f809419c07f3f80c07f3a8b6f81c0ff3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_map.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4ecef19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4ecef19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_pcolor.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb423d4e94a5c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb423d4e94a5c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f2f81c07e3fa12da1c2fe3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb433d4e94a4c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb433d4e94a4c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f3f81c07e3fa12da1c27e3f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_non_cube_coordinate.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85857a955ae17e957ec57e7a81855fc17e3a81c57e1a813a85c57a1a05.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85857a955ae17e957ec57e7a81855fc17e3a81c57e1a813a85c57a1a05.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a85857a957ac07f957ac07f3e80956ac07f3e80c07f3e813e85c07e3f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a95e15ab51a953e9485857a1f409552857e1fc1c07e5abd4a35e07f4aa5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a95e15ab51a953e9485857a1f409552857e1fc1c07e5abd4a35e07f4aa5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95955a956ac17f950ac07e3f48951ac07f3f81c0ff3ea16aa1c0be3e81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85957a957ac17e954ac17e1ca2954ac07e3e81c07f3e807a85c1ff3f81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f00953062179561f087953ad167997a80784a7fc1e5d86d9978485f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f80953860179561f087953ad167997a80784a7fc1e5d86d9978485b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f00953062179561f087953ad167997a80784a7fc1e5d86d9978485f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f80953860179561f087953ad167997a80784a7fc1e5d86d9978485b.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafdc6c9f720953030968d6795d28d6a95674b7b81304aedc9e51cad8d186c9a.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82ff8db67f94952e76159d6bb01dcd629059c962c1fbd9c1c062da74d820ca74.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82be8db67f95952e761d9d6bb01dcd628059c962c1fbd9e1c072da64d060ca74.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe8db67f95952e76159d6bb01dcd629059c962c1fbd9e1c072da64d020ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82ff8db67f94952e76159d6bb01dcd629059c962c1fbd9c1c062da74d820ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82be8db67f95952e761d9d6bb01dcd628059c962c1fbd9e1c072da64d060ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe8db67f95952e76159d6bb01dcd629059c962c1fbd9e1c072da64d020ca74.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a2ff6a967f00952eb40d9d0f900fcd62c47069f3d1f93a909c266e34d8a56f68.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aa97b70ff5f0970f20b2956a6a17957af805da71d06f5a75d02cd870d800d8f2.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e1faa549de9497090697971d60539f3ef171c87ac075487ad025d87ed801da3e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aa97b70ff5f0970f20b2956a6a17957af805da71d06f5a75d02cd870d800d8f2.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1faa549de9497090697971d60539f3ef171c87ac075487ad025d87ed801da3e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eadab54fd7a1856d90819d6df8169962e946d862802ed8809ded7e809d2d03ff.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e8faad47f784bd0596859d03969f9962c05dc96ee07189fe6870c862687178f8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a8fa2d4797859585b6959d07605f896ee051697ad061d9fad0619aaed801deae.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e8faad47f784bd0596859d03969f9962c05dc96ee07189fe6870c862687178f8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a8fa2d4797859585b6959d07605f896ee051697ad061d9fad0619aaed801deae.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aa5b3c0c978187a4b60199bc605f6976687e6873d07c99e390acdc0391fc2f7b.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a6ffb5097e84cde2224598d1649f8d6cd2388c76d0799867d009da76c9f8d866.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a6bfb5097f84cde2224599d1649f8d6cd2388c76d0799867d009da76c1f8d866.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6ffb5097e84cde2224598d1649f8d6cd2388c76d0799867d009da76c9f8d866.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6bfb5097f84cde2224599d1649f8d6cd2388c76d0799867d009da76c1f8d866.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a6fbb50cfbd0c036203598dce4c88d26d32f8cf3886e1df3dc047b4289ec6e72.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff978b7f00c9c830d7992166179e969509d866c478d964d079c876d869da26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff97837f00c9c830d79921661f9e9695099876c478d964c079c876d879da26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff978b7f00c9c830d7992166179e969509d866c478d964d079c876d869da26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff97837f00c9c830d79921661f9e9695099876c478d964c079c876d879da26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a2ffb6127f0dc9993085d960c6748d3e9b121ca49d6a1b048df34ce789ff7205.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a9ff16eb740954a9e05855a19a3c0fbc13e1ea5c07d5ad0cb58e45e3c35.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a9ff16eb740954a9e05855a19a3c0fbc13e1ea5c07d5ad0cb58e45e3c35.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95957a957ac07e954ac17e3e87950bc07f3ea4c27d3e833ac1c1e03f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e5a565b69e1a9a42917e1a19c17b3a619e59c47b3a25c53e3b8430e5c57a3e85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e5a565b69e1a9a42917e1a19c17b3a619e59c47b3a25c53e3b8430e5c57a3e85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e5a761a79a589e58c07d1e48c07c3f819e41c07f3d84c17e3fa62585c0fe3f83.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/afffe6d67700958636179d92e019992dd039daf5817d987a807a48e499684a6d.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/aeffe6d67780958636179d92e019892dd139daf5815d987a807a48e699684a6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/afffe6d67700958636179d92e019992dd039daf5817d987a807a48e499684a6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aeffe6d67780958636179d92e019892dd139daf5815d987a807a48e699684a6d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eaff6ad4f74ab16490109c9b942999add1b74bb785a41d4acd526a254acc6365.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d67d2c696ce0653c3ac2b1d976da05c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d27d2c696ce0e53c3ad2b1d976da01c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26d1e3a0f684a3c2c6913dc2497b9db8095e502ff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d67d2c696ce0653c3ac2b1d976da05c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d27d2c696ce0e53c3ad2b1d976da01c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26d1e3a0f684a3c2c6913dc2497b9db8095e502ff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3c1f97a1c3e197a1c9f37c5e390668521e0c390bdd8685b1d86096e5279f.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e968658e969692c797879e3b86929e58696d49cd6869c9a37962c923990d9c6d.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e9e1658e961e92569e9e3c7966d36c586165698c70e1ce739b3698619e1e984c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e968658e969692c797879e3b86929e58696d49cd6869c9a37962c923990d9c6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9e1658e961e92569e9e3c7966d36c586165698c70e1ce739b3698619e1e984c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e1a530e29e5ecf199a5acd8f64f1326161a538e665a198d29e52cb1d9a5e6669.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf813f80c156c05dc0fec29dc17f1a6dd05fc0ff1aa1c57e3b243b20375a1e81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf813f80c156c05dc0fec29dc17f1a6dd05fc0ff1aa1c57e3b243b20375a1e81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a81d17ec57ac07e952ac07f3aa0955ec17e3f80c07f3f803f80c0bf3f81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95629d956a996069939e9bc07f7aad856cc47e5e81857a1e254a35c1be1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95629d956a996069939e9bc07f7aad856cc47e5e81857a1e254a35c1be1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a957ac03f957ac07f3ba1954ac07e3e81c07f3ea47a85c07e3e80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f867f008d8220179852f01fd9bed1789a6c847cc877c46ac972987ec8fd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179852f01fd9bed1789a6c847cc877c468c9f6987ec8fd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179c52f01fd9bed1789a6c847cc877c560c976987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f867f008d8220179852f01fd9bed1789a6c847cc877c46ac972987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179852f01fd9bed1789a6c847cc877c468c9f6987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179c52f01fd9bed1789a6c847cc877c560c976987ec8fd.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3eded05fe11a492b000985af07fdbb4d1e3366d8c644da79fa68993180f6ec1.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600a56962df9e96f01dc926c498cc46847f9d6cd0244bf19a6b19f1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600856962df9e96f01dcd26c498cc46847f9d6cd0244bf19a6b1975.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600a56962df9e96f01dc926c498cc46847f9d6cd0244bf19a6b19f1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600856962df9e96f01dcd26c498cc46847f9d6cd0244bf19a6b1975.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aef9f93a770085e9205fd696d13c4b2485ca1a43952f1934daa66bd1ca6b3c71.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d86801f91ee6e1591fe7e117876c07d6877d068d878d800d07a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d87801b91ee6e1599fe7e117874c07d6877d068d878d800d07a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d86801f91ee6e1591fe7e117876c07d6877d068d878d800d07a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d87801b91ee6e1599fe7e117874c07d6877d068d878d800d07a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bec1329dc5be85dac01d58d73e419d423e41daa59822dc00c5fefe0091fe03ff.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ee0519d6ee07f9b6a78619b2a79711a2a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ae0519d6ee07f996a78719b2a79711a3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ee0519d6ee07f9b6a78619b2a79711a2a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ae0519d6ee07f996a78719b2a79711a3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e96ac97a168d897a5791695a19927913c3953687ecce3687c86e3487cc6c3.png" - ], + ], "iris.tests.test_quickplot.TestTimeReferenceUnitsLabels.test_not_reference_time_units.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82faa1977fdf89976200ddf6e000d9e7f75f9866d560dae4dc00d966dc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82b8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00d966fc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200ddf6e000d9e7f77f9866d560dee4dc00d966dc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00dd64dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82faa1977fdf89976200ddf6e000d9e7f75f9866d560dae4dc00d966dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82b8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00d966fc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200ddf6e000d9e7f77f9866d560dee4dc00d966dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00dd64dc005e20.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82faa19e7f51898c6001dd86845fd9a2dd7f996281ee19f389ef03ffdc007e00.png" - ], + ], "iris.tests.test_quickplot.TestTimeReferenceUnitsLabels.test_reference_time_units.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fd777ffe0002addd4002805dda8de65dde9d4625bfddc209841de20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002a9dd4002805ddaade65d9a9d5625bfddc209841de20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002addd4002805dd28df67d9a9d4625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fd777ffe0002addd4002805dda8de65dde9d4625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002a9dd4002805ddaade65d9a9d5625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002addd4002805dd28df67d9a9d4625bfddc209841de20.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82fa80997f547799a0037a00d52f0956ddaf9f7e98a1816e09f5d8260bfffe00.png" ] } \ No newline at end of file From f51ae8405e1e0c7e8a68bf05ddfbd4da9091cc62 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 4 Sep 2018 17:27:15 +0100 Subject: [PATCH 23/24] Vector plots 4 (#3158) --- docs/iris/src/whatsnew/2.2.rst | 5 +- lib/iris/plot.py | 55 +- .../integration/plot/test_vector_plots.py | 180 +++ lib/iris/tests/results/imagerepo.json | 1049 +++++++++-------- 4 files changed, 718 insertions(+), 571 deletions(-) create mode 100644 lib/iris/tests/integration/plot/test_vector_plots.py diff --git a/docs/iris/src/whatsnew/2.2.rst b/docs/iris/src/whatsnew/2.2.rst index 4edc305025..7b069f455e 100644 --- a/docs/iris/src/whatsnew/2.2.rst +++ b/docs/iris/src/whatsnew/2.2.rst @@ -25,9 +25,8 @@ Iris 2.2.0 Features contiguous, or the cube's data must be masked at the discontiguities in order to avoid plotting errors. - The iris plot functions :func:`iris.plot.quiver` and - :func:`iris.plot.streamplot` have been added, and these also work with - 2-dimensional plot coordinates. + The iris plot functions :func:`iris.plot.quiver` has been added, and this + also works with 2-dimensional plot coordinates. .. admonition:: 2-Dimensional Grid Vectors diff --git a/lib/iris/plot.py b/lib/iris/plot.py index 0b9b7c54be..f0dfccd8c5 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -1226,9 +1226,9 @@ def _vector_component_args(x_points, y_points, u_data, *args, **kwargs): if crs: if not isinstance(crs, (ccrs.PlateCarree, ccrs.RotatedPole)): msg = ('Can only plot vectors provided in a lat-lon ' - 'projection, i.e. "cartopy.crs.PlateCarree" or ' - '"cartopy.crs.RotatedPole". This ' - "cubes coordinate system is {}.") + 'projection, i.e. equivalent to "cartopy.crs.PlateCarree" ' + 'or "cartopy.crs.RotatedPole". This ' + "cube coordinate system translates as Cartopy {}.") raise ValueError(msg.format(crs)) # Given the above check, the Y points must be latitudes. # We therefore **assume** they are in degrees : I'm not sure this @@ -1237,7 +1237,7 @@ def _vector_component_args(x_points, y_points, u_data, *args, **kwargs): # TODO: investigate degree units assumptions, here + elsewhere. # Implement a latitude scaling, but preserve the given magnitudes. - v_data = v_data.copy() + u_data, v_data = [arr.copy() for arr in (u_data, v_data)] mags = np.sqrt(u_data * u_data + v_data * v_data) v_data *= np.cos(np.deg2rad(y_points)) scales = mags / np.sqrt(u_data * u_data + v_data * v_data) @@ -1294,53 +1294,6 @@ def quiver(u_cube, v_cube, *args, **kwargs): *args, **kwargs) -def streamplot(u_cube, v_cube, *args, **kwargs): - """ - Draws a streamline plot from two vector component cubes. - - 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_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.quiver` 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('streamplot', _vector_component_args, u_cube, - *args, **kwargs) - - def plot(*args, **kwargs): """ Draws a line plot based on the given cube(s) or coordinate(s). diff --git a/lib/iris/tests/integration/plot/test_vector_plots.py b/lib/iris/tests/integration/plot/test_vector_plots.py new file mode 100644 index 0000000000..00d9d48e8d --- /dev/null +++ b/lib/iris/tests/integration/plot/test_vector_plots.py @@ -0,0 +1,180 @@ +# (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 . +""" +Test some key usages of :func:`iris.plot.quiver`. + +""" + +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 numpy as np + +import cartopy.crs as ccrs +from iris.coords import AuxCoord, DimCoord +from iris.coord_systems import Mercator +from iris.cube import Cube +from iris.tests.stock import sample_2d_latlons + +# Run tests in no graphics mode if matplotlib is not available. +if tests.MPL_AVAILABLE: + import matplotlib.pyplot as plt + from iris.plot import quiver + + +@tests.skip_plot +class MixinVectorPlotCases(object): + """ + Test examples mixin, used by separate quiver + streamplot classes. + + NOTE: at present for quiver only, as streamplot does not support arbitrary + coordinates. + + """ + + def plot(self, plotname, *args, **kwargs): + plot_function = self.plot_function_to_test() + plot_function(*args, **kwargs) + plt.suptitle(plotname) + + @staticmethod + def _nonlatlon_xyuv(): + # Create common x, y, u, v arrays for quiver/streamplot testing. + x = np.array([0., 2, 3, 5]) + y = np.array([0., 2.5, 4]) + uv = np.array([[(0., 0), (0, 1), (0, -1), (2, 1)], + [(-1, 0), (-1, -1), (-1, 1), (-2, 1)], + [(1., 0), (1, -1), (1, 1), (-2, 2)]]) + uv = np.array(uv) + u, v = uv[..., 0], uv[..., 1] + return x, y, u, v + + @staticmethod + def _nonlatlon_uv_cubes(x, y, u, v): + # Create u and v test cubes from x, y, u, v arrays. + coord_cls = DimCoord if x.ndim == 1 else AuxCoord + x_coord = coord_cls(x, long_name='x') + y_coord = coord_cls(y, long_name='y') + u_cube = Cube(u, long_name='u', units='ms-1') + if x.ndim == 1: + u_cube.add_dim_coord(y_coord, 0) + u_cube.add_dim_coord(x_coord, 1) + else: + u_cube.add_aux_coord(y_coord, (0, 1)) + u_cube.add_aux_coord(x_coord, (0, 1)) + v_cube = u_cube.copy() + v_cube.rename('v') + v_cube.data = v + return u_cube, v_cube + + def test_non_latlon_1d_coords(self): + # Plot against simple 1D x and y coords. + x, y, u, v = self._nonlatlon_xyuv() + u_cube, v_cube = self._nonlatlon_uv_cubes(x, y, u, v) + self.plot('nonlatlon, 1-d coords', u_cube, v_cube) + plt.xlim(x.min() - 1, x.max() + 2) + plt.ylim(y.min() - 1, y.max() + 2) + self.check_graphic() + + def test_non_latlon_2d_coords(self): + # Plot against expanded 2D x and y coords. + x, y, u, v = self._nonlatlon_xyuv() + x, y = np.meshgrid(x, y) + u_cube, v_cube = self._nonlatlon_uv_cubes(x, y, u, v) + # Call plot : N.B. default gives wrong coords order. + self.plot('nonlatlon_2d', u_cube, v_cube, coords=('x', 'y')) + plt.xlim(x.min() - 1, x.max() + 2) + plt.ylim(y.min() - 1, y.max() + 2) + self.check_graphic() + + @staticmethod + def _latlon_uv_cubes(grid_cube): + # Make a sample grid into u and v data for quiver/streamplot testing. + u_cube = grid_cube.copy() + u_cube.rename('dx') + u_cube.units = 'ms-1' + v_cube = u_cube.copy() + v_cube.rename('dy') + ny, nx = u_cube.shape + nn = nx * ny + angles = np.arange(nn).reshape((ny, nx)) + angles = (angles * 360.0 / 5.5) % 360. + scale = np.arange(nn) % 5 + scale = (scale + 4) / 4 + scale = scale.reshape((ny, nx)) + u_cube.data = scale * np.cos(np.deg2rad(angles)) + v_cube.data = scale * np.sin(np.deg2rad(angles)) + return u_cube, v_cube + + def test_2d_plain_latlon(self): + # Test 2d vector plotting with implicit (PlateCarree) coord system. + u_cube, v_cube = self._latlon_uv_cubes(sample_2d_latlons()) + ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180)) + self.plot('latlon_2d', u_cube, v_cube, + coords=('longitude', 'latitude')) + ax.coastlines(color='red') + ax.set_global() + self.check_graphic() + + def test_2d_plain_latlon_on_polar_map(self): + # Test 2d vector plotting onto a different projection. + u_cube, v_cube = self._latlon_uv_cubes(sample_2d_latlons()) + ax = plt.axes(projection=ccrs.NorthPolarStereo()) + self.plot('latlon_2d_polar', u_cube, v_cube, + coords=('longitude', 'latitude')) + ax.coastlines(color='red') + self.check_graphic() + + def test_2d_rotated_latlon(self): + # Test plotting vectors in a rotated latlon coord system. + u_cube, v_cube = self._latlon_uv_cubes( + sample_2d_latlons(rotated=True)) + ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180)) + self.plot('2d_rotated', u_cube, v_cube, + coords=('longitude', 'latitude')) + ax.coastlines(color='red') + ax.set_global() + self.check_graphic() + + def test_fail_unsupported_coord_system(self): + # Test plotting vectors in a rotated latlon coord system. + u_cube, v_cube = self._latlon_uv_cubes(sample_2d_latlons()) + patch_coord_system = Mercator() + for cube in u_cube, v_cube: + for coord in cube.coords(): + coord.coord_system = patch_coord_system + re_msg = ('Can only plot .* lat-lon projection, .* ' + 'This .* translates as Cartopy.*Mercator') + with self.assertRaisesRegexp(ValueError, re_msg): + self.plot('2d_rotated', u_cube, v_cube, + coords=('longitude', 'latitude')) + + +class TestQuiver(MixinVectorPlotCases, tests.GraphicsTest): + def setUp(self): + super(TestQuiver, self).setUp() + + def plot_function_to_test(self): + return quiver + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json index e7614cae19..ef9bf542de 100644 --- a/lib/iris/tests/results/imagerepo.json +++ b/lib/iris/tests/results/imagerepo.json @@ -1,952 +1,967 @@ { "example_tests.test_COP_1d_plot.TestCOP1DPlot.test_COP_1d_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/baff589936602d8ec977334ae4dac9b61a6dc4d99532c86cc2913e36c4cc0f61.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/baff589936602d8ec977334ae4dac9b61a6dc4d99532c86cc2913e36c4cc0f61.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aefec91c3601249cc9b3336dc4c8cdb31a64c6d997b3c0eccb5932d285e42f33.png" - ], + ], "example_tests.test_COP_maps.TestCOPMaps.test_cop_maps.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9138db95668524913e6ac168997e85957e917e876396b96a81b5ce3c496935.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9138db95668524913e6ac168997e85957e917e876396b96a81b5ce3c496935.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea9130db95668524913c6ac178995b0d956e917ec76396b96a853dcf94696935.png" - ], + ], "example_tests.test_SOI_filtering.TestSOIFiltering.test_soi_filtering.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fac460b9c17b78723e05a5a9954edaf062332799954e9ca5c63b9a52d24e5a95.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8460b9c17b78723e05a5a9954edaf062333799954e9ca5c63b9a52d24e4a9d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac460b9c17b78723e05a5a9954edaf062332799954e9ca5c63b9a52d24e5a95.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8460b9c17b78723e05a5a9954edaf062333799954e9ca5c63b9a52d24e4a9d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa167295c5e0696a3c17a58c9568da536233da19994cdab487739b4b9b444eb5.png" - ], + ], "example_tests.test_TEC.TestTEC.test_TEC.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9a42846e9a49c7596e3cce6c907b3a83c17e1b8239b3e4f33bc4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9e43846e9a49c7596e2cce6c907b3a83c16e1b9231b3e4f33b8c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9a42846e9a49c7596e3cce6c907b3a83c17e1b8239b3e4f33bc4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1a561b69b1a9e43846e9a49c7596e2cce6c907b3a83c16e1b9231b3e4f33b8c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e5a761b69a589a4bc46f9e48c65c6631ce61d1ce3982c13739b33193c0ee3f8c.png" - ], + ], "example_tests.test_anomaly_log_colouring.TestAnomalyLogColouring.test_anomaly_log_colouring.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ec4464e185a39f93931e9b1e91696d2949dde6e63e26a47a5ad391938d9a5a0c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ec4464e185a39f93931e9b1e91696d2949dde6e63e26a47a5ad391938d9a5a0c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ecc164e78e979b19b3789b0885a564a56cc2c65e3ec69469db1bdb9a853c1e24.png" - ], + ], "example_tests.test_atlantic_profiles.TestAtlanticProfiles.test_atlantic_profiles.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc5d08fcd00fdb1c93fcb21c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc7c09f4d00fdb1c93fcb21c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/9f8a60536bd28e1320739437b5f437b0a53d66f4cc5c08f4d00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc5d08fcd00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8260536bd28e1320739437b5f437b0a51d66f4cc7c09f4d00fdb1c93fcb21c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/9f8a60536bd28e1320739437b5f437b0a53d66f4cc5c08f4d00fdb1c93fcb21c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/9fc060f462a08f07203ebc77a1f36707e61f4e38d8f7d08a910197fc877cec58.png" - ], + ], "example_tests.test_atlantic_profiles.TestAtlanticProfiles.test_atlantic_profiles.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a6eaa57e6e81ddf999311ba3b3775e20845d5889c199673b4e22a4675e8ca11c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6eaa57e6e81ddf999311ba3b3775e20845d5889c199673b4e22a4675e8ca11c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eeea64dd6ea8cd99991f1322b3761e06845718d89995b3131f32a4765ec2a1cd.png" - ], + ], "example_tests.test_coriolis_plot.TestCoriolisPlot.test_coriolis_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e78665de9a699659e55e9965886979966986c5e63e98c19e3a256679e1981a24.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e78665de9a699659e55e9965886979966986c5e63e98c19e3a256679e1981a24.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e68665de9a699659c1fe99a5896965966996c46e3e19c1da3a652669c51e1a26.png" - ], + ], "example_tests.test_cross_section.TestCrossSection.test_cross_section.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95317b9562e4d1649f5a05856e4ca4da52947e4ea5f13f1b499d42f13b1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95317b9562e4d1649f5a05856e4ca4da52947e4ea5f13f1b499d42f13b1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea91b17b9562e4d1609f5a05856e4ca45a52957e5ea5f13b1bca9dc0b17b1ac1.png" - ], + ], "example_tests.test_cross_section.TestCrossSection.test_cross_section.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394069921e93f07f4aad856cc47e4e95857a1ea5da3591ba1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394069921e93f07f4aad856cc47e4e95857a1ea5da3591ba1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea9521fb956a394068931e9be07e4aa5856cc47e4a91957a1ba55bb5b17a3b81.png" - ], + ], "example_tests.test_custom_aggregation.TestCustomAggregation.test_custom_aggregation.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81917e907eb43e873f85677ac190f0703c6a95811f1ac33ce1a57a6f18.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81917e907eb43e873f85677ac190f0703c6a95811f1ac33ce1a57a6f18.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe816e81817e907eb43e873f85637ac198d8703c6a94811f1ac73ee1a57a6f90.png" - ], + ], "example_tests.test_custom_file_loading.TestCustomFileLoading.test_custom_file_loading.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa0cbf1845e34be913787416edcc8bc3bc81f9b63332662a4ed30cdc1b2cd21.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fba0cbf1845e34be912787416edcc8bc3b881f9b62332762a5ad32cdc1b2cd21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa0cbf1845e34be913787416edcc8bc3bc81f9b63332662a4ed30cdc1b2cd21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fba0cbf1845e34be912787416edcc8bc3b881f9b62332762a5ad32cdc1b2cd21.png", "https://scitools.github.io/test-iris-imagehash/images/v4/faa1cb47845e34bc912797436cccc8343f11359b73523746c48c72d9d9b34da5.png" - ], + ], "example_tests.test_deriving_phenomena.TestDerivingPhenomena.test_deriving_phenomena.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/b9993986866952e6c9464639c4766bd9c669916e7b99c1663f99768990763e81.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/b99139de866952e6c946c639c47e6bd18769d16e7a9981662e813699d0763e89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/b9993986866952e6c9464639c4766bd9c669916e7b99c1663f99768990763e81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/b99139de866952e6c946c639c47e6bd18769d16e7a9981662e813699d0763e89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ec97681793689768943c97e8926669d186e8c33f6c99c32e6b936c83d33e2c98.png" - ], + ], "example_tests.test_global_map.TestGlobalMap.test_global_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9979468566857ef07e3e8978566b91cb0179883c89946686a96b9d83766f81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9979468566857ef07e3e8978566b91cb0179883c89946686a96b9d83766f81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa997b958466846ed13e87467a997a898d66d17e2cc9906684696f99d3162f81.png" - ], + ], "example_tests.test_hovmoller.TestGlobalMap.test_hovmoller.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bab430b4ce4bce43c5becf89c54b1a63c543c56e1e64907e3bb469b490de1ac1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bab430b4ce4bce43c5becf89c54b1a63c543c56e1e64907e3bb469b490de1ac1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eeb46cb4934b934bc07e974bc14b38949943c0fe3e94c17f6ea46cb4c07b3f00.png" - ], + ], "example_tests.test_inset_plot.TestInsetPlot.test_inset_plot.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992f50096a5b245dac4f6559496b49248dbc95dcb699529912dcf244a54.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e9ff6992b50096a5b245dac4f64594b6b49248dbc95dcb699529952dcf244a56.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992f50096a5b245dac4f6559496b49248dbc95dcb699529912dcf244a54.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9ff6992b50096a5b245dac4f64594b6b49248dbc95dcb699529952dcf244a56.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebff6992b50096ad9267dac4d64094b294924cdbc95d4b699d29952dcda46e94.png" - ], + ], "example_tests.test_lagged_ensemble.TestLaggedEnsemble.test_lagged_ensemble.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31e1c44e64e4b0459b5bb1716ecac464f496ce34618eb1079b39b193ce25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31e1c44e64e4b0459b5bb1716ecac464f496ce34618eb1079b39b193ce25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbbb31b1c44e64e4b1579b5b917133cecc61f146c414668eb1119b1bb197ce34.png" - ], + ], "example_tests.test_lagged_ensemble.TestLaggedEnsemble.test_lagged_ensemble.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abfef958fd462c993a07d87960464b81d1009687c139d3b594e9cf87c6b89687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abfef958fd462c993a07d87960464b81d1009687c139d3b594e9cf87c6b89687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aafec5e9e5e03e099a07e0f86542db879438261ec3b13ce78d8dc65a92d83d89.png" - ], + ], "example_tests.test_lineplot_with_legend.TestLineplotWithLegend.test_lineplot_with_legend.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eae942526540b869961f8da694589da69543cc9af1014afbc3fd596b84fe19a7.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/eae942146540b869961f8de694589da69543cc9af1014afbc3fd596b84fe19a7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae942526540b869961f8da694589da69543cc9af1014afbc3fd596b84fe19a7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae942146540b869961f8de694589da69543cc9af1014afbc3fd596b84fe19a7.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafd9e12a5a061e9925ec716de489e9685078ec981b229e70ddb79219cc3768d.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fb11731a94cea4ee64b35e91d1d2304e9e5ac7397b20e1fe12852487e666ce46.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fb11731a94cea4ee64b35e91d1d2304e9e5ac7397b20e1fe12852487e666ce46.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bb11721a87cce5e4cce79e81d19b3b5e1e1cd3783168e07835853485e65e2e1e.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e5a665a69a599659e5db1865c2653b869996cce63e99e19a1a912639e7181e65.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e5a665a69a599659e5db1865c2653b869996cce63e99e19a1a912639e7181e65.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e58661969e799659c1f719a6c867359a1996c0773649c09c3e612679c07b3f66.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f2c464ce9e399332e1b74ce1cc79338c6586e5b33b31b37a66c9664cc06e1a64.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f2c464ce9e399332e1b74ce1cc79338c6586e5b33b31b37a66c9664cc06e1a64.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a58660ce9e739b31c93d1cc9c8df33863383e33b3f11c03f2664366cc8ee3cc1.png" - ], + ], "example_tests.test_orca_projection.TestOrcaProjection.test_orca_projection.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a83846ea46ce539c93391de32cc86cf87a33fa168721cdb3e896e374b04.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a83846ea46ce539c93391de32cc86cf87a33fa168721cdb3e896e374b04.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be817a87845ea56cec79817a919e338436a5c1e73fa16c736c4a3e816a1e6b1c.png" - ], + ], "example_tests.test_polar_stereo.TestPolarStereo.test_polar_stereo.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e168317a92d36d89c5bb9e94c55e6f0c9a93c15a6ec584763b21716791de3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e168317a92d36d89c5bb9e94c55e6f0c9a93c15a6ec584763b21716791de3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b9e16079971e9e93c8ce0f84c31e3b929f92c0ff3ca1c17e39e03961c07e3f80.png" - ], + ], "example_tests.test_polynomial_fit.TestPolynomialFit.test_polynomial_fit.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abff4a9df26435886520c97f12414695c4b69d23934bc86adc969237d68ccc6f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/aaff4a9df26435886520c97f12414695c4b69d23934bc86adc969a17d69ccc6f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abff4a9df26435886520c97f12414695c4b69d23934bc86adc969237d68ccc6f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aaff4a9df26435886520c97f12414695c4b69d23934bc86adc969a17d69ccc6f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aeffcb34d244348be5a2c96c3a4fc6d0c4b69f2d87294ccb9f1a125684cd7c11.png" - ], + ], "example_tests.test_projections_and_annotations.TestProjectionsAndAnnotations.test_projections_and_annotations.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb179325ca7c665b0c938cb4b4e719e9cb727b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fac54f19851a30e4cc76cd0bb179325cb78665b0c938cb4b4e719e9c9727b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb7c664b0c938cb4bce739e9c37a3b5c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb78665b1c938c94bce739e9c3727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb179325ca7c665b0c938cb4b4e719e9cb727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac54f19851a30e4cc76cd0bb179325cb78665b0c938cb4b4e719e9c9727b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb7c664b0c938cb4bce739e9c37a3b5c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa854e19851a30e4cc76cd0bb179325cb78665b1c938c94bce739e9c3727b5c0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa854f19851a30e4cc76cd0bb0f932dca7c665b1c92ccb4b4ed19e9c3721b5c8.png" - ], + ], "example_tests.test_projections_and_annotations.TestProjectionsAndAnnotations.test_projections_and_annotations.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492e9b69964936dc21974b18592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896727243318f8dad5a7dc65ba492b93699649b6dc25b64938592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492b93699649b6dc25964938592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75965ba492f9b69964db4cc65b64918592.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75865ba492e9b69964db6cc65b74918592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492e9b69964936dc21974b18592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896727243318f8dad5a7dc65ba492b93699649b6dc25b64938592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e385699d9c3896627243318fcdad5a7dc6dba492b93699649b6dc25964938592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75965ba492f9b69964db4cc65b64918592.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e3856b999c3896727243318f8dad5a75865ba492e9b69964db6cc65b74918592.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e3856d999c389662734731afcdad5a7384daa592b1b69b64d26dc29974b18590.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa15615e97a193adc15e1e81c4fa3eb49d30817e3e05c17e7ba59927817e1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa15615e97a193adc15e1e81c4fa3eb49d30817e3e05c17e7ba59927817e1e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee46607e97a19781c0df1f81d0bb3e241f20c16f3fc0c1fe39263d33d06f3e80.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba056717c3e099e9b90f8e81c4da589499b696763e45e56b3b893929c17b7e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba056717c3e099e9b90f8e81c4da589499b696763e45e56b3b893929c17b7e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea57685f95a886a1c0de9da090be3e2697e1c0ff3f00c17e6b266c17c07f3f00.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a191a1b85e9e81c4da58909996b37e3a65e16f7b817939e57a1e01.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a193a1b85e9e81c4da58909996b3763a65e16f7b816939ed7a1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a191a1b85e9e81c4da58909996b37e3a65e16f7b817939e57a1e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba1e605ec7a193a1b85e9e81c4da58909996b3763a65e16f7b816939ed7a1e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a697e97a18681c6da9f8190bf3e263624c1ef3b48c17a2b223c47c0ff3f81.png" - ], + ], "example_tests.test_rotated_pole_mapping.TestRotatedPoleMapping.test_rotated_pole_mapping.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172d0847ecd2bc913939c36846c714933799cc3cc8727e67639f939996a58.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172d0847ecd2bc913939c36846c714933799cc3cc8727e67639f939996a58.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa8172c6857ecd38cb3392ce36c564311931d85ec64e9787719a39993c316e66.png" - ], + ], "example_tests.test_wind_speed.TestWindSpeed.test_wind_speed.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cd3e29847b18e639e6c14f1a09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cd3e29847b18e639e6c14f1a09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169306c1ee9e96c29e36739e13c07d3d61c07f39a139a1c07f3f01.png" - ], + ], "example_tests.test_wind_speed.TestWindSpeed.test_wind_speed.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cc3e29847b38e639e6c14f1a09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bcf924fb9306930ce12ccf97c73236b28ecec4cc3e29847b38e639e6c14f1a09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9e960e996169306c1ee9e86c29e36739e13c07d3d61c07f39a139a1c17f3f01.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe81957ac17e6a85817e6a85857e942a3e81957a7e81917a7a81d95ec17e2ca1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe81957ac17e6a85817e6a85857e942a3e81957a7e81917a7a81d95ec17e2ca1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe81c17e817e3e81817e7e81857e7e817e81c07e7e81c17e7a81817e817e8c2a.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be81c17ec17e7e81c17e3e81c57ea55a3e80c17e3e81c1fe7a81c285c95f2c03.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be81c17ec17e7e81c17e3e81c57ea55a3e80c17e3e81c1fe7a81c285c95f2c03.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe81857e817e6a85817e7a81857e7e817e81957a7e81817e7a81817e817e843e.png" - ], + ], "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957a857e957ec17e817e6a816a853e817a853e816e818d3a862ad3fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957a857e957ec17e817e6a816a853e817a853e816e818d3a862ad3fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be81857ec17e7a81c17e7e81857e3e803e81817a3e81c17e7a81c17ec97e2c2b.png" - ], + ], "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_northpolarstereo.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/e59661969e699659c0f719a6c967339a1992c07f3649c09c3f612669c07b3f66.png" - ], + ], "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_platecarree.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/ee816299954a1da699b6915ec25b6e419729c42c3f84bd9fe6d262d1d1dac076.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" + ], + "iris.tests.integration.plot.test_vector_plots.TestQuiver.test_2d_plain_latlon_on_polar_map.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/e66c6619999933666666c6d99999336663646d9999c1332667b60cf964d8672c.png" + ], + "iris.tests.integration.plot.test_vector_plots.TestQuiver.test_2d_rotated_latlon.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/eba925a5c476d25a95a56b876f3826246a449c6b96a3731ab13f6c656a5cb48a.png" + ], + "iris.tests.integration.plot.test_vector_plots.TestQuiver.test_non_latlon_1d_coords.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/a7ac24947259f3493697632df45926b6e126c4f392593b4937266f26ccf032d8.png" + ], + "iris.tests.integration.plot.test_vector_plots.TestQuiver.test_non_latlon_2d_coords.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/afac26367251d3493617632df45c26a6e126c6f392593b4937266f26ccf232d0.png" + ], "iris.tests.test_analysis.TestProject.test_cartopy_projection.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/9e1952c9c165b4fc668a9d47c1461d7a60fb2e853eb426bd62fd229c9f04c16d.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_contourf.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a346c9685cb899685c9c39695c79396ec634969ce2c74697a3864697b3c8c.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_pcolor.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a69c896a592e59bc99e3384929636c32d98cde6d964ce7e666332386465b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a69c896a592e59bc99e3384929636c32d98cde6d964ce7e666332386465b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a347c96858b8d9685c9c39696c393966c634969ce3c64697a3864697b3c9c.png" - ], + ], "iris.tests.test_mapping.TestBasic.test_unmappable.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5684eb54a947ad09eb731c521978dc2fb1cc0e4966ce26e2c6b2d3a6e691a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5684eb54a947ad09eb731c521978dc2fb1cc0e4966ce26e2c6b2d3a6e691a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853e48957ac1df957ac8be852bc1b1944e7a9878e03f4c6a253e6c7a912dc2.png" - ], + ], "iris.tests.test_mapping.TestBoundedCube.test_grid.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81917e857e6e81857e7a857a81917a7a81857e857e7e81857e7a817a81852e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81817e7a81857e857e7a81857e7a817a81857e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81917e857e6e81857e7a857a81917a7a81857e857e7e81857e7a817a81852e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81817e7a81857e857e7a81857e7a817a81857e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a817a81857a7a81857e857e7a85857e7a817a81857a.png" - ], + ], "iris.tests.test_mapping.TestBoundedCube.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81e535857e92ca8ec23d21b13ce15e7a811ea5c47e1a5ac17b652d3b05e4f2.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81e535857e92ca8ec23d21b13ce15e7a811ea5c47e1a5ac17b652d3b05e4f2.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81c17a857e1ea5857e634a7a81cd257e8584da857e3b29817e68f47a81c791.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_grid.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2b1c17f1d0ac4f7c8d739a637202749699b6bb3ce3666e4b048944d9d89.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2f1c17f1d0ac457c8d619a637213749699b6bb34e3666e4b04e944d9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2b1c17f1d0ac4f7c8d739a637202749699b6bb3ce3666e4b048944d9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf80e2f1c17f1d0ac457c8d619a637213749699b6bb34e3666e4b04e944d9d89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea05392995bac6d691ce3f21666569d86a96c6360ee195cb91e8ce54953b313b.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_outline.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e80857e7a817a817a817a81817f7a81857e857e857e857e7a81.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e21857e7a817a817a857a81857a7a81857a857e857a857e7a84.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e80857e7a817a817a817a81817f7a81857e857e857e857e7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e21857e7a817a817a857a81857a7a81857a857e857a857e7a84.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa1585e885e87a1785fa7a177a177e807a1585e85fa0857a85e86817857f6a16.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf81e6b1c17e1d4884bfc8df39a43720374969db69b34e26c4e4b0ca904f9d89.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf81e6b1c17e1d4884bfc8df39a43720374969db69b34e26c4e4b0ca904f9d89.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea57396995a8c6d691ea3f25664569d86b16c63686ed958991ea4a549531393b.png" - ], + ], "iris.tests.test_mapping.TestLimitedAreaCube.test_scatter.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d894346b24f3477acf68ad39329ed8c696e136c1ab9a71.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d895346b2473477acf68ad39329ed8c69ee126c1ab9a71.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d894346b24f3477acf68ad39329ed8c696e136c1ab9a71.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea053d2e916ac2d9c4d895346b2473477acf68ad39329ed8c69ee126c1ab9a71.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea05bd2e916ac2d984983d346b2473477acf69ad3d3296d8c696e126c1ab1e71.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_keywords.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a63a71b3e016061c1fe9b8c3e01a473847e5b94d1fb9ac3.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_keywords.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957fe3cea68c6ce0d9f29b9b6a816463953e61cc917f1ae36ac09d38.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957fe3cea68c6ce0d9f29b9b6a816463953e61cc917f1ae36ac09d38.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa819097857e6560957e7bcc7a819c316e81951e857e62c281fe79a17aa19637.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee819cb7913b63c8846e64737bb1999c6ec52633953a69c8916f6c636e92911c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee819cb7913b63c8846e64737bb1999c6ec52633953a69c8916f6c636e92911c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa8190be857e6739917a7bc47a8594337bb1911c857e6ec3913279007e819637.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a31871f7e856470c1fa9b8c7b81647384665b9ed1b998c1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be21a71bc1de58e43a63a71b3e016061c1fe9b8c3e01a473847e5b94d1fb9ac3.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_params.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957ae3cea68c6ce0c9f39b9b6a816473953e63cc917f1ae36ac09d38.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea811831957ae3cea68c6ce0c9f39b9b6a816473953e63cc917f1ae36ac09d38.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81909f857e6520957e5bcc7a8194716e31851e857e6ac281fe3f817a81963f.png" - ], + ], "iris.tests.test_mapping.TestLowLevel.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eae0943295154bcc844e6c314fb093ce7bc7c4b3a4307bc4916f3f316ed2b4ce.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eae0943295154bcc844e6c314fb093ce7bc7c4b3a4307bc4916f3f316ed2b4ce.png", "https://scitools.github.io/test-iris-imagehash/images/v4/faa0e55c855fdce7857a1ab16a85a50c3ea1e55e856658a5c11837096e8fe17a.png" - ], + ], "iris.tests.test_mapping.TestMappingSubRegion.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bd913e01d07ee07e926e87876f8196c1e0d36967393c1f181e2c3cb8b0f960d7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bd913e01d07ee07e926e87876f8196c1e0d36967393c1f181e2c3cb8b0f960d7.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b9913d90c66eca6ec66ec2f3689195b6cf5b2f00392cb3496695621d34db6c92.png" - ], + ], "iris.tests.test_mapping.TestUnmappable.test_simple.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe818d6ac17e5a958d7ab12b9d677615986e666dc4f20dea7281d98833889b22.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe818d6ac17e5a958d7ab12b9d677615986e666dc4f20dea7281d98833889b22.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81b54a817eca35817ec701857e3e64943e7bb41b846f996e817e006ee1b19b.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2ff7c00a56de9023b52e4143da5d16d7ecad1b76f2094c963929c6471c8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2ff7c00a56de9023b52e4143da5d16d7ecad1b76f2094c963929c6471c8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bfec2d77e01a5a5ed013b4ac4521c94817d4e6d91ff63349c6d61991e3278cc.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff95776a01e1f67801cc36f4075b81c5437668c1167c88d2676d39d6867b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff95776a01e1f67801cc36f4075b81c5437668c1167c88d2676d39d6867b68.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8fff941e7e01e1c2f801c878a41e5b0d85cf36e1837e2d9992c62f21769e6a4d.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbe0c21ccd179dc3b05e4b689b0771b48698961b7962da446e8ca5bb36716c6e.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff897066b41f076f81dce1fb007da79c50633e9c40626b8d1066df9d6067969.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ff897066b41f076f81dce1fb007da79c50633e9c40626b8d1066df9d6067969.png", "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/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7e0098757103a71ce4506dc3d11e7b20d2477ec094857db895217f6a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7e0098757103a71ce4506dc3d11e7b20d2477ec094857db895217f6a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8fffc1dc7e019c70f001b70ee4386de1814e7938837b6a7f84d07c9f15b02f21.png" - ], + ], "iris.tests.test_plot.Test1dPlotMultiArgs.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c2d73a09b4a76c099d26f14b0e5ad0d643b0d42763e9d51378f895867c39.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe8c0173a19b4066d599946f35f0ed5d0b74729d40369d8953678e897877879.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c2d73a09b4a76c099d26f14b0e5ad0d643b0d42763e9d51378f895867c39.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe8c0173a19b4066d599946f35f0ed5d0b74729d40369d8953678e897877879.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c0567a01b096e4019daff10b464bd4da6391943678e5879f7e3103e67f1c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e04256f68023352f6d61da5c109dec8d19bcf089cc9d99a9c85d999.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e06256f68023352f6d61da5c009decad19bcf089cc9d99a9c85d989.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e04256f68023352f6d61da5c109dec8d19bcf089cc9d99a9c85d999.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e06256f68023352f6d61da5c009decad19bcf089cc9d99a9c85d989.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83fec2777e002427e801bb4ae65a1c94813dcec999db4bbc9ccd79991f3238cc.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9dd77f00e1d73000cc1df707db8184427ef8d1367c88d2667d39d0866b68.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9d977f41e1d73000cc1df707d98184427ef8d1367c88d2667d39d0866b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9dd77f00e1d73000cc1df707db8184427ef8d1367c88d2667d39d0866b68.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe9d977f41e1d73000cc1df707d98184427ef8d1367c88d2667d39d0866b68.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ff9d9f7e01e1c2b001c8f8f63e1b1d81cf36e1837e259982ce2f215c9a626c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fbe0623dc9879d91b41e4b449b6579e78798a49b7872d2644b8c919b39306e6c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bbe0c21ccd179dc3b05e4b689b0771b48698961b7962da446e8ca5bb36716c6e.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ffb5867f0060d4301f6d9fb007d899c50699e9c8668e78d8678d69de069969.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ffb5867f0060d4301f6d9fb007d899c50699e9c8668e78d8678d69de069969.png", "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/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f90987720029f1ef458cd43811cdb60d647de609485ddb899215f62.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f94987720009f1ef458cd43810cdb60d647de609485ddb89921df62.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f90987720029f1ef458cd43811cdb60d647de609485ddb899215f62.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fec1ff7f94987720009f1ef458cd43810cdb60d647de609485ddb89921df62.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1de7e009c7030019786f438cde3810fd97c93734a778ce07c9f99b02731.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotPlotMultiArgs.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc8967e0098a6241f9d26e34b8e42f4d20bb4942759e9941f78f8d7867a39.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83f9c8967e009da6245f9946e25f9ed6f0940f29f40749d8853678e8d7857879.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc8967e0098a6241f9d26e34b8e42f4d20bb4942759e9941f78f8d7867a39.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83f9c8967e009da6245f9946e25f9ed6f0940f29f40749d8853678e8d7857879.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ffc9d67e00909624079daef160cf4bd45a439184367ae5979f7e3119e6261c.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d94ac5a1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7c248399cc7917662d84ac5a1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1b47c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d84acda0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d94ac5a1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1947c99184e62669ca7f65bc96ab81d97b7c248399cc7917662d84ac5a1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fac1b47c99184e62669ca7f65bc96ab81d97b7e248199cc7913662d84acda0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b2ecc1a8b9994a16e666b5e3ce151969a5fb4ed49909653990d46b9bfc097684.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bea06899c14eb16e9895ce46c74a396a74ed64b13390b3c61b439f1b4d2ccde6.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0211b9e066e074d83926ed8f8cd3792dad1964db0d80e9b09.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0311b9e066e074d839266d8e8cd379adad1964db0d80e9b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0211b9e066e074d83926ed8f8cd3792dad1964db0d80e9b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7f1f07f3e0e0f0311b9e066e074d839266d8e8cd379adad1964db0d80e9b09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be852fc1e078c83eb30e3607672149c098d95c5b9e4636f2c1fc299d999f7e03.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621deda3f69392cccd246db39018989ec4836de9ed249292.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a66b94c621deda3f69392cccd646db3901898dec4836de9cd249292.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621ceda3f6d392cccd246db3901898dec4836de9cd249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621deda3f69392cccd246db39018989ec4836de9ed249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a66b94c621deda3f69392cccd646db3901898dec4836de9cd249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a5f896d99a67b94c621ceda3f6d392cccd246db3901898dec4836de9cd249292.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edfa96cb9a256b4f65466d9892d9c865693a1a9c94b39ed8484b35ad9a864c32.png" - ], + ], "iris.tests.test_plot.Test1dQuickplotScatter.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a4fb19b3db04c6cd6307b98678601c738c39d71cf3866186d8616e69bd191b9e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a4fb19b3db04c6cd6307b98678601c738c39d71cf3866186d8616e69bd191b9e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e8b33c129649c78de3a773e578650c728e92279be12de1edc4f246b2939c3b01.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bbfac39d9899384a6f6694a7b613cb489c95b7b7c24a399cc5913262d84acda0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bbfac39d9899384a6f6694a7b613cb489c95b7b7c24a399cc5913262d84acda0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b2ecc12999994e16e666b5e3ce171969a5fb4ed49909e53990c44b9b7c09f684.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_coord_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bea07c99c15eb16e9891ce50c742394a3ced6cb13390f1cc73c29f1b2d0ecd66.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bea06899c14eb16e9895ce46c74a396a74ed64b13390b3c61b439f1b4d2ccde6.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_coord_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af7e1f0ff1e1e0f0d918960e6c076d8bd266d868c537365a90966db0de0e1b09.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae7e1f0ff1e1e0f0d918960e6c076d83d266d868c537365ad0966db0de4e1b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af7e1f0ff1e1e0f0d918960e6c076d8bd266d868c537365a90966db0de0e1b09.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae7e1f0ff1e1e0f0d918960e6c076d83d266d868c537365ad0966db0de4e1b09.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be812fc1c078c03e930e3627672369c1d8d85c5b96463662e1fc699d9b9f7e03.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_cube_coord.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/edf896d79a67b94c651ced23d29392cccd646d33901912fcc4836d69ed249292.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/edf896d79a67b94c651ced23d29392cccd646d33901912fcc4836d69ed249292.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edda96cb9a256b4765c26d9892dbc665693a1a9494b796c86c4b37ad92864c32.png" - ], + ], "iris.tests.test_plot.Test1dScatter.test_cube_cube.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/acf939339a16c64de306318638673c738c19d71cf3866186d8636e69bd191b9e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/acf939339a16c64de306318638673c738c19d71cf3866186d8636e69bd191b9e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/edb23c529649c78de38773e538650c729e92279be12de1edc4f246b2139c3b01.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_1d_positive_down.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87fef8117980c7c160078f1ffc049e7e90159a7a95419a7e910dcf1ece19ce3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87fef8117980c7c160078f1ffc049e7e90159a7a95419a7e910dcf1ece19ce3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7fe781b708487c360079e3bb4789869816bdb64c76b4a3cce7b4e749a6130c5.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_1d_positive_up.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47800bd9f660779d0863f49c9947f4e1e9141de38d700da28ce1d9a2b.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47a00bc9f660779d8863f49c9907f4e1e9141de38d708da28ce1d9a0b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47800bd9f660779d0863f49c9947f4e1e9141de38d700da28ce1d9a2b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ff85d47a00bc9f660779d8863f49c9907f4e1e9141de38d708da28ce1d9a0b.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff958b7a00b09c661761c9907fcb0d9163ce7895289a618f381bffccf97200.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_2d_positive_down.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba684e194fb901b3a0587641ad03b1ae7674e64c15a5b99c767c47e3a98.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba684e194fb901b3a0587641ad03b1ae7674e64c15a5b99c767c47e3a98.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fb946ba484e194dbc01f3665c0e4399a3f0fc2653f90c99e3f613e64c81e3f81.png" - ], + ], "iris.tests.test_plot.TestAttributePositive.test_2d_positive_up.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee176c7f93e093a0c50f9383815e6e156859e17e6e15e17a9be08e2d851a9b83.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee176c7f93e093a0c50f9383815e6e156859e17e6e15e17a9be08e2d851a9b83.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebc06be1941e941ec07f941f907f6fa0950fc07e6f80c07f6b806be1c07f3f80.png" - ], + ], "iris.tests.test_plot.TestContour.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/cff8a55f7a15b55a7817854ad007a5e8c04f3ce8c04f3e2ac4706ab295b37a96.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/cff8a55f7a15b55a7817854ad007a5e8c04f3ce8c04f3e2ac4706ab295b37a96.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eaece0173d17951fbd03974a914964e8c04a72e8c1531ee1cc746bb293973ecd.png" - ], + ], "iris.tests.test_plot.TestContour.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfc815e78018597fc019b65b425d121955e7eda854b7d6a80db7eb481b72b61.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfc815e78018597fc019b65b425d121955e7eda854b7d6a80db7eb481b72b61.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa8553fc01b15ab4044a269546caa5956b7e9bc0b97f2cc2d62d360b363b49.png" - ], + ], "iris.tests.test_plot.TestContour.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780185fff800955ad4027e00d517d400855f7e0085ff7e8085ff6aed.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780085fff800855fd4027e00d517d400855f7e0085ff7e8085ff6aed.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780185fff800955ad4027e00d517d400855f7e0085ff7e8085ff6aed.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe81ff780085fff800855fd4027e00d517d400855f7e0085ff7e8085ff6aed.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe817ffc00855ef0007e81d4027e80815fd56a03ff7a8085ff3aa883ff6aa5.png" - ], + ], "iris.tests.test_plot.TestContour.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa56c3cc34e891b1c9a91c36c5a170e3c71b3e5993a784e492c49b4ecec76393.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa56c3cc34e891b1c9a91c36c5a170e3c71b3e5993a784e492c49b4ecec76393.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e36cb95b199999765cd3694b06478c7396329958434c2cecb6c6d69ce1b92.png" - ], + ], "iris.tests.test_plot.TestContour.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe857f7a01a56afa05854ad015bd00d015d50a90577e80857f7ea0857f7abf.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bfe857f7a01a56afa05854ad015bd00d015d50a90577e80857f7ea0857f7abf.png", "https://scitools.github.io/test-iris-imagehash/images/v4/affe815ffc008554f8007e01d0027e808557d5ea815f7ea0817f2fea817d2aff.png" - ], + ], "iris.tests.test_plot.TestContour.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bff81ff7a0195fcf8019578d4027e00d550d402857c7e0185fe7a8385fe6aaf.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bff81ff7a0195fcf8019578d4027e00d550d402857c7e0185fe7a8385fe6aaf.png", "https://scitools.github.io/test-iris-imagehash/images/v4/abff857ff8018578f8017a80d4027e00855ec42a81fe7a8185fe6a8f85fe6ab7.png" - ], + ], "iris.tests.test_plot.TestContourf.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa562ed68569d52857abd12953a8f12951f64e0d30f3ac96a4d6a696ee06a32.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa562ed68569d52857abd12953a8f12951f64e0d30f3ac96a4d6a696ee06a32.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea857a81957ac57e957a857a957a958ac5723b0d6ac56b833e856e606a923e90.png" - ], + ], "iris.tests.test_plot.TestContourf.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5e03f957a4f80954a9e41e16e9c60970fb5b24ada634e6e93692d4ba562d8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eaa5e03f957a4f80954a9e41e16e9c60970fb5b24ada634e6e93692d4ba562d8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea851f00957ac0f7957ac07f957a628d815e7b126ab13e816a953ae46a859ed3.png" - ], + ], "iris.tests.test_plot.TestContourf.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e954a7a81857e957e857efc00857e7e007a85c02a7e859f287a85c1fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e954a7a81857e957e857efc00857e7e007a85c02a7e859f287a85c1fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7a81857e7a81857e7a81857e7a806a85857a7a85857e7a85817e.png" - ], + ], "iris.tests.test_plot.TestContourf.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a6938b6b5969193901a4fc1e594a7c69999cbce33639879526e72330e65e4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a6938b6b5969193901a4fc1e594a7c69999cbce33639879526e72330e65e4.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a3c7e968597b19685c9c696a7c79491c16e59691a387f6978396e68683184.png" - ], + ], "iris.tests.test_plot.TestContourf.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa85857ec45a7a81857e854a857ee56a917ec56a3a85c56a3a85c4ea7a8112fe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa85857ec45a7a81857e854a857ee56a917ec56a3a85c56a3a85c4ea7a8112fe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817e857e7a81857a7a81957a6e81917a6caa3a85c57a3a8585fa6a8591fe.png" - ], + ], "iris.tests.test_plot.TestContourf.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817ec40a7a81857e957e857ef40a857ef60b7a81c40a7b81e60f7a814aff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817ec40a7a81857e957e857ef40a857ef60b7a81c40a7b81e60f7a814aff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81817e857e7a81857e7a81817a7e81817a668f7a91857e7a81857e7a85817e.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee856aa5957a955ac0bf954bc17e3b819548c07f3e81c07e2ec46ea4c07f3e84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be853f80854ac17ec0bdc2f5c17a0d09cc1fc07f5ab5e1fe3f409d7a38743e00.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be853f80854ac17ec0bdc2f5c17a0d09cc1fc07f5ab5e1fe3f409d7a38743e00.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf813e85c07ec57ec17e9073c07e3f81856ec17a3f80c0fe3e813f84c2733e80.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_bounds.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/eab5313f954a7b9260f39789c5ec4cd084d0c4e45aa1c5fe3a04797bb13b3b06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ee856aa5957a955ac0bf954bc17e3b819548c07f3e81c07e2ec46ea4c07f3e84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_orography.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a95c17a6eece4b4e1333b01c07e1bb13909914b9ec1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a91c17a6ee464f4e1333b01c17e1bb1390d914b9ec1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a95c17a6eece4b4e1333b01c07e1bb13909914b9ec1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa17291f95e895e8645e7a91c17a6ee464f4e1333b01c17e1bb1390d914b9ec1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a91957a857ac4fe268cc07f6e846e05d9373b81d17b1b6a1b41c4fa2cc4.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_orography.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc4e0c6b4c31e9ee1847939a1c116c15e7b94e57e1ea9391de16e1ac3.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc6e1c6b4c31e9ee1846939a1c116c15e7b14e17e1ea9393de16e1ac3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc4e0c6b4c31e9ee1847939a1c116c15e7b94e57e1ea9391de16e1ac3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb07314fc6e1c6b4c31e9ee1846939a1c116c15e7b14e17e1ea9393de16e1ac3.png", "https://scitools.github.io/test-iris-imagehash/images/v4/af0b690f96f0d2d4c25e94a194ad3da19a52c25e3f02c07f3fa52d03c16a3fcb.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe857b91917a847ec0bd3f01c47e6ca43b11915a3ea4db3b1b4a84c4c03f3fc1.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be813fc0c15ac13dc1bfc27dc17e1d93c51fc43f1ea1c17a3ec138e4b1721a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be813fc0c15ac13dc1bfc27dc17e1d93c51fc43f1ea1c17a3ec138e4b1721a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be813a81c17ec57ec17e952ac07f3f808556c17e3f80c07f3e813f80c27e3f81.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea9561ef956a7b92609b922dc16e6ec6845ac47e5aa5c57e5ec04861957b1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea9561ef956a7b92609b922dc16e6ec6845ac47e5aa5c57e5ec04861957b1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe856a85957a955ac03f956ac17f3f809552c07f3e81c07e3e806e85c07e3f84.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea953bfb956ac4f4649f1a05c56e6ca45a53945e6ea5c13f1b498542c13f1b41.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe857b91917a847ec0bd3f01c47e6ca43b11915a3ea4db3b1b4a84c4c03f3fc1.png" - ], + ], "iris.tests.test_plot.TestHybridHeight.test_points.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/baf5347ecf0ac3f1c1f68f83850b1f83cc11c0fc7ad0c17a1be138e4b07e1a0d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/baf5347ecf0ac3f1c1f68f83850b1f83cc11c0fc7ad0c17a1be138e4b07e1a0d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b878387e978ec2f0c0f09f83878f3f81c070c0fe78d0c1763fa13856d03e3f0f.png" - ], + ], "iris.tests.test_plot.TestMissingCS.test_missing_cs.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fac16ee0953b911bc15e9648e56ec4e691be7bcc7a8184733ea16a90c17e930d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fac16ee0953b911bc15e9648e56ec4e691be7bcc7a8184733ea16a90c17e930d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816ac1857e853cc17f957ac15f3e849486c8f43e81c13b3f813e91c07e3f46.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_u.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a95c17fb51e953e9485857a1f409552856a1f81c17e5ab94e15c0ff5a85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a95c17fb51e953e9485857a1f409552856a1f81c17e5ab94e15c0ff5a85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95955a954ac17f954ac07e3f48951ec07e3e81c0ff7ea16a81c0bf3e81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_u.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/be0e695ac3f096b5943fd2a185fc1e8590e594ee1e05c17a4f403d0fe1fe4b42.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/be0e695ac3f096b5943fd2a185fc1e8590e594ee1e05c17a4f403d0fe1fe4b42.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea956ab5954a954ac17f954a817e3f40950ac07f3e81c0ff7a856aa1c0ff3f80.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_v.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0773d09956a955a857a1d88845ec57e3f81c07e4ae56b21d0ff5a85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0773d09956a955a857a1d88845ec57e3f81c07e4ae56b21d0ff5a85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a857ac17e954ac17e1fa2950bc07e3e81c07f3e807a85c17f3f81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_no_v.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562d4c7c43d0bb57b97e0857a3f1995d284763a05c17a7b856a2dc0f45a84.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562d4c7c43d0bb57b97e0857a3f1995d284763a05c17a7b856a2dc0f45a84.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a85957a857ac17e954ac17e9d02954ac07e3e81c07f3e857a85c2fd3f80.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_none.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0763d09b54a955a857a3f88845ec57a3e85c07e6a616b25d0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b6c0763d09b54a955a857a3f88845ec57a3e85c07e6a616b25d0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a857ac17e954ac17e3fa29506c07e3e81c07f3e807a84c1ff3f81.png" - ], + ], "iris.tests.test_plot.TestMissingCoord.test_none.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562f6c0773d09b54a955a857a3f81955ac47e3e85c17e7aa16a25c0765aa1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562f6c0773d09b54a955a857a3f81955ac47e3e85c17e7aa16a25c0765aa1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a85957a957ac17e954ac17a1f06954ac07e3e81c07f3e817a85c0ff3f80.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4f63066318695ec5c9695e6c49c6a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4f63066318695ec5c9695e6c49c6a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea817a81957e857e957e953e957e857e857e6aa06a816ac16a017a816a9585fa.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eab478972a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eab478972a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea953f83954ac2bc956ac07e956a3509c0de61796ab57a816a856ad16ab590fb.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7a84857a7a85857e7a813a2f7a817a85857a7a85857a7a85857a.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b793b19a1ec3c191c5c6e596191e4e693269336c36391a6e3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b793b19a1ec3c191c5c6e596191e4e693269336c36391a6e3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a387e968596319697c3c19284a62c93a560c36933393a6c7e793b6c6b31cd.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a1695a97a17d5a97a17906785a7816685a7e86685ad687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a1695a97a17d5a97a17906785a7816685a7e86685ad687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7e01857e7a81857e7a81e8177a816a8585fa7a85857e7a81857e.png" - ], + ], "iris.tests.test_plot.TestPcolor.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f003d527f002f427ea82f42d6a8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527e0d2f52d4ad.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f003d527f002f427ea82f42d6a8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527e0d2f52d4ad.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e80857e7a81857e7a812d557a817a85857e7a81857e7a80857e.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1f94e2c119938e9463678c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1f94e2c119938e9463678c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea858782957a703f957a3878957a7a65957a6bc06ae76f806ad50fd06a859c50.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a5695a96a53c0f711b3c0f7d1b97b46943c3e0cc416b5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a5695a96a53c0f711b3c0f7d1b97b46943c3e0cc416b5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85857a857e7e81957a7a81957a6a85857acac6c1fb6aa67a81956e6a81b506.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a5eb40c3f0ebe0c1c0c3f07c0b3e3e96a13c1e6d5b694a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e381f96a096a5d6a5eb40c3f0ebf0c1e0c3f07c0a3e3e96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a5eb40c3f0ebe0c1c0c3f07c0b3e3e96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e381f96a096a5d6a5eb40c3f0ebf0c1e0c3f07c0a3e3e96a13c1e6d5b694a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817e81857e857a857e7a81857e6a85817b81e63a913e857e81c17e7a81956e.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc585c39986cdc79ec792e3a6960d584939793c3438743873.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc585c39986cdc79ec792e3a6960d584939793c3438743873.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e96ac78796953c4c9685383996c538e69692637263696b49693ac796693ac71b.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e885e0954295e195ea95a085e87a153e7f95e06a1778557a1f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e885e0954295e195ea95a085e87a153e7f95e06a1778557a1f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a81857e857e857e7a81857e6a81c17f95786aa77a807e81c17c7e819558.png" - ], + ], "iris.tests.test_plot.TestPcolorNoBounds.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba173a1795e895e8c5e8f400c1f8c1f895a8c5e87a077a5ec5e83e173e177e02.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba173a1795e895e8c5e8f400c1f8c1f895a8c5e87a077a5ec5e83e173e177e02.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a80857e857e857e7a81817e3e81817e857f6aa07a857e80c17f7e80c15f.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4fe3026318695ec5c9695e6c49c6a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e67c9c7e1391e97a596b03a3696a13c4fe3026318695ec5c9695e6c49c6a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea817a81957e857e957e953e957e857e857e6aa06a816ac16a017a816a9585fa.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eabc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b3878958b38f8c7236a557a542c7868d54b877875978abc789722.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea74c707958b387895ab38f8c7236a557a542c7868d54b05787197eabc789722.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea953f83954ac2fc956ac07e956a3509c0de61796ab57a816a854ad16ab590fb.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e874978b978b6875978b6875978b7854950b78506855787468747ea2687597aa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857a7a84857a7a85857e7a813a2f7a817a85857a7a85857a7a85857a.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b593b19a1ec3c591c5c6e596191e4e693269336c36391a6e3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95e696994b196b593b19a1ec3c591c5c6e596191e4e693269336c36391a6e3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e9693878969696139296c38f9bcc3474692169cb6c7339393c6cc387c78796cc.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a16d5a97a17d5a97a17806785a7816685a7e86685ad687.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e87a197a1695a97a16d5a97a17d5a97a17806785a7816685a7e86685ad687.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e6e05857e7a81857e7a81a0577a816a8585fa7a85857e7a81857e.png" - ], + ], "iris.tests.test_plot.TestPcolormesh.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f002d527f002f427fa82f42d6a8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/af4280bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527f0d2f52d4ad.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af42c0bdd0ad2f52d0bd3f42d0bd7f02d0bd7f002d527f002f427fa82f42d6a8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/af4280bdd0ad2f52d0ad2b52d0bd7f02d0bd7f002d527f002f527f0d2f52d4ad.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e80857e7a81857e7a8129577a817a85857e7a81857e7a80857e.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d84f17a1d94e2c11d938e9463678e.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1d94e2c11993ae9463678c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d84f17a1d94e2c11d938e9463678e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa1594f3858a670c94e37b1cccb13e736a1d8cf17a1d94e2c11993ae9463678c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea858782957a703f957a3878957a7a65957e6bc06ae56f806ad50fd06a859c50.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_ty.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a1695a96a53c1f711b3c0f791b97b46943c3e06c436b5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad5e94a5c3b0c3f096a1695a96a53c1f711b3c0f791b97b46943c3e06c436b5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85857a857e7e81957a7a81957a6a85857acae6c1fb6aa47a81956e6a81b506.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_tz.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a56b40c3f06be2c1c0c3f07c0b3ebe96a13c1e6d5b694a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a95e3c1f96a096a5d6a56b40c3f06be2c1c0c3f07c0b3ebe96a13c1e6d5b694a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa813e81857e857a857e7a81857e6a85817b00e63eb93e857e81c17e7a81956e.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc785c39986cdc78ec792e7a6960d584939793c3438703873.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bc7a1c32d3c366cdc785c39986cdc78ec792e7a6960d584939793c3438703873.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e129c7169ed638ec9ed6387196c761c665396724612dcf0d693896929ed698c9.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e895e0fd4295e095ea95a085e87a153e7e95e06a1778157a17.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea1f781f95e085e895e0fd4295e095ea95a085e87a153e7e95e06a1778157a17.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a85857a857e857e7a81857e7a81817f95506aaf7a807e81c17c7a81957a.png" - ], + ], "iris.tests.test_plot.TestPcolormeshNoBounds.test_zy.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ba176a1795e895e8c5e87c00c1f8c1f894a8c5e87a077adec5e83e173e177a06.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ba176a1795e895e8c5e87c00c1f8c1f894a8c5e87a077adec5e83e173e177a06.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa817a80857a857e857e7a81857e3e81817e2fc56aa07a857e80c17f7e80c17f.png" - ], + ], "iris.tests.test_plot.TestPlot.test_t.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83fe955f6a05e5137305d9c4f443127195187e9cd5467fa3d4917b68fc007a1a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95027e05e7007305d9c4a447127f853f069f814f2fa7d4d12b6cfc007e5a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83fe955f6a05e5137305d9c4f443127195187e9cd5467fa3d4917b68fc007a1a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95027e05e7007305d9c4a447127f853f069f814f2fa7d4d12b6cfc007e5a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe9c1a7e05e718f305d9d2e46312718138049e824e2fa783db2bed76b4fe00.png" - ], + ], "iris.tests.test_plot.TestPlot.test_t_dates.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15cdb6b10178d7d4082e57d7290906f685814277b1dc88724cfd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15c9b6a10178d7d4082c57d7290906f6c58942f7b1dc88724cfd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/abffd4a02a01cc84f10078d7d4082c77d73909ded6ef816273bd9c98725cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15cdb6b10178d7d4082e57d7290906f685814277b1dc88724cfd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd5ae2a15c9b6a10178d7d4082c57d7290906f6c58942f7b1dc88724cfd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/abffd4a02a01cc84f10078d7d4082c77d73909ded6ef816273bd9c98725cdd26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/87fc9d8a7e054d83f5067bc1c1423471927ba73c8d9f864e09a1a7b358c8276f.png" - ], + ], "iris.tests.test_plot.TestPlot.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95297e87c74a6a059158f89c3d6ed0536597c0387836d0f87866d0697097.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe95297e87c74a6a059158f89c3d6ed0536597c0387836d0f87866d0697097.png", "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/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" - ], + ], "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/8ffac1547a0792546c179db7f1254f6d945b7392841678e895017e3e91c17a0f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8ff8c1fa7a05b4ea6c059d2ff1494e4b90f26304846d78d1872a6cfc938b2e3e.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test_axes.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCitation.test_figure.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/abf895067a1d9506f811783585437abd85426ab995067af9f00687f96afe87c8.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_non_cube_coordinate.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e81857e7a81857e7a81857e7a818576c02a7e95856a7e81c17a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e7e81857e7a81857e7a81857e7a818576c02a7e95856a7e81c17a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa81857e857e3e85857e7a81857e7a81857e7a817e81780b7a81c56a7a81857e.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe8142f5c17ebd2cc16eb548954a9542916a347a915e60bd4afd68793f916296.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe8142f5c17ebd2cc16eb548954a9542916a347a915e60bd4afd68793f916296.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853f10956ac5e1957a854e957a203e955e6aa76ae17aa16a856aaf6ab19e12.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6b91d968c161b972d76aa462b5.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6a91d96ac163b972d36aa462b5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6b91d968c161b972d76aa462b5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8542b7b503b548857abd08857abd09945eed6a91d96ac163b972d36aa462b5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853a85857a857a957a857a957ad05a857b3e946a606b917a816f647a853af4.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8bf88f457a03b5307e16b561f007b53ed067217ac1786afec0f570bf8178681a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8bf98f057a03b5307e16b561f007b53ad067217ac1786afec0f570bf8178685a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bf88f457a03b5307e16b561f007b53ed067217ac1786afec0f570bf8178681a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8bf98f057a03b5307e16b561f007b53ad067217ac1786afec0f570bf8178685a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafdcec9bc219530b696a56694c2852a95656b7b81986acdc0e516adad186eda.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe8f367e05952afe05a50b980ded4bd05d69c2c1fb71c1c06272f4d0a06af4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffe8f367e05952afe05a50b980ded4bd05d69c2c1fb71c1c06272f4d0a06af4.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aff24ab7bd05952fbd0f950f914fcd48c47860f3e1b9329094266e345a850f6c.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aa953d0f85fab50fd0f2956a7a1785fafa176877d00f68f1d02c60f2f008d0f0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e94b5019e97950d685395bee05361fad05560fad01570fef001dabe.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e95b5419e97950d6853953ee053617ad05560fad01570fef001dabe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aa953d0f85fab50fd0f2956a7a1785fafa176877d00f68f1d02c60f2f008d0f0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e94b5019e97950d685395bee05361fad05560fad01570fef001dabe.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebeaa5419e95b5419e97950d6853953ee053617ad05560fad01570fef001dabe.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaa56f96a1856cd681a56ee8162d52e8467e12c50c7e8095ad7e0095ad03ff.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_tx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaaf439e87b5019687b5019687b56ac05561fae07103fe6079687a607178f8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa2d4b968795059e87970f6854697ae055697ac08561fad041d7aef001d6ae.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebfaaf439e87b5019687b5019687b56ac05561fae07103fe6079687a607178f8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ebfa2d4b968795059e87970f6854697ae055697ac08561fad041d7aef001d6ae.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eb7a3e0c978187a4950190bc6856687a607e687bc0fcc1e394acfc0197fc2bfb.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aeb8b5095a87cd60386592d9ec97ad6dd23ca4f6d0797827f0096216c1f878e6.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aeb8b5095a87cd60386592d9ec97ad6dd23ca4f6d0797827f0096216c1f878e6.png", "https://scitools.github.io/test-iris-imagehash/images/v4/affa950ddb13c03634359ad8a4c80f26911f26f3c06e0ff3f4007b4285fd6e72.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8fea97194f07c9c830d79169ce16269f91097af6c47861f6d0796076d0797a16.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fee970b4f07c9c930d79129ce16269f91097af6c4f861f4d0786076d0797a16.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/afea97094f07c9c870d79129ce16269f91096af6c4f861f6c07960f6d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fea97194f07c9c830d79169ce16269f91097af6c47861f6d0796076d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fee970b4f07c9c930d79129ce16269f91097af6c4f861f4d0786076d0797a16.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/afea97094f07c9c870d79129ce16269f91096af6c4f861f6c07960f6d0797a16.png", "https://scitools.github.io/test-iris-imagehash/images/v4/afee9632de05c9d9f180d168c454a53e931b3e84956a3b8c85d94ce703ff7284.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea85603f956a9741951e9d83c1fa8d2fd0a55af0d25f345ae5f062c72d68612d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea85603f956a9741951e9d83c1fa8d2fd0a55af0d25f345ae5f062c72d68612d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea853f00957ac07c957ac0be951a69f3c47c7a5f3a6127816b953e646b813761.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85a69cc96ad92e193c9963385929e1cc3819acde6d965ce6e666b30386e65b1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e97a346c9685cb899685c9c39695c79396ec634969ce2c74697a3864697b3c8c.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65767039740bc069d9ad00b8dadd03f52f181dd347a847a62ff81e8626c.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65777039740bc069d9ad00b8dadd03d52f181dd707a847a62ff81e8626c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65767039740bc069d9ad00b8dadd03f52f181dd347a847a62ff81e8626c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8ffcc65777039740bc069d9ad00b8dadd03d52f181dd707a847a62ff81e8626c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ebffca44f502b36498309c9b940999add1bb62bba784374acc5a6a246acc6b65.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5649c434ac92e5d9c9361b95b39c38c3835a5ec6d966ced34c633099ace5a5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5649c434ac92e5d9c9361b95b39c38c3835a5ec6d966ced34c633099ace5a5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a6b6c96a597a591c9949b94b61b69c7926b5bccce66646b3869b831a52c26.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2dd2d09295c3c0c7d13c1bc6d23d2c696de0e53c3ac393daf6d205c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c3c0c7d13c1bc6d23d2c696ce0e53c3ac393dbf6d205c2c0.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2f92d09295c3d0c7d13c1bc6d23d2c696cf0e53c3ac2b3d9f6d201c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26c1e360f684a3c2c6913dca497b9d38097a903ff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2dd2d09295c3c0c7d13c1bc6d23d2c696de0e53c3ac393daf6d205c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c3c0c7d13c1bc6d23d2c696ce0e53c3ac393dbf6d205c2c0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2f92d09295c3d0c7d13c1bc6d23d2c696cf0e53c3ac2b3d9f6d201c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26c1e360f684a3c2c6913dca497b9d38097a903ff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e3f96a1c3e197a169f1785e3b0e68523e1c398bc58687b1d86096e1039f.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_yx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9686d8c9696924797879e3b86929e58696d69cc6869659379626133398d9ccd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e961658f961e92469e1e1c7966f36cd86165618c70e166b39b9698719e1e9ec8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9686d8c9696924797879e3b86929e58696d69cc6869659379626133398d9ccd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e961658f961e92469e1e1c7966f36cd86165618c70e166b39b9698719e1e9ec8.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e1a530e29e5ecf199a5acd8f64f1326161a530e265999cd29e52cf199a5e6669.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf803f00c05fc4bfc07ec15dc05fd8bbc07cc96c333a32113bd02dd27ced3ec0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf803f00c05fc4bfc07ec15dc05fd8bbc07cc96c333a32113bd02dd27ced3ec0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be813ea0c17ec55ac17ed23dc07e295ac57e3b653f803f813e816e853e81b542.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95956a95626993941a6a2d956e6ed6845a6e65c4bec7b64a9594686ea19578.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95956a95626993941a6a2d956e6ed6845a6e65c4bec7b64a9594686ea19578.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea85856e857e4893957a7aa1956a7b81954b3b817a856fd46a85846e6e85857e.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a327c847860fdc57a69beb0be68bd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a32fd847860fdc57269beb0be689d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a327c847860fdc57a69beb0be68bd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8fe82f047c018c83bc01bc5af01fd1bcd15a32fd847860fdc57269beb0be689d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/8bedcf25bc03a4929c103a5bf03fdbbc81cb364d86e46da70f86899b3a0f6cc0.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/cee8953a7a15856978579696d03d672cc49a6e5a842d3d2cc0b66bd1c2ea39f1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/cee8953a7a15856978579696d03d672cc49a6e5a842d3d2cc0b66bd1c2ea39f1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aee1f93a63168569b852d697913d632485ca2e43952d3bbcc2b66bd1426b3c71.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ee953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a3dd038c0fef000d0fa.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a7dd068c0fef000d0fa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ee953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a3dd038c0fef000d0fa.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f0591ea3f07914a95fa7e07d1fa68156a15d07c6a7dd068c0fef000d0fa.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bec11ab5c1be857ac13e7ae53c422d423e017a85b542fc00c1fefe0091fe03ff.png" - ], + ], "iris.tests.test_plot.TestPlotCoordinatesGiven.test_zx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a973d96a56953968769439685a54ae05117eae0511fba60513bba69717aba.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96a56953968769439685a54ae85197eae0511fba60513bba69717aba.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a973d96a56953968769439685a54ae05117eae0511fba60513bba69717aba.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96a56953968769439685a54ae85197eae0511fba60513bba69717aba.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a96ac97a16c5897a1791e95a53b0b913c6953687c4ec3685cc6c36e7c87c3.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coord_names.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coord_names.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b8a53b59c71ac5a6b8791c1867876b63d9e0e65c96199d871cc23339633664ce.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coords.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_coords.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9a53a59961ec5a62c691a587b9662e1c0e1e53e9e0e9b873ec15a7161bc642f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b8a53b59c71ac5a6b8791c1867876b63d9e0e65c96199d871cc23339633664ce.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_default.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/f9789b388786678686966c9093879ce592c79bc94d19929b6939cf66316c672c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/b87830b0c786cf269ec766c99399cce998d3b3166f2530d3658c692d30ec6735.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_yx_order.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa81948e857e4971907ea72e95fa66b2952e4ead6d429b527ac7a5286e981836.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa81948e857e4971907ea72e95fa66b2952e4ead6d429b527ac7a5286e981836.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa85978e837e68f094d3673089626ad792073985659a9b1a7a15b52869f19f56.png" - ], + ], "iris.tests.test_plot.TestPlotDimAndAuxCoordsKwarg.test_yx_order.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea159694856a6b5096afa53a36941da1e4f5c369cd1ae6d69b6a1c80625af2f6.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea159694856a6b5096afa53a36941da1e4f5c369cd1ae6d69b6a1c80625af2f6.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea95969c874a63d39ca3ad2a231cdbc9c4973631cd6336c633182cbc61c3d3f2.png" - ], + ], "iris.tests.test_plot.TestPlotOtherCoordSystems.test_plot_tmerc.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e63399cd99cd64b29999335965369b262649c98c9b3966c6998d3319ccd69333.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e63399cd99cd64b29999335965369b262649c98c9b3966c6998d3319ccd69333.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e665326d999ecc9b3319b3246666cce69b496cccccc9669923193336666699a6.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_t.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5d67fd4e5962211d9c6a443da77d5389c8ed346d923d011d968dc00da48.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82ffb5d67fdde5962211d9c6a441da77d5389c8cd346d927d011d968dc00da48.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fabd867fd5e5822201d9c6a4539a77953d8cbf834f99e7d051996cdc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5d67fd4e5962211d9c6a443da77d5389c8ed346d923d011d968dc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82ffb5d67fdde5962211d9c6a441da77d5389c8cd346d927d011d968dc00da48.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fabd867fd5e5822201d9c6a4539a77953d8cbf834f99e7d051996cdc00da48.png", "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb59a7f00e59a2205d9d6e4619a74d9388c8e884e8da799d30b6dddb47e00.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_t_dates.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd5ae7f51efb6200378d7d4082c17d7280906d6e58962db31d800da6cdd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4ae7f55efbe200178d7d4082c17d7280906d6e58962df319800da6cdd26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4827f51ef94200078d7c4082c57d739095ed6ed8962db759808da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd5ae7f51efb6200378d7d4082c17d7280906d6e58962db31d800da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4ae7f55efbe200178d7d4082c17d7280906d6e58962df319800da6cdd26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffd4827f51ef94200078d7c4082c57d739095ed6ed8962db759808da6cdd26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82fd958a7e006f9ba0077bc5c9462c759873dd3c8d8f826699a187b358c82f67.png" - ], + ], "iris.tests.test_plot.TestQuickplotPlot.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5097e84c54a621799d8601d9966d213cd67c039d876d078d866d869d8f7.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/83ffbd097e84c54a621799d8601d9966d253cc27c039d876d078d866d869d8f7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffb5097e84c54a621799d8601d9966d213cd67c039d876d078d866d869d8f7.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/83ffbd097e84c54a621799d8601d9966d253cc27c039d876d078d866d869d8f7.png", "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/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" - ], + ], "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/83ffc1dc7e00b0dc66179d95f127cfc9d44959ba846658e891075a3e99415a2f.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3ffc1d87e00b49964179d28f16bce4b98724b268c6d58e1972e4874998b2e7e.png" - ], + ], "iris.tests.test_plot.TestSimple.test_bounds.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562fcc7c0b50bb53b9f8085727a157a95c0f67a85e07e0be08069e07d9fa0.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa9562fcc7c0b50bb53b9f8085727a157a95c0f67a85e07e0be08069e07d9fa0.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85954a957ac17e954ac17a9c3e956ac07e3e81c07f3e857aa5c2753f80.png" - ], + ], "iris.tests.test_plot.TestSimple.test_points.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85957a957ac17e954ac17e1ca2954ac07e3e81c07f3e807a85c1ff3f81.png" - ], + ], "iris.tests.test_plot.TestSymbols.test_cloud_cover.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e95a330c96a5ccf2695a330c96a5ccf2695a330c96b5ccf3694a330c96b5ccf3.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e95a330c96a5ccf2695a330c96a5ccf2695a330c96b5ccf3694a330c96b5ccf3.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eb52916494ad6e1b6b5291e494ad6e1b6b5291e494ad6e1b6b5291e494ad6e1b.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_alignment.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad2f0c1f66ac1c55a4af4e550a52b3e05905e1e419e6f937e3b21.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad3f0c1f66a81e55a4af4e550a52b3e05905e1e419e6f937e1b21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad2f0c1f66ac1c55a4af4e550a52b3e05905e1e419e6f937e3b21.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa95350f952ad3f0c1f66a81e55a4af4e550a52b3e05905e1e419e6f937e1b21.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be8137f4954ac03fc0ff3e81d03f496a6d00b4af3ea0c07f6fa232c0db7f2d00.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contour.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee321fc96666919b6ec15fdca593600d2586785a259dfa5a01.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee3217c9e66691996ec15fdca593680d2586785a259dfa5a01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee321fc96666919b6ec15fdca593600d2586785a259dfa5a01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a3fd956a7a01a5ee3217c9e66691996ec15fdca593680d2586785a259dfa5a01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a7fd95da7a01654a3217c962e4819a56c96f3c8593624da584da3b658db662db.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contour.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf802f85c17fc17fc07eb42ac07f3f929130c07e3f80c07f7aa02e85c07f3e81.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fe812f88957a955a857a9257c17f7aa5c03dc0bf5a85c07e7f402d40a57a3f01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fe812f88957a955a857a9257c17f7aa5c03dc0bf5a85c07e7f402d40a57a3f01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a95957a957ac0fe1e8bc07f7f806e01c07f3f80c07f3fa23f00c07f3d00.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa12bc1954ef43fc0bf9f02854a4ee48548c17a5ab5c17e7a0d7875a17e3a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bf802f85c17fc17fc07eb42ac07f3f929130c07e3f80c07f7aa02e85c07f3e81.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa852f81955ac532c0bf9e89c57edae69357e13f4ea0c05a3f8561a4935a3e01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa852f81955ac532c0bf9e89c57edae69357e13f4ea0c05a3f8561a4935a3e01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a95907ae508c17e955ac07f3fa0945bc07f3f80c07f3aa36f01c0ff3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_contourf_nameless.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/faa52ec1955ac536c0bf9e09c57edae69357e13f4e80c0da2f81618493da3f01.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/faa52ec1955ac536c0bf9e09c57edae69357e13f4e80c0da2f81618493da3f01.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816af5907ee508c17e955ac03f3f809419c07f3f80c07f3a8b6f81c0ff3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_map.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_map.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4ecef19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4ecef19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_pcolor.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb423d4e94a5c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb423d4e94a5c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f2f81c07e3fa12da1c2fe3f80.png" - ], + ], "iris.tests.test_quickplot.TestLabels.test_pcolormesh.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bb433d4e94a4c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bb433d4e94a4c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f3f81c07e3fa12da1c27e3f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_non_cube_coordinate.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85857a955ae17e957ec57e7a81855fc17e3a81c57e1a813a85c57a1a05.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85857a955ae17e957ec57e7a81855fc17e3a81c57e1a813a85c57a1a05.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a85857a957ac07f957ac07f3e80956ac07f3e80c07f3e813e85c07e3f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a95e15ab51a953e9485857a1f409552857e1fc1c07e5abd4a35e07f4aa5.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa856a95e15ab51a953e9485857a1f409552857e1fc1c07e5abd4a35e07f4aa5.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95955a956ac17f950ac07e3f48951ac07f3f81c0ff3ea16aa1c0be3e81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/fa8562b7c2763d09956a955a855a1d88d45ec57a3f81c07e6ae16b21c0ff7a81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a85957a957ac17e954ac17e1ca2954ac07e3e81c07f3e807a85c1ff3f81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f00953062179561f087953ad167997a80784a7fc1e5d86d9978485f.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f80953860179561f087953ad167997a80784a7fc1e5d86d9978485b.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f00953062179561f087953ad167997a80784a7fc1e5d86d9978485f.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/8aff878b7f80953860179561f087953ad167997a80784a7fc1e5d86d9978485b.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eafdc6c9f720953030968d6795d28d6a95674b7b81304aedc9e51cad8d186c9a.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82ff8db67f94952e76159d6bb01dcd629059c962c1fbd9c1c062da74d820ca74.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82be8db67f95952e761d9d6bb01dcd628059c962c1fbd9e1c072da64d060ca74.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe8db67f95952e76159d6bb01dcd629059c962c1fbd9e1c072da64d020ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82ff8db67f94952e76159d6bb01dcd629059c962c1fbd9c1c062da74d820ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82be8db67f95952e761d9d6bb01dcd628059c962c1fbd9e1c072da64d060ca74.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe8db67f95952e76159d6bb01dcd629059c962c1fbd9e1c072da64d020ca74.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a2ff6a967f00952eb40d9d0f900fcd62c47069f3d1f93a909c266e34d8a56f68.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/aa97b70ff5f0970f20b2956a6a17957af805da71d06f5a75d02cd870d800d8f2.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e1faa549de9497090697971d60539f3ef171c87ac075487ad025d87ed801da3e.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aa97b70ff5f0970f20b2956a6a17957af805da71d06f5a75d02cd870d800d8f2.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e1faa549de9497090697971d60539f3ef171c87ac075487ad025d87ed801da3e.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eadab54fd7a1856d90819d6df8169962e946d862802ed8809ded7e809d2d03ff.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_tx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e8faad47f784bd0596859d03969f9962c05dc96ee07189fe6870c862687178f8.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a8fa2d4797859585b6959d07605f896ee051697ad061d9fad0619aaed801deae.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e8faad47f784bd0596859d03969f9962c05dc96ee07189fe6870c862687178f8.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a8fa2d4797859585b6959d07605f896ee051697ad061d9fad0619aaed801deae.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aa5b3c0c978187a4b60199bc605f6976687e6873d07c99e390acdc0391fc2f7b.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_x.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a6ffb5097e84cde2224598d1649f8d6cd2388c76d0799867d009da76c9f8d866.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a6bfb5097f84cde2224599d1649f8d6cd2388c76d0799867d009da76c1f8d866.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6ffb5097e84cde2224598d1649f8d6cd2388c76d0799867d009da76c9f8d866.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a6bfb5097f84cde2224599d1649f8d6cd2388c76d0799867d009da76c1f8d866.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a6fbb50cfbd0c036203598dce4c88d26d32f8cf3886e1df3dc047b4289ec6e72.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_y.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff978b7f00c9c830d7992166179e969509d866c478d964d079c876d869da26.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff97837f00c9c830d79921661f9e9695099876c478d964c079c876d879da26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff978b7f00c9c830d7992166179e969509d866c478d964d079c876d869da26.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a7ff97837f00c9c830d79921661f9e9695099876c478d964c079c876d879da26.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a2ffb6127f0dc9993085d960c6748d3e9b121ca49d6a1b048df34ce789ff7205.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a9ff16eb740954a9e05855a19a3c0fbc13e1ea5c07d5ad0cb58e45e3c35.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a9ff16eb740954a9e05855a19a3c0fbc13e1ea5c07d5ad0cb58e45e3c35.png", "https://scitools.github.io/test-iris-imagehash/images/v4/ea856a95957a957ac07e954ac17e3e87950bc07f3ea4c27d3e833ac1c1e03f80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e5a565b69e1a9a42917e1a19c17b3a619e59c47b3a25c53e3b8430e5c57a3e85.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e5a565b69e1a9a42917e1a19c17b3a619e59c47b3a25c53e3b8430e5c57a3e85.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e5a761a79a589e58c07d1e48c07c3f819e41c07f3d84c17e3fa62585c0fe3f83.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/afffe6d67700958636179d92e019992dd039daf5817d987a807a48e499684a6d.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/aeffe6d67780958636179d92e019892dd139daf5815d987a807a48e699684a6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/afffe6d67700958636179d92e019992dd039daf5817d987a807a48e499684a6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/aeffe6d67780958636179d92e019892dd139daf5815d987a807a48e699684a6d.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eaff6ad4f74ab16490109c9b942999add1b74bb785a41d4acd526a254acc6365.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea5e618434ac36e5c1c9369b95b39c38c3a39a4fcee19a6e9b64cb609925cd25.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85a636c86a597a593c9b49b94b79969c396c95bccc69a64db30d9b039a52c26.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d67d2c696ce0653c3ac2b1d976da05c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d27d2c696ce0e53c3ad2b1d976da01c2c4.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26d1e3a0f684a3c2c6913dc2497b9db8095e502ff.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d67d2c696ce0653c3ac2b1d976da05c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ad2f6d2fd2d09295c2d1c3d33c1bc2d27d2c696ce0e53c3ad2b1d976da01c2c4.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3e2f97a1c19996a1c8f26d1e3a0f684a3c2c6913dc2497b9db8095e502ff.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e3c1f97a1c3e197a1c9f37c5e390668521e0c390bdd8685b1d86096e5279f.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_yx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e968658e969692c797879e3b86929e58696d49cd6869c9a37962c923990d9c6d.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e9e1658e961e92569e9e3c7966d36c586165698c70e1ce739b3698619e1e984c.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e968658e969692c797879e3b86929e58696d49cd6869c9a37962c923990d9c6d.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e9e1658e961e92569e9e3c7966d36c586165698c70e1ce739b3698619e1e984c.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e1a530e29e5ecf199a5acd8f64f1326161a538e665a198d29e52cb1d9a5e6669.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/bf813f80c156c05dc0fec29dc17f1a6dd05fc0ff1aa1c57e3b243b20375a1e81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/bf813f80c156c05dc0fec29dc17f1a6dd05fc0ff1aa1c57e3b243b20375a1e81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/be816a81d17ec57ac07e952ac07f3aa0955ec17e3f80c07f3f803f80c0bf3f81.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.1": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ea95629d956a996069939e9bc07f7aad856cc47e5e81857a1e254a35c1be1b81.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ea95629d956a996069939e9bc07f7aad856cc47e5e81857a1e254a35c1be1b81.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85957a957ac03f957ac07f3ba1954ac07e3e81c07f3ea47a85c07e3e80.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.2": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f867f008d8220179852f01fd9bed1789a6c847cc877c46ac972987ec8fd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179852f01fd9bed1789a6c847cc877c468c9f6987ec8fd.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179c52f01fd9bed1789a6c847cc877c560c976987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f867f008d8220179852f01fd9bed1789a6c847cc877c46ac972987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179852f01fd9bed1789a6c847cc877c468c9f6987ec8fd.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/87ed2f067f008d8220179c52f01fd9bed1789a6c847cc877c560c976987ec8fd.png", "https://scitools.github.io/test-iris-imagehash/images/v4/a3eded05fe11a492b000985af07fdbb4d1e3366d8c644da79fa68993180f6ec1.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.3": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600a56962df9e96f01dc926c498cc46847f9d6cd0244bf19a6b19f1.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600856962df9e96f01dcd26c498cc46847f9d6cd0244bf19a6b1975.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600a56962df9e96f01dc926c498cc46847f9d6cd0244bf19a6b19f1.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/a2f9b5ba7600856962df9e96f01dcd26c498cc46847f9d6cd0244bf19a6b1975.png", "https://scitools.github.io/test-iris-imagehash/images/v4/aef9f93a770085e9205fd696d13c4b2485ca1a43952f1934daa66bd1ca6b3c71.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.4": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d86801f91ee6e1591fe7e117876c07d6877d068d878d800d07a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d87801b91ee6e1599fe7e117874c07d6877d068d878d800d07a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d86801f91ee6e1591fe7e117876c07d6877d068d878d800d07a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/ae953f87d5e82d87801b91ee6e1599fe7e117874c07d6877d068d878d800d07a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/bec1329dc5be85dac01d58d73e419d423e41daa59822dc00c5fefe0091fe03ff.png" - ], + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_zx.5": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ee0519d6ee07f9b6a78619b2a79711a2a.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ae0519d6ee07f996a78719b2a79711a3a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ee0519d6ee07f9b6a78619b2a79711a2a.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/e87a952d96856943969f694696858d4ae0519d6ee07f996a78719b2a79711a3a.png", "https://scitools.github.io/test-iris-imagehash/images/v4/e85e96ac97a168d897a5791695a19927913c3953687ecce3687c86e3487cc6c3.png" - ], + ], "iris.tests.test_quickplot.TestTimeReferenceUnitsLabels.test_not_reference_time_units.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82faa1977fdf89976200ddf6e000d9e7f75f9866d560dae4dc00d966dc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82b8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00d966fc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200ddf6e000d9e7f77f9866d560dee4dc00d966dc005e20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00dd64dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82faa1977fdf89976200ddf6e000d9e7f75f9866d560dae4dc00d966dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82b8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00d966fc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200ddf6e000d9e7f77f9866d560dee4dc00d966dc005e20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82f8a1977fdf89876200dde6e000d9e7f77f9866d560dfe4dc00dd64dc005e20.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82faa19e7f51898c6001dd86845fd9a2dd7f996281ee19f389ef03ffdc007e00.png" - ], + ], "iris.tests.test_quickplot.TestTimeReferenceUnitsLabels.test_reference_time_units.0": [ - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fd777ffe0002addd4002805dda8de65dde9d4625bfddc209841de20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002a9dd4002805ddaade65d9a9d5625bfddc209841de20.png", - "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002addd4002805dd28df67d9a9d4625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fd777ffe0002addd4002805dda8de65dde9d4625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002a9dd4002805ddaade65d9a9d5625bfddc209841de20.png", + "https://scitools.github.io/test-iris-imagehash/images/v4/82fe81987fdf77ffe0002addd4002805dd28df67d9a9d4625bfddc209841de20.png", "https://scitools.github.io/test-iris-imagehash/images/v4/82fa80997f547799a0037a00d52f0956ddaf9f7e98a1816e09f5d8260bfffe00.png" ] } \ No newline at end of file From 5923aaa8e725831a1575678fd93c938bfad36e95 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Wed, 5 Sep 2018 11:39:06 +0100 Subject: [PATCH 24/24] Fix contig_ tolerance kwarg bug (#3162) --- lib/iris/plot.py | 9 +++++---- lib/iris/tests/unit/coords/test_Coord.py | 10 ++++++++++ .../test__check_bounds_contiguity_and_mask.py | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/iris/plot.py b/lib/iris/plot.py index f0dfccd8c5..e3193e96a4 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -317,13 +317,14 @@ def _check_bounds_contiguity_and_mask(coord, data, atol=None): elif coord.ndim == 2: if atol: - args = {'atol': atol} + kwargs = {'atol': atol} else: - args = {} - contiguous, diffs = coord._discontiguity_in_bounds(*args) - diffs_along_x, diffs_along_y = diffs + kwargs = {} + contiguous, diffs = coord._discontiguity_in_bounds(**kwargs) if not contiguous and data_is_masked: + diffs_along_x, diffs_along_y = diffs + # Check along both dimensions. not_masked_at_discontiguity_along_x = np.any( np.logical_and(mask_invert[:, :-1], diffs_along_x)) diff --git a/lib/iris/tests/unit/coords/test_Coord.py b/lib/iris/tests/unit/coords/test_Coord.py index 58fc3e1f9e..8cf79caad3 100644 --- a/lib/iris/tests/unit/coords/test_Coord.py +++ b/lib/iris/tests/unit/coords/test_Coord.py @@ -504,6 +504,16 @@ def test_2d_discontiguous_along_x_and_y(self): self.assertArrayEqual(diffs_along_x, exp_x_diffs) self.assertArrayEqual(diffs_along_y, exp_y_diffs) + def test_2d_contiguous_along_x_atol(self): + coord = AuxCoord(self.points_3by3[:, ::2], + bounds=self.lon_bounds_3by3[:, ::2, :]) + # Set a high atol that allows small discontiguities. + contiguous, diffs = coord._discontiguity_in_bounds(atol=2) + diffs_along_x, diffs_along_y = diffs + self.assertTrue(contiguous) + self.assertArrayEqual(diffs_along_x, np.array([2, 2, 2]).reshape(3, 1)) + self.assertTrue(not diffs_along_y.any()) + def test_2d_one_cell(self): # Test a 2D coord with a single cell, where the coord has shape (1, 1). coord = AuxCoord(self.points_3by3[:1, :1], diff --git a/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py b/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py index b067b3f387..225aa2890e 100644 --- a/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py +++ b/lib/iris/tests/unit/plot/test__check_bounds_contiguity_and_mask.py @@ -24,6 +24,8 @@ # importing anything else. import iris.tests as tests +import mock + import numpy as np import numpy.ma as ma @@ -72,6 +74,19 @@ def test_2d_contiguous(self): iplt._check_bounds_contiguity_and_mask(cube.coord('longitude'), cube.data) + def test_2d_contiguous_atol(self): + # Check the atol is passed correctly. + cube = sample_2d_latlons() + with mock.patch('iris.coords.Coord._discontiguity_in_bounds' + ) as discontiguity_check: + # Discontiguity returns two objects that are unpacked in + # `_check_bounds_contiguity_and_mask`. + discontiguity_check.return_value = [True, None] + iplt._check_bounds_contiguity_and_mask(cube.coord('longitude'), + cube.data, + atol=1e-3) + discontiguity_check.assert_called_with(atol=1e-3) + def test_2d_discontigous_masked(self): # Test a 2D coordinate which is discontiguous but masked at # discontiguities.