def __init__(self, src_grid_cube, tgt_grid_cube, method, extrapolation_mode): """ Create a regridder for conversions between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * tgt_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. * method: Either 'linear' or 'nearest'. * extrapolation_mode: Must be one of the following strings: * 'extrapolate' - The extrapolation points will be calculated by extending the gradient of the closest two points. * 'nan' - The extrapolation points will be be set to NaN. * 'error' - An exception will be raised, notifying an attempt to extrapolate. * 'mask' - The extrapolation points will always be masked, even if the source data is not a MaskedArray. * 'nanmask' - If the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. """ from iris.cube import Cube # Validity checks. if not isinstance(src_grid_cube, Cube): raise TypeError("'src_grid_cube' must be a Cube") if not isinstance(tgt_grid_cube, Cube): raise TypeError("'tgt_grid_cube' must be a Cube") # Snapshot the state of the cubes to ensure that the regridder # is impervious to external changes to the original source cubes. self._src_grid = snapshot_grid(src_grid_cube) self._tgt_grid = snapshot_grid(tgt_grid_cube) # Check the target grid units. for coord in self._tgt_grid: self._check_units(coord) # Whether to use linear or nearest-neighbour interpolation. if method not in ("linear", "nearest"): msg = "Regridding method {!r} not supported.".format(method) raise ValueError(msg) self._method = method # The extrapolation mode. if extrapolation_mode not in EXTRAPOLATION_MODES: msg = "Invalid extrapolation mode {!r}" raise ValueError(msg.format(extrapolation_mode)) self._extrapolation_mode = extrapolation_mode
def __init__(self, src_grid_cube, tgt_grid_cube, method, extrapolation_mode): """ Create a regridder for conversions between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * tgt_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. * method: Either 'linear' or 'nearest'. * extrapolation_mode: Must be one of the following strings: * 'extrapolate' - The extrapolation points will be calculated by extending the gradient of the closest two points. * 'nan' - The extrapolation points will be be set to NaN. * 'error' - An exception will be raised, notifying an attempt to extrapolate. * 'mask' - The extrapolation points will always be masked, even if the source data is not a MaskedArray. * 'nanmask' - If the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. """ # Validity checks. if not isinstance(src_grid_cube, iris.cube.Cube): raise TypeError("'src_grid_cube' must be a Cube") if not isinstance(tgt_grid_cube, iris.cube.Cube): raise TypeError("'tgt_grid_cube' must be a Cube") # Snapshot the state of the cubes to ensure that the regridder # is impervious to external changes to the original source cubes. self._src_grid = snapshot_grid(src_grid_cube) self._tgt_grid = snapshot_grid(tgt_grid_cube) # Check the target grid units. for coord in self._tgt_grid: self._check_units(coord) # Whether to use linear or nearest-neighbour interpolation. if method not in ('linear', 'nearest'): msg = 'Regridding method {!r} not supported.'.format(method) raise ValueError(msg) self._method = method # The extrapolation mode. if extrapolation_mode not in EXTRAPOLATION_MODES: msg = 'Invalid extrapolation mode {!r}' raise ValueError(msg.format(extrapolation_mode)) self._extrapolation_mode = extrapolation_mode
def __init__(self, src_grid_cube, target_grid_cube, extrapolation_mode='linear'): """ Create a linear regridder for conversions between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * target_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. Kwargs: * extrapolation_mode: Must be one of the following strings: * 'linear' - The extrapolation points will be calculated by extending the gradient of closest two points. * 'nan' - The extrapolation points will be be set to NaN. * 'error' - An exception will be raised, notifying an attempt to extrapolate. * 'mask' - The extrapolation points will always be masked, even if the source data is not a MaskedArray. * 'nanmask' - If the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. Default mode of extrapolation is 'linear' """ # Snapshot the state of the cubes to ensure that the regridder # is impervious to external changes to the original source cubes. self._src_grid = snapshot_grid(src_grid_cube) self._target_grid = snapshot_grid(target_grid_cube) # The extrapolation mode. if extrapolation_mode not in _LINEAR_EXTRAPOLATION_MODES: msg = 'Extrapolation mode {!r} not supported.' raise ValueError(msg.format(extrapolation_mode)) self._extrapolation_mode = extrapolation_mode # The need for an actual Cube is an implementation quirk # caused by the current usage of the experimental regrid # function. self._target_grid_cube_cache = None
def __init__(self, src_grid_cube, target_grid_cube, mdtol=1): """ Create an area-weighted regridder for conversions between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * target_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. Kwargs: * mdtol (float): Tolerance of missing data. The value returned in each element of the returned array will be masked if the fraction of masked data exceeds mdtol. mdtol=0 means no missing data is tolerated while mdtol=1 will mean the resulting element will be masked if and only if all the contributing elements of data are masked. Defaults to 1. .. Note:: Both source and target cubes must have an XY grid defined by separate X and Y dimensions with dimension coordinates. All of the XY dimension coordinates must also be bounded, and have the same coordinate system. """ from iris.experimental.regrid import ( _regrid_area_weighted_rectilinear_src_and_grid__prepare, ) # Snapshot the state of the source cube to ensure that the regridder is # impervious to external changes to the original cubes. self._src_grid = snapshot_grid(src_grid_cube) # Missing data tolerance. if not (0 <= mdtol <= 1): msg = "Value for mdtol must be in range 0 - 1, got {}." raise ValueError(msg.format(mdtol)) self._mdtol = mdtol # Store regridding information _regrid_info = _regrid_area_weighted_rectilinear_src_and_grid__prepare( src_grid_cube, target_grid_cube) ( src_x, src_y, src_x_dim, src_y_dim, self.grid_x, self.grid_y, self.meshgrid_x, self.meshgrid_y, self.weights_info, ) = _regrid_info
def __init__(self, src_cube, tgt_grid_cube, method, projection=None): """ Create a regridder for conversions between the source and target grids. Args: * src_cube: The :class:`~iris.cube.Cube` providing the source points. * tgt_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. * method: Either 'linear' or 'nearest'. * projection: The projection in which the interpolation is performed. If None, a PlateCarree projection is used. Defaults to None. """ # Validity checks. if not isinstance(src_cube, iris.cube.Cube): raise TypeError("'src_cube' must be a Cube") if not isinstance(tgt_grid_cube, iris.cube.Cube): raise TypeError("'tgt_grid_cube' must be a Cube") # Snapshot the state of the target cube to ensure that the regridder # is impervious to external changes to the original source cubes. self._tgt_grid = snapshot_grid(tgt_grid_cube) # Check the target grid units. for coord in self._tgt_grid: self._check_units(coord) # Whether to use linear or nearest-neighbour interpolation. if method not in ('linear', 'nearest'): msg = 'Regridding method {!r} not supported.'.format(method) raise ValueError(msg) self._method = method src_x_coord, src_y_coord = get_xy_coords(src_cube) if src_x_coord.coord_system != src_y_coord.coord_system: raise ValueError("'src_cube' lateral geographic coordinates have " "differing coordinate sytems.") if src_x_coord.coord_system is None: raise ValueError("'src_cube' lateral geographic coordinates have " "no coordinate sytem.") tgt_x_coord, tgt_y_coord = get_xy_dim_coords(tgt_grid_cube) if tgt_x_coord.coord_system != tgt_y_coord.coord_system: raise ValueError("'tgt_grid_cube' lateral geographic coordinates " "have differing coordinate sytems.") if tgt_x_coord.coord_system is None: raise ValueError("'tgt_grid_cube' lateral geographic coordinates " "have no coordinate sytem.") if projection is None: globe = src_x_coord.coord_system.as_cartopy_globe() projection = ccrs.Sinusoidal(globe=globe) self._projection = projection
def __init__(self, src_grid_cube, target_grid_cube, mdtol=1): """ Create an area-weighted regridder for conversions between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * target_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. Kwargs: * mdtol (float): Tolerance of missing data. The value returned in each element of the returned array will be masked if the fraction of masked data exceeds mdtol. mdtol=0 means no missing data is tolerated while mdtol=1 will mean the resulting element will be masked if and only if all the contributing elements of data are masked. Defaults to 1. .. Note:: Both sourge and target cubes must have an XY grid defined by separate X and Y dimensions with dimension coordinates. All of the XY dimension coordinates must also be bounded, and have the same cooordinate system. """ # Snapshot the state of the cubes to ensure that the regridder is # impervious to external changes to the original source cubes. self._src_grid = snapshot_grid(src_grid_cube) self._target_grid = snapshot_grid(target_grid_cube) # Missing data tolerance. if not (0 <= mdtol <= 1): msg = 'Value for mdtol must be in range 0 - 1, got {}.' raise ValueError(msg.format(mdtol)) self._mdtol = mdtol # The need for an actual Cube is an implementation quirk caused by the # current usage of the experimental regrid function. self._target_grid_cube_cache = None
def __init__(self, src_grid_cube, tgt_grid_cube): """ Creates a area-weighted regridder which uses an Anti-Grain Geometry (AGG) backend to rasterise the conversion between the source and target grids. Args: * src_grid_cube: The :class:`~iris.cube.Cube` providing the source grid. * tgt_grid_cube: The :class:`~iris.cube.Cube` providing the target grid. """ if not isinstance(src_grid_cube, iris.cube.Cube): raise TypeError('This source grid must be a cube.') if not isinstance(tgt_grid_cube, iris.cube.Cube): raise TypeError('The target grid must be a cube.') # Snapshot the state of the grid cubes to ensure that the regridder # is impervious to external changes to the original cubes. self._src_grid = snapshot_grid(src_grid_cube) self._gx, self._gy = snapshot_grid(tgt_grid_cube) # Check the grid cube coordinate system. if self._gx.coord_system is None: msg = 'The grid cube requires a native coordinate system.' raise ValueError(msg) # Cache the grid bounds converted to the source crs. self._gx_bounds = None self._gy_bounds = None # Cache the source contiguous bounds. self._sx_bounds = None self._sy_bounds = None
def __init__(self, src_cube, target_grid_cube): """ A nearest-neighbour regridder to perform regridding from the source grid to the target grid. This can then be applied to any source data with the same structure as the original 'src_cube'. Args: * src_cube: The :class:`~iris.cube.Cube` defining the source grid. The X and Y coordinates can have any shape, but must be mapped over the same cube dimensions. * target_grid_cube: A :class:`~iris.cube.Cube`, whose X and Y coordinates specify a desired target grid. The X and Y coordinates must be one-dimensional dimension coordinates, mapped to different dimensions. All other cube components are ignored. Returns: regridder : (object) A callable object with the interface: `result_cube = regridder(data)` where `data` is a cube with the same grid as the original `src_cube`, that is to be regridded to the `target_grid_cube`. .. Note:: For latitude-longitude coordinates, the nearest-neighbour distances are computed on the sphere, otherwise flat Euclidean distances are used. The source and target X and Y coordinates must all have the same coordinate system, which may also be None. If any X and Y coordinates are latitudes or longitudes, they *all* must be. Otherwise, the corresponding X and Y coordinates must have the same units in the source and grid cubes. """ # Make a copy of the source cube, so we can convert coordinate units. src_cube = src_cube.copy() # Snapshot the target grid and check it is a "normal" grid. tgt_x_coord, tgt_y_coord = snapshot_grid(target_grid_cube) # Check that the source has unique X and Y coords over common dims. if not src_cube.coords(axis="x") or not src_cube.coords(axis="y"): msg = "Source cube must have X- and Y-axis coordinates." raise ValueError(msg) src_x_coord = src_cube.coord(axis="x") src_y_coord = src_cube.coord(axis="y") if src_cube.coord_dims(src_x_coord) != src_cube.coord_dims( src_y_coord ): msg = ( "Source cube X and Y coordinates must have the same " "cube dimensions." ) raise ValueError(msg) # Record *copies* of the original grid coords, in the desired # dimension order. # This lets us convert the actual ones in use to units of "degrees". self.src_grid_coords = [src_y_coord.copy(), src_x_coord.copy()] self.tgt_grid_coords = [tgt_y_coord.copy(), tgt_x_coord.copy()] # Check that all XY coords have suitable coordinate systems and units. coords_all = [src_x_coord, src_y_coord, tgt_x_coord, tgt_y_coord] cs = coords_all[0].coord_system if not all(coord.coord_system == cs for coord in coords_all): msg = ( "Source and target cube X and Y coordinates must all have " "the same coordinate system." ) raise ValueError(msg) # Check *all* X and Y coords are lats+lons, if any are. latlons = [ "latitude" in coord.name() or "longitude" in coord.name() for coord in coords_all ] if any(latlons) and not all(latlons): msg = ( "If any X and Y coordinates are latitudes/longitudes, " "then they all must be." ) raise ValueError(msg) self.grid_is_latlon = any(latlons) if self.grid_is_latlon: # Convert all XY coordinates to units of "degrees". # N.B. already copied the target grid, so the result matches that. for coord in coords_all: try: coord.convert_units("degrees") except ValueError: msg = ( "Coordinate {!r} has units of {!r}, which does not " 'convert to "degrees".' ) raise ValueError( msg.format(coord.name(), str(coord.units)) ) else: # Check that source and target have the same X and Y units. if ( src_x_coord.units != tgt_x_coord.units or src_y_coord.units != tgt_y_coord.units ): msg = ( "Source and target cube X and Y coordinates must " "have the same units." ) raise ValueError(msg) # Record the resulting grid shape. self.tgt_grid_shape = tgt_y_coord.shape + tgt_x_coord.shape # Calculate sample points as 2d arrays, like broadcast (NY,1)*(1,NX). x_2d, y_2d = _meshgrid(tgt_x_coord.points, tgt_y_coord.points) # Cast as a "trajectory", to suit the method used. self.trajectory = ( (tgt_x_coord.name(), x_2d.flatten()), (tgt_y_coord.name(), y_2d.flatten()), )
def __init__(self, src_cube, target_grid_cube): """ A nearest-neighbour regridder to perform regridding from the source grid to the target grid. This can then be applied to any source data with the same structure as the original 'src_cube'. Args: * src_cube: The :class:`~iris.cube.Cube` defining the source grid. The X and Y coordinates can have any shape, but must be mapped over the same cube dimensions. * target_grid_cube: A :class:`~iris.cube.Cube`, whose X and Y coordinates specify a desired target grid. The X and Y coordinates must be one-dimensional dimension coordinates, mapped to different dimensions. All other cube components are ignored. Returns: regridder : (object) A callable object with the interface: `result_cube = regridder(data)` where `data` is a cube with the same grid as the original `src_cube`, that is to be regridded to the `target_grid_cube`. .. Note:: For latitude-longitude coordinates, the nearest-neighbour distances are computed on the sphere, otherwise flat Euclidean distances are used. The source and target X and Y coordinates must all have the same coordinate system, which may also be None. If any X and Y coordinates are latitudes or longitudes, they *all* must be. Otherwise, the corresponding X and Y coordinates must have the same units in the source and grid cubes. """ # Make a copy of the source cube, so we can convert coordinate units. src_cube = src_cube.copy() # Snapshot the target grid and check it is a "normal" grid. tgt_x_coord, tgt_y_coord = snapshot_grid(target_grid_cube) # Check that the source has unique X and Y coords over common dims. if (not src_cube.coords(axis='x') or not src_cube.coords(axis='y')): msg = 'Source cube must have X- and Y-axis coordinates.' raise ValueError(msg) src_x_coord = src_cube.coord(axis='x') src_y_coord = src_cube.coord(axis='y') if (src_cube.coord_dims(src_x_coord) != src_cube.coord_dims(src_y_coord)): msg = ('Source cube X and Y coordinates must have the same ' 'cube dimensions.') raise ValueError(msg) # Record *copies* of the original grid coords, in the desired # dimension order. # This lets us convert the actual ones in use to units of "degrees". self.src_grid_coords = [src_y_coord.copy(), src_x_coord.copy()] self.tgt_grid_coords = [tgt_y_coord.copy(), tgt_x_coord.copy()] # Check that all XY coords have suitable coordinate systems and units. coords_all = [src_x_coord, src_y_coord, tgt_x_coord, tgt_y_coord] cs = coords_all[0].coord_system if not all(coord.coord_system == cs for coord in coords_all): msg = ('Source and target cube X and Y coordinates must all have ' 'the same coordinate system.') raise ValueError(msg) # Check *all* X and Y coords are lats+lons, if any are. latlons = ['latitude' in coord.name() or 'longitude' in coord.name() for coord in coords_all] if any(latlons) and not all(latlons): msg = ('If any X and Y coordinates are latitudes/longitudes, ' 'then they all must be.') raise ValueError(msg) self.grid_is_latlon = any(latlons) if self.grid_is_latlon: # Convert all XY coordinates to units of "degrees". # N.B. already copied the target grid, so the result matches that. for coord in coords_all: try: coord.convert_units('degrees') except ValueError: msg = ('Coordinate {!r} has units of {!r}, which does not ' 'convert to "degrees".') raise ValueError(msg.format(coord.name(), str(coord.units))) else: # Check that source and target have the same X and Y units. if (src_x_coord.units != tgt_x_coord.units or src_y_coord.units != tgt_y_coord.units): msg = ('Source and target cube X and Y coordinates must ' 'have the same units.') raise ValueError(msg) # Record the resulting grid shape. self.tgt_grid_shape = tgt_y_coord.shape + tgt_x_coord.shape # Calculate sample points as 2d arrays, like broadcast (NY,1)*(1,NX). x_2d, y_2d = np.meshgrid(tgt_x_coord.points, tgt_y_coord.points) # Cast as a "trajectory", to suit the method used. self.trajectory = ((tgt_x_coord.name(), x_2d.flatten()), (tgt_y_coord.name(), y_2d.flatten()))