def test_nearest_minimum_dz(self): """Test name generated when using the nearest neighbour with the smallest vertical displacment method.""" plugin = NeighbourSelection(minimum_dz=True) expected = 'nearest_minimum_dz' result = plugin.neighbour_finding_method_name() self.assertEqual(result, expected)
def test_nearest(self): """Test name generated when using the default nearest neighbour method.""" plugin = NeighbourSelection() expected = 'nearest' result = plugin.neighbour_finding_method_name() self.assertEqual(result, expected)
def test_nearest_land(self): """Test name generated when using the nearest land neighbour method.""" plugin = NeighbourSelection(land_constraint=True) expected = 'nearest_land' result = plugin.neighbour_finding_method_name() self.assertEqual(result, expected)
def test_all_valid(self): """Test case in which all sites are valid and fall within domain.""" plugin = NeighbourSelection() sites = [{ 'projection_x_coordinate': 1.0E4, 'projection_y_coordinate': 1.0E4 }, { 'projection_x_coordinate': 1.0E5, 'projection_y_coordinate': 5.0E4 }] x_points = np.array( [site['projection_x_coordinate'] for site in sites]) y_points = np.array( [site['projection_y_coordinate'] for site in sites]) site_coords = np.stack((x_points, y_points), axis=1) sites_out, site_coords_out, out_x, out_y = ( plugin.check_sites_are_within_domain(sites, site_coords, x_points, y_points, self.region_orography)) self.assertArrayEqual(sites_out, sites) self.assertArrayEqual(site_coords_out, site_coords) self.assertArrayEqual(out_x, x_points) self.assertArrayEqual(out_y, y_points)
def test_nearest_land_minimum_dz(self): """Test name generated when using the nearest land neighbour with smallest vertical displacment method.""" plugin = NeighbourSelection(land_constraint=True, minimum_dz=True) expected = "nearest_land_minimum_dz" result = plugin.neighbour_finding_method_name() self.assertEqual(result, expected)
def test_global_invalid(self, warning_list=None): """Test case with some sites falling outside the global domain.""" plugin = NeighbourSelection() sites = [{ 'latitude': 0.0, 'longitude': 0.0 }, { 'latitude': 50.0, 'longitude': 0.0 }, { 'latitude': 100.0, 'longitude': 0.0 }] x_points = np.array([site['longitude'] for site in sites]) y_points = np.array([site['latitude'] for site in sites]) site_coords = np.stack((x_points, y_points), axis=1) plugin.global_coordinate_system = True sites_out, site_coords_out, out_x, out_y = ( plugin.check_sites_are_within_domain(sites, site_coords, x_points, y_points, self.global_orography)) self.assertArrayEqual(sites_out, sites[0:2]) self.assertArrayEqual(site_coords_out[0:2], site_coords[0:2]) self.assertArrayEqual(out_x, x_points[0:2]) self.assertArrayEqual(out_y, y_points[0:2]) msg = "1 spot sites fall outside the grid" self.assertTrue(any([msg in str(warning) for warning in warning_list])) self.assertTrue( any(item.category == UserWarning for item in warning_list))
def test_global_circular_valid(self): """Test case with a site defined using a longitide exceeding 180 degrees (e.g. with longitudes that run 0 to 360) is still included as the circular x-coordinate means it will still be used correctly.""" plugin = NeighbourSelection() sites = [{ 'latitude': 0.0, 'longitude': 100.0 }, { 'latitude': 30.0, 'longitude': 200.0 }, { 'latitude': 60.0, 'longitude': 300.0 }] x_points = np.array([site['longitude'] for site in sites]) y_points = np.array([site['latitude'] for site in sites]) site_coords = np.stack((x_points, y_points), axis=1) plugin.global_coordinate_system = True sites_out, site_coords_out, out_x, out_y = ( plugin.check_sites_are_within_domain(sites, site_coords, x_points, y_points, self.global_orography)) self.assertArrayEqual(sites_out, sites) self.assertArrayEqual(site_coords_out, site_coords) self.assertArrayEqual(out_x, x_points) self.assertArrayEqual(out_y, y_points)
def test_some_invalid(self, warning_list=None): """Test case with some sites falling outside the regional domain.""" plugin = NeighbourSelection() sites = [{ 'projection_x_coordinate': 1.0E4, 'projection_y_coordinate': 1.0E4 }, { 'projection_x_coordinate': 1.0E5, 'projection_y_coordinate': 5.0E4 }, { 'projection_x_coordinate': 1.0E6, 'projection_y_coordinate': 1.0E5 }] x_points = np.array( [site['projection_x_coordinate'] for site in sites]) y_points = np.array( [site['projection_y_coordinate'] for site in sites]) site_coords = np.stack((x_points, y_points), axis=1) sites_out, site_coords_out, out_x, out_y = ( plugin.check_sites_are_within_domain(sites, site_coords, x_points, y_points, self.region_orography)) self.assertArrayEqual(sites_out, sites[0:2]) self.assertArrayEqual(site_coords_out[0:2], site_coords[0:2]) self.assertArrayEqual(out_x, x_points[0:2]) self.assertArrayEqual(out_y, y_points[0:2]) msg = "1 spot sites fall outside the grid" self.assertTrue(any([msg in str(warning) for warning in warning_list])) self.assertTrue( any(item.category == UserWarning for item in warning_list))
def test_different_cube_grids(self): """Test an error is raised if the land mask and orography cubes provided are on different grids.""" msg = 'Orography and land_mask cubes are not on the same' plugin = NeighbourSelection() with self.assertRaisesRegex(ValueError, msg): plugin.process(self.region_sites, self.region_orography, self.global_land_mask)
def test_global_land(self): """Test how the neighbour index changes when a land constraint is imposed. Here we expect to go 'west' to the first band of land which has an altitude of 5m. So we expect [1, 4, -3].""" plugin = NeighbourSelection(land_constraint=True, search_radius=1E7) result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[1, 4, -3]]] self.assertArrayEqual(result.data, expected)
def test_non_metre_spatial_dimensions(self): """Test an error is raised if a regional grid is provided for which the spatial coordinates do not have units of metres.""" self.region_orography.coord(axis='x').convert_units('feet') msg = 'Cube spatial coordinates for regional grids' plugin = NeighbourSelection() with self.assertRaisesRegex(ValueError, msg): plugin.process(self.region_sites, self.region_orography, self.region_land_mask)
def test_only_land(self): """Test that the expected number of nodes are created and that a tree is returned. In this case the number of nodes should be this should be equal to the number of land points.""" plugin = NeighbourSelection(land_constraint=True) result, result_nodes = plugin.build_KDTree(self.region_land_mask) expected_length = np.nonzero(self.region_land_mask.data)[0].shape[0] self.assertEqual(result_nodes.shape[0], expected_length) self.assertIsInstance(result, scipy.spatial.ckdtree.cKDTree)
def test_global_to_global(self): """Test coordinates generated when the input and output coordinate systems are the same, in this case Plate-Carree.""" plugin = NeighbourSelection() x_points = np.array([0, 10, 20]) y_points = np.array([0, 0, 10]) expected = np.stack((x_points, y_points), axis=1) result = plugin._transform_sites_coordinate_system( x_points, y_points, self.global_orography) self.assertArrayAlmostEqual(result, expected)
def test_global_attribute(self): """Test that a cube is returned with a model_grid_hash that matches that of the global input grids.""" expected = create_coordinate_hash(self.global_orography) plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) self.assertIsInstance(result, iris.cube.Cube) self.assertEqual(result.attributes['model_grid_hash'], expected)
def test_global_nearest(self): """Test that a cube is returned, here using a conventional site list with lat/lon site coordinates. Neighbour coordinates of [2, 4] are expected, with a vertical displacement of 2..""" plugin = NeighbourSelection() result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[2, 4, 2]]] self.assertIsInstance(result, iris.cube.Cube) self.assertArrayEqual(result.data, expected)
def test_basic(self): """Test that the expected number of nodes are created and that a tree is returned; this should be the lengths of the x and y coordinates multiplied in the simple case.""" plugin = NeighbourSelection() result, result_nodes = plugin.build_KDTree(self.region_land_mask) expected_length = (self.region_land_mask.shape[0] * self.region_land_mask.shape[1]) self.assertEqual(result_nodes.shape[0], expected_length) self.assertIsInstance(result, scipy.spatial.ckdtree.cKDTree)
def test_wmo_ids(self): """Test that the returned cube has the wmo_ids present when they are available. Should be None when they are not provided.""" plugin = NeighbourSelection() sites = self.global_sites + [self.global_sites.copy()[0].copy()] sites[1]["wmo_id"] = None expected = ["1", "None"] result = plugin.process(sites, self.global_orography, self.global_land_mask) self.assertArrayEqual(result.coord("wmo_id").points, expected)
def test_basic(self): """Test a (0, 0) coordinate conversion to geocentric cartesian. This is expected to give an x coordinate which is the semi-major axis of the globe defined in the global coordinate system.""" plugin = NeighbourSelection() x_points = np.array([0]) y_points = np.array([0]) result = plugin.geocentric_cartesian(self.global_orography, x_points, y_points) radius = self.global_orography.coord_system().semi_major_axis expected = [[radius, 0, 0]] self.assertArrayAlmostEqual(result, expected)
def test_error_for_unique_ids_longer_than_8_digits(self): """Test that an error is raised if the list of unique IDs contains values that are more than 8 characters in length. This limitation is inteded to give consistent length identifiers.""" plugin = NeighbourSelection(unique_site_id_key="met_office_site_id") sites = self.global_sites sites[0]["met_office_site_id"] = 123456789 msg = "Unique IDs should be 8 digits or less" with self.assertRaisesRegex(ValueError, msg): plugin.process(sites, self.global_orography, self.global_land_mask)
def test_region_to_region(self): """Test coordinates generated when the input and output coordinate systems are the same, in this case Lambert Azimuthal Equal Areas.""" plugin = NeighbourSelection( site_coordinate_system=self.region_projection.as_cartopy_crs()) x_points = np.array([0, 1, 2]) y_points = np.array([0, 0, 1]) expected = np.stack((x_points, y_points), axis=1) result = plugin._transform_sites_coordinate_system( x_points, y_points, self.region_orography) self.assertArrayAlmostEqual(result, expected)
def test_global_to_region(self): """Test coordinates generated when transforming from a global to regional coordinate system, in this case PlateCarree to Lambert Azimuthal Equal Areas.""" plugin = NeighbourSelection() x_points = np.array([0, 10, 20]) y_points = np.array([0, 0, 10]) expected = [[0., 0.], [1111782.53516264, 0.], [2189747.33076441, 1121357.32401753]] result = plugin._transform_sites_coordinate_system( x_points, y_points, self.region_orography) self.assertArrayAlmostEqual(result, expected)
def test_all_invalid_points(self): """Test a case where all nodes are beyond the imposed search_radius, so the returned value should be None.""" plugin = NeighbourSelection() site_altitude = 5. nodes = np.array([[0, 4], [1, 4], [2, 4], [3, 4], [4, 4]]) distance = np.full(5, np.inf) indices = np.arange(5) result = plugin.select_minimum_dz(self.region_orography, site_altitude, nodes, distance, indices) self.assertEqual(result, None)
def test_north_pole(self): """Test a (0, 90) coordinate conversion to geocentric cartesian, this being the north pole. This is expected to give an x coordinate which 0 and a z coordinate equivalent to the semi-major axis of the globe defined in the global coordinate system.""" plugin = NeighbourSelection() x_points = np.array([0]) y_points = np.array([90]) result = plugin.geocentric_cartesian(self.global_orography, x_points, y_points) radius = self.global_orography.coord_system().semi_major_axis expected = [[0, 0, radius]] self.assertArrayAlmostEqual(result, expected)
def test_region_to_global(self): """Test coordinates generated when transforming from a regional to global coordinate system, in this case Lambert Azimuthal Equal Areas to PlateCarree.""" plugin = NeighbourSelection( site_coordinate_system=self.region_projection.as_cartopy_crs()) x_points = np.array([0, 1, 2]) y_points = np.array([0, 0, 1]) expected = [[0., 0.], [8.98315284e-06, 0.], [1.79663057e-05, 9.04369476e-06]] result = plugin._transform_sites_coordinate_system( x_points, y_points, self.global_orography) self.assertArrayAlmostEqual(result, expected)
def test_global_land_minimum_dz(self): """Test how the neighbour index changes when a land constraint is imposed and a minimum height difference condition. Here we expect to go 'west' to the second band of land that we encounter, which has an altitude closer to that of the site. So we expect [0, 4, 1].""" plugin = NeighbourSelection(land_constraint=True, minimum_dz=True, search_radius=1E8) result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[0, 4, 1]]] self.assertArrayEqual(result.data, expected)
def test_global_tied_case_nearest_land(self): """Test which neighbour is returned in an artificial case in which two neighbouring grid points are identically close. Identical to the test above except for the land constraint is now applied, so the neigbour is found using the KDTree. Using the KDTree the neighbour to the east is returned everytime the test is run.""" self.global_sites[0]['longitude'] = -60.0 plugin = NeighbourSelection(land_constraint=True, search_radius=1E8) result = plugin.process(self.global_sites, self.global_orography, self.global_land_mask) expected = [[[4, 4, 2]]] self.assertArrayEqual(result.data, expected)
def test_region_attribute(self): """Test that a cube is returned with a model_grid_hash that matches that of the regional input grids.""" expected = create_coordinate_hash(self.region_orography) plugin = NeighbourSelection( site_coordinate_system=self.region_projection.as_cartopy_crs(), site_x_coordinate='projection_x_coordinate', site_y_coordinate='projection_y_coordinate') result = plugin.process(self.region_sites, self.region_orography, self.region_land_mask) self.assertIsInstance(result, iris.cube.Cube) self.assertEqual(result.attributes['model_grid_hash'], expected)
def test_basic(self): """Test that expected coordinates are returned.""" plugin = NeighbourSelection() x_points = np.array( [site['projection_x_coordinate'] for site in self.region_sites]) y_points = np.array( [site['projection_y_coordinate'] for site in self.region_sites]) site_coords = np.stack((x_points, y_points), axis=1) expected = [[2, 4]] result = plugin.get_nearest_indices(site_coords, self.region_orography) self.assertArrayEqual(result, expected)
def test_use_of_unique_ids(self): """Test that the returned cube has the unique_id present when they are provided. These are stored as 8 digits encoded as strings, so zero-padding is expected.""" plugin = NeighbourSelection(unique_site_id_key="met_office_site_id") sites = self.global_sites + [self.global_sites[0].copy()] sites[0]["met_office_site_id"] = sites[0]["wmo_id"] sites[1]["wmo_id"] = None sites[1]["met_office_site_id"] = 353 expected = ["00000001", "00000353"] result = plugin.process(sites, self.global_orography, self.global_land_mask) self.assertArrayEqual(result.coord("met_office_site_id").points, expected)
def test_basic(self): """Test a simple case where the first element in the provided lists has the smallest vertical displacement to the site. Expect the coordinates of the first node to be returned.""" plugin = NeighbourSelection() site_altitude = 3. nodes = np.array([[0, 4], [1, 4], [2, 4], [3, 4], [4, 4]]) distance = np.arange(5) indices = np.arange(5) result = plugin.select_minimum_dz(self.region_orography, site_altitude, nodes, distance, indices) self.assertArrayEqual(result, nodes[0])