diff --git a/CHANGES b/CHANGES index 1e554ea347..d0db75ff67 100644 --- a/CHANGES +++ b/CHANGES @@ -25,10 +25,13 @@ Incompatible changes get_lat_lon_contiguous_grids(). They have been replaced with generalised versions: xy_range(), get_xy_grids(), and get_xy_contiguous_bounded_grids(). +* Cube.coord_dims() now returns a tuple instead of a list. Deprecations ------------ * The methods `Coord.cos()` and `Coord.sin()` have been deprecated. +* The function `load_strict()` function has been deprecated. Code should + now use `iris.load_cube()` and `iris.load_cubes()` instead. ---------------------------- @@ -114,7 +117,7 @@ Incompatible changes Previously the signature was ``loader(filename, callback)`` and now looks like ``loader(filenames, callback)`` where ``filenames`` is an iterable of filenames. * Custom aggregators should now create :class:`iris.analysis.Aggregator` instances rather than defining - a the partial creation of an Aggregate (i.e. ``partial(iris.analysis.Aggregate, ...)`` becomes ``iris.analsysis.Aggregator(...)``. + a the partial creation of an Aggregate (i.e. ``partial(iris.analysis.Aggregate, ...)`` becomes ``iris.analysis.Aggregator(...)``. * PP STASH code ingestion is now stricter. In particular handling of partial stash codes (i.e. those without a model or section) is no longer done, meaning that some cubes which previously had a standard name may no longer have a standard name. Further work is planned to support field code translation which should provide more robust and accurate PP interpretations. @@ -188,7 +191,7 @@ Release 0.5 (31 Oct, 2011) * Added ability to derive area weights from a shapely geometry. -* Cubes can now be operated on (added, muliplied, etc.) using numpy arrays. +* Cubes can now be operated on (added, multiplied, etc.) using numpy arrays. * New example of how to load custom ascii files into Cubes. diff --git a/docs/iris/src/whats_new.rst b/docs/iris/src/whats_new.rst index 590b4b4711..fed7998086 100644 --- a/docs/iris/src/whats_new.rst +++ b/docs/iris/src/whats_new.rst @@ -46,6 +46,7 @@ Incompatible changes -------------------- * The "source" and "history" metadata are now represented as Cube attributes, where previously they used coordinates. +* Cube.coord_dims() now returns a tuple instead of a list. Deprecations ------------ @@ -196,7 +197,7 @@ Where previously it would have appeared as:: source: Data from Met Office Unified Model ... -.. note:: This change breaks backwards compatibiltiy with Iris 0.9. But +.. note:: This change breaks backwards compatibility with Iris 0.9. But if it is desirable to have the "source" metadata expressed as a coordinate then it can be done with the following pattern:: diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py index 408be3ea1f..fcc59f3dc2 100644 --- a/lib/iris/analysis/__init__.py +++ b/lib/iris/analysis/__init__.py @@ -240,7 +240,7 @@ def coord_comparison(*cubes): # get all coordinate groups which don't describe a dimension # (None -> doesn't describe a dimension) - no_data_dim_fn = lambda cube, coord: cube.coord_dims(coord=coord) == [] + no_data_dim_fn = lambda cube, coord: cube.coord_dims(coord=coord) == () if coord_group.matches_all(no_data_dim_fn): no_data_dimension.add(coord_group) diff --git a/lib/iris/cube.py b/lib/iris/cube.py index be625c3cdc..d9fa29551b 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -272,7 +272,7 @@ def __init__(self, data, standard_name=None, long_name=None, units=None, * dim_coords_and_dims A list of coordinates with scalar dimension mappings, e.g ``[(lat_coord, 0), (lon_coord, 1)]``. * aux_coords_and_dims - A list of coordinates with dimension mappings, e.g ``[(lat_coord, [0]), (lon_coord, [0,1])]``. + A list of coordinates with dimension mappings, e.g ``[(lat_coord, 0), (lon_coord, (0, 1))]``. See also :meth:`Cube.add_dim_coord()` and :meth:`Cube.add_aux_coord()`. * aux_factories A list of auxiliary coordinate factories. See :mod:`iris.aux_factory`. @@ -381,7 +381,7 @@ def add_aux_coord(self, coord, data_dims=None): Kwargs: * data_dims - Integer/list of integers giving the data dimensions spanned by the coordinate. + Integer or iterable of integers giving the data dimensions spanned by the coordinate. Raises a ValueError if a coordinate with identical metadata already exists on the cube. @@ -390,14 +390,13 @@ def add_aux_coord(self, coord, data_dims=None): """ + # Convert to a tuple of integers if data_dims is None: - data_dims = [] - - # Convert to list of integers - if isinstance(data_dims, collections.Container): - data_dims = [int(d) for d in data_dims] + data_dims = tuple() + elif isinstance(data_dims, collections.Container): + data_dims = tuple(int(d) for d in data_dims) else: - data_dims = [int(data_dims)] + data_dims = (int(data_dims),) if data_dims: if len(data_dims) != coord.ndim: @@ -458,21 +457,22 @@ def add_dim_coord(self, dim_coord, data_dim=None): if data_dim is None: raise ValueError('You must supply a data dimension for a dimensioned coord.') if isinstance(data_dim, collections.Container) and len(data_dim) != 1: - raise ValueError('The supplied data dimension must be a single number') - if self.coords(dimensions=data_dim, dim_coords=True): - raise ValueError('A dim_coord is already associated with dimension %d.' % data_dim) + raise ValueError('The supplied data dimension must be a single number') - #TODO tidy this up # Convert data_dim to a single integer if isinstance(data_dim, collections.Container): data_dim = int(list(data_dim)[0]) else: data_dim = int(data_dim) - + # Check data_dim value is valid if data_dim < 0 or data_dim >= self.ndim: raise ValueError('The cube does not have the specified dimension (%d)' % data_dim) + # Check dimension is available + if self.coords(dimensions=data_dim, dim_coords=True): + raise ValueError('A dim_coord is already associated with dimension %d.' % data_dim) + # Check compatibility with the shape of the data if dim_coord.shape[0] != self.shape[data_dim]: msg = 'Unequal lengths. Cube dimension {} => {}; coord {!r} => {}.' @@ -528,10 +528,12 @@ def replace_coord(self, new_coord): def coord_dims(self, coord): """ - Returns the list of data dimensions relevant to the given coordinate. + Returns a tuple of the data dimensions relevant to the given + coordinate. - When searching for the given coordinate in the cube the comparison is made using coordinate metadata - equality. Hence the given coordinate instance need not exist on the cube, and may contain different + When searching for the given coordinate in the cube the comparison is + made using coordinate metadata equality. Hence the given coordinate + instance need not exist on the cube, and may contain different coordinate values. Args: @@ -545,7 +547,7 @@ def coord_dims(self, coord): ### Search by coord definition first # Search dim coords first - matches = [[dim] for coord_, dim in self._dim_coords_and_dims if coord_._as_defn() == target_defn] + matches = [(dim,) for coord_, dim in self._dim_coords_and_dims if coord_._as_defn() == target_defn] # Search aux coords if not matches: @@ -561,7 +563,7 @@ def coord_dims(self, coord): # XXX Where did this come from? And why isn't it reflected in the docstring? if not matches: - matches = [[dim] for coord_, dim in self._dim_coords_and_dims if coord_.name() == coord.name()] + matches = [(dim,) for coord_, dim in self._dim_coords_and_dims if coord_.name() == coord.name()] # Search aux coords if not matches: @@ -653,8 +655,8 @@ def coords(self, name=None, standard_name=None, long_name=None, attributes=None, * contains_dimension The desired coordinate contains the data dimension. If None, does not check for the dimension. * dimensions - The exact data dimensions of the desired coordinate. Coordinates with no data dimension can be found with an empty list (i.e. ``[]``). - If None, does not check for dimensions. + The exact data dimensions of the desired coordinate. Coordinates with no data dimension can be found with an + empty tuple or list (i.e. ``()`` or ``[]``). If None, does not check for dimensions. * coord Whether the desired coordinates have metadata equal to the given coordinate instance. If None, no check is done. Accepts either a :class:'iris.coords.DimCoord`, :class:`iris.coords.AuxCoord` or :class:`iris.coords.CoordDefn`. @@ -711,7 +713,7 @@ def coords(self, name=None, standard_name=None, long_name=None, attributes=None, if dimensions is not None: if not isinstance(dimensions, collections.Container): dimensions = [dimensions] - coords_and_factories = filter(lambda coord_: dimensions == self.coord_dims(coord_), coords_and_factories) + coords_and_factories = filter(lambda coord_: tuple(dimensions) == self.coord_dims(coord_), coords_and_factories) # If any factories remain after the above filters we have to make the coords so they can be returned def extract_coord(coord_or_factory): @@ -1441,9 +1443,10 @@ def remap_dim_coord(coord_and_dim): return coord, dim_mapping[dim] self._dim_coords_and_dims = map(remap_dim_coord, self._dim_coords_and_dims) - for coord, dims in self._aux_coords_and_dims: - for i, dim in enumerate(dims): - dims[i] = dim_mapping[dim] + def remap_aux_coord(coord_and_dims): + coord, dims = coord_and_dims + return coord, tuple(dim_mapping[dim] for dim in dims) + self._aux_coords_and_dims = map(remap_aux_coord, self._aux_coords_and_dims) def xml(self, checksum=False): """ @@ -1488,7 +1491,7 @@ def _xml_element(self, doc, checksum=False): cube_coord_xml_element = doc.createElement("coord") coords_xml_element.appendChild(cube_coord_xml_element) - dims = self.coord_dims(coord) + dims = list(self.coord_dims(coord)) if dims: cube_coord_xml_element.setAttribute("datadims", repr(dims)) diff --git a/lib/iris/tests/results/COLPEX/uwind_and_orog.cml b/lib/iris/tests/results/COLPEX/uwind_and_orog.cml index 2a73f6d3b0..bfcb52e5fc 100644 --- a/lib/iris/tests/results/COLPEX/uwind_and_orog.cml +++ b/lib/iris/tests/results/COLPEX/uwind_and_orog.cml @@ -6,7 +6,7 @@ - + - + - + - + - + - + - + - + - + - + - +