Exemple #1
0
 def _make_partially_collapsed_coord(self, coord, grid, guessed_axis):
     """
     Make a new DimCoord which represents a partially collapsed (aggregated into bins) coordinate.
     This dimcoord will have a grid
     :type coord: data_io.Coord.Coord
     :param coord: Coordinate to partially collapse
     :type grid: aggregation.aggregation_grid.AggregationGrid
     :param grid: grid on which this coordinate will aggregate
     :type guessed_axis: str
     :param guessed_axis: String identifier of the axis to which this coordinate belongs (e.g. 'T', 'X')
     :return: DimCoord
     """
     if grid.is_time or guessed_axis == 'T':
         # Ensure that the limits are date/times.
         dt = parse_datetime.convert_datetime_components_to_datetime(grid.start, True)
         grid_start = Subset._convert_datetime_to_coord_unit(coord, dt)
         dt = parse_datetime.convert_datetime_components_to_datetime(grid.end, False)
         grid_end = Subset._convert_datetime_to_coord_unit(coord, dt)
         grid_delta = grid.delta
     else:
         # Assume to be a non-time axis
         (grid_start, grid_end) = Subset._fix_non_circular_limits(float(grid.start), float(grid.end))
         grid_delta = float(grid.delta)
     new_coordinate_grid = aggregation_grid_array(grid_start, grid_end, grid_delta, grid.is_time, coord)
     new_coord = DimCoord(new_coordinate_grid, var_name=coord.name(), standard_name=coord.standard_name,
                          units=coord.units)
     if len(new_coord.points) == 1:
         new_coord.bounds = [[grid_start, grid_end]]
     else:
         new_coord.guess_bounds()
     return new_coord
Exemple #2
0
 def test_long_time_interval__bounded(self):
     coord = DimCoord([5, 6], standard_name='time',
                      units='years since 1970-01-01')
     coord.guess_bounds()
     expected = ("DimCoord([5 6], bounds=[[4.5 5.5]\n [5.5 6.5]], "
                 "standard_name='time', calendar='gregorian')")
     result = coord.__str__()
     self.assertEqual(expected, result)
Exemple #3
0
 def _cube(self, ellipsoid=None):
     data = np.arange(12).reshape(3, 4).astype("u1")
     cube = Cube(data, "air_pressure_anomaly")
     coord = DimCoord(np.arange(3), "latitude", units="degrees", coord_system=ellipsoid)
     coord.guess_bounds()
     cube.add_dim_coord(coord, 0)
     coord = DimCoord(np.arange(4), "longitude", units="degrees", coord_system=ellipsoid)
     coord.guess_bounds()
     cube.add_dim_coord(coord, 1)
     return cube
Exemple #4
0
 def _cube(self, dtype):
     data = np.arange(12).reshape(3, 4).astype(dtype) + 20
     cube = Cube(data, "air_pressure_anomaly")
     coord = DimCoord(np.arange(3), "latitude", units="degrees")
     coord.guess_bounds()
     cube.add_dim_coord(coord, 0)
     coord = DimCoord(np.arange(4), "longitude", units="degrees")
     coord.guess_bounds()
     cube.add_dim_coord(coord, 1)
     return cube
Exemple #5
0
 def test_short_time_interval__bounded(self):
     coord = DimCoord([5, 6], standard_name='time',
                      units='days since 1970-01-01')
     coord.guess_bounds()
     expected = ("DimCoord([1970-01-06 00:00:00, 1970-01-07 00:00:00], "
                 "bounds=[[1970-01-05 12:00:00, 1970-01-06 12:00:00],\n"
                 "       [1970-01-06 12:00:00, 1970-01-07 12:00:00]], "
                 "standard_name='time', calendar='gregorian')")
     result = coord.__str__()
     self.assertEqual(expected, result)
Exemple #6
0
 def _cube(self, ellipsoid=None):
     data = np.arange(12).reshape(3, 4).astype('u1')
     cube = Cube(data, 'air_pressure_anomaly')
     coord = DimCoord(np.arange(3), 'latitude', units='degrees',
                      coord_system=ellipsoid)
     coord.guess_bounds()
     cube.add_dim_coord(coord, 0)
     coord = DimCoord(np.arange(4), 'longitude', units='degrees',
                      coord_system=ellipsoid)
     coord.guess_bounds()
     cube.add_dim_coord(coord, 1)
     return cube
Exemple #7
0
 def test_(self):
     data = np.arange(12).reshape(3, 4).astype(np.uint8)
     cube = Cube(data, "air_pressure_anomaly")
     coord = DimCoord([30, 40, 50], "latitude", units="degrees")
     coord.guess_bounds()
     cube.add_dim_coord(coord, 0)
     coord = DimCoord([-10, -5, 0, 5], "longitude", units="degrees")
     coord.guess_bounds()
     cube.add_dim_coord(coord, 1)
     with self.temp_filename(".tif") as temp_filename:
         export_geotiff(cube, temp_filename)
         dataset = gdal.Open(temp_filename, gdal.GA_ReadOnly)
         self.assertEqual(dataset.GetGeoTransform(), (-12.5, 5, 0, 55, 0, -10))
Exemple #8
0
class Test_guess_bounds(tests.IrisTest):
    def setUp(self):
        self.coord = DimCoord(np.array([-160, -120, 0, 30, 150, 170]),
                              units='degree', standard_name='longitude',
                              circular=True)

    def test_non_circular(self):
        self.coord.circular = False
        self.coord.guess_bounds()
        target = np.array([[-180., -140.], [-140., -60.], [-60., 15.],
                           [15., 90.], [90., 160.], [160., 180.]])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_increasing(self):
        self.coord.guess_bounds()
        target = np.array([[-175., -140.], [-140., -60.], [-60., 15.],
                           [15., 90.], [90., 160.], [160., 185.]])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_decreasing(self):
        self.coord.points = self.coord.points[::-1]
        self.coord.guess_bounds()
        target = np.array([[185., 160.], [160., 90.], [90., 15.],
                           [15., -60.], [-60., -140.], [-140., -175.]])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_increasing_alt_range(self):
        self.coord.points = np.array([10, 30, 90, 150, 210, 220])
        self.coord.guess_bounds()
        target = np.array([[-65., 20.], [20., 60.], [60., 120.],
                           [120., 180.], [180., 215.], [215., 295.]])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_decreasing_alt_range(self):
        self.coord.points = np.array([10, 30, 90, 150, 210, 220])[::-1]
        self.coord.guess_bounds()
        target = np.array([[295, 215], [215, 180], [180, 120], [120, 60],
                           [60, 20], [20, -65]])
        self.assertArrayEqual(target, self.coord.bounds)
Exemple #9
0
 def test_points_inside_bounds_outside_wrong_name_2(self):
     lat = DimCoord([-80, 0, 70],
                    units='degree',
                    long_name='other_latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-120, -40], [-40, 35], [35, 105]])
Exemple #10
0
 def test_points_to_edges_bounds_outside(self):
     lat = DimCoord([-90, 0, 90], units='degree', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-90, -45], [-45, 45], [45, 90]])
Exemple #11
0
class Test_guess_bounds(tests.IrisTest):
    def setUp(self):
        self.coord = DimCoord(
            np.array([-160, -120, 0, 30, 150, 170]),
            units="degree",
            standard_name="longitude",
            circular=True,
        )

    def test_non_circular(self):
        self.coord.circular = False
        self.coord.guess_bounds()
        target = np.array([
            [-180.0, -140.0],
            [-140.0, -60.0],
            [-60.0, 15.0],
            [15.0, 90.0],
            [90.0, 160.0],
            [160.0, 180.0],
        ])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_increasing(self):
        self.coord.guess_bounds()
        target = np.array([
            [-175.0, -140.0],
            [-140.0, -60.0],
            [-60.0, 15.0],
            [15.0, 90.0],
            [90.0, 160.0],
            [160.0, 185.0],
        ])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_decreasing(self):
        self.coord.points = self.coord.points[::-1]
        self.coord.guess_bounds()
        target = np.array([
            [185.0, 160.0],
            [160.0, 90.0],
            [90.0, 15.0],
            [15.0, -60.0],
            [-60.0, -140.0],
            [-140.0, -175.0],
        ])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_increasing_alt_range(self):
        self.coord.points = np.array([10, 30, 90, 150, 210, 220])
        self.coord.guess_bounds()
        target = np.array([
            [-65.0, 20.0],
            [20.0, 60.0],
            [60.0, 120.0],
            [120.0, 180.0],
            [180.0, 215.0],
            [215.0, 295.0],
        ])
        self.assertArrayEqual(target, self.coord.bounds)

    def test_circular_decreasing_alt_range(self):
        self.coord.points = np.array([10, 30, 90, 150, 210, 220])[::-1]
        self.coord.guess_bounds()
        target = np.array([
            [295, 215],
            [215, 180],
            [180, 120],
            [120, 60],
            [60, 20],
            [20, -65],
        ])
        self.assertArrayEqual(target, self.coord.bounds)
def _generate_cubes(header,
                    column_headings,
                    coords,
                    data_arrays,
                    cell_methods=None):
    """
    Yield :class:`iris.cube.Cube` instances given
    the headers, column headings, coords and data_arrays extracted
    from a NAME file.

    """
    for i, data_array in enumerate(data_arrays):
        # Turn the dictionary of column headings with a list of header
        # information for each field into a dictionary of headings for
        # just this field.
        field_headings = {k: v[i] for k, v in six.iteritems(column_headings)}

        # Make a cube.
        cube = iris.cube.Cube(data_array)

        # Determine the name and units.
        name = '{} {}'.format(field_headings['Species'],
                              field_headings['Quantity'])
        name = name.upper().replace(' ', '_')
        cube.rename(name)

        # Some units are not in SI units, are missing spaces or typed
        # in the wrong case. _parse_units returns units that are
        # recognised by Iris.
        cube.units = _parse_units(field_headings['Units'])

        # Define and add the singular coordinates of the field (flight
        # level, time etc.)
        if 'Z' in field_headings:
            upper_bound, = [
                field_headings['... to [Z]']
                if '... to [Z]' in field_headings else None
            ]
            lower_bound, = [
                field_headings['... from [Z]']
                if '... from [Z]' in field_headings else None
            ]
            z_coord = _cf_height_from_name(field_headings['Z'],
                                           upper_bound=upper_bound,
                                           lower_bound=lower_bound)
            cube.add_aux_coord(z_coord)

        # Define the time unit and use it to serialise the datetime for
        # the time coordinate.
        time_unit = cf_units.Unit('hours since epoch',
                                  calendar=cf_units.CALENDAR_GREGORIAN)

        # Build time, height, latitude and longitude coordinates.
        for coord in coords:
            pts = coord.values
            coord_sys = None
            if coord.name == 'latitude' or coord.name == 'longitude':
                coord_units = 'degrees'
                coord_sys = iris.coord_systems.GeogCS(EARTH_RADIUS)
            if coord.name == 'projection_x_coordinate' \
                    or coord.name == 'projection_y_coordinate':
                coord_units = 'm'
                coord_sys = iris.coord_systems.OSGB()
            if coord.name == 'height':
                coord_units = 'm'
                long_name = 'height above ground level'
                pts = coord.values
            if coord.name == 'altitude':
                coord_units = 'm'
                long_name = 'altitude above sea level'
                pts = coord.values
            if coord.name == 'air_pressure':
                coord_units = 'Pa'
                pts = coord.values
            if coord.name == 'flight_level':
                pts = coord.values
                long_name = 'flight_level'
                coord_units = _parse_units('FL')
            if coord.name == 'time':
                coord_units = time_unit
                pts = time_unit.date2num(coord.values)

            if coord.dimension is not None:
                if coord.name == 'longitude':
                    circular = iris.util._is_circular(pts, 360.0)
                else:
                    circular = False
                if coord.name == 'flight_level':
                    icoord = DimCoord(
                        points=pts,
                        units=coord_units,
                        long_name=long_name,
                    )
                else:
                    icoord = DimCoord(points=pts,
                                      standard_name=coord.name,
                                      units=coord_units,
                                      coord_system=coord_sys,
                                      circular=circular)
                if coord.name == 'height' or coord.name == 'altitude':
                    icoord.long_name = long_name
                if coord.name == 'time' and 'Av or Int period' in \
                        field_headings:
                    dt = coord.values - \
                        field_headings['Av or Int period']
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds
                else:
                    icoord.guess_bounds()
                cube.add_dim_coord(icoord, coord.dimension)
            else:
                icoord = AuxCoord(points=pts[i],
                                  standard_name=coord.name,
                                  coord_system=coord_sys,
                                  units=coord_units)
                if coord.name == 'time' and 'Av or Int period' in \
                        field_headings:
                    dt = coord.values - \
                        field_headings['Av or Int period']
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds[i, :]
                cube.add_aux_coord(icoord)

        # Headings/column headings which are encoded elsewhere.
        headings = [
            'X',
            'Y',
            'Z',
            'Time',
            'T',
            'Units',
            'Av or Int period',
            '... from [Z]',
            '... to [Z]',
            'X grid origin',
            'Y grid origin',
            'X grid size',
            'Y grid size',
            'X grid resolution',
            'Y grid resolution',
            'Number of field cols',
            'Number of preliminary cols',
            'Number of fields',
            'Number of series',
            'Output format',
        ]

        # Add the Main Headings as attributes.
        for key, value in six.iteritems(header):
            if value is not None and value != '' and \
                    key not in headings:
                cube.attributes[key] = value

        # Add the Column Headings as attributes
        for key, value in six.iteritems(field_headings):
            if value is not None and value != '' and \
                    key not in headings:
                cube.attributes[key] = value

        if cell_methods is not None:
            cube.add_cell_method(cell_methods[i])

        yield cube
def _make_cube_3d(x, y, z, data, aux=None, offset=0):
    """
    A convenience test function that creates a custom 3D cube.

    Args:

    * x:
        A (start, stop, step) tuple for specifying the
        x-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * y:
        A (start, stop, step) tuple for specifying the
        y-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * z:
        A (start, stop, step) tuple for specifying the
        z-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * data:
        The data payload for the cube.

    Kwargs:

    * aux:
        A CSV string specifying which points only auxiliary
        coordinates to create. Accepts either of 'x', 'y', 'z',
        'xy', 'xz', 'yz', 'xyz'.

    * offset:
        Offset value to be added to non-1D auxiliary coordinate
        points.

    Returns:
        The newly created 3D :class:`iris.cube.Cube`.

    """
    x_range = np.arange(*x, dtype=np.float32)
    y_range = np.arange(*y, dtype=np.float32)
    z_range = np.arange(*z, dtype=np.float32)
    x_size, y_size, z_size = len(x_range), len(y_range), len(z_range)

    cube_data = np.empty((x_size, y_size, z_size), dtype=np.float32)
    cube_data[:] = data
    cube = iris.cube.Cube(cube_data)
    coord = DimCoord(z_range, long_name='z')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 0)
    coord = DimCoord(y_range, long_name='y')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 1)
    coord = DimCoord(x_range, long_name='x')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 2)

    if aux is not None:
        aux = aux.split(',')
        if 'z' in aux:
            coord = AuxCoord(z_range * 10, long_name='z-aux')
            cube.add_aux_coord(coord, (0,))
        if 'y' in aux:
            coord = AuxCoord(y_range * 10, long_name='y-aux')
            cube.add_aux_coord(coord, (1,))
        if 'x' in aux:
            coord = AuxCoord(x_range * 10, long_name='x-aux')
            cube.add_aux_coord(coord, (2,))
        if 'xy' in aux:
            payload = np.arange(x_size * y_size,
                                dtype=np.float32).reshape(y_size, x_size)
            coord = AuxCoord(payload + offset, long_name='xy-aux')
            cube.add_aux_coord(coord, (1, 2))
        if 'xz' in aux:
            payload = np.arange(x_size * z_size,
                                dtype=np.float32).reshape(z_size, x_size)
            coord = AuxCoord(payload * 10 + offset, long_name='xz-aux')
            cube.add_aux_coord(coord, (0, 2))
        if 'yz' in aux:
            payload = np.arange(y_size * z_size,
                                dtype=np.float32).reshape(z_size, y_size)
            coord = AuxCoord(payload * 100 + offset, long_name='yz-aux')
            cube.add_aux_coord(coord, (0, 1))
        if 'xyz' in aux:
            payload = np.arange(x_size * y_size * z_size,
                                dtype=np.float32).reshape(z_size,
                                                          y_size,
                                                          x_size)
            coord = AuxCoord(payload * 1000 + offset, long_name='xyz-aux')
            cube.add_aux_coord(coord, (0, 1, 2))

    return cube
Exemple #14
0
def reference_solution(container_type, weights):
    """Obtain a reference EOF analysis solution.

    **Arguments:**

    *container_type*
        Either 'standard', 'cdms', or 'iris'.

    *weights*
        Weights method. One of 'equal', 'latitude', or 'area'.

    """
    container_type = container_type.lower()
    weights = weights.lower()
    if container_type not in ('standard', 'iris', 'cdms'):
        raise ValueError("unknown container type "
                         "'{!s}'".format(container_type))
    solution = _read_reference_solution(weights)
    time_units = 'months since 0-1-1 00:00:0.0'
    neofs = len(solution['eigenvalues'])
    if container_type == 'cdms':
        try:
            time_dim = cdms2.createAxis(solution['time'], id='time')
            time_dim.designateTime()
            time_dim.units = time_units
            lat_dim = cdms2.createAxis(solution['latitude'], id='latitude')
            lat_dim.designateLatitude()
            lon_dim = cdms2.createAxis(solution['longitude'], id='longitude')
            lon_dim.designateLongitude()
            eof_dim = cdms2.createAxis(np.arange(1, neofs+1), id='eof')
            eof_dim.long_name = 'eof_number'
            solution['sst'] = cdms2.createVariable(
                solution['sst'], 
                axes=[time_dim, lat_dim, lon_dim],
                id='sst')
            solution['eigenvalues'] = cdms2.createVariable(
                solution['eigenvalues'],
                axes=[eof_dim],
                id='eigenvalues')
            solution['eofs'] = cdms2.createVariable(
                solution['eofs'],
                axes=[eof_dim, lat_dim, lon_dim],
                id='eofs')
            solution['pcs'] = cdms2.createVariable(
                solution['pcs'],
                axes=[time_dim, eof_dim],
                id='pcs')
            solution['variance'] = cdms2.createVariable(
                solution['variance'],
                axes=[eof_dim],
                id='variance')
            solution['eofscor'] = cdms2.createVariable(
                solution['eofscor'],
                axes=[eof_dim, lat_dim, lon_dim],
                id='eofscor')
            solution['eofscov'] = cdms2.createVariable(
                solution['eofscov'],
                axes=[eof_dim, lat_dim, lon_dim],
                id='eofscov')
            solution['errors'] = cdms2.createVariable(
                solution['errors'],
                axes=[eof_dim],
                id='errors')
            solution['scaled_errors'] = cdms2.createVariable(
                solution['scaled_errors'],
                axes=[eof_dim],
                id='scaled_errors')
            solution['rcon'] = cdms2.createVariable(
                solution['rcon'],
                axes=[time_dim, lat_dim, lon_dim],
                id='reconstructed_sst')
        except NameError:
            raise ValueError("cannot use container 'cdms' without the "
                             "cdms2 module")
    elif container_type == 'iris':
        try:
            time_dim = DimCoord(solution['time'],
                                standard_name='time',
                                units=Unit(time_units, 'gregorian'))
            lat_dim = DimCoord(solution['latitude'],
                               standard_name='latitude',
                               units='degrees_north')
            lat_dim.guess_bounds()
            lon_dim = DimCoord(solution['longitude'],
                               standard_name='longitude',
                               units='degrees_east')
            lon_dim.guess_bounds()
            eof_dim = DimCoord(np.arange(1, neofs+1),
                               long_name='eof')
            solution['sst']= Cube(
                solution['sst'],
                dim_coords_and_dims=zip((time_dim, lat_dim, lon_dim),
                                        range(3)),
                long_name='sst')
            solution['eigenvalues']= Cube(
                solution['eigenvalues'],
                dim_coords_and_dims=zip((eof_dim,),
                                        range(1)),
                long_name='eigenvalues')
            solution['eofs']= Cube(
                solution['eofs'],
                dim_coords_and_dims=zip((eof_dim, lat_dim, lon_dim),
                                        range(3)),
                long_name='eofs')
            solution['pcs']= Cube(
                solution['pcs'],
                dim_coords_and_dims=zip((time_dim, eof_dim),
                                        range(2)),
                long_name='pcs')
            solution['variance']= Cube(
                solution['variance'],
                dim_coords_and_dims=zip((eof_dim,),
                                        range(1)),
                long_name='variance')
            solution['eofscor']= Cube(
                solution['eofscor'],
                dim_coords_and_dims=zip((eof_dim, lat_dim, lon_dim),
                                        range(3)),
                long_name='eofscor')
            solution['eofscov']= Cube(
                solution['eofscov'],
                dim_coords_and_dims=zip((eof_dim, lat_dim, lon_dim),
                                        range(3)),
                long_name='eofscov')
            solution['errors']= Cube(
                solution['errors'],
                dim_coords_and_dims=zip((eof_dim,),
                                        range(1)),
                long_name='errors')
            solution['scaled_errors']= Cube(
                solution['scaled_errors'],
                dim_coords_and_dims=zip((eof_dim,),
                                        range(1)),
                long_name='scaled_errors')
            solution['rcon']= Cube(
                solution['rcon'],
                dim_coords_and_dims=zip((time_dim, lat_dim, lon_dim),
                                        range(3)),
                long_name='reconstructed_sst')
        except NameError:
            raise ValueError("cannot use container 'iris' without the "
                             "iris module")
    return solution
Exemple #15
0
 def test_points_outside(self):
     lat = DimCoord([-100, 0, 120],
                    units='degree',
                    standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-150, -50], [-50, 60], [60, 180]])
Exemple #16
0
 def test_points_inside_bounds_outside_wrong_name_2(self):
     lat = DimCoord([-80, 0, 70], units='degree',
                    long_name='other_latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-120, -40], [-40, 35], [35, 105]])
Exemple #17
0
    def to_cube(self):
        """Return a new :class:`~iris.cube.Cube` from this ABFField."""

        cube = iris.cube.Cube(self.data)

        # Name.
        if self.format.lower() == "abf":
            cube.rename("FAPAR")
        elif self.format.lower() == "abl":
            cube.rename("leaf_area_index")
        else:
            msg = "Unknown ABF/ABL format: {}".format(self.format)
            raise iris.exceptions.TranslationError(msg)
        cube.units = "%"

        # Grid.
        step = 1.0 / 12.0

        llcs = GeogCS(semi_major_axis=6378137.0, semi_minor_axis=6356752.31424)

        x_coord = DimCoord(np.arange(X_SIZE) * step + (step / 2) - 180,
                           standard_name="longitude",
                           units="degrees",
                           coord_system=llcs)

        y_coord = DimCoord(np.arange(Y_SIZE) * step + (step / 2) - 90,
                           standard_name="latitude",
                           units="degrees",
                           coord_system=llcs)

        x_coord.guess_bounds()
        y_coord.guess_bounds()

        cube.add_dim_coord(x_coord, 1)
        cube.add_dim_coord(y_coord, 0)

        # Time.
        if self.period == "a":
            start = 1
            end = 15
        elif self.period == "b":
            start = 16
            end = calendar.monthrange(self.year, self.month)[1]
        else:
            raise iris.exceptions.TranslationError("Unknown period: "
                                                   "{}".format(self.period))

        start = datetime.date(year=self.year, month=self.month, day=start)
        end = datetime.date(year=self.year, month=self.month, day=end)

        # Convert to "days since 0001-01-01".
        # Iris will have proper datetime objects in the future.
        # This step will not be necessary.
        start = start.toordinal() - 1
        end = end.toordinal() - 1

        # TODO: Should we put the point in the middle of the period instead?
        cube.add_aux_coord(
            AuxCoord(start,
                     standard_name="time",
                     units="days since 0001-01-01",
                     bounds=[start, end]))

        # TODO: Do they only come from Boston?
        # Attributes.
        cube.attributes["source"] = "Boston University"

        return cube
Exemple #18
0
 def test_points_to_edges_bounds_outside(self):
     lat = DimCoord([-90, 0, 90], units='degree', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-135, -45], [-45, 45], [45, 135]])
Exemple #19
0
def _make_cube(x, y, data, aux=None, offset=0, scalar=None):
    """
    A convenience test function that creates a custom 2D cube.

    Args:

    * x:
        A (start, stop, step) tuple for specifying the
        x-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * y:
        A (start, stop, step) tuple for specifying the
        y-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * data:
        The data payload for the cube.

    Kwargs:

    * aux:
        A CSV string specifying which points only auxiliary
        coordinates to create. Accepts either of 'x', 'y', 'xy'.

    * offset:
        Offset value to be added to the 'xy' auxiliary coordinate
        points.

    * scalar:
        Create a 'height' scalar coordinate with the given value.

    Returns:
        The newly created 2D :class:`iris.cube.Cube`.

    """
    x_range = np.arange(*x, dtype=np.float32)
    y_range = np.arange(*y, dtype=np.float32)
    x_size = len(x_range)
    y_size = len(y_range)

    cube_data = np.empty((y_size, x_size), dtype=np.float32)
    cube_data[:] = data
    cube = iris.cube.Cube(cube_data)
    coord = DimCoord(y_range, long_name='y')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 0)
    coord = DimCoord(x_range, long_name='x')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 1)

    if aux is not None:
        aux = aux.split(',')
        if 'y' in aux:
            coord = AuxCoord(y_range * 10, long_name='y-aux')
            cube.add_aux_coord(coord, (0, ))
        if 'x' in aux:
            coord = AuxCoord(x_range * 10, long_name='x-aux')
            cube.add_aux_coord(coord, (1, ))
        if 'xy' in aux:
            payload = np.arange(y_size * x_size,
                                dtype=np.float32).reshape(y_size, x_size)
            coord = AuxCoord(payload * 100 + offset, long_name='xy-aux')
            cube.add_aux_coord(coord, (0, 1))

    if scalar is not None:
        data = np.array([scalar], dtype=np.float32)
        coord = AuxCoord(data, long_name='height', units='m')
        cube.add_aux_coord(coord, ())

    return cube
Exemple #20
0
def _make_cube_3d(x, y, z, data, aux=None, offset=0):
    """
    A convenience test function that creates a custom 3D cube.

    Args:

    * x:
        A (start, stop, step) tuple for specifying the
        x-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * y:
        A (start, stop, step) tuple for specifying the
        y-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * z:
        A (start, stop, step) tuple for specifying the
        z-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * data:
        The data payload for the cube.

    Kwargs:

    * aux:
        A CSV string specifying which points only auxiliary
        coordinates to create. Accepts either of 'x', 'y', 'z',
        'xy', 'xz', 'yz', 'xyz'.

    * offset:
        Offset value to be added to non-1D auxiliary coordinate
        points.

    Returns:
        The newly created 3D :class:`iris.cube.Cube`.

    """
    x_range = np.arange(*x, dtype=np.float32)
    y_range = np.arange(*y, dtype=np.float32)
    z_range = np.arange(*z, dtype=np.float32)
    x_size, y_size, z_size = len(x_range), len(y_range), len(z_range)

    cube_data = np.empty((x_size, y_size, z_size), dtype=np.float32)
    cube_data[:] = data
    cube = iris.cube.Cube(cube_data)
    coord = DimCoord(z_range, long_name='z')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 0)
    coord = DimCoord(y_range, long_name='y')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 1)
    coord = DimCoord(x_range, long_name='x')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 2)

    if aux is not None:
        aux = aux.split(',')
        if 'z' in aux:
            coord = AuxCoord(z_range * 10, long_name='z-aux')
            cube.add_aux_coord(coord, (0, ))
        if 'y' in aux:
            coord = AuxCoord(y_range * 10, long_name='y-aux')
            cube.add_aux_coord(coord, (1, ))
        if 'x' in aux:
            coord = AuxCoord(x_range * 10, long_name='x-aux')
            cube.add_aux_coord(coord, (2, ))
        if 'xy' in aux:
            payload = np.arange(x_size * y_size,
                                dtype=np.float32).reshape(y_size, x_size)
            coord = AuxCoord(payload + offset, long_name='xy-aux')
            cube.add_aux_coord(coord, (1, 2))
        if 'xz' in aux:
            payload = np.arange(x_size * z_size,
                                dtype=np.float32).reshape(z_size, x_size)
            coord = AuxCoord(payload * 10 + offset, long_name='xz-aux')
            cube.add_aux_coord(coord, (0, 2))
        if 'yz' in aux:
            payload = np.arange(y_size * z_size,
                                dtype=np.float32).reshape(z_size, y_size)
            coord = AuxCoord(payload * 100 + offset, long_name='yz-aux')
            cube.add_aux_coord(coord, (0, 1))
        if 'xyz' in aux:
            payload = np.arange(x_size * y_size * z_size,
                                dtype=np.float32).reshape(
                                    z_size, y_size, x_size)
            coord = AuxCoord(payload * 1000 + offset, long_name='xyz-aux')
            cube.add_aux_coord(coord, (0, 1, 2))

    return cube
Exemple #21
0
class Test_make_coord(tests.IrisTest):
    @staticmethod
    def coord_dims(coord):
        mapping = dict(sigma=(0, ),
                       eta=(1, 2),
                       depth=(1, 2),
                       depth_c=(),
                       nsigma=(),
                       zlev=(0, ))
        return mapping[coord.name()]

    @staticmethod
    def derive(sigma, eta, depth, depth_c, nsigma, zlev, coord=True):
        nsigma_slice = slice(0, int(nsigma))
        temp = eta + sigma * (np.minimum(depth_c, depth) + eta)
        shape = temp.shape
        result = np.ones(shape, dtype=temp.dtype) * zlev
        result[nsigma_slice] = temp[nsigma_slice]
        if coord:
            name = 'sea_surface_height_above_reference_ellipsoid'
            result = AuxCoord(result,
                              standard_name=name,
                              units='m',
                              attributes=dict(positive='up'))
        return result

    def setUp(self):
        self.sigma = DimCoord(np.arange(5, dtype=np.float) * 10,
                              long_name='sigma',
                              units='1')
        self.eta = AuxCoord(np.arange(4, dtype=np.float).reshape(2, 2),
                            long_name='eta',
                            units='m')
        self.depth = AuxCoord(np.arange(4, dtype=np.float).reshape(2, 2) * 10,
                              long_name='depth',
                              units='m')
        self.depth_c = AuxCoord([15], long_name='depth_c', units='m')
        self.nsigma = AuxCoord([3], long_name='nsigma')
        self.zlev = DimCoord(np.arange(5, dtype=np.float) * 10,
                             long_name='zlev',
                             units='m')
        self.kwargs = dict(sigma=self.sigma,
                           eta=self.eta,
                           depth=self.depth,
                           depth_c=self.depth_c,
                           nsigma=self.nsigma,
                           zlev=self.zlev)

    def test_derived_points(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_derived_points_with_bounds(self):
        self.sigma.guess_bounds()
        self.zlev.guess_bounds()
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected coordinate with points.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Broadcast expected bounds given the known dimensional mapping.
        sigma = self.sigma.bounds.reshape(sigma.shape + (2, ))
        eta = self.eta.points.reshape(eta.shape + (1, ))
        depth = self.depth.points.reshape(depth.shape + (1, ))
        depth_c = self.depth_c.points.reshape(depth_c.shape + (1, ))
        nsigma = self.nsigma.points.reshape(nsigma.shape + (1, ))
        zlev = self.zlev.bounds.reshape(zlev.shape + (2, ))
        # Calculate the expected bounds.
        bounds = self.derive(sigma,
                             eta,
                             depth,
                             depth_c,
                             nsigma,
                             zlev,
                             coord=False)
        expected_coord.bounds = bounds
        # Calculate the actual result.
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_eta(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = 0
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['eta'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_sigma(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = 0
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['sigma'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_depth_c(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = 0
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['depth_c'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_depth(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = 0
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['depth'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)
Exemple #22
0
 def test_points_outside(self):
     lat = DimCoord([-100, 0, 120], units='degree',
                    standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-150, -50], [-50, 60], [60, 180]])
Exemple #23
0
 def test_all_inside(self):
     lat = DimCoord([-10, 0, 20], units='degree', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-15, -5], [-5, 10], [10, 30]])
Exemple #24
0
 def test_points_inside_bounds_outside_wrong_unit(self):
     lat = DimCoord([-80, 0, 70], units='feet', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-120, -40], [-40, 35], [35, 105]])
Exemple #25
0
def _generate_cubes(header, column_headings, coords, data_arrays,
                    cell_methods=None):
    """
    Yield :class:`iris.cube.Cube` instances given
    the headers, column headings, coords and data_arrays extracted
    from a NAME file.

    """
    for i, data_array in enumerate(data_arrays):
        # Turn the dictionary of column headings with a list of header
        # information for each field into a dictionary of headings for
        # just this field.
        field_headings = {k: v[i] for k, v in six.iteritems(column_headings)}

        # Make a cube.
        cube = iris.cube.Cube(data_array)

        # Determine the name and units.
        name = '{} {}'.format(field_headings['Species'],
                              field_headings['Quantity'])
        name = name.upper().replace(' ', '_')
        cube.rename(name)

        # Some units are not in SI units, are missing spaces or typed
        # in the wrong case. _parse_units returns units that are
        # recognised by Iris.
        cube.units = _parse_units(field_headings['Unit'])

        # Define and add the singular coordinates of the field (flight
        # level, time etc.)
        z_coord = _cf_height_from_name(field_headings['Z'])
        cube.add_aux_coord(z_coord)

        # Define the time unit and use it to serialise the datetime for
        # the time coordinate.
        time_unit = cf_units.Unit(
            'hours since epoch', calendar=cf_units.CALENDAR_GREGORIAN)

        # Build time, latitude and longitude coordinates.
        for coord in coords:
            pts = coord.values
            coord_sys = None
            if coord.name == 'latitude' or coord.name == 'longitude':
                coord_units = 'degrees'
                coord_sys = iris.coord_systems.GeogCS(EARTH_RADIUS)
            if coord.name == 'time':
                coord_units = time_unit
                pts = time_unit.date2num(coord.values)

            if coord.dimension is not None:
                if coord.name == 'longitude':
                    circular = iris.util._is_circular(pts, 360.0)
                else:
                    circular = False
                icoord = DimCoord(points=pts,
                                  standard_name=coord.name,
                                  units=coord_units,
                                  coord_system=coord_sys,
                                  circular=circular)
                if coord.name == 'time' and 'Av or Int period' in \
                        field_headings:
                    dt = coord.values - \
                        field_headings['Av or Int period']
                    bnds = time_unit.date2num(
                        np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds
                else:
                    icoord.guess_bounds()
                cube.add_dim_coord(icoord, coord.dimension)
            else:
                icoord = AuxCoord(points=pts[i],
                                  standard_name=coord.name,
                                  coord_system=coord_sys,
                                  units=coord_units)
                if coord.name == 'time' and 'Av or Int period' in \
                        field_headings:
                    dt = coord.values - \
                        field_headings['Av or Int period']
                    bnds = time_unit.date2num(
                        np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds[i, :]
                cube.add_aux_coord(icoord)

        # Headings/column headings which are encoded elsewhere.
        headings = ['X', 'Y', 'Z', 'Time', 'Unit', 'Av or Int period',
                    'X grid origin', 'Y grid origin',
                    'X grid size', 'Y grid size',
                    'X grid resolution', 'Y grid resolution', ]

        # Add the Main Headings as attributes.
        for key, value in six.iteritems(header):
            if value is not None and value != '' and \
                    key not in headings:
                cube.attributes[key] = value

        # Add the Column Headings as attributes
        for key, value in six.iteritems(field_headings):
            if value is not None and value != '' and \
                    key not in headings:
                cube.attributes[key] = value

        if cell_methods is not None:
            cube.add_cell_method(cell_methods[i])

        yield cube
Exemple #26
0
def _wrap_iris(solution, neofs, time_units):
    try:
        from iris.cube import Cube
        from iris.coords import DimCoord
        from cf_units import Unit
    except ImportError:
        raise ValueError("cannot use container 'iris' without "
                         "the iris module")
    time_dim = DimCoord(solution['time'],
                        standard_name='time',
                        units=Unit(time_units, 'gregorian'))
    lat_dim = DimCoord(solution['latitude'],
                       standard_name='latitude',
                       units='degrees_north')
    lat_dim.guess_bounds()
    lon_dim = DimCoord(solution['longitude'],
                       standard_name='longitude',
                       units='degrees_east')
    lon_dim.guess_bounds()
    eof_dim = DimCoord(np.arange(1, neofs+1),
                       long_name='eof')
    solution['sst'] = Cube(
        solution['sst'],
        dim_coords_and_dims=list(zip((time_dim, lat_dim, lon_dim), range(3))),
        long_name='sst')
    solution['eigenvalues'] = Cube(
        solution['eigenvalues'],
        dim_coords_and_dims=list(zip((eof_dim,), range(1))),
        long_name='eigenvalues')
    solution['eofs'] = Cube(
        solution['eofs'],
        dim_coords_and_dims=list(zip((eof_dim, lat_dim, lon_dim), range(3))),
        long_name='eofs')
    solution['pcs'] = Cube(
        solution['pcs'],
        dim_coords_and_dims=list(zip((time_dim, eof_dim), range(2))),
        long_name='pcs')
    solution['variance'] = Cube(
        solution['variance'],
        dim_coords_and_dims=list(zip((eof_dim,), range(1))),
        long_name='variance')
    solution['eofscor'] = Cube(
        solution['eofscor'],
        dim_coords_and_dims=list(zip((eof_dim, lat_dim, lon_dim), range(3))),
        long_name='eofscor')
    solution['eofscov'] = Cube(
        solution['eofscov'],
        dim_coords_and_dims=list(zip((eof_dim, lat_dim, lon_dim), range(3))),
        long_name='eofscov')
    solution['errors'] = Cube(
        solution['errors'],
        dim_coords_and_dims=list(zip((eof_dim,), range(1))),
        long_name='errors')
    solution['scaled_errors'] = Cube(
        solution['scaled_errors'],
        dim_coords_and_dims=list(zip((eof_dim,), range(1))),
        long_name='scaled_errors')
    solution['rcon'] = Cube(
        solution['rcon'],
        dim_coords_and_dims=list(zip((time_dim, lat_dim, lon_dim), range(3))),
        long_name='reconstructed_sst')
Exemple #27
0
def _generate_cubes(header,
                    column_headings,
                    coords,
                    data_arrays,
                    cell_methods=None):
    """
    Yield :class:`iris.cube.Cube` instances given
    the headers, column headings, coords and data_arrays extracted
    from a NAME file.

    """
    for i, data_array in enumerate(data_arrays):
        # Turn the dictionary of column headings with a list of header
        # information for each field into a dictionary of headings for
        # just this field.
        field_headings = {k: v[i] for k, v in column_headings.items()}

        # Make a cube.
        cube = iris.cube.Cube(data_array)

        # Determine the name and units.
        name = "{} {}".format(field_headings["Species"],
                              field_headings["Quantity"])
        name = name.upper().replace(" ", "_")
        cube.rename(name)

        # Some units are not in SI units, are missing spaces or typed
        # in the wrong case. _parse_units returns units that are
        # recognised by Iris.
        cube.units = _parse_units(field_headings["Units"])

        # Define and add the singular coordinates of the field (flight
        # level, time etc.)
        if "Z" in field_headings:
            (upper_bound, ) = [
                field_headings["... to [Z]"]
                if "... to [Z]" in field_headings else None
            ]
            (lower_bound, ) = [
                field_headings["... from [Z]"]
                if "... from [Z]" in field_headings else None
            ]
            z_coord = _cf_height_from_name(
                field_headings["Z"],
                upper_bound=upper_bound,
                lower_bound=lower_bound,
            )
            cube.add_aux_coord(z_coord)

        # Define the time unit and use it to serialise the datetime for
        # the time coordinate.
        time_unit = cf_units.Unit("hours since epoch",
                                  calendar=cf_units.CALENDAR_GREGORIAN)

        # Build time, height, latitude and longitude coordinates.
        for coord in coords:
            pts = coord.values
            coord_sys = None
            if coord.name == "latitude" or coord.name == "longitude":
                coord_units = "degrees"
                coord_sys = iris.coord_systems.GeogCS(EARTH_RADIUS)
            if (coord.name == "projection_x_coordinate"
                    or coord.name == "projection_y_coordinate"):
                coord_units = "m"
                coord_sys = iris.coord_systems.OSGB()
            if coord.name == "height":
                coord_units = "m"
                long_name = "height above ground level"
                pts = coord.values
            if coord.name == "altitude":
                coord_units = "m"
                long_name = "altitude above sea level"
                pts = coord.values
            if coord.name == "air_pressure":
                coord_units = "Pa"
                pts = coord.values
            if coord.name == "flight_level":
                pts = coord.values
                long_name = "flight_level"
                coord_units = _parse_units("FL")
            if coord.name == "time":
                coord_units = time_unit
                pts = time_unit.date2num(coord.values)

            if coord.dimension is not None:
                if coord.name == "longitude":
                    circular = iris.util._is_circular(pts, 360.0)
                else:
                    circular = False
                if coord.name == "flight_level":
                    icoord = DimCoord(points=pts,
                                      units=coord_units,
                                      long_name=long_name)
                else:
                    icoord = DimCoord(
                        points=pts,
                        standard_name=coord.name,
                        units=coord_units,
                        coord_system=coord_sys,
                        circular=circular,
                    )
                if coord.name == "height" or coord.name == "altitude":
                    icoord.long_name = long_name
                if (coord.name == "time"
                        and "Av or Int period" in field_headings):
                    dt = coord.values - field_headings["Av or Int period"]
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds
                else:
                    icoord.guess_bounds()
                cube.add_dim_coord(icoord, coord.dimension)
            else:
                icoord = AuxCoord(
                    points=pts[i],
                    standard_name=coord.name,
                    coord_system=coord_sys,
                    units=coord_units,
                )
                if (coord.name == "time"
                        and "Av or Int period" in field_headings):
                    dt = coord.values - field_headings["Av or Int period"]
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds[i, :]
                cube.add_aux_coord(icoord)

        # Headings/column headings which are encoded elsewhere.
        headings = [
            "X",
            "Y",
            "Z",
            "Time",
            "T",
            "Units",
            "Av or Int period",
            "... from [Z]",
            "... to [Z]",
            "X grid origin",
            "Y grid origin",
            "X grid size",
            "Y grid size",
            "X grid resolution",
            "Y grid resolution",
            "Number of field cols",
            "Number of preliminary cols",
            "Number of fields",
            "Number of series",
            "Output format",
        ]

        # Add the Main Headings as attributes.
        for key, value in header.items():
            if value is not None and value != "" and key not in headings:
                cube.attributes[key] = value

        # Add the Column Headings as attributes
        for key, value in field_headings.items():
            if value is not None and value != "" and key not in headings:
                cube.attributes[key] = value

        if cell_methods is not None:
            cube.add_cell_method(cell_methods[i])

        yield cube
def _make_cube(x, y, data, aux=None, offset=0, scalar=None):
    """
    A convenience test function that creates a custom 2D cube.

    Args:

    * x:
        A (start, stop, step) tuple for specifying the
        x-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * y:
        A (start, stop, step) tuple for specifying the
        y-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * data:
        The data payload for the cube.

    Kwargs:

    * aux:
        A CSV string specifying which points only auxiliary
        coordinates to create. Accepts either of 'x', 'y', 'xy'.

    * offset:
        Offset value to be added to the 'xy' auxiliary coordinate
        points.

    * scalar:
        Create a 'height' scalar coordinate with the given value.

    Returns:
        The newly created 2D :class:`iris.cube.Cube`.

    """
    x_range = np.arange(*x, dtype=np.float32)
    y_range = np.arange(*y, dtype=np.float32)
    x_size = len(x_range)
    y_size = len(y_range)

    cube_data = np.empty((y_size, x_size), dtype=np.float32)
    cube_data[:] = data
    cube = iris.cube.Cube(cube_data)
    coord = DimCoord(y_range, long_name='y')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 0)
    coord = DimCoord(x_range, long_name='x')
    coord.guess_bounds()
    cube.add_dim_coord(coord, 1)

    if aux is not None:
        aux = aux.split(',')
        if 'y' in aux:
            coord = AuxCoord(y_range * 10, long_name='y-aux')
            cube.add_aux_coord(coord, (0,))
        if 'x' in aux:
            coord = AuxCoord(x_range * 10, long_name='x-aux')
            cube.add_aux_coord(coord, (1,))
        if 'xy' in aux:
            payload = np.arange(y_size * x_size,
                                dtype=np.float32).reshape(y_size, x_size)
            coord = AuxCoord(payload * 100 + offset, long_name='xy-aux')
            cube.add_aux_coord(coord, (0, 1))

    if scalar is not None:
        data = np.array([scalar], dtype=np.float32)
        coord = AuxCoord(data, long_name='height', units='m')
        cube.add_aux_coord(coord, ())

    return cube
Exemple #29
0
 def test_points_inside_bounds_outside_wrong_name(self):
     lat = DimCoord([-80, 0, 70], units="degree", standard_name="longitude")
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-120, -40], [-40, 35], [35, 105]])
class Test_make_coord(tests.IrisTest):
    @staticmethod
    def coord_dims(coord):
        mapping = dict(sigma=(0,), eta=(1, 2), depth=(1, 2),
                       depth_c=(), nsigma=(), zlev=(0,))
        return mapping[coord.name()]

    @staticmethod
    def derive(sigma, eta, depth, depth_c, nsigma, zlev, coord=True):
        nsigma_slice = slice(0, int(nsigma))
        temp = eta + sigma * (np.minimum(depth_c, depth) + eta)
        shape = temp.shape
        result = np.ones(shape, dtype=temp.dtype) * zlev
        result[nsigma_slice] = temp[nsigma_slice]
        if coord:
            name = 'sea_surface_height_above_reference_ellipsoid'
            result = AuxCoord(result,
                              standard_name=name,
                              units='m',
                              attributes=dict(positive='up'))
        return result

    def setUp(self):
        self.sigma = DimCoord(np.arange(5, dtype=np.float) * 10,
                              long_name='sigma', units='1')
        self.eta = AuxCoord(np.arange(4, dtype=np.float).reshape(2, 2),
                            long_name='eta', units='m')
        self.depth = AuxCoord(np.arange(4, dtype=np.float).reshape(2, 2) * 10,
                              long_name='depth', units='m')
        self.depth_c = AuxCoord([15], long_name='depth_c', units='m')
        self.nsigma = AuxCoord([3], long_name='nsigma')
        self.zlev = DimCoord(np.arange(5, dtype=np.float) * 10,
                             long_name='zlev', units='m')
        self.kwargs = dict(sigma=self.sigma, eta=self.eta, depth=self.depth,
                           depth_c=self.depth_c, nsigma=self.nsigma,
                           zlev=self.zlev)

    def test_derived_points(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_derived_points_with_bounds(self):
        self.sigma.guess_bounds()
        self.zlev.guess_bounds()
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected coordinate with points.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Broadcast expected bounds given the known dimensional mapping.
        sigma = self.sigma.bounds.reshape(sigma.shape + (2,))
        eta = self.eta.points.reshape(eta.shape + (1,))
        depth = self.depth.points.reshape(depth.shape + (1,))
        depth_c = self.depth_c.points.reshape(depth_c.shape + (1,))
        nsigma = self.nsigma.points.reshape(nsigma.shape + (1,))
        zlev = self.zlev.bounds.reshape(zlev.shape + (2,))
        # Calculate the expected bounds.
        bounds = self.derive(sigma, eta, depth, depth_c, nsigma, zlev,
                             coord=False)
        expected_coord.bounds = bounds
        # Calculate the actual result.
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_eta(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = 0
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['eta'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_sigma(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = 0
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['sigma'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_depth_c(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = self.depth.points[np.newaxis, ...]
        depth_c = 0
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['depth_c'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)

    def test_no_depth(self):
        # Broadcast expected points given the known dimensional mapping.
        sigma = self.sigma.points[..., np.newaxis, np.newaxis]
        eta = self.eta.points[np.newaxis, ...]
        depth = 0
        depth_c = self.depth_c.points
        nsigma = self.nsigma.points
        zlev = self.zlev.points[..., np.newaxis, np.newaxis]
        # Calculate the expected result.
        expected_coord = self.derive(sigma, eta, depth, depth_c, nsigma, zlev)
        # Calculate the actual result.
        self.kwargs['depth'] = None
        factory = OceanSigmaZFactory(**self.kwargs)
        coord = factory.make_coord(self.coord_dims)
        self.assertEqual(expected_coord, coord)
Exemple #31
0
 def test_all_inside(self):
     lat = DimCoord([-10, 0, 20], units='degree', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-15, -5], [-5, 10], [10, 30]])
Exemple #32
0
def _make_cube(
    x, y, data, aux=None, cell_measure=None, ancil=None, offset=0, scalar=None
):
    """
    A convenience test function that creates a custom 2D cube.

    Args:

    * x:
        A (start, stop, step) tuple for specifying the
        x-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * y:
        A (start, stop, step) tuple for specifying the
        y-axis dimensional coordinate points. Bounds are
        automatically guessed.

    * data:
        The data payload for the cube.

    Kwargs:

    * aux:
        A CSV string specifying which points only auxiliary
        coordinates to create. Accepts either of 'x', 'y', 'xy'.

    * offset:
        Offset value to be added to the 'xy' auxiliary coordinate
        points.

    * scalar:
        Create a 'height' scalar coordinate with the given value.

    Returns:
        The newly created 2D :class:`iris.cube.Cube`.

    """
    x_range = np.arange(*x, dtype=np.float32)
    y_range = np.arange(*y, dtype=np.float32)
    x_size = len(x_range)
    y_size = len(y_range)

    cube_data = np.empty((y_size, x_size), dtype=np.float32)
    cube_data[:] = data
    cube = iris.cube.Cube(cube_data)
    coord = DimCoord(y_range, long_name="y", units="1")
    coord.guess_bounds()
    cube.add_dim_coord(coord, 0)
    coord = DimCoord(x_range, long_name="x", units="1")
    coord.guess_bounds()
    cube.add_dim_coord(coord, 1)

    if aux is not None:
        aux = aux.split(",")
        if "y" in aux:
            coord = AuxCoord(y_range * 10, long_name="y-aux", units="1")
            cube.add_aux_coord(coord, (0,))
        if "x" in aux:
            coord = AuxCoord(x_range * 10, long_name="x-aux", units="1")
            cube.add_aux_coord(coord, (1,))
        if "xy" in aux:
            payload = np.arange(y_size * x_size, dtype=np.float32).reshape(
                y_size, x_size
            )
            coord = AuxCoord(
                payload * 100 + offset, long_name="xy-aux", units="1"
            )
            cube.add_aux_coord(coord, (0, 1))

    if cell_measure is not None:
        cell_measure = cell_measure.split(",")
        if "y" in cell_measure:
            cm = CellMeasure(y_range * 10, long_name="y-aux", units="1")
            cube.add_cell_measure(cm, (0,))
        if "x" in cell_measure:
            cm = CellMeasure(x_range * 10, long_name="x-aux", units="1")
            cube.add_cell_measure(cm, (1,))
        if "xy" in cell_measure:
            payload = x_range + y_range[:, np.newaxis]
            cm = CellMeasure(
                payload * 100 + offset, long_name="xy-aux", units="1"
            )
            cube.add_cell_measure(cm, (0, 1))

    if ancil is not None:
        ancil = ancil.split(",")
        if "y" in ancil:
            av = AncillaryVariable(y_range * 10, long_name="y-aux", units="1")
            cube.add_ancillary_variable(av, (0,))
        if "x" in ancil:
            av = AncillaryVariable(x_range * 10, long_name="x-aux", units="1")
            cube.add_ancillary_variable(av, (1,))
        if "xy" in ancil:
            payload = x_range + y_range[:, np.newaxis]
            av = AncillaryVariable(
                payload * 100 + offset, long_name="xy-aux", units="1"
            )
            cube.add_ancillary_variable(av, (0, 1))

    if scalar is not None:
        data = np.array([scalar], dtype=np.float32)
        coord = AuxCoord(data, long_name="height", units="m")
        cube.add_aux_coord(coord, ())

    return cube
Exemple #33
0
 def test_points_inside_bounds_outside_grid_latitude(self):
     lat = DimCoord([-80, 0, 70], units='degree',
                    standard_name='grid_latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-90, -40], [-40, 35], [35, 90]])
Exemple #34
0
 def test_points_inside_bounds_outside_grid_latitude(self):
     lat = DimCoord([-80, 0, 70],
                    units='degree',
                    standard_name='grid_latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-90, -40], [-40, 35], [35, 90]])
Exemple #35
0
 def test_points_inside_bounds_outside_wrong_unit(self):
     lat = DimCoord([-80, 0, 70], units='feet', standard_name='latitude')
     lat.guess_bounds()
     self.assertArrayEqual(lat.bounds, [[-120, -40], [-40, 35], [35, 105]])
Exemple #36
0
    def to_cube(self):
        """Return a new :class:`~iris.cube.Cube` from this ABFField."""

        cube = iris.cube.Cube(self.data)

        # Name.
        if self.format.lower() == "abf":
            cube.rename("leaf_area_index")
        elif self.format.lower() == "abl":
            cube.rename("FAPAR")
        else:
            msg = "Unknown ABF/ABL format: {}".format(self.format)
            raise iris.exceptions.TranslationError(msg)
        cube.units = "%"

        # Grid.
        step = 1.0 / 12.0

        llcs = GeogCS(semi_major_axis=6378137.0, semi_minor_axis=6356752.31424)

        x_coord = DimCoord(np.arange(X_SIZE) * step + (step / 2) - 180,
                           standard_name="longitude", units="degrees",
                           coord_system=llcs)

        y_coord = DimCoord(np.arange(Y_SIZE) * step + (step / 2) - 90,
                           standard_name="latitude",  units="degrees",
                           coord_system=llcs)

        x_coord.guess_bounds()
        y_coord.guess_bounds()

        cube.add_dim_coord(x_coord, 1)
        cube.add_dim_coord(y_coord, 0)

        # Time.
        if self.period == "a":
            start = 1
            end = 15
        elif self.period == "b":
            start = 16
            end = calendar.monthrange(self.year, self.month)[1]
        else:
            raise iris.exceptions.TranslationError("Unknown period: "
                                                   "{}".format(self.period))

        start = datetime.date(year=self.year, month=self.month, day=start)
        end = datetime.date(year=self.year, month=self.month, day=end)

        # Convert to "days since 0001-01-01".
        # Iris will have proper datetime objects in the future.
        # This step will not be necessary.
        start = start.toordinal() - 1
        end = end.toordinal() - 1

        # TODO: Should we put the point in the middle of the period instead?
        cube.add_aux_coord(AuxCoord(start, standard_name="time",
                                    units="days since 0001-01-01",
                                    bounds=[start, end]))

        # TODO: Do they only come from Boston?
        # Attributes.
        cube.attributes["source"] = "Boston University"

        return cube
Exemple #37
0
def _generate_cubes(header, column_headings, coords, data_arrays, cell_methods=None):
    """
    Yield :class:`iris.cube.Cube` instances given
    the headers, column headings, coords and data_arrays extracted
    from a NAME file.

    """
    for i, data_array in enumerate(data_arrays):
        # Turn the dictionary of column headings with a list of header
        # information for each field into a dictionary of headings for
        # just this field.
        field_headings = {k: v[i] for k, v in six.iteritems(column_headings)}

        # Make a cube.
        cube = iris.cube.Cube(data_array)

        # Determine the name and units.
        name = "{} {}".format(field_headings["Species"], field_headings["Quantity"])
        name = name.upper().replace(" ", "_")
        cube.rename(name)

        # Some units are not in SI units, are missing spaces or typed
        # in the wrong case. _parse_units returns units that are
        # recognised by Iris.
        cube.units = _parse_units(field_headings["Units"])

        # Define and add the singular coordinates of the field (flight
        # level, time etc.)
        if "Z" in field_headings:
            upper_bound, = [field_headings["... to [Z]"] if "... to [Z]" in field_headings else None]
            lower_bound, = [field_headings["... from [Z]"] if "... from [Z]" in field_headings else None]
            z_coord = _cf_height_from_name(field_headings["Z"], upper_bound=upper_bound, lower_bound=lower_bound)
            cube.add_aux_coord(z_coord)

        # Define the time unit and use it to serialise the datetime for
        # the time coordinate.
        time_unit = cf_units.Unit("hours since epoch", calendar=cf_units.CALENDAR_GREGORIAN)

        # Build time, height, latitude and longitude coordinates.
        for coord in coords:
            pts = coord.values
            coord_sys = None
            if coord.name == "latitude" or coord.name == "longitude":
                coord_units = "degrees"
                coord_sys = iris.coord_systems.GeogCS(EARTH_RADIUS)
            if coord.name == "projection_x_coordinate" or coord.name == "projection_y_coordinate":
                coord_units = "m"
                coord_sys = iris.coord_systems.OSGB()
            if coord.name == "height":
                coord_units = "m"
                long_name = "height above ground level"
                pts = coord.values
            if coord.name == "altitude":
                coord_units = "m"
                long_name = "altitude above sea level"
                pts = coord.values
            if coord.name == "air_pressure":
                coord_units = "Pa"
                pts = coord.values
            if coord.name == "flight_level":
                pts = coord.values
                long_name = "flight_level"
                coord_units = _parse_units("FL")
            if coord.name == "time":
                coord_units = time_unit
                pts = time_unit.date2num(coord.values)

            if coord.dimension is not None:
                if coord.name == "longitude":
                    circular = iris.util._is_circular(pts, 360.0)
                else:
                    circular = False
                if coord.name == "flight_level":
                    icoord = DimCoord(points=pts, units=coord_units, long_name=long_name)
                else:
                    icoord = DimCoord(
                        points=pts,
                        standard_name=coord.name,
                        units=coord_units,
                        coord_system=coord_sys,
                        circular=circular,
                    )
                if coord.name == "height" or coord.name == "altitude":
                    icoord.long_name = long_name
                if coord.name == "time" and "Av or Int period" in field_headings:
                    dt = coord.values - field_headings["Av or Int period"]
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds
                else:
                    icoord.guess_bounds()
                cube.add_dim_coord(icoord, coord.dimension)
            else:
                icoord = AuxCoord(points=pts[i], standard_name=coord.name, coord_system=coord_sys, units=coord_units)
                if coord.name == "time" and "Av or Int period" in field_headings:
                    dt = coord.values - field_headings["Av or Int period"]
                    bnds = time_unit.date2num(np.vstack((dt, coord.values)).T)
                    icoord.bounds = bnds[i, :]
                cube.add_aux_coord(icoord)

        # Headings/column headings which are encoded elsewhere.
        headings = [
            "X",
            "Y",
            "Z",
            "Time",
            "T",
            "Units",
            "Av or Int period",
            "... from [Z]",
            "... to [Z]",
            "X grid origin",
            "Y grid origin",
            "X grid size",
            "Y grid size",
            "X grid resolution",
            "Y grid resolution",
            "Number of field cols",
            "Number of preliminary cols",
            "Number of fields",
            "Number of series",
            "Output format",
        ]

        # Add the Main Headings as attributes.
        for key, value in six.iteritems(header):
            if value is not None and value != "" and key not in headings:
                cube.attributes[key] = value

        # Add the Column Headings as attributes
        for key, value in six.iteritems(field_headings):
            if value is not None and value != "" and key not in headings:
                cube.attributes[key] = value

        if cell_methods is not None:
            cube.add_cell_method(cell_methods[i])

        yield cube