def test_coord_metadata_mismatch(self): # Check for failure when coordinate definitions differ. uk = uk_cube() self.remove_coord_systems(uk) lat_lon = lat_lon_cube() self.remove_coord_systems(lat_lon) with self.assertRaises(ValueError): regrid(uk, lat_lon)
def demote_coord(coord_name): bad = global_pp() coord = bad.coord(coord_name) dims = bad.coord_dims(coord) bad.remove_coord(coord_name) aux_coord = AuxCoord.from_coord(coord) bad.add_aux_coord(aux_coord, dims) with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad)
def test_bad_units(self): ok = global_pp() bad = global_pp() bad.coord('longitude').units = 'radians' with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad) bad = Cube(np.arange(12, dtype=np.float32).reshape(3, 4)) cs = OSGB() y_coord = DimCoord(range(3), 'projection_y_coordinate', units='m', coord_system=cs) x_coord = DimCoord(range(4), 'projection_x_coordinate', units='km', coord_system=cs) bad.add_dim_coord(y_coord, 0) bad.add_dim_coord(x_coord, 1) with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad)
def test_different_units(self): src = uk_cube() self.remove_coord_systems(src) # Move to unusual units (i.e. not metres or degrees). for coord in src.coords(): coord.units = 'feet' grid = src.copy() grid.coord('projection_y_coordinate').units = 'yards' # We change the coordinate *values* to ensure that does not # prevent the regridding operation. for coord in grid.dim_coords: coord.points = coord.points + 1 with self.assertRaisesRegexp(ValueError, 'matching coordinate metadata'): regrid(src, grid)
def test_circular_src(self): # Circular src -> non-circular grid src = self.src src.coord('longitude').circular = True result = regrid(src, self.grid) self.assertFalse(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('circular_src.cml', ))
def test_grid_subset_big_transposed(self): # The order of the grid's dimensions (including the X and Y # dimensions) must not affect the result. big_grid = self._big_grid() big_grid.transpose([4, 0, 3, 1, 2]) result = regrid(self.src, big_grid) self.assertCMLApproxData(result, RESULT_DIR + ('subset.cml',))
def test_src_xy_not_2d(self): new_shape = (2, 2, 3) # Reshape the source cube, including the X and Y coordinates, # from (3, 4) to (2, 2, 3). # This is really an "invalid" reshape, but should still work because # the XY shape is actually irrelevant to the regrid operation. src = iris.cube.Cube(self.test_src_data.reshape(new_shape), standard_name=self.test_src_name, units=self.test_src_units, aux_coords_and_dims=[(self.test_scalar_coord, None)], attributes=self.test_src_attributes) co_src_y = self.src_y.copy(points=self.src_y.points.reshape(new_shape)) co_src_x = self.src_x_positive.copy( points=self.src_x_positive.points.reshape(new_shape)) src.add_aux_coord(co_src_y, (0, 1, 2)) src.add_aux_coord(co_src_x, (0, 1, 2)) self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) weights = self.weights.reshape(new_shape) result = regrid(src, weights, self.grid) # NOTE: set the grid of self.src to make '_expected_cube' work ... self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_positive, (0, 1)) # ... given that, we expect exactly the same 'normal' result. data = np.array([ 0, self._weighted_mean([3]), self._weighted_mean([7, 8]), self._weighted_mean([9, 10, 11]) ]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_non_latlon(self): odd_coord_system = LambertConformal() co_src_y = AuxCoord(self.src_y.points, standard_name='projection_y_coordinate', units='km', coord_system=odd_coord_system) co_src_x = AuxCoord(self.src_x_positive.points, standard_name='projection_x_coordinate', units='km', coord_system=odd_coord_system) co_grid_y = DimCoord(self.grid_y_inc.points, bounds=self.grid_y_inc.bounds, standard_name='projection_y_coordinate', units='km', coord_system=odd_coord_system) co_grid_x = DimCoord(self.grid_x_inc.points, bounds=self.grid_x_inc.bounds, standard_name='projection_x_coordinate', units='km', coord_system=odd_coord_system) self.src.add_aux_coord(co_src_y, (0, 1)) self.src.add_aux_coord(co_src_x, (0, 1)) self.grid.add_dim_coord(co_grid_y, 0) self.grid.add_dim_coord(co_grid_x, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([ 0, self._weighted_mean([3]), self._weighted_mean([7, 8]), self._weighted_mean([9, 10, 11]) ]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_float_tolerant_equality(self): # Ensure that floating point numbers are treated appropriately when # introducing precision difference from wrap_around. source = Cube([[1]]) cs = GeogCS(6371229) bounds = np.array([[-91, 0]], dtype='float') points = bounds.mean(axis=1) lon_coord = DimCoord(points, bounds=bounds, standard_name='longitude', units='degrees', coord_system=cs) source.add_aux_coord(lon_coord, 1) bounds = np.array([[-90, 90]], dtype='float') points = bounds.mean(axis=1) lat_coord = DimCoord(points, bounds=bounds, standard_name='latitude', units='degrees', coord_system=cs) source.add_aux_coord(lat_coord, 0) grid = Cube([[0]]) bounds = np.array([[270, 360]], dtype='float') points = bounds.mean(axis=1) lon_coord = DimCoord(points, bounds=bounds, standard_name='longitude', units='degrees', coord_system=cs) grid.add_aux_coord(lon_coord, 1) grid.add_aux_coord(lat_coord, 0) res = regrid(source, grid) # The result should be equal to the source data and NOT be masked. self.assertArrayEqual(res.data, np.array([1.0]))
def test_circular_grid(self): # Non-circular src -> circular grid grid = self.grid grid.coord('longitude').circular = True result = regrid(self.src, grid) self.assertTrue(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('circular_grid.cml', ))
def test_src_xy_not_2d(self): new_shape = (2, 2, 3) # Reshape the source cube, including the X and Y coordinates, # from (3, 4) to (2, 2, 3). # This is really an "invalid" reshape, but should still work because # the XY shape is actually irrelevant to the regrid operation. src = iris.cube.Cube( self.test_src_data.reshape(new_shape), standard_name=self.test_src_name, units=self.test_src_units, aux_coords_and_dims=[(self.test_scalar_coord, None)], attributes=self.test_src_attributes) co_src_y = self.src_y.copy( points=self.src_y.points.reshape(new_shape)) co_src_x = self.src_x_positive.copy( points=self.src_x_positive.points.reshape(new_shape)) src.add_aux_coord(co_src_y, (0, 1, 2)) src.add_aux_coord(co_src_x, (0, 1, 2)) self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) weights = self.weights.reshape(new_shape) result = regrid(src, weights, self.grid) # NOTE: set the grid of self.src to make '_expected_cube' work ... self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_positive, (0, 1)) # ... given that, we expect exactly the same 'normal' result. data = np.array([0, self._weighted_mean([3]), self._weighted_mean([7, 8]), self._weighted_mean([9, 10, 11])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_non_latlon(self): odd_coord_system = LambertConformal() co_src_y = AuxCoord(self.src_y.points, standard_name='projection_y_coordinate', units='km', coord_system=odd_coord_system) co_src_x = AuxCoord(self.src_x_positive.points, standard_name='projection_x_coordinate', units='km', coord_system=odd_coord_system) co_grid_y = DimCoord(self.grid_y_inc.points, bounds=self.grid_y_inc.bounds, standard_name='projection_y_coordinate', units='km', coord_system=odd_coord_system) co_grid_x = DimCoord(self.grid_x_inc.points, bounds=self.grid_x_inc.bounds, standard_name='projection_x_coordinate', units='km', coord_system=odd_coord_system) self.src.add_aux_coord(co_src_y, (0, 1)) self.src.add_aux_coord(co_src_x, (0, 1)) self.grid.add_dim_coord(co_grid_y, 0) self.grid.add_dim_coord(co_grid_x, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([0, self._weighted_mean([3]), self._weighted_mean([7, 8]), self._weighted_mean([9, 10, 11])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_subsample(self): src = global_pp() grid = src[::2, ::3] result = regrid(src, grid) qplt.pcolormesh(result) qplt.plt.gca().coastlines() self.check_graphic()
def test_scalar_no_overlap(self): # Slice src so result collapses to a scalar. src_cube = self.src_cube[:, 1, :] # Regrid to a single cell with no overlap with masked src cells. grid_cube = self.grid_cube[2, 1, 3] res = regrid(src_cube, grid_cube, mdtol=0.8) self.assertFalse(ma.isMaskedArray(res.data))
def test_grid_subset_big_transposed(self): # The order of the grid's dimensions (including the X and Y # dimensions) must not affect the result. big_grid = self._big_grid() big_grid.transpose([4, 0, 3, 1, 2]) result = regrid(self.src, big_grid) self.assertCMLApproxData(result, RESULT_DIR + ('subset.cml', ))
def test_grid_subset_anon(self): # Must cope OK with anonymous source dimensions. src = self.src src.remove_coord('time') grid = self._grid_subset() result = regrid(src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('subset_anon.cml',))
def test_grid_subset_anon(self): # Must cope OK with anonymous source dimensions. src = self.src src.remove_coord('time') grid = self._grid_subset() result = regrid(src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('subset_anon.cml', ))
def test_nop(self): # The destination grid points are exactly the same as the # src grid points. src = realistic_4d()[:5, :2, ::40, ::30] grid = src.copy() result = regrid(src, grid) self.assertEqual(result, src)
def test_circular_grid(self): # Non-circular src -> circular grid grid = self.grid grid.coord('longitude').circular = True result = regrid(self.src, grid) self.assertTrue(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('circular_grid.cml',))
def test_circular_src(self): # Circular src -> non-circular grid src = self.src src.coord('longitude').circular = True result = regrid(src, self.grid) self.assertFalse(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('circular_src.cml',))
def test_grid_no_overlap(self): # The destination grid points are NOT contained within the # src grid points. grid = global_pp()[:4, :4] grid.coord('longitude').points = np.linspace(-3.3, -3.2, 4) grid.coord('latitude').points = np.linspace(52.377, 52.43, 4) result = regrid(self.src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('no_overlap.cml',))
def test_transposed_src(self): # The source dimensions are in a non-standard order. src = self.src src.transpose([3, 1, 2, 0]) grid = self._grid_subset() result = regrid(src, grid) result.transpose([3, 1, 2, 0]) self.assertCMLApproxData(result, RESULT_DIR + ('subset.cml',))
def test_grid_subset_missing_data_aux(self): # The destination grid points are entirely contained within the # src grid points AND we have missing data on the aux coordinate. src = self.src src.coord('surface_altitude').points[1, 2] = np.ma.masked grid = self._grid_subset() result = regrid(src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('masked_altitude.cml',))
def test_transposed_src(self): # The source dimensions are in a non-standard order. src = self.src src.transpose([3, 1, 2, 0]) grid = self._grid_subset() result = regrid(src, grid) result.transpose([3, 1, 2, 0]) self.assertCMLApproxData(result, RESULT_DIR + ('subset.cml', ))
def test_grid_no_overlap(self): # The destination grid points are NOT contained within the # src grid points. grid = global_pp()[:4, :4] grid.coord('longitude').points = np.linspace(-3.3, -3.2, 4) grid.coord('latitude').points = np.linspace(52.377, 52.43, 4) result = regrid(self.src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('no_overlap.cml', ))
def test_scalar_with_overlap_above_mdtol(self): # Slice src so result collapses to a scalar. src_cube = self.src_cube[:, 1, :] # Regrid to a single cell with 50% overlap with masked src cells. grid_cube = self.grid_cube[3, 1, 4] # Set threshold (mdtol) to less than 0.5 (50%). res = regrid(src_cube, grid_cube, mdtol=0.4) self.assertEqual(ma.count_masked(res.data), 1)
def test_grid_subset_missing_data_2(self): # The destination grid points are entirely contained within the # src grid points AND we have missing data. src = self.src src.data = np.ma.MaskedArray(src.data) src.data[:, :, 1, 2] = np.ma.masked grid = self._grid_subset() result = regrid(src, grid) self.assertCMLApproxData(result, RESULT_DIR + ('subset_masked_2.cml',))
def test_circular_src_and_grid(self): # Circular src -> circular grid src = self.src src.coord('longitude').circular = True grid = self.grid grid.coord('longitude').circular = True result = regrid(src, grid) self.assertTrue(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('both_circular.cml',))
def test_single_point(self): src = self.src[0, 0] grid = global_pp()[:1, :1] # These coordinate values have been derived by converting the # rotated coordinates of src[1, 1] into lat/lon by using cs2cs. grid.coord('longitude').points = -3.144870 grid.coord('latitude').points = 52.406444 result = regrid(src, grid) self.assertEqual(src.data[1, 1], result.data)
def test_circular_src_and_grid(self): # Circular src -> circular grid src = self.src src.coord('longitude').circular = True grid = self.grid grid.coord('longitude').circular = True result = regrid(src, grid) self.assertTrue(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('both_circular.cml', ))
def test_non_cube(self): array = np.zeros((3, 4)) cube = global_pp() with self.assertRaises(TypeError): regrid(array, cube) with self.assertRaises(TypeError): regrid(cube, array) with self.assertRaises(TypeError): regrid(42, cube) with self.assertRaises(TypeError): regrid(cube, 42)
def test_fraction_between_min_and_max(self): # Threshold between min and max fraction. See # test_fraction_below_min() comment for picture showing # the fractions of masked data. mdtol = 0.6 res = regrid(self.src_cube, self.grid_cube, mdtol=mdtol) expected_mask = np.zeros((7, 2, 9), bool) expected_mask[2:5, 1, 5] = True expected_mask[3, 1, 6] = True self.assertArrayEqual(res.data.mask, expected_mask)
def test_aligned_src_x_zero_weights(self): self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_positive, (0, 1)) self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) self.weights[:, 2] = 0 self.weights[1, :] = 0 result = regrid(self.src, self.weights, self.grid) data = np.array([0, 0, 0, self._weighted_mean([9, 10])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, True], [True, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_misaligned_tgt_dec(self): self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_negative, (0, 1)) self.grid.add_dim_coord(self.grid_y_dec, 0) self.grid.add_dim_coord(self.grid_x_dec, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([self._weighted_mean([10, 11, 12]), self._weighted_mean([6, 7, 8, 9]), self._weighted_mean([4]), self._weighted_mean([2, 3])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[False, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def _regrid(self, src, extrapolation_mode=None): grid = src.copy() for coord in grid.dim_coords: coord.points = coord.points + 1 kwargs = {} if extrapolation_mode is not None: kwargs['extrapolation_mode'] = extrapolation_mode result = regrid(src, grid, **kwargs) surface = result.coord('surface_altitude').points self.assertNotIsInstance(surface, np.ma.MaskedArray) self.assertArrayEqual(surface, self.surface_values) return result.data
def test_aligned_src_y_transpose(self): self.src.add_aux_coord(self.src_y_transpose, (1, 0)) self.src.add_aux_coord(self.src_x_positive, (0, 1)) self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([0, self._weighted_mean([3]), self._weighted_mean([7, 8]), self._weighted_mean([9, 10, 11])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_aligned_src_x_mask(self): self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_positive, (0, 1)) self.src.data[([1, 2, 2], [3, 0, 2])] = ma.masked self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([0, self._weighted_mean([3]), self._weighted_mean([7]), self._weighted_mean([10])]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[True, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_ok(self): # Ensure regridding is supported when the coordinate definitions match. # NB. We change the coordinate *values* to ensure that does not # prevent the regridding operation. src = uk_cube() self.remove_coord_systems(src) grid = src.copy() for coord in grid.dim_coords: coord.points = coord.points + 1 result = regrid(src, grid) for coord in result.dim_coords: self.assertEqual(coord, grid.coord(coord)) expected = np.ma.arange(12).reshape((3, 4)) + 5 expected[:, 3] = np.ma.masked expected[2, :] = np.ma.masked self.assertMaskedArrayEqual(result.data, expected)
def test_misaligned_src_x_negative(self): self.src.add_aux_coord(self.src_y, (0, 1)) self.src.add_aux_coord(self.src_x_negative, (0, 1)) self.grid.add_dim_coord(self.grid_y_inc, 0) self.grid.add_dim_coord(self.grid_x_inc, 1) result = regrid(self.src, self.weights, self.grid) data = np.array([ self._weighted_mean([1, 2]), self._weighted_mean([3, 4]), self._weighted_mean([5, 6, 7, 8]), self._weighted_mean([9, 10, 11]), ]).reshape(2, 2) expected = self._expected_cube(data) self.assertEqual(result, expected) mask = np.array([[False, False], [False, False]]) self.assertArrayEqual(result.data.mask, mask)
def test_osgb_to_latlon(self): path = tests.get_data_path( ('NIMROD', 'uk2km', 'WO0000000003452', '201007020900_u1096_ng_ey00_visibility0180_screen_2km')) src = iris.load_cube(path)[0] src.data = src.data.astype(np.float32) grid = Cube(np.empty((73, 96))) cs = GeogCS(6370000) lat = DimCoord(np.linspace(46, 65, 73), 'latitude', units='degrees', coord_system=cs) lon = DimCoord(np.linspace(-14, 8, 96), 'longitude', units='degrees', coord_system=cs) grid.add_dim_coord(lat, 0) grid.add_dim_coord(lon, 1) result = regrid(src, grid) qplt.pcolor(result, antialiased=False) qplt.plt.gca().coastlines() self.check_graphic()
def test_fraction_below_min(self): # Cells in target grid that overlap with the masked src cell # have the following fractions (approx. due to spherical area). # 4 5 6 7 # 2 ---------------------- # | 0.33 | 0.66 | 0.50 | # 3 ---------------------- # | 0.33 | 1.00 | 0.75 | # 4 ---------------------- # | 0.33 | 0.66 | 0.50 | # 5 ---------------------- # # Threshold less than minimum fraction. mdtol = 0.2 res = regrid(self.src_cube, self.grid_cube, mdtol=mdtol) expected_mask = np.zeros((7, 2, 9), bool) expected_mask[2:5, 1, 4:7] = True self.assertArrayEqual(res.data.mask, expected_mask)
def test_non_circular(self): # Non-circular src -> non-circular grid result = regrid(self.src, self.grid) self.assertFalse(result.coord('longitude').circular) self.assertCMLApproxData(result, RESULT_DIR + ('non_circular.cml', ))
def test_non_rectilinear_src(self): ok = global_pp() # Lat and/or lon missing bad = global_pp() bad.remove_coord('latitude') with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad) bad = global_pp() bad.remove_coord('longitude') with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad) bad = global_pp() bad.remove_coord('latitude') bad.remove_coord('longitude') with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad) # Lat/lon not a DimCoord def demote_coord(coord_name): bad = global_pp() coord = bad.coord(coord_name) dims = bad.coord_dims(coord) bad.remove_coord(coord_name) aux_coord = AuxCoord.from_coord(coord) bad.add_aux_coord(aux_coord, dims) with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad) demote_coord('latitude') demote_coord('longitude') # Lat/lon share a single dimension bad = global_pp() lat = bad.coord('latitude') bad = bad[0, :lat.shape[0]] bad.remove_coord('latitude') bad.add_aux_coord(lat, 0) with self.assertRaises(ValueError): regrid(bad, ok) with self.assertRaises(ValueError): regrid(ok, bad)