def test_unallowed_cartopy(self): """Test the raising of an error when requesting a projection not in the allowed list.""" allowed = ['Mercator'] msg = 'Function/method/object "Miller" not available in module' with self.assertRaisesRegex(TypeError, msg): safe_eval('Miller', ccrs, allowed=allowed)
def test_cartopy_projection(self): """Test the return of a cartopy projection.""" allowed = ['Mercator', 'Miller'] result = safe_eval('Mercator', ccrs, allowed=allowed) self.assertEqual(result, ccrs.Mercator)
def process(orography, landmask, site_list, metadata_dict=None, all_methods=False, land_constraint=None, minimum_dz=None, search_radius=None, node_limit=None, site_coordinate_system=None, site_x_coordinate=None, site_y_coordinate=None): """Module to create neighbour cubes for extracting spot data. Determine grid point coordinates within the provided cubes that neighbour spot data sites defined within the provided JSON/Dictionary. If no options are set the returned cube will contain the nearest neighbour found for each site. Other constrained neighbour finding methods can be set with options below. 1. Nearest neighbour. 2. Nearest land point neighbour. 3. Nearest neighbour with minimum height difference. 4. Nearest land point neighbour with minimum height difference. Args: orography (iris.cube.Cube): Cube of model orography for the model grid on which neighbours are being found. landmask (iris.cube.Cube): Cube of model land mask for the model grid on which neighbours are being found. site_list (dict): Dictionary that contains the spot sites for which neighbouring grid points are to be found. metadata_dict (dict): Dictionary that can be used to modify the metadata of the returned cube. Default is None. all_methods (bool): If True, this will return a cube containing the nearest grid point neighbours to spot sites as defined by each possible combination of constraints. Default is False. land_constraint (bool): If True, this will return a cube containing the nearest grid point neighbours to spot sites that are also land points. May be used with the minimum_dz option. Default is None. minimum_dz (bool): If True, this will return a cube containing the nearest grid point neighbour to each spot site that is found, within a given search radius, to minimise the height difference between the two. May be used with the land_constraint option. Default is None. search_radius (float): The radius in metres about a spot site within which to search for a grid point neighbour that is land or which has a smaller height difference than the nearest. Default is None. node_limit (int): When searching within the defined search_radius for suitable neighbours, a KDTree is constructed. This node_limit prevents the tree from becoming too large for large search radii. A default of 36 will be set, which is to say the nearest 36 grid points will be considered. If the search radius is likely to contain more than 36 points, this value should be increased to ensure all point are considered. Default is None. site_coordinate_system (cartopy coordinate system): The coordinate system in which the site coordinates are provided within the site list. This must be provided as the name of a cartopy coordinate system. The Default will become PlateCarree. This can be a complete definition, including parameters required to modify a default system. e.g Miller(central_longitude=90) If a globe is required this can be specified as Globe(semimajor_axis=100, semiminor_axis=100) Default is None. site_x_coordinate (str): The key that identifies site x coordinates in the provided site dictionary. Defaults to longitude. Default is None. site_y_coordinate (str): The key that identifies site y coordinates in the provided site dictionary. Defaults to latitude. Default is None. Returns: result (iris.cube.Cube): The processed Cube. Raises: ValueError: If all_methods is used with land_constraint or minimum_dz. """ # Check valid options have been selected. if all_methods is True and (land_constraint or minimum_dz): raise ValueError( 'Cannot use all_methods option with other constraints.') # Filter kwargs for those expected by plugin and which are set. # This preserves the plugin defaults for unset options. args = { 'land_constraint': land_constraint, 'minimum_dz': minimum_dz, 'search_radius': search_radius, 'site_coordinate_system': site_coordinate_system, 'site_x_coordinate': site_x_coordinate, 'node_limit': node_limit, 'site_y_coordinate': site_y_coordinate } fargs = (site_list, orography, landmask) kwargs = {k: v for (k, v) in args.items() if v is not None} # Deal with coordinate systems for sites other than PlateCarree. if 'site_coordinate_system' in kwargs.keys(): scrs = kwargs['site_coordinate_system'] kwargs['site_coordinate_system'] = safe_eval(scrs, ccrs, PROJECTION_LIST) # Call plugin to generate neighbour cubes if all_methods: methods = [{ **kwargs, 'land_constraint': False, 'minimum_dz': False }, { **kwargs, 'land_constraint': True, 'minimum_dz': False }, { **kwargs, 'land_constraint': False, 'minimum_dz': True }, { **kwargs, 'land_constraint': True, 'minimum_dz': True }] all_methods = iris.cube.CubeList([]) for method in methods: all_methods.append(NeighbourSelection(**method).process(*fargs)) squeezed_cubes = iris.cube.CubeList([]) for index, cube in enumerate(all_methods): cube.coord('neighbour_selection_method').points = np.int32(index) squeezed_cubes.append(iris.util.squeeze(cube)) result = merge_cubes(squeezed_cubes) else: result = NeighbourSelection(**kwargs).process(*fargs) result = enforce_coordinate_ordering( result, ['spot_index', 'neighbour_selection_method', 'grid_attributes']) # Modify final metadata as described by provided JSON file. if metadata_dict: result = amend_metadata(result, **metadata_dict) return result
def test_iris_coords(self): """Test the return of an iris.coords component.""" allowed = ['coords'] result = safe_eval('coords', iris, allowed=allowed) self.assertEqual(result, iris.coords)
def main(argv=None): """Load in arguments and get going.""" description = ( "Determine grid point coordinates within the provided cubes that " "neighbour spot data sites defined within the provided JSON " "file. If no options are set the returned netCDF file will contain the" " nearest neighbour found for each site. Other constrained neighbour " "finding methods can be set with options below.") options = ("\n\nThese methods are:\n\n 1. nearest neighbour\n" " 2. nearest land point neighbour\n" " 3. nearest neighbour with minimum height difference\n" " 4. nearest land point neighbour with minimum height " "difference") parser = ArgParser(description=('\n'.join(wrap(description, width=79)) + options), formatter_class=RawDescriptionHelpFormatter) parser.add_argument("site_list_filepath", metavar="SITE_LIST_FILEPATH", help="Path to a JSON file that contains the spot sites" " for which neighbouring grid points are to be found.") parser.add_argument("orography_filepath", metavar="OROGRAPHY_FILEPATH", help="Path to a NetCDF file of model orography for the" " model grid on which neighbours are being found.") parser.add_argument("landmask_filepath", metavar="LANDMASK_FILEPATH", help="Path to a NetCDF file of model land mask for the" " model grid on which neighbours are being found.") parser.add_argument("output_filepath", metavar="OUTPUT_FILEPATH", help="The output path for the resulting NetCDF") parser.add_argument( "--all_methods", default=False, action='store_true', help="If set this will return a cube containing the nearest grid point" " neighbours to spot sites as defined by each possible combination of" " constraints.") group = parser.add_argument_group('Apply constraints to neighbour choice') group.add_argument( "--land_constraint", default=False, action='store_true', help="If set this will return a cube containing the nearest grid point" " neighbours to spot sites that are also land points. May be used with" " the minimum_dz option.") group.add_argument( "--minimum_dz", default=False, action='store_true', help="If set this will return a cube containing the nearest grid point" " neighbour to each spot site that is found, within a given search" " radius, to minimise the height difference between the two. May be" " used with the land_constraint option.") group.add_argument( "--search_radius", metavar="SEARCH_RADIUS", type=float, help="The radius in metres about a spot site within which to search" " for a grid point neighbour that is land or which has a smaller " " height difference than the nearest. The default value is 10000m " "(10km).") group.add_argument( "--node_limit", metavar="NODE_LIMIT", type=int, help="When searching within the defined search_radius for suitable " "neighbours, a KDTree is constructed. This node_limit prevents the " "tree from becoming too large for large search radii. A default of 36" " is set, which is to say the nearest 36 grid points will be " "considered. If the search_radius is likely to contain more than 36 " "points, this value should be increased to ensure all points are " "considered.") s_group = parser.add_argument_group('Site list options') s_group.add_argument( "--site_coordinate_system", metavar="SITE_COORDINATE_SYSTEM", help="The coordinate system in which the site coordinates are provided" " within the site list. This must be provided as the name of a cartopy" " coordinate system. The default is a PlateCarree system, with site" " coordinates given by latitude/longitude pairs. This can be a" " complete definition, including parameters required to modify a" " default system, e.g. Miller(central_longitude=90). If a globe is" " required this can be specified as e.g." " Globe(semimajor_axis=100, semiminor_axis=100).") s_group.add_argument( "--site_x_coordinate", metavar="SITE_X_COORDINATE", help="The x coordinate key within the JSON file. The plugin default is" " 'longitude', but can be changed using this option if required.") s_group.add_argument( "--site_y_coordinate", metavar="SITE_Y_COORDINATE", help="The y coordinate key within the JSON file. The plugin default is" " 'latitude', but can be changed using this option if required.") meta_group = parser.add_argument_group("Metadata") meta_group.add_argument( "--metadata_json", metavar="METADATA_JSON", default=None, help="If provided, this JSON file can be used to modify the metadata " "of the returned netCDF file. Defaults to None.") args = parser.parse_args(args=argv) # Open input files with open(args.site_list_filepath, 'r') as site_file: sitelist = json.load(site_file) orography = load_cube(args.orography_filepath) landmask = load_cube(args.landmask_filepath) fargs = (sitelist, orography, landmask) # Filter kwargs for those expected by plugin and which are set. # This preserves the plugin defaults for unset options. kwarg_list = [ 'land_constraint', 'minimum_dz', 'search_radius', 'site_coordinate_system', 'site_x_coordinate', 'node_limit', 'site_y_coordinate' ] kwargs = { k: v for (k, v) in vars(args).items() if k in kwarg_list and v is not None } # Deal with coordinate systems for sites other than PlateCarree. if 'site_coordinate_system' in kwargs.keys(): scrs = kwargs['site_coordinate_system'] kwargs['site_coordinate_system'] = safe_eval(scrs, ccrs, PROJECTION_LIST) # Check valid options have been selected. if args.all_methods is True and (kwargs['land_constraint'] is True or kwargs['minimum_dz'] is True): raise ValueError( 'Cannot use all_methods option with other constraints.') # Call plugin to generate neighbour cubes if args.all_methods: methods = [] methods.append({ **kwargs, 'land_constraint': False, 'minimum_dz': False }) methods.append({ **kwargs, 'land_constraint': True, 'minimum_dz': False }) methods.append({ **kwargs, 'land_constraint': False, 'minimum_dz': True }) methods.append({**kwargs, 'land_constraint': True, 'minimum_dz': True}) all_methods = iris.cube.CubeList([]) for method in methods: all_methods.append(NeighbourSelection(**method).process(*fargs)) squeezed_cubes = iris.cube.CubeList([]) for index, cube in enumerate(all_methods): cube.coord('neighbour_selection_method').points = index squeezed_cubes.append(iris.util.squeeze(cube)) result = merge_cubes(squeezed_cubes) else: result = NeighbourSelection(**kwargs).process(*fargs) result = enforce_coordinate_ordering( result, ['spot_index', 'neighbour_selection_method', 'grid_attributes']) # Modify final metadata as described by provided JSON file. if args.metadata_json: with open(args.metadata_json, 'r') as input_file: metadata_dict = json.load(input_file) result = amend_metadata(result, **metadata_dict) # Save the neighbour cube save_netcdf(result, args.output_filepath)