Example #1
0
    def _calculate_ratio(self, cube, cube_name, radius):
        """
        Calculates the ratio of actual to potential value transitions in a
        neighbourhood about each cell.

        The process is as follows:

            1. For each grid cell find the number of cells of value 1 in a surrounding
               neighbourhood of a size defined by the arg radius. The potential
               transitions within that neighbourhood are defined as the number of
               orthogonal neighbours (up, down, left, right) about cells of value 1.
               This is 4 times the number of cells of value 1.
            2. Calculate the number of actual transitions within the neighbourhood,
               that is the number of cells of value 0 that orthogonally abut cells
               of value 1.
            3. Calculate the ratio of actual to potential transitions.

        Ratios approaching 1 indicate that there are many transitions, so the field
        is highly textured (rough). Ratios close to 0 indicate a smoother field.

        A neighbourhood full of cells of value 1 will return ratios of 0; the
        diagnostic that has been thresholded to produce the binary field is found
        everywhere within that neighbourhood, giving a smooth field. At the other
        extreme, in neighbourhoods in which there are no cells of value 1 the ratio
        is set to 1.

        Args:
            cube (iris.cube.Cube):
                Input data in cube format containing a two-dimensional field
                of binary data.

            cube_name (str):
                Name of input data cube, used for determining output texture cube name.

            radius (float):
                Radius for neighbourhood in metres.

        Returns:
            iris.cube.Cube:
                A ratio between 0 and 1 of actual transitions over potential transitions.
        """
        # Calculate the potential transitions within neighbourhoods.
        potential_transitions = SquareNeighbourhood(sum_or_fraction="sum").run(
            cube, radius=radius)
        potential_transitions.data = 4 * potential_transitions.data

        # Calculate the actual transitions for each grid cell of value 1 and
        # store them in a cube.
        actual_transitions = potential_transitions.copy(
            data=self._calculate_transitions(cube.data))

        # Sum the number of actual transitions within the neighbourhood.
        actual_transitions = SquareNeighbourhood(sum_or_fraction="sum").run(
            actual_transitions, radius=radius)

        # Calculate the ratio of actual to potential transitions in areas where the
        # original diagnostic value was greater than zero. Where the original value
        # was zero, set ratio value to one.

        ratio = np.ones_like(actual_transitions.data)
        ratio[cube.data > 0] = (actual_transitions.data[cube.data > 0] /
                                potential_transitions.data[cube.data > 0])

        # Create a new cube to contain the resulting ratio data.
        ratio = create_new_diagnostic_cube(
            "texture_of_{}".format(cube_name),
            "1",
            cube,
            mandatory_attributes=generate_mandatory_attributes(
                [cube], model_id_attr=self.model_id_attr),
            data=ratio,
        )
        return ratio
    def process(self, cube, alphas_x=None, alphas_y=None, mask_cube=None):
        """
        Set up the alpha parameters and run the recursive filter.

        The steps undertaken are:

        1. Split the input cube into slices determined by the co-ordinates in
           the x and y directions.
        2. Construct an array of filter parameters (alphas_x and alphas_y) for
           each cube slice that are used to weight the recursive filter in
           the x- and y-directions.
        3. Pad each cube slice with a square-neighbourhood halo and apply
           the recursive filter for the required number of iterations.
        4. Remove the halo from the cube slice and append the recursed cube
           slice to a 'recursed cube'.
        5. Merge all the cube slices in the 'recursed cube' into a 'new cube'.
        6. Modify the 'new cube' so that its scalar dimension co-ordinates are
           consistent with those in the original input cube.
        7. Return the 'new cube' which now contains the recursively filtered
           values for the original input cube.

        Args:
            cube (Iris.cube.Cube):
                Cube containing the input data to which the recursive filter
                will be applied.

        Keyword Args:
            alphas_x (Iris.cube.Cube or None):
                Cube containing array of alpha values that will be used when
                applying the recursive filter along the x-axis.
            alphas_y (Iris.cube.Cube or None):
                Cube containing array of alpha values that will be used when
                applying the recursive filter along the y-axis.
            mask_cube (Iris.cube.Cube or None):
                Cube containing an external mask to apply to the cube before
                applying the recursive filter.

        Returns:
            new_cube (Iris.cube.Cube):
                Cube containing the smoothed field after the recursive filter
                method has been applied.
        """
        cube_format = next(cube.slices([cube.coord(axis='y'),
                                        cube.coord(axis='x')]))
        alphas_x = self.set_alphas(cube_format, self.alpha_x, alphas_x)
        alphas_y = self.set_alphas(cube_format, self.alpha_y, alphas_y)

        recursed_cube = iris.cube.CubeList()
        for output in cube.slices([cube.coord(axis='y'),
                                   cube.coord(axis='x')]):

            # Setup cube and mask for processing.
            # This should set up a mask full of 1.0 if None is provided
            # and set the data 0.0 where mask is 0.0 or the data is NaN
            output, mask, nan_array = (
                SquareNeighbourhood().set_up_cubes_to_be_neighbourhooded(
                    output, mask_cube))
            mask = mask.data.squeeze()

            padded_cube = SquareNeighbourhood().pad_cube_with_halo(
                output, self.edge_width, self.edge_width)

            new_cube = self.run_recursion(padded_cube, alphas_x, alphas_y,
                                          self.iterations)
            new_cube = SquareNeighbourhood().remove_halo_from_cube(
                new_cube, self.edge_width, self.edge_width)
            if self.re_mask:
                new_cube.data[nan_array.astype(bool)] = np.nan
                new_cube.data = np.ma.masked_array(new_cube.data,
                                                   mask=np.logical_not(mask))

            recursed_cube.append(new_cube)

        new_cube = recursed_cube.merge_cube()
        new_cube = check_cube_coordinates(cube, new_cube)

        return new_cube
Example #3
0
    def process(self, cube, alphas_x=None, alphas_y=None, mask_cube=None):
        """
        Set up the alpha parameters and run the recursive filter.

        The steps undertaken are:

        1. Split the input cube into slices determined by the co-ordinates in
           the x and y directions.
        2. Construct an array of filter parameters (alphas_x and alphas_y) for
           each cube slice that are used to weight the recursive filter in
           the x- and y-directions.
        3. Pad each cube slice with a square-neighbourhood halo and apply
           the recursive filter for the required number of iterations.
        4. Remove the halo from the cube slice and append the recursed cube
           slice to a 'recursed cube'.
        5. Merge all the cube slices in the 'recursed cube' into a 'new cube'.
        6. Modify the 'new cube' so that its scalar dimension co-ordinates are
           consistent with those in the original input cube.
        7. Return the 'new cube' which now contains the recursively filtered
           values for the original input cube.

        Args:
            cube (Iris.cube.Cube):
                Cube containing the input data to which the recursive filter
                will be applied.

        Keyword Args:
            alphas_x (Iris.cube.Cube or None):
                Cube containing array of alpha values that will be used when
                applying the recursive filter along the x-axis.
            alphas_y (Iris.cube.Cube or None):
                Cube containing array of alpha values that will be used when
                applying the recursive filter along the y-axis.
            mask_cube (Iris.cube.Cube or None):
                Cube containing an external mask to apply to the cube before
                applying the recursive filter.

        Returns:
            new_cube (Iris.cube.Cube):
                Cube containing the smoothed field after the recursive filter
                method has been applied.
        """
        cube_format = next(
            cube.slices([cube.coord(axis='y'),
                         cube.coord(axis='x')]))
        alphas_x = self.set_alphas(cube_format, self.alpha_x, alphas_x)
        alphas_y = self.set_alphas(cube_format, self.alpha_y, alphas_y)

        # Extract mask if present on input cube or provided separately.
        try:
            mask, = SquareNeighbourhood._set_up_cubes_to_be_neighbourhooded(
                cube, mask_cube).extract('mask_data')
            mask = mask.data.squeeze()
        except ValueError:
            mask = np.ones((cube_format.data.shape))

        recursed_cube = iris.cube.CubeList()
        for output in cube.slices([cube.coord(axis='y'),
                                   cube.coord(axis='x')]):

            # Use mask to zero masked areas.
            output.data = output.data * mask
            # Zero any remaining NaN values not covered by mask.
            output.data = np.nan_to_num(output.data)

            padded_cube = SquareNeighbourhood().pad_cube_with_halo(
                output, self.edge_width, self.edge_width)
            new_cube = self.run_recursion(padded_cube, alphas_x, alphas_y,
                                          self.iterations)
            new_cube = SquareNeighbourhood().remove_halo_from_cube(
                new_cube, self.edge_width, self.edge_width)
            if self.re_mask:
                new_cube.data = np.ma.masked_array(new_cube.data,
                                                   mask=np.logical_not(mask))
            recursed_cube.append(new_cube)

        new_cube = recursed_cube.merge_cube()
        new_cube = check_cube_coordinates(cube, new_cube)

        return new_cube