Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/src/further_topics/ugrid/data_model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions docs/src/further_topics/ugrid/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -392,6 +395,9 @@ etcetera:
Mesh coordinates:
latitude x -
longitude x -
Mesh:
name my_mesh
location face
Attributes:
Conventions 'CF-1.7'

Expand Down Expand Up @@ -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])
Expand Down
6 changes: 3 additions & 3 deletions lib/iris/_representation/cube_printout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
59 changes: 49 additions & 10 deletions lib/iris/_representation/cube_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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

Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
78 changes: 32 additions & 46 deletions lib/iris/experimental/representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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):
"""
Expand Down Expand Up @@ -272,47 +276,29 @@ def _make_row(self, title, body=None, col_span=0):
row.append("</tr>")
return row

def _expand_last_cell(self, element, body):
"""Expand an element containing a cell by adding a new line."""
split_point = element.index("</td>")
element = element[:split_point] + "<br>" + 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)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<tr class="iris">
<td class="iris-title iris-word-cell">Mesh coordinates</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> latitude</td>
<td class="iris-inclusion-cell">x</td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> longitude</td>
<td class="iris-inclusion-cell">x</td>
</tr>
<tr class="iris">
<td class="iris-title iris-word-cell">Mesh</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> name</td>
<td class="iris-word-cell" colspan="1">unknown</td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> location</td>
<td class="iris-word-cell" colspan="1">face</td>
</tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<tr class="iris">
<td class="iris-title iris-word-cell">Attributes</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> newlines-string</td>
<td class="iris-word-cell" colspan="1">&#x27;string\nwith\nnewlines&#x27;</td>
</tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<tr class="iris">
<td class="iris-title iris-word-cell">Attributes</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> long-string</td>
<td class="iris-word-cell" colspan="1">&#x27;long string.. long string.. long string.. long string.. long string.. long ...&#x27;</td>
</tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<tr class="iris">
<td class="iris-title iris-word-cell">Attributes</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> multi-string</td>
<td class="iris-word-cell" colspan="1">[&#x27;vector&#x27;, &#x27;of&#x27;, &#x27;strings&#x27;]</td>
</tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<tr class="iris">
<td class="iris-title iris-word-cell">Attributes</td>
<td class="iris-title"></td>
</tr>
<tr class="iris">
<td class="iris-word-cell iris-subheading-cell"> single-string</td>
<td class="iris-word-cell" colspan="1">&#x27;single string&#x27;</td>
</tr>
Loading