-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: stephen.worsley <[email protected]>
- Loading branch information
1 parent
1c62f07
commit a136915
Showing
3 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
# Copyright Iris contributors | ||
# | ||
# This file is part of Iris and is released under the LGPL license. | ||
# See COPYING and COPYING.LESSER in the root of the repository for full | ||
# licensing details. | ||
""" | ||
Provides objects describing cube summaries. | ||
""" | ||
|
||
import iris.util | ||
|
||
|
||
class DimensionHeader: | ||
def __init__(self, cube): | ||
if cube.shape == (): | ||
self.scalar = True | ||
self.dim_names = [] | ||
self.shape = [] | ||
self.contents = ["scalar cube"] | ||
else: | ||
self.scalar = False | ||
self.dim_names = [] | ||
for dim in range(len(cube.shape)): | ||
dim_coords = cube.coords( | ||
contains_dimension=dim, dim_coords=True | ||
) | ||
if dim_coords: | ||
self.dim_names.append(dim_coords[0].name()) | ||
else: | ||
self.dim_names.append("-- ") | ||
self.shape = list(cube.shape) | ||
self.contents = [ | ||
name + ": %d" % dim_len | ||
for name, dim_len in zip(self.dim_names, self.shape) | ||
] | ||
|
||
|
||
class FullHeader: | ||
def __init__(self, cube, name_padding=35): | ||
self.name = cube.name() | ||
self.unit = cube.units | ||
self.nameunit = "{name} / ({units})".format( | ||
name=self.name, units=self.unit | ||
) | ||
self.name_padding = name_padding | ||
self.dimension_header = DimensionHeader(cube) | ||
|
||
|
||
class CoordSummary: | ||
def _summary_coord_extra(self, cube, coord): | ||
# Returns the text needed to ensure this coordinate can be | ||
# distinguished from all others with the same name. | ||
extra = "" | ||
similar_coords = cube.coords(coord.name()) | ||
if len(similar_coords) > 1: | ||
# Find all the attribute keys | ||
keys = set() | ||
for similar_coord in similar_coords: | ||
keys.update(similar_coord.attributes.keys()) | ||
# Look for any attributes that vary | ||
vary = set() | ||
attributes = {} | ||
for key in keys: | ||
for similar_coord in similar_coords: | ||
if key not in similar_coord.attributes: | ||
vary.add(key) | ||
break | ||
value = similar_coord.attributes[key] | ||
if attributes.setdefault(key, value) != value: | ||
vary.add(key) | ||
break | ||
keys = sorted(vary & set(coord.attributes.keys())) | ||
bits = [ | ||
"{}={!r}".format(key, coord.attributes[key]) for key in keys | ||
] | ||
if bits: | ||
extra = ", ".join(bits) | ||
return extra | ||
|
||
|
||
class VectorSummary(CoordSummary): | ||
def __init__(self, cube, vector, iscoord): | ||
self.name = iris.util.clip_string(vector.name()) | ||
dims = vector.cube_dims(cube) | ||
self.dim_chars = [ | ||
"x" if dim in dims else "-" for dim in range(len(cube.shape)) | ||
] | ||
if iscoord: | ||
extra = self._summary_coord_extra(cube, vector) | ||
self.extra = iris.util.clip_string(extra) | ||
else: | ||
self.extra = "" | ||
|
||
|
||
class ScalarSummary(CoordSummary): | ||
def __init__(self, cube, coord): | ||
self.name = coord.name() | ||
if ( | ||
coord.units in ["1", "no_unit", "unknown"] | ||
or coord.units.is_time_reference() | ||
): | ||
self.unit = "" | ||
else: | ||
self.unit = " {!s}".format(coord.units) | ||
coord_cell = coord.cell(0) | ||
if isinstance(coord_cell.point, str): | ||
self.string_type = True | ||
self.lines = [ | ||
iris.util.clip_string(str(item)) | ||
for item in coord_cell.point.split("\n") | ||
] | ||
self.point = None | ||
self.bound = None | ||
self.content = "\n".join(self.lines) | ||
else: | ||
self.string_type = False | ||
self.lines = None | ||
self.point = "{!s}".format(coord_cell.point) | ||
coord_cell_cbound = coord_cell.bound | ||
if coord_cell_cbound is not None: | ||
self.bound = "({})".format( | ||
", ".join(str(val) for val in coord_cell_cbound) | ||
) | ||
self.content = "{}{}, bound={}{}".format( | ||
self.point, self.unit, self.bound, self.unit | ||
) | ||
else: | ||
self.bound = None | ||
self.content = "{}{}".format(self.point, self.unit) | ||
extra = self._summary_coord_extra(cube, coord) | ||
self.extra = iris.util.clip_string(extra) | ||
|
||
|
||
class Section: | ||
def _init_(self): | ||
self.contents = [] | ||
|
||
def is_empty(self): | ||
return self.contents == [] | ||
|
||
|
||
class VectorSection(Section): | ||
def __init__(self, title, cube, vectors, iscoord): | ||
self.title = title | ||
self.contents = [ | ||
VectorSummary(cube, vector, iscoord) for vector in vectors | ||
] | ||
|
||
|
||
class ScalarSection(Section): | ||
def __init__(self, title, cube, scalars): | ||
self.title = title | ||
self.contents = [ScalarSummary(cube, scalar) for scalar in scalars] | ||
|
||
|
||
class ScalarCellMeasureSection(Section): | ||
def __init__(self, title, cell_measures): | ||
self.title = title | ||
self.contents = [cm.name() for cm in cell_measures] | ||
|
||
|
||
class AttributeSection(Section): | ||
def __init__(self, title, attributes): | ||
self.title = title | ||
self.names = [] | ||
self.values = [] | ||
self.contents = [] | ||
for name, value in sorted(attributes.items()): | ||
value = iris.util.clip_string(str(value)) | ||
self.names.append(name) | ||
self.values.append(value) | ||
content = "{}: {}".format(name, value) | ||
self.contents.append(content) | ||
|
||
|
||
class CellMethodSection(Section): | ||
def __init__(self, title, cell_methods): | ||
self.title = title | ||
self.contents = [str(cm) for cm in cell_methods] | ||
|
||
|
||
class CubeSummary: | ||
def __init__(self, cube, shorten=False, name_padding=35): | ||
self.section_indent = 5 | ||
self.item_indent = 10 | ||
self.extra_indent = 13 | ||
self.shorten = shorten | ||
self.header = FullHeader(cube, name_padding) | ||
|
||
# Cache the derived coords so we can rely on consistent | ||
# object IDs. | ||
derived_coords = cube.derived_coords | ||
# Determine the cube coordinates that are scalar (single-valued) | ||
# AND non-dimensioned. | ||
dim_coords = cube.dim_coords | ||
aux_coords = cube.aux_coords | ||
all_coords = dim_coords + aux_coords + derived_coords | ||
scalar_coords = [ | ||
coord | ||
for coord in all_coords | ||
if not cube.coord_dims(coord) and coord.shape == (1,) | ||
] | ||
# Determine the cube coordinates that are not scalar BUT | ||
# dimensioned. | ||
scalar_coord_ids = set(map(id, scalar_coords)) | ||
vector_dim_coords = [ | ||
coord for coord in dim_coords if id(coord) not in scalar_coord_ids | ||
] | ||
vector_aux_coords = [ | ||
coord for coord in aux_coords if id(coord) not in scalar_coord_ids | ||
] | ||
vector_derived_coords = [ | ||
coord | ||
for coord in derived_coords | ||
if id(coord) not in scalar_coord_ids | ||
] | ||
|
||
# cell measures | ||
vector_cell_measures = [ | ||
cm for cm in cube.cell_measures() if cm.shape != (1,) | ||
] | ||
|
||
# Ancillary Variables | ||
vector_ancillary_variables = [av for av in cube.ancillary_variables()] | ||
|
||
# Sort scalar coordinates by name. | ||
scalar_coords.sort(key=lambda coord: coord.name()) | ||
# Sort vector coordinates by data dimension and name. | ||
vector_dim_coords.sort( | ||
key=lambda coord: (cube.coord_dims(coord), coord.name()) | ||
) | ||
vector_aux_coords.sort( | ||
key=lambda coord: (cube.coord_dims(coord), coord.name()) | ||
) | ||
vector_derived_coords.sort( | ||
key=lambda coord: (cube.coord_dims(coord), coord.name()) | ||
) | ||
scalar_cell_measures = [ | ||
cm for cm in cube.cell_measures() if cm.shape == (1,) | ||
] | ||
|
||
self.vector_sections = {} | ||
|
||
def add_vector_section(title, contents, iscoord=True): | ||
self.vector_sections[title] = VectorSection( | ||
title, cube, contents, iscoord | ||
) | ||
|
||
add_vector_section("Dimension coordinates:", vector_dim_coords) | ||
add_vector_section("Auxiliary coordinates:", vector_aux_coords) | ||
add_vector_section("Derived coordinates:", vector_derived_coords) | ||
add_vector_section("Cell Measures:", vector_cell_measures, False) | ||
add_vector_section( | ||
"Ancillary Variables:", vector_ancillary_variables, False | ||
) | ||
|
||
self.scalar_sections = {} | ||
|
||
def add_scalar_section(section_class, title, *args): | ||
self.scalar_sections[title] = section_class(title, *args) | ||
|
||
add_scalar_section( | ||
ScalarSection, "Scalar Coordinates:", cube, scalar_coords | ||
) | ||
add_scalar_section( | ||
ScalarCellMeasureSection, | ||
"Scalar cell measures:", | ||
scalar_cell_measures, | ||
) | ||
add_scalar_section(AttributeSection, "Attributes:", cube.attributes) | ||
add_scalar_section( | ||
CellMethodSection, "Cell methods:", cube.cell_methods | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Copyright Iris contributors | ||
# | ||
# This file is part of Iris and is released under the LGPL license. | ||
# See COPYING and COPYING.LESSER in the root of the repository for full | ||
# licensing details. | ||
"""Unit tests for the :mod:`iris._representation` module.""" |
Oops, something went wrong.