def setUp(self): # Load a source cube, then generate an interpolator instance, calculate # the interpolation weights and set up a target grid. self.cube = stock.simple_2d() x_points = self.cube.coord('bar').points y_points = self.cube.coord('foo').points self.interpolator = _RegularGridInterpolator([x_points, y_points], self.cube.data, method='linear', bounds_error=False, fill_value=None) newx = x_points + 0.7 newy = y_points + 0.7 d_0 = self.cube.data[0, 0] d_1 = self.cube.data[0, 1] d_2 = self.cube.data[1, 0] d_3 = self.cube.data[1, 1] px_0, px_1 = x_points[0], x_points[1] py_0, py_1 = y_points[0], y_points[1] px_t = px_0 + 0.7 py_t = py_0 + 0.7 dyt_0 = self._interpolate_point(py_t, py_0, py_1, d_0, d_1) dyt_1 = self._interpolate_point(py_t, py_0, py_1, d_2, d_3) self.test_increment = self._interpolate_point(px_t, px_0, px_1, dyt_0, dyt_1) xv, yv = np.meshgrid(newy, newx) self.tgrid = np.dstack((yv, xv)) self.weights = self.interpolator.compute_interp_weights(self.tgrid)
def setUp(self): # Load a source cube, then generate an interpolator instance, calculate # the interpolation weights and set up a target grid. self.cube = stock.simple_2d() x_points = self.cube.coord("bar").points y_points = self.cube.coord("foo").points self.interpolator = _RegularGridInterpolator( [x_points, y_points], self.cube.data, method="linear", bounds_error=False, fill_value=None, ) newx = x_points + 0.7 newy = y_points + 0.7 d_0 = self.cube.data[0, 0] d_1 = self.cube.data[0, 1] d_2 = self.cube.data[1, 0] d_3 = self.cube.data[1, 1] px_0, px_1 = x_points[0], x_points[1] py_0, py_1 = y_points[0], y_points[1] px_t = px_0 + 0.7 py_t = py_0 + 0.7 dyt_0 = self._interpolate_point(py_t, py_0, py_1, d_0, d_1) dyt_1 = self._interpolate_point(py_t, py_0, py_1, d_2, d_3) self.test_increment = self._interpolate_point( px_t, px_0, px_1, dyt_0, dyt_1 ) xv, yv = np.meshgrid(newy, newx) self.tgrid = np.dstack((yv, xv)) self.weights = self.interpolator.compute_interp_weights(self.tgrid)
def _interpolate(self, data, interp_points): """ Interpolate a data array over N dimensions. Create and cache the underlying interpolator instance before invoking it to perform interpolation over the data at the given coordinate point values. * data (ndarray): A data array, to be interpolated in its first 'N' dimensions. * interp_points (ndarray): An array of interpolation coordinate values. Its shape is (..., N) where N is the number of interpolation dimensions. "interp_points[..., i]" are interpolation point values for the i'th coordinate, which is mapped to the i'th data dimension. The other (leading) dimensions index over the different required sample points. Returns: A :class:`np.ndarray`. Its shape is "points_shape + extra_shape", where "extra_shape" is the remaining non-interpolated dimensions of the data array (i.e. 'data.shape[N:]'), and "points_shape" is the leading dimensions of interp_points, (i.e. 'interp_points.shape[:-1]'). """ dtype = self._interpolated_dtype(data.dtype) if data.dtype != dtype: # Perform dtype promotion. data = data.astype(dtype) if self._interpolator is None: # Cache the interpolator instance. bounds_error, fill_value = _LINEAR_EXTRAPOLATION_MODES[self._mode] self._interpolator = _RegularGridInterpolator(self._src_points, data, bounds_error=False, fill_value=None) # The constructor of the _RegularGridInterpolator class does # some unnecessary checks on these values, so we set them # afterwards instead. Sneaky. ;-) self._interpolator.bounds_error = bounds_error self._interpolator.fill_value = fill_value else: self._interpolator.values = data result = self._interpolator(interp_points) if result.dtype != data.dtype: # Cast the data dtype to be as expected. Note that, the dtype # of the interpolated result is influenced by the dtype of the # interpolation points. result = result.astype(data.dtype) return result
def _interpolate(self, data, interp_points): """ Interpolate a data array over N dimensions. Create and cache the underlying interpolator instance before invoking it to perform interpolation over the data at the given coordinate point values. * data (ndarray): A data array, to be interpolated in its first 'N' dimensions. * interp_points (ndarray): An array of interpolation coordinate values. Its shape is (..., N) where N is the number of interpolation dimensions. "interp_points[..., i]" are interpolation point values for the i'th coordinate, which is mapped to the i'th data dimension. The other (leading) dimensions index over the different required sample points. Returns: A :class:`np.ndarray`. Its shape is "points_shape + extra_shape", where "extra_shape" is the remaining non-interpolated dimensions of the data array (i.e. 'data.shape[N:]'), and "points_shape" is the leading dimensions of interp_points, (i.e. 'interp_points.shape[:-1]'). """ dtype = self._interpolated_dtype(data.dtype) if data.dtype != dtype: # Perform dtype promotion. data = data.astype(dtype) if self._interpolator is None: # Cache the interpolator instance. self._interpolator = _RegularGridInterpolator(self._src_points, data, bounds_error=False, fill_value=None) # The constructor of the _RegularGridInterpolator class does # some unnecessary checks on these values, so we set them # afterwards instead. Sneaky. ;-) bounds_error, fill_value = _LINEAR_EXTRAPOLATION_MODES[self._mode] self._interpolator.bounds_error = bounds_error self._interpolator.fill_value = fill_value else: self._interpolator.values = data result = self._interpolator(interp_points) if result.dtype != data.dtype: # Cast the data dtype to be as expected. Note that, the dtype # of the interpolated result is influenced by the dtype of the # interpolation points. result = result.astype(data.dtype) return result
def _regrid(src_data, x_dim, y_dim, src_x_coord, src_y_coord, sample_grid_x, sample_grid_y, method='linear', extrapolation_mode='nanmask'): """ Regrid the given data from the src grid to the sample grid. The result will be a MaskedArray if either/both of: - the source array is a MaskedArray, - the extrapolation_mode is 'mask' and the result requires extrapolation. If the result is a MaskedArray the mask for each element will be set if either/both of: - there is a non-zero contribution from masked items in the input data - the element requires extrapolation and the extrapolation_mode dictates a masked value. Args: * src_data: An N-dimensional NumPy array or MaskedArray. * x_dim: The X dimension within `src_data`. * y_dim: The Y dimension within `src_data`. * src_x_coord: The X :class:`iris.coords.DimCoord`. * src_y_coord: The Y :class:`iris.coords.DimCoord`. * sample_grid_x: A 2-dimensional array of sample X values. * sample_grid_y: A 2-dimensional array of sample Y values. Kwargs: * method: Either 'linear' or 'nearest'. The default method is 'linear'. * extrapolation_mode: Must be one of the following strings: * 'linear' - 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' - A ValueError 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. The default mode of extrapolation is 'nanmask'. Returns: The regridded data as an N-dimensional NumPy array. The lengths of the X and Y dimensions will now match those of the sample grid. """ # # XXX: At the moment requires to be a static method as used by # experimental regrid_area_weighted_rectilinear_src_and_grid # if sample_grid_x.shape != sample_grid_y.shape: raise ValueError('Inconsistent sample grid shapes.') if sample_grid_x.ndim != 2: raise ValueError('Sample grid must be 2-dimensional.') # Prepare the result data array shape = list(src_data.shape) assert shape[x_dim] == src_x_coord.shape[0] assert shape[y_dim] == src_y_coord.shape[0] shape[y_dim] = sample_grid_x.shape[0] shape[x_dim] = sample_grid_x.shape[1] # If we're given integer values, convert them to the smallest # possible float dtype that can accurately preserve the values. dtype = src_data.dtype if dtype.kind == 'i': dtype = np.promote_types(dtype, np.float16) if isinstance(src_data, ma.MaskedArray): data = ma.empty(shape, dtype=dtype) data.mask = np.zeros(data.shape, dtype=np.bool) else: data = np.empty(shape, dtype=dtype) # The interpolation class requires monotonically increasing # coordinates, so flip the coordinate(s) and data if the aren't. reverse_x = src_x_coord.points[0] > src_x_coord.points[1] reverse_y = src_y_coord.points[0] > src_y_coord.points[1] flip_index = [slice(None)] * src_data.ndim if reverse_x: src_x_coord = src_x_coord[::-1] flip_index[x_dim] = slice(None, None, -1) if reverse_y: src_y_coord = src_y_coord[::-1] flip_index[y_dim] = slice(None, None, -1) src_data = src_data[tuple(flip_index)] if src_x_coord.circular: x_points, src_data = extend_circular_coord_and_data(src_x_coord, src_data, x_dim) else: x_points = src_x_coord.points # Slice out the first full 2D piece of data for construction of the # interpolator. index = [0] * src_data.ndim index[x_dim] = index[y_dim] = slice(None) initial_data = src_data[tuple(index)] if y_dim < x_dim: initial_data = initial_data.T # Construct the interpolator, we will fill in any values out of bounds # manually. interpolator = _RegularGridInterpolator([x_points, src_y_coord.points], initial_data, method=method, bounds_error=False, fill_value=None) # The constructor of the _RegularGridInterpolator class does # some unnecessary checks on these values, so we set them # afterwards instead. Sneaky. ;-) try: mode = EXTRAPOLATION_MODES[extrapolation_mode] except KeyError: raise ValueError('Invalid extrapolation mode.') interpolator.bounds_error = mode.bounds_error interpolator.fill_value = mode.fill_value # Construct the target coordinate points array, suitable for passing to # the interpolator multiple times. interp_coords = [sample_grid_x.astype(np.float64)[..., np.newaxis], sample_grid_y.astype(np.float64)[..., np.newaxis]] # Map all the requested values into the range of the source # data (centred over the centre of the source data to allow # extrapolation where required). min_x, max_x = x_points.min(), x_points.max() min_y, max_y = src_y_coord.points.min(), src_y_coord.points.max() if src_x_coord.units.modulus: modulus = src_x_coord.units.modulus offset = (max_x + min_x - modulus) * 0.5 interp_coords[0] -= offset interp_coords[0] = (interp_coords[0] % modulus) + offset interp_coords = np.dstack(interp_coords) def interpolate(data): # Update the interpolator for this data slice. data = data.astype(interpolator.values.dtype) if y_dim < x_dim: data = data.T interpolator.values = data data = interpolator(interp_coords) if y_dim > x_dim: data = data.T return data # Build up a shape suitable for passing to ndindex, inside the loop we # will insert slice(None) on the data indices. iter_shape = list(shape) iter_shape[x_dim] = iter_shape[y_dim] = 1 # Iterate through each 2d slice of the data, updating the interpolator # with the new data as we go. for index in np.ndindex(tuple(iter_shape)): index = list(index) index[x_dim] = index[y_dim] = slice(None) src_subset = src_data[tuple(index)] interpolator.fill_value = mode.fill_value data[tuple(index)] = interpolate(src_subset) if isinstance(data, ma.MaskedArray) or mode.force_mask: # NB. np.ma.getmaskarray returns an array of `False` if # `src_subset` is not a masked array. src_mask = np.ma.getmaskarray(src_subset) interpolator.fill_value = mode.mask_fill_value mask_fraction = interpolate(src_mask) new_mask = (mask_fraction > 0) if np.ma.isMaskedArray(data): data.mask[tuple(index)] = new_mask elif np.any(new_mask): # Set mask=False to ensure we have an expanded mask array. data = np.ma.MaskedArray(data, mask=False) data.mask[tuple(index)] = new_mask return data
def _interpolate(self, data, interp_points): """ Interpolate a data array over N dimensions. Create and cache the underlying interpolator instance before invoking it to perform interpolation over the data at the given coordinate point values. * data (ndarray): A data array, to be interpolated in its first 'N' dimensions. * interp_points (ndarray): An array of interpolation coordinate values. Its shape is (..., N) where N is the number of interpolation dimensions. "interp_points[..., i]" are interpolation point values for the i'th coordinate, which is mapped to the i'th data dimension. The other (leading) dimensions index over the different required sample points. Returns: A :class:`np.ndarray`. Its shape is "points_shape + extra_shape", where "extra_shape" is the remaining non-interpolated dimensions of the data array (i.e. 'data.shape[N:]'), and "points_shape" is the leading dimensions of interp_points, (i.e. 'interp_points.shape[:-1]'). """ dtype = self._interpolated_dtype(data.dtype) if data.dtype != dtype: # Perform dtype promotion. data = data.astype(dtype) mode = _LINEAR_EXTRAPOLATION_MODES[self._mode] if self._interpolator is None: # Cache the interpolator instance. # NB. The constructor of the _RegularGridInterpolator class does # some unnecessary checks on the fill_value parameter, # so we set it afterwards instead. Sneaky. ;-) self._interpolator = _RegularGridInterpolator( self._src_points, data, bounds_error=mode.bounds_error, fill_value=None) else: self._interpolator.values = data # We may be re-using a cached interpolator, so ensure the fill # value is set appropriately for extrapolating data values. self._interpolator.fill_value = mode.fill_value result = self._interpolator(interp_points) if result.dtype != data.dtype: # Cast the data dtype to be as expected. Note that, the dtype # of the interpolated result is influenced by the dtype of the # interpolation points. result = result.astype(data.dtype) if np.ma.isMaskedArray(data) or mode.force_mask: # NB. np.ma.getmaskarray returns an array of `False` if # `data` is not a masked array. src_mask = np.ma.getmaskarray(data) # Switch the extrapolation to work with mask values. self._interpolator.fill_value = mode.mask_fill_value self._interpolator.values = src_mask mask_fraction = self._interpolator(interp_points) new_mask = (mask_fraction > 0) if isinstance(data, ma.MaskedArray) or np.any(new_mask): result = np.ma.MaskedArray(result, new_mask) return result
def _regrid(src_data, x_dim, y_dim, src_x_coord, src_y_coord, sample_grid_x, sample_grid_y, method='linear', extrapolation_mode='nanmask'): """ Regrid the given data from the src grid to the sample grid. The result will be a MaskedArray if either/both of: - the source array is a MaskedArray, - the extrapolation_mode is 'mask' and the result requires extrapolation. If the result is a MaskedArray the mask for each element will be set if either/both of: - there is a non-zero contribution from masked items in the input data - the element requires extrapolation and the extrapolation_mode dictates a masked value. Args: * src_data: An N-dimensional NumPy array or MaskedArray. * x_dim: The X dimension within `src_data`. * y_dim: The Y dimension within `src_data`. * src_x_coord: The X :class:`iris.coords.DimCoord`. * src_y_coord: The Y :class:`iris.coords.DimCoord`. * sample_grid_x: A 2-dimensional array of sample X values. * sample_grid_y: A 2-dimensional array of sample Y values. Kwargs: * method: Either 'linear' or 'nearest'. The default method is 'linear'. * extrapolation_mode: Must be one of the following strings: * 'linear' - 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' - A ValueError 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. The default mode of extrapolation is 'nanmask'. Returns: The regridded data as an N-dimensional NumPy array. The lengths of the X and Y dimensions will now match those of the sample grid. """ # # XXX: At the moment requires to be a static method as used by # experimental regrid_area_weighted_rectilinear_src_and_grid # if sample_grid_x.shape != sample_grid_y.shape: raise ValueError('Inconsistent sample grid shapes.') if sample_grid_x.ndim != 2: raise ValueError('Sample grid must be 2-dimensional.') # Prepare the result data array shape = list(src_data.shape) assert shape[x_dim] == src_x_coord.shape[0] assert shape[y_dim] == src_y_coord.shape[0] shape[y_dim] = sample_grid_x.shape[0] shape[x_dim] = sample_grid_x.shape[1] dtype = src_data.dtype if method == 'linear': # If we're given integer values, convert them to the smallest # possible float dtype that can accurately preserve the values. if dtype.kind == 'i': dtype = np.promote_types(dtype, np.float16) if ma.isMaskedArray(src_data): data = ma.empty(shape, dtype=dtype) data.mask = np.zeros(data.shape, dtype=np.bool) else: data = np.empty(shape, dtype=dtype) # The interpolation class requires monotonically increasing # coordinates, so flip the coordinate(s) and data if the aren't. reverse_x = src_x_coord.points[0] > src_x_coord.points[1] reverse_y = src_y_coord.points[0] > src_y_coord.points[1] flip_index = [slice(None)] * src_data.ndim if reverse_x: src_x_coord = src_x_coord[::-1] flip_index[x_dim] = slice(None, None, -1) if reverse_y: src_y_coord = src_y_coord[::-1] flip_index[y_dim] = slice(None, None, -1) src_data = src_data[tuple(flip_index)] if src_x_coord.circular: x_points, src_data = extend_circular_coord_and_data( src_x_coord, src_data, x_dim) else: x_points = src_x_coord.points # Slice out the first full 2D piece of data for construction of the # interpolator. index = [0] * src_data.ndim index[x_dim] = index[y_dim] = slice(None) initial_data = src_data[tuple(index)] if y_dim < x_dim: initial_data = initial_data.T # Construct the interpolator, we will fill in any values out of bounds # manually. interpolator = _RegularGridInterpolator([x_points, src_y_coord.points], initial_data, method=method, bounds_error=False, fill_value=None) # The constructor of the _RegularGridInterpolator class does # some unnecessary checks on these values, so we set them # afterwards instead. Sneaky. ;-) try: mode = EXTRAPOLATION_MODES[extrapolation_mode] except KeyError: raise ValueError('Invalid extrapolation mode.') interpolator.bounds_error = mode.bounds_error interpolator.fill_value = mode.fill_value # Construct the target coordinate points array, suitable for passing to # the interpolator multiple times. interp_coords = [ sample_grid_x.astype(np.float64)[..., np.newaxis], sample_grid_y.astype(np.float64)[..., np.newaxis] ] # Map all the requested values into the range of the source # data (centred over the centre of the source data to allow # extrapolation where required). min_x, max_x = x_points.min(), x_points.max() min_y, max_y = src_y_coord.points.min(), src_y_coord.points.max() if src_x_coord.units.modulus: modulus = src_x_coord.units.modulus offset = (max_x + min_x - modulus) * 0.5 interp_coords[0] -= offset interp_coords[0] = (interp_coords[0] % modulus) + offset interp_coords = np.dstack(interp_coords) weights = interpolator.compute_interp_weights(interp_coords) def interpolate(data): # Update the interpolator for this data slice. data = data.astype(interpolator.values.dtype) if y_dim < x_dim: data = data.T interpolator.values = data data = interpolator.interp_using_pre_computed_weights(weights) if y_dim > x_dim: data = data.T return data # Build up a shape suitable for passing to ndindex, inside the loop we # will insert slice(None) on the data indices. iter_shape = list(shape) iter_shape[x_dim] = iter_shape[y_dim] = 1 # Iterate through each 2d slice of the data, updating the interpolator # with the new data as we go. for index in np.ndindex(tuple(iter_shape)): index = list(index) index[x_dim] = index[y_dim] = slice(None) src_subset = src_data[tuple(index)] interpolator.fill_value = mode.fill_value data[tuple(index)] = interpolate(src_subset) if ma.isMaskedArray(data) or mode.force_mask: # NB. np.ma.getmaskarray returns an array of `False` if # `src_subset` is not a masked array. src_mask = np.ma.getmaskarray(src_subset) interpolator.fill_value = mode.mask_fill_value mask_fraction = interpolate(src_mask) new_mask = (mask_fraction > 0) if np.ma.isMaskedArray(data): data.mask[tuple(index)] = new_mask elif np.any(new_mask): # Set mask=False to ensure we have an expanded mask array. data = np.ma.MaskedArray(data, mask=False) data.mask[tuple(index)] = new_mask return data
def _regrid_iris_coastline_correction_deprecated( self, input_cubes, algorithm='linear'): """ Use iris.analysis._interpolate for coastline correction and then combine with Iris.regrid; For 'area-weighted', use '_regrid_iris_two_stage' for 'cc'. ------ Args: input_cubes: the source input cubes algorithm: a string descrbing the regridding scheme used, i.e. only "linear" and "nearest" Returns: The interpolated cube with coastline correction. """ # kafka_log, full_log = getLoggers() # If the input is only a param_cube, turn it into CubeList if not isinstance(input_cubes, iris.cube.CubeList): input_cube_list = iris.cube.CubeList([]) input_cube_list.append(input_cubes) input_cubes = input_cube_list regridded_cubes = iris.cube.CubeList([]) if self.lsm_src is None or self.lsm_tgt is None: raise ValueError("Need land/sea mask to initialize MdsRegridder!") for cube in input_cubes: full_log.info("Processing " + cube.name()) # Need check if the input cube has the same shape of grid if cube.shape != self.lsm_src.shape: cube = cube.regrid(self.lsm_src, iris.analysis.Linear()) # Separate land/sea data cube_src_land_data = np.where( (self.lsm_src.data != 0), cube.data, np.nan) cube_src_sea_data = np.where( (self.lsm_src.data == 0), cube.data, np.nan) # Prepare the interpolator # We need to extropolate the points outside bounds # So set bounds_error=False, fill_value=None interpolator_land = _RegularGridInterpolator( (self.lat_src, self.lon_src), cube_src_land_data, method=algorithm, bounds_error=False, fill_value=None) interpolator_sea = _RegularGridInterpolator( (self.lat_src, self.lon_src), cube_src_sea_data, method=algorithm, bounds_error=False, fill_value=None) # Interpolate separately by land/sea output_data_land = self._interp_masked_grid( interpolator_land, self.grid_tgt) output_data_sea = self._interp_masked_grid( interpolator_sea, self.grid_tgt) # Combine the data combined_data = np.where( (self.lsm_tgt.data == 0), output_data_sea, output_data_land) # Since the combined_data has 'nan', where it is a point # along the coastline. Find it and do correction coast_pnt_bool = np.isnan(combined_data) # Now retrieving the points being both coastline and land coast_pnt_bool_land = np.logical_and( coast_pnt_bool, self.lsm_land_bool_tgt) coast_points_land = self.grid_tgt[coast_pnt_bool_land] # The way to pass an array of coast points to # 'latlons' is much faster. out_value_land = self._regrid_coastline_pnt( cube, self.grid_src, coast_points_land, self.lsm_land_bool_src, algorithm) # Do the same with sea coast_pnt_bool_sea = np.logical_and( coast_pnt_bool, self.lsm_sea_bool_tgt) coast_points_sea = self.grid_tgt[coast_pnt_bool_sea] # More works (using Cython) need to optimize the function out_value_sea = self._regrid_coastline_pnt( cube, self.grid_src, coast_points_sea, self.lsm_sea_bool_src, algorithm) # Now need to replace those coastline points with value nan combined_data[coast_pnt_bool_land] = out_value_land combined_data[coast_pnt_bool_sea] = out_value_sea drv_cube = cube.regrid(self.topo_tgt, iris.analysis.Linear()) drv_cube.data = combined_data regridded_cubes.append(drv_cube) return regridded_cubes
def _interpolate(self, data, interp_points): """ Interpolate a data array over N dimensions. Create and cache the underlying interpolator instance before invoking it to perform interpolation over the data at the given coordinate point values. * data (ndarray): A data array, to be interpolated in its first 'N' dimensions. * interp_points (ndarray): An array of interpolation coordinate values. Its shape is (..., N) where N is the number of interpolation dimensions. "interp_points[..., i]" are interpolation point values for the i'th coordinate, which is mapped to the i'th data dimension. The other (leading) dimensions index over the different required sample points. Returns: A :class:`np.ndarray`. Its shape is "points_shape + extra_shape", where "extra_shape" is the remaining non-interpolated dimensions of the data array (i.e. 'data.shape[N:]'), and "points_shape" is the leading dimensions of interp_points, (i.e. 'interp_points.shape[:-1]'). """ dtype = self._interpolated_dtype(data.dtype) if data.dtype != dtype: # Perform dtype promotion. data = data.astype(dtype) mode = EXTRAPOLATION_MODES[self._mode] if self._interpolator is None: # Cache the interpolator instance. # NB. The constructor of the _RegularGridInterpolator class does # some unnecessary checks on the fill_value parameter, # so we set it afterwards instead. Sneaky. ;-) self._interpolator = _RegularGridInterpolator( self._src_points, data, method=self.method, bounds_error=mode.bounds_error, fill_value=None) else: self._interpolator.values = data # We may be re-using a cached interpolator, so ensure the fill # value is set appropriately for extrapolating data values. self._interpolator.fill_value = mode.fill_value result = self._interpolator(interp_points) if result.dtype != data.dtype: # Cast the data dtype to be as expected. Note that, the dtype # of the interpolated result is influenced by the dtype of the # interpolation points. result = result.astype(data.dtype) if np.ma.isMaskedArray(data) or mode.force_mask: # NB. np.ma.getmaskarray returns an array of `False` if # `data` is not a masked array. src_mask = np.ma.getmaskarray(data) # Switch the extrapolation to work with mask values. self._interpolator.fill_value = mode.mask_fill_value self._interpolator.values = src_mask mask_fraction = self._interpolator(interp_points) new_mask = (mask_fraction > 0) if ma.isMaskedArray(data) or np.any(new_mask): result = np.ma.MaskedArray(result, new_mask) return result