Example #1
0
    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
Example #2
0
    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
Example #3
0
File: _linear.py Project: ckmo/iris
    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
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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
Example #9
0
    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
Example #10
0
    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()),
        )
Example #11
0
    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()))