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
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)
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
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
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)
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
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))
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)
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]])
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]])
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
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
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]])
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
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]])
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
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
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)
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]])
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]])
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
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')
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
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)
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
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]])
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
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