diff --git a/docs/src/further_topics/ugrid/data_model.rst b/docs/src/further_topics/ugrid/data_model.rst
index 55e4f79a96..cc3cc7b793 100644
--- a/docs/src/further_topics/ugrid/data_model.rst
+++ b/docs/src/further_topics/ugrid/data_model.rst
@@ -405,6 +405,9 @@ the :class:`~iris.cube.Cube`\'s unstructured dimension.
Mesh coordinates:
latitude x -
longitude x -
+ Mesh:
+ name my_mesh
+ location edge
>>> print(edge_cube.location)
edge
diff --git a/docs/src/further_topics/ugrid/operations.rst b/docs/src/further_topics/ugrid/operations.rst
index c636043640..a4e0e593d7 100644
--- a/docs/src/further_topics/ugrid/operations.rst
+++ b/docs/src/further_topics/ugrid/operations.rst
@@ -189,6 +189,9 @@ Creating a :class:`~iris.cube.Cube` is unchanged; the
Mesh coordinates:
latitude x -
longitude x -
+ Mesh:
+ name my_mesh
+ location edge
Save
@@ -392,6 +395,9 @@ etcetera:
Mesh coordinates:
latitude x -
longitude x -
+ Mesh:
+ name my_mesh
+ location face
Attributes:
Conventions 'CF-1.7'
@@ -620,6 +626,9 @@ the link between :class:`~iris.cube.Cube` and
Mesh coordinates:
latitude x -
longitude x -
+ Mesh:
+ name my_mesh
+ location edge
# Sub-setted MeshCoords have become AuxCoords.
>>> print(edge_cube[:-1])
diff --git a/lib/iris/_representation/cube_printout.py b/lib/iris/_representation/cube_printout.py
index 81d46bb29f..b0b569512d 100644
--- a/lib/iris/_representation/cube_printout.py
+++ b/lib/iris/_representation/cube_printout.py
@@ -252,15 +252,15 @@ def add_scalar_row(name, value=""):
# Add a row for each item
# NOTE: different section types need different handling
title = sect_name.lower()
- if "scalar coordinate" in title:
+ if title == "scalar coordinates:":
for item in sect.contents:
add_scalar_row(item.name, item.content)
if item.extra:
add_scalar_row(item_to_extra_indent + item.extra)
- elif "attribute" in title or "cell method" in title:
+ elif title in ("attributes:", "cell methods:", "mesh:"):
for title, value in zip(sect.names, sect.values):
add_scalar_row(title, value)
- elif "scalar cell measure" in title:
+ elif title == "scalar cell measures:":
# These are just strings: nothing in the 'value' column.
for name in sect.contents:
add_scalar_row(name)
diff --git a/lib/iris/_representation/cube_summary.py b/lib/iris/_representation/cube_summary.py
index 1e78a92fd1..885de9acf3 100644
--- a/lib/iris/_representation/cube_summary.py
+++ b/lib/iris/_representation/cube_summary.py
@@ -48,11 +48,25 @@ def __init__(self, cube, name_padding=35):
self.dimension_header = DimensionHeader(cube)
-def string_repr(text, quote_strings=False):
+def string_repr(text, quote_strings=False, clip_strings=False):
"""Produce a one-line printable form of a text string."""
- if re.findall("[\n\t]", text) or quote_strings:
+ force_quoted = re.findall("[\n\t]", text) or quote_strings
+ if force_quoted:
# Replace the string with its repr (including quotes).
text = repr(text)
+ if clip_strings:
+ # First check for quotes.
+ # N.B. not just 'quote_strings', but also array values-as-strings
+ has_quotes = text[0] in "\"'"
+ if has_quotes:
+ # Strip off (and store) any outer quotes before clipping.
+ pre_quote, post_quote = text[0], text[-1]
+ text = text[1:-1]
+ # clipping : use 'rider' with extra space in case it ends in a '.'
+ text = iris.util.clip_string(text, rider=" ...")
+ if has_quotes:
+ # Replace in original quotes
+ text = pre_quote + text + post_quote
return text
@@ -62,17 +76,20 @@ def array_repr(arr):
text = repr(arr)
# ..then reduce any multiple spaces and newlines.
text = re.sub("[ \t\n]+", " ", text)
+ text = string_repr(text, quote_strings=False, clip_strings=True)
return text
-def value_repr(value, quote_strings=False):
+def value_repr(value, quote_strings=False, clip_strings=False):
"""
Produce a single-line printable version of an attribute or scalar value.
"""
if hasattr(value, "dtype"):
value = array_repr(value)
elif isinstance(value, str):
- value = string_repr(value, quote_strings=quote_strings)
+ value = string_repr(
+ value, quote_strings=quote_strings, clip_strings=clip_strings
+ )
value = str(value)
return value
@@ -132,7 +149,7 @@ def __init__(self, cube, vector, iscoord):
self.extra = ""
-class ScalarSummary(CoordSummary):
+class ScalarCoordSummary(CoordSummary):
def __init__(self, cube, coord):
self.name = coord.name()
if (
@@ -188,10 +205,12 @@ def __init__(self, title, cube, vectors, iscoord):
]
-class ScalarSection(Section):
+class ScalarCoordSection(Section):
def __init__(self, title, cube, scalars):
self.title = title
- self.contents = [ScalarSummary(cube, scalar) for scalar in scalars]
+ self.contents = [
+ ScalarCoordSummary(cube, scalar) for scalar in scalars
+ ]
class ScalarCellMeasureSection(Section):
@@ -207,14 +226,32 @@ def __init__(self, title, attributes):
self.values = []
self.contents = []
for name, value in sorted(attributes.items()):
- value = value_repr(value, quote_strings=True)
- value = iris.util.clip_string(value)
+ value = value_repr(value, quote_strings=True, clip_strings=True)
self.names.append(name)
self.values.append(value)
content = "{}: {}".format(name, value)
self.contents.append(content)
+class ScalarMeshSection(AttributeSection):
+ # This happens to behave just like an attribute sections, but it
+ # initialises direct from the cube.
+ def __init__(self, title, cube):
+ self.title = title
+ self.names = []
+ self.values = []
+ self.contents = []
+ if cube.mesh is not None:
+ self.names.extend(["name", "location"])
+ self.values.extend([cube.mesh.name(), cube.location])
+ self.contents.extend(
+ [
+ "{}: {}".format(name, value)
+ for name, value in zip(self.names, self.values)
+ ]
+ )
+
+
class CellMethodSection(Section):
def __init__(self, title, cell_methods):
self.title = title
@@ -322,8 +359,10 @@ def add_vector_section(title, contents, iscoord=True):
def add_scalar_section(section_class, title, *args):
self.scalar_sections[title] = section_class(title, *args)
+ add_scalar_section(ScalarMeshSection, "Mesh:", cube)
+
add_scalar_section(
- ScalarSection, "Scalar coordinates:", cube, scalar_coords
+ ScalarCoordSection, "Scalar coordinates:", cube, scalar_coords
)
add_scalar_section(
ScalarCellMeasureSection,
diff --git a/lib/iris/experimental/representation.py b/lib/iris/experimental/representation.py
index 48e11e1fb0..116b340592 100644
--- a/lib/iris/experimental/representation.py
+++ b/lib/iris/experimental/representation.py
@@ -85,28 +85,32 @@ def __init__(self, cube):
self.cube_id = id(self.cube)
self.cube_str = escape(str(self.cube))
- self.str_headings = {
- "Dimension coordinates:": None,
- "Auxiliary coordinates:": None,
- "Mesh coordinates:": None,
- "Derived coordinates:": None,
- "Cell measures:": None,
- "Ancillary variables:": None,
- "Scalar coordinates:": None,
- "Scalar cell measures:": None,
- "Cell methods:": None,
- "Attributes:": None,
- }
- self.dim_desc_coords = [
+ # Define the expected vector and scalar sections in output, in expected
+ # order of appearance.
+ # NOTE: if we recoded this to use a CubeSummary, these section titles
+ # would be available from that.
+ self.vector_section_names = [
"Dimension coordinates:",
- "Auxiliary coordinates:",
"Mesh coordinates:",
+ "Auxiliary coordinates:",
"Derived coordinates:",
"Cell measures:",
"Ancillary variables:",
]
-
- self.two_cell_headers = ["Scalar coordinates:", "Attributes:"]
+ self.scalar_section_names = [
+ "Mesh:",
+ "Scalar coordinates:",
+ "Scalar cell measures:",
+ "Cell methods:",
+ "Attributes:",
+ ]
+ self.sections_data = {
+ name: None
+ for name in self.vector_section_names + self.scalar_section_names
+ }
+ # 'Scalar-cell-measures' is currently alone amongst the scalar sections,
+ # in displaying only a 'name' and no 'value' field.
+ self.single_cell_section_names = ["Scalar cell measures:"]
# Important content that summarises a cube is defined here.
self.shapes = self.cube.shape
@@ -160,7 +164,7 @@ def _get_bits(self, bits):
# Get heading indices within the printout.
start_inds = []
- for hdg in self.str_headings.keys():
+ for hdg in self.sections_data.keys():
heading = "{}{}".format(left_indent, hdg)
try:
start_ind = bits.index(heading)
@@ -178,7 +182,7 @@ def _get_bits(self, bits):
content = bits[i0 + 1 : i1]
else:
content = bits[i0 + 1 :]
- self.str_headings[str_heading_name] = content
+ self.sections_data[str_heading_name] = content
def _make_header(self):
"""
@@ -272,47 +276,29 @@ def _make_row(self, title, body=None, col_span=0):
row.append("")
return row
- def _expand_last_cell(self, element, body):
- """Expand an element containing a cell by adding a new line."""
- split_point = element.index("")
- element = element[:split_point] + "
" + body + element[split_point:]
- return element
-
def _make_content(self):
elements = []
- for k, v in self.str_headings.items():
+ for k, v in self.sections_data.items():
if v is not None:
# Add the sub-heading title.
elements.extend(self._make_row(k))
for line in v:
# Add every other row in the sub-heading.
- if k in self.dim_desc_coords:
+ if k in self.vector_section_names:
body = re.findall(r"[\w-]+", line)
title = body.pop(0)
colspan = 0
- elif k in self.two_cell_headers:
- try:
- split_point = line.index(":")
- except ValueError:
- # When a line exists in v without a ':', we expect
- # that this is due to the value of some attribute
- # containing multiple lines. We collect all these
- # lines in the same cell.
- body = line.strip()
- # We choose the element containing the last cell
- # in the last row.
- element = elements[-2]
- element = self._expand_last_cell(element, body)
- elements[-2] = element
- continue
+ else:
+ colspan = self.ndims
+ if k in self.single_cell_section_names:
+ title = line.strip()
+ body = ""
else:
+ line = line.strip()
+ split_point = line.index(" ")
title = line[:split_point].strip()
body = line[split_point + 2 :].strip()
- colspan = self.ndims
- else:
- title = line.strip()
- body = ""
- colspan = self.ndims
+
elements.extend(
self._make_row(title, body=body, col_span=colspan)
)
diff --git a/lib/iris/tests/results/unit/experimental/representation/CubeRepresentation/_make_content/mesh_result.txt b/lib/iris/tests/results/unit/experimental/representation/CubeRepresentation/_make_content/mesh_result.txt
new file mode 100644
index 0000000000..e20527cb49
--- /dev/null
+++ b/lib/iris/tests/results/unit/experimental/representation/CubeRepresentation/_make_content/mesh_result.txt
@@ -0,0 +1,24 @@
+