def test_preserves_dimension_order(self): """Test order of original cube dimensions is preserved on subsetting""" self.uk_cube.transpose([1, 0]) expected_dims = get_dim_coord_names(self.uk_cube) result = subset_data(self.uk_cube, grid_spec=self.grid_spec) result_dims = get_dim_coord_names(result) self.assertSequenceEqual(result_dims, expected_dims)
def test_subset_global_grid_pacific(self): """Extract subset of global lat-lon grid over the international date line""" gl_pacific_cube = set_up_variable_cube( self.gl_cube.data.copy(), name="screen_temperature", units="degC", spatial_grid="latlon", domain_corner=(0, 175), grid_spacing=2, ) expected_data = np.array([[2.0, 4.0], [18.0, 20.0]]) grid_spec = { "longitude": { "min": 178, "max": 185, "thin": 2 }, "latitude": { "min": 0, "max": 7, "thin": 2 }, } result = subset_data(gl_pacific_cube, grid_spec=grid_spec) self.assertArrayAlmostEqual(result.data, expected_data) self.assertArrayAlmostEqual( result.coord("longitude").points, [179.0, 183.0]) self.assertArrayAlmostEqual( result.coord("latitude").points, [0.0, 4.0])
def test_subset_spot_cube(self): """Extract a list of spot sites""" site_list = ["3005", "3023"] result = subset_data(self.spot_cube, site_list=site_list) self.assertIsInstance(result, iris.cube.Cube) self.assertArrayEqual(result.coord("wmo_id").points, site_list) self.assertArrayAlmostEqual(result.data, [1, 3])
def test_error_single_point(self): """Function does not support extraction of a single (non-dimensioned) point from a grid - test a suitable error is raised if this is requested""" grid_spec = { "projection_x_coordinate": { "min": 0, "max": 1000, "thin": 1 }, "projection_y_coordinate": { "min": 0, "max": 1000, "thin": 1 }, } msg = "Function does not support single point extraction" with self.assertRaisesRegex(ValueError, msg): subset_data(self.uk_cube, grid_spec=grid_spec)
def test_error_non_overlapping_grid_spec(self): """Test error raised when defined cutout does not overlap with input cube""" grid_spec_xy = { "projection_x_coordinate": { "min": -10000, "max": -6000, "thin": 3 }, "projection_y_coordinate": { "min": 0, "max": 10000, "thin": 3 }, } msg = "Cube domain does not overlap with cutout specified" with self.assertRaisesRegex(ValueError, msg): subset_data(self.uk_cube, grid_spec=grid_spec_xy)
def test_error_wrong_grid_spec(self): """Test error raised when required coordinates are not in the grid spec dictionary""" grid_spec_latlon = { "longitude": { "min": 0, "max": 7, "thin": 2 }, "latitude": { "min": 42, "max": 52, "thin": 2 }, } msg = "Cube coordinates .* are not present" with self.assertRaisesRegex(ValueError, msg): subset_data(self.uk_cube, grid_spec=grid_spec_latlon)
def test_subset_uk_grid(self): """Extract subset of UK equal area grid""" expected_data = np.array([[27, 30], [51, 54]]) result = subset_data(self.uk_cube, grid_spec=self.grid_spec) self.assertArrayAlmostEqual(result.data, expected_data) for axis in ["x", "y"]: coord = f"projection_{axis}_coordinate" self.assertArrayAlmostEqual( result.coord(coord).points, self.expected_points[coord])
def test_subset_global_grid(self): """Extract subset of global lat-lon grid""" expected_data = np.array([[1, 3], [17, 19]]) result = subset_data(self.gl_cube, grid_spec=self.grid_spec) self.assertArrayAlmostEqual(result.data, expected_data) self.assertArrayAlmostEqual( result.coord("longitude").points, self.expected_points["longitude"]) self.assertArrayAlmostEqual( result.coord("latitude").points, self.expected_points["latitude"])
def test_error_order_agnostic(self): """Test non-overlapping grid error is correctly raised regardless of which coordinate is non-overlapping. This is needed because there are ways of calling iris.extract that appear to modify the input constraints, such that the output of this function was not always correct depending on which coordinate did not match.""" grid_spec_xy = { "projection_y_coordinate": { "min": -10000, "max": -6000, "thin": 3 }, "projection_x_coordinate": { "min": 0, "max": 10000, "thin": 3 }, } msg = "Cube domain does not overlap with cutout specified" with self.assertRaisesRegex(ValueError, msg): subset_data(self.uk_cube, grid_spec=grid_spec_xy)
def test_subset_no_thinning(self): """Test grid subsetting can handle a "thin" value of 1""" grid_spec = { "longitude": { "min": 0, "max": 7, "thin": 1 }, "latitude": { "min": 42, "max": 52, "thin": 2 }, } expected_data = np.array([[1, 2, 3, 4], [17, 18, 19, 20]]) result = subset_data(self.gl_cube, grid_spec=grid_spec) self.assertArrayAlmostEqual(result.data, expected_data) self.assertArrayAlmostEqual( result.coord("longitude").points, [0.0, 2.0, 4.0, 6.0]) self.assertArrayAlmostEqual( result.coord("latitude").points, [45.0, 49.0])
def process( cube: cli.inputcube, *, grid_spec: cli.inputjson = None, site_list: cli.comma_separated_list = None, ): """ Extract a thinned spatial cutout or subset of sites from a data file. Supports extraction from a spot file based on a list of sites, or grid subsetting using a dictionary specification of the following form: {"projection_x_coordinate": {"min": -100000, "max": 150000, "thin": 5}, "projection_y_coordinate": {"min": -100000, "max": 200000, "thin": 5}, "latitude": {"min": 45, "max": 52, "thin": 2}, "longitude": {"min": -2, "max": 6, "thin": 2}} Args: cube (iris.cube.Cube): Input dataset grid_spec (Dict[str, Dict[str, int]]): Dictionary containing bounding grid points and an integer "thinning factor" for each of UK and global grid, to create cutouts. Eg a "thinning factor" of 10 would mean every 10th point being taken for the cutout. The expected dictionary has keys that are spatial coordinate names, with values that are dictionaries with "min", "max" and "thin" keys. The dictionary MUST contain entries for the spatial coordinates on the input cube, and MAY contain additional entries (which will be ignored). site_list (list): List of WMO site IDs to extract. These IDs must match the type and format of the "wmo_id" coordinate on the input spot cube. Returns: iris.cube.Cube: Subset of input cube as specified by input constraints """ from improver.utilities.cube_extraction import subset_data return subset_data(cube, grid_spec=grid_spec, site_list=site_list)
def test_error_no_sites_available(self): """Test error raised when none of the required sites are present in the input spot cube""" msg = "Cube does not contain any of the required sites" with self.assertRaisesRegex(ValueError, msg): subset_data(self.spot_cube, site_list=["3100"])
def test_error_no_grid_spec(self): """Test error when required grid_spec is not provided""" msg = "grid_spec required" with self.assertRaisesRegex(ValueError, msg): subset_data(self.uk_cube)
def test_error_no_site_list(self): """Test error when required site_list is not provided""" msg = "site_list required" with self.assertRaisesRegex(ValueError, msg): subset_data(self.spot_cube)