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_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.assertRaisesRegex(ValueError, "must have same coordinate system"): gridcell_angles(cube)
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_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_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 _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 _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.0, -45.0, 135, -140.0, 90.0]) 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_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 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 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): _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)
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 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(resolution="110m", 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(resolution="110m", color="red") ax.set_global() self.check_graphic()
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] _check_bounds_contiguity_and_mask(cube.coord('longitude'), cube.data, atol=1e-3) discontiguity_check.assert_called_with(atol=1e-3)
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(resolution="110m", 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'))
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_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 = (r"Can only plot .* lat-lon projection, .* " r"This .* translates as Cartopy \+proj=merc .*") with self.assertRaisesRegex(ValueError, re_msg): self.plot("2d_rotated", u_cube, v_cube, coords=("longitude", "latitude"))
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_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_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])
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_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_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])
def test_2d_contiguous(self): # Test that a 2D coordinate which is contiguous does not throw # an error. cube = sample_2d_latlons() _check_bounds_contiguity_and_mask(cube.coord('longitude'), cube.data)
def test_2d_discontigous_masked(self): # Test that a 2D coordinate which is discontiguous but masked at # discontiguities doesn't error. cube = sample_2d_latlons() make_bounds_discontiguous_at_point(cube, 3, 4) _check_bounds_contiguity_and_mask(cube.coord('longitude'), cube.data)
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 full2d_global(): return sample_2d_latlons(transformed=True)
def test_2d_discontigous_masked(self): # Test that a 2D coordinate which is discontiguous but masked at # discontiguities doesn't error. cube = sample_2d_latlons() make_bounds_discontiguous_at_point(cube, 3, 4) _check_bounds_contiguity_and_mask(cube.coord("longitude"), cube.data)
def test_2d_contiguous(self): # Test that a 2D coordinate which is contiguous does not throw # an error. cube = sample_2d_latlons() _check_bounds_contiguity_and_mask(cube.coord("longitude"), cube.data)