Repository: ecmwf/earthkit-data
Pull request #122 (2023-06-19 10:39:57)

Return list of BoundingBox objects on a FieldList
This PR alters the way the `bounding_box` method used on a `FieldList` in line with the internal discussions regarding the FieldList API.  It also changes some of the method names of `BoundingBox`. The outcome is summarised below:

- the `bounding_box` method on a FieldList returns a list of `BoundingBox` objects (one per field, both for GRIB and NetCDF)
- `BoundingBox`  has a classmethod `union` generating the union of the input list of `BoundingBox` objects
- `BoundingBox`  has an instance method `union_with` generating the union with another object

```python
# getting bounding boxes on a FieldList
bb_list = ds.bounding_box()

# forming union
bb = BoundingBox.union(bb_list)

# forming union with the instance method
bb1 = BoundinBox(...)
bb2 = BoundingBox(...)
bb = bb1.union_with(bb2)
```


Patch:
diff --git a/docs/api.rst b/docs/api.rst
index 62f9e79..3ff050f 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -15,3 +15,9 @@ GRIB
 .. -----
 
 .. - :py:class:`BufrReader <data.readers.bufr.BUFRReader>`
+
+
+Other
+--------
+
+- :py:class:`BoundingBox <data.utils.bbox.BoundingBox>`
diff --git a/docs/conf.py b/docs/conf.py
index f0841ad..ae19a73 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -117,6 +117,8 @@ def _skip_for_api(app, what, name, obj, skip, options):
         "data.readers",
         "data.readers.grib.codes",
         "data.readers.grib.index",
+        "data.utils",
+        "data.utils.bbox",
     ]:
         skip = True
     elif what == "package" and name not in [
@@ -124,18 +126,24 @@ def _skip_for_api(app, what, name, obj, skip, options):
         "data.readers",
         "data.readers.grib",
         "data.readers.grib.index",
+        "data.utils",
+        "data.utils.bbox",
     ]:
         skip = True
     elif what == "class" and name not in [
         "data.readers.bufr.BUFRReader",
         "data.readers.grib.codes.GribField",
         "data.readers.grib.index.FieldList",
+        "data.utils.bbox.BoundingBox",
     ]:
         skip = True
     elif what == "method" and "abstractmethod" in getattr(obj, "properties", []):
         skip = True
     elif what in ("function", "attribute", "data"):
         skip = True
+
+    if not skip:
+        print(f"{what} {name}")
     return skip
 
 
diff --git a/docs/examples/grib_metadata.ipynb b/docs/examples/grib_metadata.ipynb
index 1a60310..39efe17 100644
--- a/docs/examples/grib_metadata.ipynb
+++ b/docs/examples/grib_metadata.ipynb
@@ -1330,27 +1330,27 @@
      "data": {
       "text/html": [
        "<style type=\"text/css\">\n",
-       "#T_bf5b3 th {\n",
+       "#T_4b99e th {\n",
        "  text-align: left;\n",
        "}\n",
-       "#T_bf5b3_row0_col0, #T_bf5b3_row0_col1, #T_bf5b3_row0_col2, #T_bf5b3_row0_col3, #T_bf5b3_row0_col4, #T_bf5b3_row0_col5, #T_bf5b3_row0_col6, #T_bf5b3_row0_col7, #T_bf5b3_row0_col8, #T_bf5b3_row1_col0, #T_bf5b3_row1_col1, #T_bf5b3_row1_col2, #T_bf5b3_row1_col3, #T_bf5b3_row1_col4, #T_bf5b3_row1_col5, #T_bf5b3_row1_col6, #T_bf5b3_row1_col7, #T_bf5b3_row1_col8, #T_bf5b3_row2_col0, #T_bf5b3_row2_col1, #T_bf5b3_row2_col2, #T_bf5b3_row2_col3, #T_bf5b3_row2_col4, #T_bf5b3_row2_col5, #T_bf5b3_row2_col6, #T_bf5b3_row2_col7, #T_bf5b3_row2_col8 {\n",
+       "#T_4b99e_row0_col0, #T_4b99e_row0_col1, #T_4b99e_row0_col2, #T_4b99e_row0_col3, #T_4b99e_row0_col4, #T_4b99e_row0_col5, #T_4b99e_row0_col6, #T_4b99e_row0_col7, #T_4b99e_row0_col8, #T_4b99e_row1_col0, #T_4b99e_row1_col1, #T_4b99e_row1_col2, #T_4b99e_row1_col3, #T_4b99e_row1_col4, #T_4b99e_row1_col5, #T_4b99e_row1_col6, #T_4b99e_row1_col7, #T_4b99e_row1_col8, #T_4b99e_row2_col0, #T_4b99e_row2_col1, #T_4b99e_row2_col2, #T_4b99e_row2_col3, #T_4b99e_row2_col4, #T_4b99e_row2_col5, #T_4b99e_row2_col6, #T_4b99e_row2_col7, #T_4b99e_row2_col8 {\n",
        "  text-align: left;\n",
        "}\n",
        "</style>\n",
-       "<table id=\"T_bf5b3\">\n",
+       "<table id=\"T_4b99e\">\n",
        "  <thead>\n",
        "    <tr>\n",
        "      <th class=\"blank\" >&nbsp;</th>\n",
        "      <th class=\"blank level0\" >&nbsp;</th>\n",
-       "      <th id=\"T_bf5b3_level0_col0\" class=\"col_heading level0 col0\" >level</th>\n",
-       "      <th id=\"T_bf5b3_level0_col1\" class=\"col_heading level0 col1\" >date</th>\n",
-       "      <th id=\"T_bf5b3_level0_col2\" class=\"col_heading level0 col2\" >time</th>\n",
-       "      <th id=\"T_bf5b3_level0_col3\" class=\"col_heading level0 col3\" >step</th>\n",
-       "      <th id=\"T_bf5b3_level0_col4\" class=\"col_heading level0 col4\" >paramId</th>\n",
-       "      <th id=\"T_bf5b3_level0_col5\" class=\"col_heading level0 col5\" >class</th>\n",
-       "      <th id=\"T_bf5b3_level0_col6\" class=\"col_heading level0 col6\" >stream</th>\n",
-       "      <th id=\"T_bf5b3_level0_col7\" class=\"col_heading level0 col7\" >type</th>\n",
-       "      <th id=\"T_bf5b3_level0_col8\" class=\"col_heading level0 col8\" >experimentVersionNumber</th>\n",
+       "      <th id=\"T_4b99e_level0_col0\" class=\"col_heading level0 col0\" >level</th>\n",
+       "      <th id=\"T_4b99e_level0_col1\" class=\"col_heading level0 col1\" >date</th>\n",
+       "      <th id=\"T_4b99e_level0_col2\" class=\"col_heading level0 col2\" >time</th>\n",
+       "      <th id=\"T_4b99e_level0_col3\" class=\"col_heading level0 col3\" >step</th>\n",
+       "      <th id=\"T_4b99e_level0_col4\" class=\"col_heading level0 col4\" >paramId</th>\n",
+       "      <th id=\"T_4b99e_level0_col5\" class=\"col_heading level0 col5\" >class</th>\n",
+       "      <th id=\"T_4b99e_level0_col6\" class=\"col_heading level0 col6\" >stream</th>\n",
+       "      <th id=\"T_4b99e_level0_col7\" class=\"col_heading level0 col7\" >type</th>\n",
+       "      <th id=\"T_4b99e_level0_col8\" class=\"col_heading level0 col8\" >experimentVersionNumber</th>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th class=\"index_name level0\" >shortName</th>\n",
@@ -1368,49 +1368,49 @@
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
-       "      <th id=\"T_bf5b3_level0_row0\" class=\"row_heading level0 row0\" >t</th>\n",
-       "      <th id=\"T_bf5b3_level1_row0\" class=\"row_heading level1 row0\" >isobaricInhPa</th>\n",
-       "      <td id=\"T_bf5b3_row0_col0\" class=\"data row0 col0\" >1000,850</td>\n",
-       "      <td id=\"T_bf5b3_row0_col1\" class=\"data row0 col1\" >20180801</td>\n",
-       "      <td id=\"T_bf5b3_row0_col2\" class=\"data row0 col2\" >1200</td>\n",
-       "      <td id=\"T_bf5b3_row0_col3\" class=\"data row0 col3\" >0</td>\n",
-       "      <td id=\"T_bf5b3_row0_col4\" class=\"data row0 col4\" >130</td>\n",
-       "      <td id=\"T_bf5b3_row0_col5\" class=\"data row0 col5\" >od</td>\n",
-       "      <td id=\"T_bf5b3_row0_col6\" class=\"data row0 col6\" >oper</td>\n",
-       "      <td id=\"T_bf5b3_row0_col7\" class=\"data row0 col7\" >an</td>\n",
-       "      <td id=\"T_bf5b3_row0_col8\" class=\"data row0 col8\" >0001</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th id=\"T_bf5b3_level0_row1\" class=\"row_heading level0 row1\" >u</th>\n",
-       "      <th id=\"T_bf5b3_level1_row1\" class=\"row_heading level1 row1\" >isobaricInhPa</th>\n",
-       "      <td id=\"T_bf5b3_row1_col0\" class=\"data row1 col0\" >1000,850</td>\n",
-       "      <td id=\"T_bf5b3_row1_col1\" class=\"data row1 col1\" >20180801</td>\n",
-       "      <td id=\"T_bf5b3_row1_col2\" class=\"data row1 col2\" >1200</td>\n",
-       "      <td id=\"T_bf5b3_row1_col3\" class=\"data row1 col3\" >0</td>\n",
-       "      <td id=\"T_bf5b3_row1_col4\" class=\"data row1 col4\" >131</td>\n",
-       "      <td id=\"T_bf5b3_row1_col5\" class=\"data row1 col5\" >od</td>\n",
-       "      <td id=\"T_bf5b3_row1_col6\" class=\"data row1 col6\" >oper</td>\n",
-       "      <td id=\"T_bf5b3_row1_col7\" class=\"data row1 col7\" >an</td>\n",
-       "      <td id=\"T_bf5b3_row1_col8\" class=\"data row1 col8\" >0001</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th id=\"T_bf5b3_level0_row2\" class=\"row_heading level0 row2\" >v</th>\n",
-       "      <th id=\"T_bf5b3_level1_row2\" class=\"row_heading level1 row2\" >isobaricInhPa</th>\n",
-       "      <td id=\"T_bf5b3_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
-       "      <td id=\"T_bf5b3_row2_col1\" class=\"data row2 col1\" >20180801</td>\n",
-       "      <td id=\"T_bf5b3_row2_col2\" class=\"data row2 col2\" >1200</td>\n",
-       "      <td id=\"T_bf5b3_row2_col3\" class=\"data row2 col3\" >0</td>\n",
-       "      <td id=\"T_bf5b3_row2_col4\" class=\"data row2 col4\" >132</td>\n",
-       "      <td id=\"T_bf5b3_row2_col5\" class=\"data row2 col5\" >od</td>\n",
-       "      <td id=\"T_bf5b3_row2_col6\" class=\"data row2 col6\" >oper</td>\n",
-       "      <td id=\"T_bf5b3_row2_col7\" class=\"data row2 col7\" >an</td>\n",
-       "      <td id=\"T_bf5b3_row2_col8\" class=\"data row2 col8\" >0001</td>\n",
+       "      <th id=\"T_4b99e_level0_row0\" class=\"row_heading level0 row0\" >t</th>\n",
+       "      <th id=\"T_4b99e_level1_row0\" class=\"row_heading level1 row0\" >isobaricInhPa</th>\n",
+       "      <td id=\"T_4b99e_row0_col0\" class=\"data row0 col0\" >1000,850</td>\n",
+       "      <td id=\"T_4b99e_row0_col1\" class=\"data row0 col1\" >20180801</td>\n",
+       "      <td id=\"T_4b99e_row0_col2\" class=\"data row0 col2\" >1200</td>\n",
+       "      <td id=\"T_4b99e_row0_col3\" class=\"data row0 col3\" >0</td>\n",
+       "      <td id=\"T_4b99e_row0_col4\" class=\"data row0 col4\" >130</td>\n",
+       "      <td id=\"T_4b99e_row0_col5\" class=\"data row0 col5\" >od</td>\n",
+       "      <td id=\"T_4b99e_row0_col6\" class=\"data row0 col6\" >oper</td>\n",
+       "      <td id=\"T_4b99e_row0_col7\" class=\"data row0 col7\" >an</td>\n",
+       "      <td id=\"T_4b99e_row0_col8\" class=\"data row0 col8\" >0001</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th id=\"T_4b99e_level0_row1\" class=\"row_heading level0 row1\" >u</th>\n",
+       "      <th id=\"T_4b99e_level1_row1\" class=\"row_heading level1 row1\" >isobaricInhPa</th>\n",
+       "      <td id=\"T_4b99e_row1_col0\" class=\"data row1 col0\" >1000,850</td>\n",
+       "      <td id=\"T_4b99e_row1_col1\" class=\"data row1 col1\" >20180801</td>\n",
+       "      <td id=\"T_4b99e_row1_col2\" class=\"data row1 col2\" >1200</td>\n",
+       "      <td id=\"T_4b99e_row1_col3\" class=\"data row1 col3\" >0</td>\n",
+       "      <td id=\"T_4b99e_row1_col4\" class=\"data row1 col4\" >131</td>\n",
+       "      <td id=\"T_4b99e_row1_col5\" class=\"data row1 col5\" >od</td>\n",
+       "      <td id=\"T_4b99e_row1_col6\" class=\"data row1 col6\" >oper</td>\n",
+       "      <td id=\"T_4b99e_row1_col7\" class=\"data row1 col7\" >an</td>\n",
+       "      <td id=\"T_4b99e_row1_col8\" class=\"data row1 col8\" >0001</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th id=\"T_4b99e_level0_row2\" class=\"row_heading level0 row2\" >v</th>\n",
+       "      <th id=\"T_4b99e_level1_row2\" class=\"row_heading level1 row2\" >isobaricInhPa</th>\n",
+       "      <td id=\"T_4b99e_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
+       "      <td id=\"T_4b99e_row2_col1\" class=\"data row2 col1\" >20180801</td>\n",
+       "      <td id=\"T_4b99e_row2_col2\" class=\"data row2 col2\" >1200</td>\n",
+       "      <td id=\"T_4b99e_row2_col3\" class=\"data row2 col3\" >0</td>\n",
+       "      <td id=\"T_4b99e_row2_col4\" class=\"data row2 col4\" >132</td>\n",
+       "      <td id=\"T_4b99e_row2_col5\" class=\"data row2 col5\" >od</td>\n",
+       "      <td id=\"T_4b99e_row2_col6\" class=\"data row2 col6\" >oper</td>\n",
+       "      <td id=\"T_4b99e_row2_col7\" class=\"data row2 col7\" >an</td>\n",
+       "      <td id=\"T_4b99e_row2_col8\" class=\"data row2 col8\" >0001</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n"
       ],
       "text/plain": [
-       "<pandas.io.formats.style.Styler at 0x10417fc10>"
+       "<pandas.io.formats.style.Styler at 0x129fd6640>"
       ]
      },
      "execution_count": 14,
@@ -1438,66 +1438,66 @@
      "data": {
       "text/html": [
        "<style type=\"text/css\">\n",
-       "#T_41753 th {\n",
+       "#T_12d62 th {\n",
        "  text-align: left;\n",
        "}\n",
-       "#T_41753_row0_col0, #T_41753_row1_col0, #T_41753_row2_col0, #T_41753_row3_col0, #T_41753_row4_col0, #T_41753_row5_col0, #T_41753_row6_col0, #T_41753_row7_col0, #T_41753_row8_col0, #T_41753_row9_col0, #T_41753_row10_col0 {\n",
+       "#T_12d62_row0_col0, #T_12d62_row1_col0, #T_12d62_row2_col0, #T_12d62_row3_col0, #T_12d62_row4_col0, #T_12d62_row5_col0, #T_12d62_row6_col0, #T_12d62_row7_col0, #T_12d62_row8_col0, #T_12d62_row9_col0, #T_12d62_row10_col0 {\n",
        "  text-align: left;\n",
        "}\n",
        "</style>\n",
-       "<table id=\"T_41753\">\n",
+       "<table id=\"T_12d62\">\n",
        "  <thead>\n",
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row0\" class=\"row_heading level0 row0\" >shortName</th>\n",
-       "      <td id=\"T_41753_row0_col0\" class=\"data row0 col0\" >t</td>\n",
+       "      <th id=\"T_12d62_level0_row0\" class=\"row_heading level0 row0\" >shortName</th>\n",
+       "      <td id=\"T_12d62_row0_col0\" class=\"data row0 col0\" >t</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row1\" class=\"row_heading level0 row1\" >typeOfLevel</th>\n",
-       "      <td id=\"T_41753_row1_col0\" class=\"data row1 col0\" >isobaricInhPa</td>\n",
+       "      <th id=\"T_12d62_level0_row1\" class=\"row_heading level0 row1\" >typeOfLevel</th>\n",
+       "      <td id=\"T_12d62_row1_col0\" class=\"data row1 col0\" >isobaricInhPa</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row2\" class=\"row_heading level0 row2\" >level</th>\n",
-       "      <td id=\"T_41753_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
+       "      <th id=\"T_12d62_level0_row2\" class=\"row_heading level0 row2\" >level</th>\n",
+       "      <td id=\"T_12d62_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row3\" class=\"row_heading level0 row3\" >date</th>\n",
-       "      <td id=\"T_41753_row3_col0\" class=\"data row3 col0\" >20180801</td>\n",
+       "      <th id=\"T_12d62_level0_row3\" class=\"row_heading level0 row3\" >date</th>\n",
+       "      <td id=\"T_12d62_row3_col0\" class=\"data row3 col0\" >20180801</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row4\" class=\"row_heading level0 row4\" >time</th>\n",
-       "      <td id=\"T_41753_row4_col0\" class=\"data row4 col0\" >1200</td>\n",
+       "      <th id=\"T_12d62_level0_row4\" class=\"row_heading level0 row4\" >time</th>\n",
+       "      <td id=\"T_12d62_row4_col0\" class=\"data row4 col0\" >1200</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row5\" class=\"row_heading level0 row5\" >step</th>\n",
-       "      <td id=\"T_41753_row5_col0\" class=\"data row5 col0\" >0</td>\n",
+       "      <th id=\"T_12d62_level0_row5\" class=\"row_heading level0 row5\" >step</th>\n",
+       "      <td id=\"T_12d62_row5_col0\" class=\"data row5 col0\" >0</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row6\" class=\"row_heading level0 row6\" >paramId</th>\n",
-       "      <td id=\"T_41753_row6_col0\" class=\"data row6 col0\" >130</td>\n",
+       "      <th id=\"T_12d62_level0_row6\" class=\"row_heading level0 row6\" >paramId</th>\n",
+       "      <td id=\"T_12d62_row6_col0\" class=\"data row6 col0\" >130</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row7\" class=\"row_heading level0 row7\" >class</th>\n",
-       "      <td id=\"T_41753_row7_col0\" class=\"data row7 col0\" >od</td>\n",
+       "      <th id=\"T_12d62_level0_row7\" class=\"row_heading level0 row7\" >class</th>\n",
+       "      <td id=\"T_12d62_row7_col0\" class=\"data row7 col0\" >od</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row8\" class=\"row_heading level0 row8\" >stream</th>\n",
-       "      <td id=\"T_41753_row8_col0\" class=\"data row8 col0\" >oper</td>\n",
+       "      <th id=\"T_12d62_level0_row8\" class=\"row_heading level0 row8\" >stream</th>\n",
+       "      <td id=\"T_12d62_row8_col0\" class=\"data row8 col0\" >oper</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row9\" class=\"row_heading level0 row9\" >type</th>\n",
-       "      <td id=\"T_41753_row9_col0\" class=\"data row9 col0\" >an</td>\n",
+       "      <th id=\"T_12d62_level0_row9\" class=\"row_heading level0 row9\" >type</th>\n",
+       "      <td id=\"T_12d62_row9_col0\" class=\"data row9 col0\" >an</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_41753_level0_row10\" class=\"row_heading level0 row10\" >experimentVersionNumber</th>\n",
-       "      <td id=\"T_41753_row10_col0\" class=\"data row10 col0\" >0001</td>\n",
+       "      <th id=\"T_12d62_level0_row10\" class=\"row_heading level0 row10\" >experimentVersionNumber</th>\n",
+       "      <td id=\"T_12d62_row10_col0\" class=\"data row10 col0\" >0001</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n"
       ],
       "text/plain": [
-       "<pandas.io.formats.style.Styler at 0x1183f7460>"
+       "<pandas.io.formats.style.Styler at 0x129fe28b0>"
       ]
      },
      "execution_count": 15,
@@ -1518,66 +1518,66 @@
      "data": {
       "text/html": [
        "<style type=\"text/css\">\n",
-       "#T_4242a th {\n",
+       "#T_62be1 th {\n",
        "  text-align: left;\n",
        "}\n",
-       "#T_4242a_row0_col0, #T_4242a_row1_col0, #T_4242a_row2_col0, #T_4242a_row3_col0, #T_4242a_row4_col0, #T_4242a_row5_col0, #T_4242a_row6_col0, #T_4242a_row7_col0, #T_4242a_row8_col0, #T_4242a_row9_col0, #T_4242a_row10_col0 {\n",
+       "#T_62be1_row0_col0, #T_62be1_row1_col0, #T_62be1_row2_col0, #T_62be1_row3_col0, #T_62be1_row4_col0, #T_62be1_row5_col0, #T_62be1_row6_col0, #T_62be1_row7_col0, #T_62be1_row8_col0, #T_62be1_row9_col0, #T_62be1_row10_col0 {\n",
        "  text-align: left;\n",
        "}\n",
        "</style>\n",
-       "<table id=\"T_4242a\">\n",
+       "<table id=\"T_62be1\">\n",
        "  <thead>\n",
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row0\" class=\"row_heading level0 row0\" >shortName</th>\n",
-       "      <td id=\"T_4242a_row0_col0\" class=\"data row0 col0\" >u</td>\n",
+       "      <th id=\"T_62be1_level0_row0\" class=\"row_heading level0 row0\" >shortName</th>\n",
+       "      <td id=\"T_62be1_row0_col0\" class=\"data row0 col0\" >u</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row1\" class=\"row_heading level0 row1\" >typeOfLevel</th>\n",
-       "      <td id=\"T_4242a_row1_col0\" class=\"data row1 col0\" >isobaricInhPa</td>\n",
+       "      <th id=\"T_62be1_level0_row1\" class=\"row_heading level0 row1\" >typeOfLevel</th>\n",
+       "      <td id=\"T_62be1_row1_col0\" class=\"data row1 col0\" >isobaricInhPa</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row2\" class=\"row_heading level0 row2\" >level</th>\n",
-       "      <td id=\"T_4242a_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
+       "      <th id=\"T_62be1_level0_row2\" class=\"row_heading level0 row2\" >level</th>\n",
+       "      <td id=\"T_62be1_row2_col0\" class=\"data row2 col0\" >1000,850</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row3\" class=\"row_heading level0 row3\" >date</th>\n",
-       "      <td id=\"T_4242a_row3_col0\" class=\"data row3 col0\" >20180801</td>\n",
+       "      <th id=\"T_62be1_level0_row3\" class=\"row_heading level0 row3\" >date</th>\n",
+       "      <td id=\"T_62be1_row3_col0\" class=\"data row3 col0\" >20180801</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row4\" class=\"row_heading level0 row4\" >time</th>\n",
-       "      <td id=\"T_4242a_row4_col0\" class=\"data row4 col0\" >1200</td>\n",
+       "      <th id=\"T_62be1_level0_row4\" class=\"row_heading level0 row4\" >time</th>\n",
+       "      <td id=\"T_62be1_row4_col0\" class=\"data row4 col0\" >1200</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row5\" class=\"row_heading level0 row5\" >step</th>\n",
-       "      <td id=\"T_4242a_row5_col0\" class=\"data row5 col0\" >0</td>\n",
+       "      <th id=\"T_62be1_level0_row5\" class=\"row_heading level0 row5\" >step</th>\n",
+       "      <td id=\"T_62be1_row5_col0\" class=\"data row5 col0\" >0</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row6\" class=\"row_heading level0 row6\" >paramId</th>\n",
-       "      <td id=\"T_4242a_row6_col0\" class=\"data row6 col0\" >131</td>\n",
+       "      <th id=\"T_62be1_level0_row6\" class=\"row_heading level0 row6\" >paramId</th>\n",
+       "      <td id=\"T_62be1_row6_col0\" class=\"data row6 col0\" >131</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row7\" class=\"row_heading level0 row7\" >class</th>\n",
-       "      <td id=\"T_4242a_row7_col0\" class=\"data row7 col0\" >od</td>\n",
+       "      <th id=\"T_62be1_level0_row7\" class=\"row_heading level0 row7\" >class</th>\n",
+       "      <td id=\"T_62be1_row7_col0\" class=\"data row7 col0\" >od</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row8\" class=\"row_heading level0 row8\" >stream</th>\n",
-       "      <td id=\"T_4242a_row8_col0\" class=\"data row8 col0\" >oper</td>\n",
+       "      <th id=\"T_62be1_level0_row8\" class=\"row_heading level0 row8\" >stream</th>\n",
+       "      <td id=\"T_62be1_row8_col0\" class=\"data row8 col0\" >oper</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row9\" class=\"row_heading level0 row9\" >type</th>\n",
-       "      <td id=\"T_4242a_row9_col0\" class=\"data row9 col0\" >an</td>\n",
+       "      <th id=\"T_62be1_level0_row9\" class=\"row_heading level0 row9\" >type</th>\n",
+       "      <td id=\"T_62be1_row9_col0\" class=\"data row9 col0\" >an</td>\n",
        "    </tr>\n",
        "    <tr>\n",
-       "      <th id=\"T_4242a_level0_row10\" class=\"row_heading level0 row10\" >experimentVersionNumber</th>\n",
-       "      <td id=\"T_4242a_row10_col0\" class=\"data row10 col0\" >0001</td>\n",
+       "      <th id=\"T_62be1_level0_row10\" class=\"row_heading level0 row10\" >experimentVersionNumber</th>\n",
+       "      <td id=\"T_62be1_row10_col0\" class=\"data row10 col0\" >0001</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n"
       ],
       "text/plain": [
-       "<pandas.io.formats.style.Styler at 0x1183f6bf0>"
+       "<pandas.io.formats.style.Styler at 0x129fe2e80>"
       ]
      },
      "execution_count": 16,
@@ -1865,8 +1865,8 @@
        "<div class=\"eh-tabs-block\">\n",
        "<div class=\"eh-tabs\">\n",
        "\n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"1237fa78-77bc-4beb-9c57-568b3cc5af30\"  />\n",
-       "<label for=\"1237fa78-77bc-4beb-9c57-568b3cc5af30\" title=\"Keys in the ecCodes default namespace\">default</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"e0ebf7e6-7ee4-4c3b-9248-e08bfc9b34f9\"  />\n",
+       "<label for=\"e0ebf7e6-7ee4-4c3b-9248-e08bfc9b34f9\" title=\"Keys in the ecCodes default namespace\">default</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1874,12 +1874,12 @@
        "}\n",
        "</style>\n",
        "<table class=\"eh-table\">\n",
-       "<tr><td><b>globalDomain</b></td><td>g</td></tr> <tr><td><b>GRIBEditionNumber</b></td><td>1</td></tr> <tr><td><b>eps</b></td><td>0</td></tr> <tr><td><b>offsetSection0</b></td><td>0</td></tr> <tr><td><b>section0Length</b></td><td>8</td></tr> <tr><td><b>totalLength</b></td><td>150</td></tr> <tr><td><b>editionNumber</b></td><td>1</td></tr> <tr><td><b>WMO</b></td><td>0</td></tr> <tr><td><b>productionStatusOfProcessedData</b></td><td>0</td></tr> <tr><td><b>section1Length</b></td><td>52</td></tr> <tr><td><b>wrongPadding</b></td><td>0</td></tr> <tr><td><b>table2Version</b></td><td>128</td></tr> <tr><td><b>centre</b></td><td>ecmf</td></tr> <tr><td><b>centreDescription</b></td><td>European Centre for Medium-Range Weather Forecasts</td></tr> <tr><td><b>generatingProcessIdentifier</b></td><td>254</td></tr> <tr><td><b>gridDefinition</b></td><td>255</td></tr> <tr><td><b>indicatorOfParameter</b></td><td>130</td></tr> <tr><td><b>parameterName</b></td><td>Temperature</td></tr> <tr><td><b>parameterUnits</b></td><td>K</td></tr> <tr><td><b>indicatorOfTypeOfLevel</b></td><td>pl</td></tr> <tr><td><b>pressureUnits</b></td><td>hPa</td></tr> <tr><td><b>typeOfLevelECMF</b></td><td>isobaricInhPa</td></tr> <tr><td><b>typeOfLevel</b></td><td>isobaricInhPa</td></tr> <tr><td><b>level</b></td><td>1000</td></tr> <tr><td><b>yearOfCentury</b></td><td>18</td></tr> <tr><td><b>month</b></td><td>8</td></tr> <tr><td><b>day</b></td><td>1</td></tr> <tr><td><b>hour</b></td><td>12</td></tr> <tr><td><b>minute</b></td><td>0</td></tr> <tr><td><b>second</b></td><td>0</td></tr> <tr><td><b>unitOfTimeRange</b></td><td>1</td></tr> <tr><td><b>P1</b></td><td>0</td></tr> <tr><td><b>P2</b></td><td>0</td></tr> <tr><td><b>timeRangeIndicator</b></td><td>0</td></tr> <tr><td><b>numberIncludedInAverage</b></td><td>0</td></tr> <tr><td><b>numberMissingFromAveragesOrAccumulations</b></td><td>0</td></tr> <tr><td><b>centuryOfReferenceTimeOfData</b></td><td>21</td></tr> <tr><td><b>subCentre</b></td><td>0</td></tr> <tr><td><b>paramIdECMF</b></td><td>130</td></tr> <tr><td><b>paramId</b></td><td>130</td></tr> <tr><td><b>cfNameECMF</b></td><td>air_temperature</td></tr> <tr><td><b>cfName</b></td><td>air_temperature</td></tr> <tr><td><b>cfVarNameECMF</b></td><td>t</td></tr> <tr><td><b>cfVarName</b></td><td>t</td></tr> <tr><td><b>unitsECMF</b></td><td>K</td></tr> <tr><td><b>units</b></td><td>K</td></tr> <tr><td><b>nameECMF</b></td><td>Temperature</td></tr> <tr><td><b>name</b></td><td>Temperature</td></tr> <tr><td><b>decimalScaleFactor</b></td><td>0</td></tr> <tr><td><b>setLocalDefinition</b></td><td>0</td></tr> <tr><td><b>optimizeScaleFactor</b></td><td>0</td></tr> <tr><td><b>dataDate</b></td><td>20180801</td></tr> <tr><td><b>year</b></td><td>2018</td></tr> <tr><td><b>dataTime</b></td><td>1200</td></tr> <tr><td><b>julianDay</b></td><td>2458332.0</td></tr> <tr><td><b>stepUnits</b></td><td>1</td></tr> <tr><td><b>stepType</b></td><td>instant</td></tr> <tr><td><b>stepRange</b></td><td>0</td></tr> <tr><td><b>startStep</b></td><td>0</td></tr> <tr><td><b>endStep</b></td><td>0</td></tr> <tr><td><b>marsParam</b></td><td>130.128</td></tr> <tr><td><b>validityDate</b></td><td>20180801</td></tr> <tr><td><b>validityTime</b></td><td>1200</td></tr> <tr><td><b>deleteLocalDefinition</b></td><td>0</td></tr> <tr><td><b>localUsePresent</b></td><td>1</td></tr> <tr><td><b>reservedNeedNotBePresent</b></td><td>['']</td></tr> <tr><td><b>localDefinitionNumber</b></td><td>1</td></tr> <tr><td><b>GRIBEXSection1Problem</b></td><td>0</td></tr> <tr><td><b>marsClass</b></td><td>od</td></tr> <tr><td><b>marsType</b></td><td>an</td></tr> <tr><td><b>marsStream</b></td><td>oper</td></tr> <tr><td><b>experimentVersionNumber</b></td><td>0001</td></tr> <tr><td><b>perturbationNumber</b></td><td>0</td></tr> <tr><td><b>numberOfForecastsInEnsemble</b></td><td>0</td></tr> <tr><td><b>padding_local1_1</b></td><td>00</td></tr> <tr><td><b>grib2LocalSectionNumber</b></td><td>1</td></tr> <tr><td><b>localExtensionPadding</b></td><td></td></tr> <tr><td><b>_x</b></td><td>None</td></tr> <tr><td><b>section1Padding</b></td><td></td></tr> <tr><td><b>shortNameECMF</b></td><td>t</td></tr> <tr><td><b>shortName</b></td><td>t</td></tr> <tr><td><b>ifsParam</b></td><td>130</td></tr> <tr><td><b>stepTypeForConversion</b></td><td>unknown</td></tr> <tr><td><b>md5Section1</b></td><td>3ae6b1f6f34f431c6134c40ec42f27fa</td></tr> <tr><td><b>md5Product</b></td><td>54697e1d910c88b76a8759b67e5c7a9c</td></tr> <tr><td><b>gridDescriptionSectionPresent</b></td><td>1</td></tr> <tr><td><b>bitmapPresent</b></td><td>0</td></tr> <tr><td><b>angleSubdivisions</b></td><td>1000</td></tr> <tr><td><b>section2Length</b></td><td>32</td></tr> <tr><td><b>radius</b></td><td>6367470</td></tr> <tr><td><b>numberOfVerticalCoordinateValues</b></td><td>0</td></tr> <tr><td><b>neitherPresent</b></td><td>255</td></tr> <tr><td><b>pvlLocation</b></td><td>255</td></tr> <tr><td><b>dataRepresentationType</b></td><td>0</td></tr> <tr><td><b>gridDefinitionDescription</b></td><td>Latitude/Longitude Grid</td></tr> <tr><td><b>gridDefinitionTemplateNumber</b></td><td>0</td></tr> <tr><td><b>Ni</b></td><td>12</td></tr> <tr><td><b>Nj</b></td><td>7</td></tr> <tr><td><b>latitudeOfFirstGridPoint</b></td><td>90000</td></tr> <tr><td><b>latitudeOfFirstGridPointInDegrees</b></td><td>90.0</td></tr> <tr><td><b>longitudeOfFirstGridPoint</b></td><td>0</td></tr> <tr><td><b>longitudeOfFirstGridPointInDegrees</b></td><td>0.0</td></tr> <tr><td><b>resolutionAndComponentFlags</b></td><td>128</td></tr> <tr><td><b>ijDirectionIncrementGiven</b></td><td>1</td></tr> <tr><td><b>earthIsOblate</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags3</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags4</b></td><td>0</td></tr> <tr><td><b>uvRelativeToGrid</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags6</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags7</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags8</b></td><td>0</td></tr> <tr><td><b>latitudeOfLastGridPoint</b></td><td>-90000</td></tr> <tr><td><b>latitudeOfLastGridPointInDegrees</b></td><td>-90.0</td></tr> <tr><td><b>longitudeOfLastGridPoint</b></td><td>330000</td></tr> <tr><td><b>longitudeOfLastGridPointInDegrees</b></td><td>330.0</td></tr> <tr><td><b>iDirectionIncrement</b></td><td>30000</td></tr> <tr><td><b>jDirectionIncrement</b></td><td>30000</td></tr> <tr><td><b>scanningMode</b></td><td>0</td></tr> <tr><td><b>iScansNegatively</b></td><td>0</td></tr> <tr><td><b>jScansPositively</b></td><td>0</td></tr> <tr><td><b>jPointsAreConsecutive</b></td><td>0</td></tr> <tr><td><b>alternativeRowScanning</b></td><td>0</td></tr> <tr><td><b>iScansPositively</b></td><td>1</td></tr> <tr><td><b>jScansNegatively</b></td><td>1</td></tr> <tr><td><b>scanningMode4</b></td><td>0</td></tr> <tr><td><b>scanningMode5</b></td><td>0</td></tr> <tr><td><b>scanningMode6</b></td><td>0</td></tr> <tr><td><b>scanningMode7</b></td><td>0</td></tr> <tr><td><b>scanningMode8</b></td><td>0</td></tr> <tr><td><b>swapScanningAlternativeRows</b></td><td>0</td></tr> <tr><td><b>jDirectionIncrementInDegrees</b></td><td>30.0</td></tr> <tr><td><b>iDirectionIncrementInDegrees</b></td><td>30.0</td></tr> <tr><td><b>numberOfDataPoints</b></td><td>84</td></tr> <tr><td><b>numberOfValues</b></td><td>84</td></tr> <tr><td><b>zeros</b></td><td></td></tr> <tr><td><b>PVPresent</b></td><td>0</td></tr> <tr><td><b>padding_sec2_2</b></td><td></td></tr> <tr><td><b>PLPresent</b></td><td>0</td></tr> <tr><td><b>padding_sec2_1</b></td><td></td></tr> <tr><td><b>deletePV</b></td><td>1</td></tr> <tr><td><b>padding_sec2_3</b></td><td></td></tr> <tr><td><b>md5Section2</b></td><td>e09e4d6171c0ac85da1d256b2f8acf88</td></tr> <tr><td><b>lengthOfHeaders</b></td><td>85</td></tr> <tr><td><b>md5Headers</b></td><td>9160fb809a9d7b1efc95163bf36e6b51</td></tr> <tr><td><b>missingValue</b></td><td>9999</td></tr> <tr><td><b>tableReference</b></td><td>0</td></tr> <tr><td><b>section4Length</b></td><td>54</td></tr> <tr><td><b>halfByte</b></td><td>8</td></tr> <tr><td><b>dataFlag</b></td><td>8</td></tr> <tr><td><b>binaryScaleFactor</b></td><td>3</td></tr> <tr><td><b>referenceValue</b></td><td>240.56417846679688</td></tr> <tr><td><b>referenceValueError</b></td><td>1.52587890625e-05</td></tr> <tr><td><b>sphericalHarmonics</b></td><td>0</td></tr> <tr><td><b>complexPacking</b></td><td>0</td></tr> <tr><td><b>integerPointValues</b></td><td>0</td></tr> <tr><td><b>additionalFlagPresent</b></td><td>0</td></tr> <tr><td><b>orderOfSPD</b></td><td>2</td></tr> <tr><td><b>boustrophedonic</b></td><td>0</td></tr> <tr><td><b>hideThis</b></td><td>0</td></tr> <tr><td><b>packingType</b></td><td>grid_simple</td></tr> <tr><td><b>bitsPerValue</b></td><td>4</td></tr> <tr><td><b>constantFieldHalfByte</b></td><td>8</td></tr> <tr><td><b>bitMapIndicator</b></td><td>255</td></tr> <tr><td><b>numberOfCodedValues</b></td><td>84</td></tr> <tr><td><b>packingError</b></td><td>4.000007629394531</td></tr> <tr><td><b>unpackedError</b></td><td>1.52587890625e-05</td></tr> <tr><td><b>maximum</b></td><td>320.5641784667969</td></tr> <tr><td><b>minimum</b></td><td>240.56417846679688</td></tr> <tr><td><b>average</b></td><td>279.70703560965404</td></tr> <tr><td><b>standardDeviation</b></td><td>19.67421739058438</td></tr> <tr><td><b>skewness</b></td><td>-0.7312302105044429</td></tr> <tr><td><b>kurtosis</b></td><td>-0.16904561574741717</td></tr> <tr><td><b>isConstant</b></td><td>0.0</td></tr> <tr><td><b>numberOfMissing</b></td><td>0</td></tr> <tr><td><b>dataLength</b></td><td>5</td></tr> <tr><td><b>changeDecimalPrecision</b></td><td>0</td></tr> <tr><td><b>decimalPrecision</b></td><td>0</td></tr> <tr><td><b>bitsPerValueAndRepack</b></td><td>4</td></tr> <tr><td><b>scaleValuesBy</b></td><td>1.0</td></tr> <tr><td><b>offsetValuesBy</b></td><td>0.0</td></tr> <tr><td><b>gridType</b></td><td>regular_ll</td></tr> <tr><td><b>getNumberOfValues</b></td><td>84</td></tr> <tr><td><b>padding_sec4_1</b></td><td></td></tr> <tr><td><b>md5Section4</b></td><td>7ea3331d80a962b5dc99276b76451eac</td></tr> <tr><td><b>section5Length</b></td><td>4</td></tr> <tr><td><b>7777</b></td><td>7777</td></tr>\n",
+       "<tr><td><b>globalDomain</b></td><td>g</td></tr> <tr><td><b>GRIBEditionNumber</b></td><td>1</td></tr> <tr><td><b>eps</b></td><td>0</td></tr> <tr><td><b>offsetSection0</b></td><td>0</td></tr> <tr><td><b>section0Length</b></td><td>8</td></tr> <tr><td><b>totalLength</b></td><td>150</td></tr> <tr><td><b>editionNumber</b></td><td>1</td></tr> <tr><td><b>WMO</b></td><td>0</td></tr> <tr><td><b>productionStatusOfProcessedData</b></td><td>0</td></tr> <tr><td><b>section1Length</b></td><td>52</td></tr> <tr><td><b>wrongPadding</b></td><td>0</td></tr> <tr><td><b>table2Version</b></td><td>128</td></tr> <tr><td><b>centre</b></td><td>ecmf</td></tr> <tr><td><b>centreDescription</b></td><td>European Centre for Medium-Range Weather Forecasts</td></tr> <tr><td><b>generatingProcessIdentifier</b></td><td>254</td></tr> <tr><td><b>gridDefinition</b></td><td>255</td></tr> <tr><td><b>indicatorOfParameter</b></td><td>130</td></tr> <tr><td><b>parameterName</b></td><td>Temperature</td></tr> <tr><td><b>parameterUnits</b></td><td>K</td></tr> <tr><td><b>indicatorOfTypeOfLevel</b></td><td>pl</td></tr> <tr><td><b>pressureUnits</b></td><td>hPa</td></tr> <tr><td><b>typeOfLevelECMF</b></td><td>isobaricInhPa</td></tr> <tr><td><b>typeOfLevel</b></td><td>isobaricInhPa</td></tr> <tr><td><b>level</b></td><td>1000</td></tr> <tr><td><b>yearOfCentury</b></td><td>18</td></tr> <tr><td><b>month</b></td><td>8</td></tr> <tr><td><b>day</b></td><td>1</td></tr> <tr><td><b>hour</b></td><td>12</td></tr> <tr><td><b>minute</b></td><td>0</td></tr> <tr><td><b>second</b></td><td>0</td></tr> <tr><td><b>unitOfTimeRange</b></td><td>1</td></tr> <tr><td><b>P1</b></td><td>0</td></tr> <tr><td><b>P2</b></td><td>0</td></tr> <tr><td><b>timeRangeIndicator</b></td><td>0</td></tr> <tr><td><b>numberIncludedInAverage</b></td><td>0</td></tr> <tr><td><b>mybits</b></td><td>None</td></tr> <tr><td><b>numberMissingFromAveragesOrAccumulations</b></td><td>0</td></tr> <tr><td><b>centuryOfReferenceTimeOfData</b></td><td>21</td></tr> <tr><td><b>subCentre</b></td><td>0</td></tr> <tr><td><b>paramIdECMF</b></td><td>130</td></tr> <tr><td><b>paramId</b></td><td>130</td></tr> <tr><td><b>cfNameECMF</b></td><td>air_temperature</td></tr> <tr><td><b>cfName</b></td><td>air_temperature</td></tr> <tr><td><b>cfVarNameECMF</b></td><td>t</td></tr> <tr><td><b>cfVarName</b></td><td>t</td></tr> <tr><td><b>unitsECMF</b></td><td>K</td></tr> <tr><td><b>units</b></td><td>K</td></tr> <tr><td><b>nameECMF</b></td><td>Temperature</td></tr> <tr><td><b>name</b></td><td>Temperature</td></tr> <tr><td><b>decimalScaleFactor</b></td><td>0</td></tr> <tr><td><b>setLocalDefinition</b></td><td>0</td></tr> <tr><td><b>optimizeScaleFactor</b></td><td>0</td></tr> <tr><td><b>dataDate</b></td><td>20180801</td></tr> <tr><td><b>year</b></td><td>2018</td></tr> <tr><td><b>dataTime</b></td><td>1200</td></tr> <tr><td><b>julianDay</b></td><td>2458332.0</td></tr> <tr><td><b>stepUnits</b></td><td>1</td></tr> <tr><td><b>stepType</b></td><td>instant</td></tr> <tr><td><b>stepRange</b></td><td>0</td></tr> <tr><td><b>startStep</b></td><td>0</td></tr> <tr><td><b>endStep</b></td><td>0</td></tr> <tr><td><b>marsParam</b></td><td>130.128</td></tr> <tr><td><b>validityDate</b></td><td>20180801</td></tr> <tr><td><b>validityTime</b></td><td>1200</td></tr> <tr><td><b>deleteLocalDefinition</b></td><td>0</td></tr> <tr><td><b>localUsePresent</b></td><td>1</td></tr> <tr><td><b>reservedNeedNotBePresent</b></td><td>None</td></tr> <tr><td><b>localDefinitionNumber</b></td><td>1</td></tr> <tr><td><b>GRIBEXSection1Problem</b></td><td>0</td></tr> <tr><td><b>marsClass</b></td><td>od</td></tr> <tr><td><b>marsType</b></td><td>an</td></tr> <tr><td><b>marsStream</b></td><td>oper</td></tr> <tr><td><b>experimentVersionNumber</b></td><td>0001</td></tr> <tr><td><b>perturbationNumber</b></td><td>0</td></tr> <tr><td><b>numberOfForecastsInEnsemble</b></td><td>0</td></tr> <tr><td><b>padding_local1_1</b></td><td>None</td></tr> <tr><td><b>grib2LocalSectionNumber</b></td><td>1</td></tr> <tr><td><b>localExtensionPadding</b></td><td>None</td></tr> <tr><td><b>_x</b></td><td>None</td></tr> <tr><td><b>section1Padding</b></td><td>None</td></tr> <tr><td><b>shortNameECMF</b></td><td>t</td></tr> <tr><td><b>shortName</b></td><td>t</td></tr> <tr><td><b>ifsParam</b></td><td>130</td></tr> <tr><td><b>stepTypeForConversion</b></td><td>unknown</td></tr> <tr><td><b>md5Section1</b></td><td>3ae6b1f6f34f431c6134c40ec42f27fa</td></tr> <tr><td><b>md5Product</b></td><td>54697e1d910c88b76a8759b67e5c7a9c</td></tr> <tr><td><b>gridDescriptionSectionPresent</b></td><td>1</td></tr> <tr><td><b>bitmapPresent</b></td><td>0</td></tr> <tr><td><b>angleSubdivisions</b></td><td>1000</td></tr> <tr><td><b>section2Length</b></td><td>32</td></tr> <tr><td><b>radius</b></td><td>6367470</td></tr> <tr><td><b>numberOfVerticalCoordinateValues</b></td><td>0</td></tr> <tr><td><b>neitherPresent</b></td><td>255</td></tr> <tr><td><b>pvlLocation</b></td><td>255</td></tr> <tr><td><b>dataRepresentationType</b></td><td>0</td></tr> <tr><td><b>gridDefinitionDescription</b></td><td>Latitude/Longitude Grid</td></tr> <tr><td><b>gridDefinitionTemplateNumber</b></td><td>0</td></tr> <tr><td><b>Ni</b></td><td>12</td></tr> <tr><td><b>Nj</b></td><td>7</td></tr> <tr><td><b>latitudeOfFirstGridPoint</b></td><td>90000</td></tr> <tr><td><b>latitudeOfFirstGridPointInDegrees</b></td><td>90.0</td></tr> <tr><td><b>longitudeOfFirstGridPoint</b></td><td>0</td></tr> <tr><td><b>longitudeOfFirstGridPointInDegrees</b></td><td>0.0</td></tr> <tr><td><b>resolutionAndComponentFlags</b></td><td>128</td></tr> <tr><td><b>ijDirectionIncrementGiven</b></td><td>1</td></tr> <tr><td><b>earthIsOblate</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags3</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags4</b></td><td>0</td></tr> <tr><td><b>uvRelativeToGrid</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags6</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags7</b></td><td>0</td></tr> <tr><td><b>resolutionAndComponentFlags8</b></td><td>0</td></tr> <tr><td><b>latitudeOfLastGridPoint</b></td><td>-90000</td></tr> <tr><td><b>latitudeOfLastGridPointInDegrees</b></td><td>-90.0</td></tr> <tr><td><b>longitudeOfLastGridPoint</b></td><td>330000</td></tr> <tr><td><b>longitudeOfLastGridPointInDegrees</b></td><td>330.0</td></tr> <tr><td><b>iDirectionIncrement</b></td><td>30000</td></tr> <tr><td><b>jDirectionIncrement</b></td><td>30000</td></tr> <tr><td><b>scanningMode</b></td><td>0</td></tr> <tr><td><b>iScansNegatively</b></td><td>0</td></tr> <tr><td><b>jScansPositively</b></td><td>0</td></tr> <tr><td><b>jPointsAreConsecutive</b></td><td>0</td></tr> <tr><td><b>alternativeRowScanning</b></td><td>0</td></tr> <tr><td><b>iScansPositively</b></td><td>1</td></tr> <tr><td><b>scanningMode4</b></td><td>0</td></tr> <tr><td><b>scanningMode5</b></td><td>0</td></tr> <tr><td><b>scanningMode6</b></td><td>0</td></tr> <tr><td><b>scanningMode7</b></td><td>0</td></tr> <tr><td><b>scanningMode8</b></td><td>0</td></tr> <tr><td><b>swapScanningAlternativeRows</b></td><td>0</td></tr> <tr><td><b>jDirectionIncrementInDegrees</b></td><td>30.0</td></tr> <tr><td><b>iDirectionIncrementInDegrees</b></td><td>30.0</td></tr> <tr><td><b>numberOfDataPoints</b></td><td>84</td></tr> <tr><td><b>numberOfValues</b></td><td>84</td></tr> <tr><td><b>zeros</b></td><td></td></tr> <tr><td><b>PVPresent</b></td><td>0</td></tr> <tr><td><b>padding_sec2_2</b></td><td>None</td></tr> <tr><td><b>PLPresent</b></td><td>0</td></tr> <tr><td><b>padding_sec2_1</b></td><td>None</td></tr> <tr><td><b>deletePV</b></td><td>1</td></tr> <tr><td><b>padding_sec2_3</b></td><td>None</td></tr> <tr><td><b>md5Section2</b></td><td>e09e4d6171c0ac85da1d256b2f8acf88</td></tr> <tr><td><b>lengthOfHeaders</b></td><td>85</td></tr> <tr><td><b>md5Headers</b></td><td>9160fb809a9d7b1efc95163bf36e6b51</td></tr> <tr><td><b>missingValue</b></td><td>9999</td></tr> <tr><td><b>tableReference</b></td><td>0</td></tr> <tr><td><b>section4Length</b></td><td>54</td></tr> <tr><td><b>halfByte</b></td><td>8</td></tr> <tr><td><b>dataFlag</b></td><td>8</td></tr> <tr><td><b>binaryScaleFactor</b></td><td>3</td></tr> <tr><td><b>referenceValue</b></td><td>240.56417846679688</td></tr> <tr><td><b>referenceValueError</b></td><td>1.52587890625e-05</td></tr> <tr><td><b>sphericalHarmonics</b></td><td>0</td></tr> <tr><td><b>complexPacking</b></td><td>0</td></tr> <tr><td><b>integerPointValues</b></td><td>0</td></tr> <tr><td><b>additionalFlagPresent</b></td><td>0</td></tr> <tr><td><b>orderOfSPD</b></td><td>2</td></tr> <tr><td><b>boustrophedonic</b></td><td>0</td></tr> <tr><td><b>hideThis</b></td><td>0</td></tr> <tr><td><b>packingType</b></td><td>grid_simple</td></tr> <tr><td><b>bitsPerValue</b></td><td>4</td></tr> <tr><td><b>constantFieldHalfByte</b></td><td>8</td></tr> <tr><td><b>bitMapIndicator</b></td><td>255</td></tr> <tr><td><b>numberOfCodedValues</b></td><td>84</td></tr> <tr><td><b>packingError</b></td><td>4.000007629394531</td></tr> <tr><td><b>unpackedError</b></td><td>1.52587890625e-05</td></tr> <tr><td><b>maximum</b></td><td>320.5641784667969</td></tr> <tr><td><b>minimum</b></td><td>240.56417846679688</td></tr> <tr><td><b>average</b></td><td>279.70703560965404</td></tr> <tr><td><b>numberOfMissing</b></td><td>0</td></tr> <tr><td><b>standardDeviation</b></td><td>19.67421739058438</td></tr> <tr><td><b>skewness</b></td><td>-0.7312302105044429</td></tr> <tr><td><b>kurtosis</b></td><td>-0.16904561574741717</td></tr> <tr><td><b>isConstant</b></td><td>0.0</td></tr> <tr><td><b>dataLength</b></td><td>5</td></tr> <tr><td><b>changeDecimalPrecision</b></td><td>0</td></tr> <tr><td><b>decimalPrecision</b></td><td>0</td></tr> <tr><td><b>bitsPerValueAndRepack</b></td><td>4</td></tr> <tr><td><b>scaleValuesBy</b></td><td>1.0</td></tr> <tr><td><b>offsetValuesBy</b></td><td>0.0</td></tr> <tr><td><b>gridType</b></td><td>regular_ll</td></tr> <tr><td><b>getNumberOfValues</b></td><td>84</td></tr> <tr><td><b>padding_sec4_1</b></td><td>None</td></tr> <tr><td><b>md5Section4</b></td><td>7ea3331d80a962b5dc99276b76451eac</td></tr> <tr><td><b>section5Length</b></td><td>4</td></tr> <tr><td><b>7777</b></td><td>7777</td></tr>\n",
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"660b2bd9-ba06-4cab-8d6d-de437246ee47\"  />\n",
-       "<label for=\"660b2bd9-ba06-4cab-8d6d-de437246ee47\" title=\"Keys in the ecCodes ls namespace\">ls</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"6eed7ed1-704a-4bca-a101-92fedbcee0ab\"  />\n",
+       "<label for=\"6eed7ed1-704a-4bca-a101-92fedbcee0ab\" title=\"Keys in the ecCodes ls namespace\">ls</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1891,8 +1891,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"07810b0f-a4b1-4379-b3fd-54115d7a7327\"  />\n",
-       "<label for=\"07810b0f-a4b1-4379-b3fd-54115d7a7327\" title=\"Keys in the ecCodes geography namespace\">geography</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"22d00c24-4642-46e7-994f-59f51c20145d\"  />\n",
+       "<label for=\"22d00c24-4642-46e7-994f-59f51c20145d\" title=\"Keys in the ecCodes geography namespace\">geography</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1904,8 +1904,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"fe651588-7589-48ea-b8c5-1bb902aae4c6\"  />\n",
-       "<label for=\"fe651588-7589-48ea-b8c5-1bb902aae4c6\" title=\"Keys in the ecCodes mars namespace\">mars</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"d865fc2c-0b04-4fa5-8014-bfa876a76659\"  />\n",
+       "<label for=\"d865fc2c-0b04-4fa5-8014-bfa876a76659\" title=\"Keys in the ecCodes mars namespace\">mars</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1917,8 +1917,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"984edc29-3daf-42ee-9f2f-dc9874e451d4\" checked />\n",
-       "<label for=\"984edc29-3daf-42ee-9f2f-dc9874e451d4\" title=\"Keys in the ecCodes parameter namespace\">parameter</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"1681bf82-c48b-4b48-9a97-e8b6e560b2a9\" checked />\n",
+       "<label for=\"1681bf82-c48b-4b48-9a97-e8b6e560b2a9\" title=\"Keys in the ecCodes parameter namespace\">parameter</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1930,8 +1930,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"19377454-1b25-425f-8762-a875206af34f\"  />\n",
-       "<label for=\"19377454-1b25-425f-8762-a875206af34f\" title=\"Keys in the ecCodes statistics namespace\">statistics</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"1c4536e8-6eab-46ae-9ad7-fd7c20644933\"  />\n",
+       "<label for=\"1c4536e8-6eab-46ae-9ad7-fd7c20644933\" title=\"Keys in the ecCodes statistics namespace\">statistics</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1943,8 +1943,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"b66af60e-0a9b-485a-b8f2-344f21ec0d3c\"  />\n",
-       "<label for=\"b66af60e-0a9b-485a-b8f2-344f21ec0d3c\" title=\"Keys in the ecCodes time namespace\">time</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"65ee5095-4ada-4c61-b642-7cc6e31a84ef\"  />\n",
+       "<label for=\"65ee5095-4ada-4c61-b642-7cc6e31a84ef\" title=\"Keys in the ecCodes time namespace\">time</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1956,8 +1956,8 @@
        "</table>\n",
        "</div>\n",
        "     \n",
-       "<input type=\"radio\" name=\"eh-tabs\" id=\"fb8fdf33-e801-49f5-a974-b9d53996a226\"  />\n",
-       "<label for=\"fb8fdf33-e801-49f5-a974-b9d53996a226\" title=\"Keys in the ecCodes vertical namespace\">vertical</label>\n",
+       "<input type=\"radio\" name=\"eh-tabs\" id=\"0edf0e6a-29e5-4974-940a-e84544b414c7\"  />\n",
+       "<label for=\"0edf0e6a-29e5-4974-940a-e84544b414c7\" title=\"Keys in the ecCodes vertical namespace\">vertical</label>\n",
        "<div class=\"tab\">\n",
        "<style type=\"text/css\">table.eh td {\n",
        "    vertical-align: top;\n",
@@ -1977,246 +1977,7 @@
        "        "
       ],
       "text/plain": [
-       "{'default': {'globalDomain': 'g',\n",
-       "  'GRIBEditionNumber': 1,\n",
-       "  'eps': 0,\n",
-       "  'offsetSection0': 0,\n",
-       "  'section0Length': 8,\n",
-       "  'totalLength': 150,\n",
-       "  'editionNumber': 1,\n",
-       "  'WMO': 0,\n",
-       "  'productionStatusOfProcessedData': 0,\n",
-       "  'section1Length': 52,\n",
-       "  'wrongPadding': 0,\n",
-       "  'table2Version': 128,\n",
-       "  'centre': 'ecmf',\n",
-       "  'centreDescription': 'European Centre for Medium-Range Weather Forecasts',\n",
-       "  'generatingProcessIdentifier': 254,\n",
-       "  'gridDefinition': 255,\n",
-       "  'indicatorOfParameter': 130,\n",
-       "  'parameterName': 'Temperature',\n",
-       "  'parameterUnits': 'K',\n",
-       "  'indicatorOfTypeOfLevel': 'pl',\n",
-       "  'pressureUnits': 'hPa',\n",
-       "  'typeOfLevelECMF': 'isobaricInhPa',\n",
-       "  'typeOfLevel': 'isobaricInhPa',\n",
-       "  'level': 1000,\n",
-       "  'yearOfCentury': 18,\n",
-       "  'month': 8,\n",
-       "  'day': 1,\n",
-       "  'hour': 12,\n",
-       "  'minute': 0,\n",
-       "  'second': 0,\n",
-       "  'unitOfTimeRange': 1,\n",
-       "  'P1': 0,\n",
-       "  'P2': 0,\n",
-       "  'timeRangeIndicator': 0,\n",
-       "  'numberIncludedInAverage': 0,\n",
-       "  'numberMissingFromAveragesOrAccumulations': 0,\n",
-       "  'centuryOfReferenceTimeOfData': 21,\n",
-       "  'subCentre': 0,\n",
-       "  'paramIdECMF': '130',\n",
-       "  'paramId': 130,\n",
-       "  'cfNameECMF': 'air_temperature',\n",
-       "  'cfName': 'air_temperature',\n",
-       "  'cfVarNameECMF': 't',\n",
-       "  'cfVarName': 't',\n",
-       "  'unitsECMF': 'K',\n",
-       "  'units': 'K',\n",
-       "  'nameECMF': 'Temperature',\n",
-       "  'name': 'Temperature',\n",
-       "  'decimalScaleFactor': 0,\n",
-       "  'setLocalDefinition': 0,\n",
-       "  'optimizeScaleFactor': 0,\n",
-       "  'dataDate': 20180801,\n",
-       "  'year': 2018,\n",
-       "  'dataTime': 1200,\n",
-       "  'julianDay': 2458332.0,\n",
-       "  'stepUnits': 1,\n",
-       "  'stepType': 'instant',\n",
-       "  'stepRange': '0',\n",
-       "  'startStep': 0,\n",
-       "  'endStep': 0,\n",
-       "  'marsParam': '130.128',\n",
-       "  'validityDate': 20180801,\n",
-       "  'validityTime': 1200,\n",
-       "  'deleteLocalDefinition': 0,\n",
-       "  'localUsePresent': 1,\n",
-       "  'reservedNeedNotBePresent': [''],\n",
-       "  'localDefinitionNumber': 1,\n",
-       "  'GRIBEXSection1Problem': 0,\n",
-       "  'marsClass': 'od',\n",
-       "  'marsType': 'an',\n",
-       "  'marsStream': 'oper',\n",
-       "  'experimentVersionNumber': '0001',\n",
-       "  'perturbationNumber': 0,\n",
-       "  'numberOfForecastsInEnsemble': 0,\n",
-       "  'padding_local1_1': '00',\n",
-       "  'grib2LocalSectionNumber': 1,\n",
-       "  'localExtensionPadding': '',\n",
-       "  '_x': None,\n",
-       "  'section1Padding': '',\n",
-       "  'shortNameECMF': 't',\n",
-       "  'shortName': 't',\n",
-       "  'ifsParam': 130,\n",
-       "  'stepTypeForConversion': 'unknown',\n",
-       "  'md5Section1': '3ae6b1f6f34f431c6134c40ec42f27fa',\n",
-       "  'md5Product': '54697e1d910c88b76a8759b67e5c7a9c',\n",
-       "  'gridDescriptionSectionPresent': 1,\n",
-       "  'bitmapPresent': 0,\n",
-       "  'angleSubdivisions': 1000,\n",
-       "  'section2Length': 32,\n",
-       "  'radius': 6367470,\n",
-       "  'numberOfVerticalCoordinateValues': 0,\n",
-       "  'neitherPresent': 255,\n",
-       "  'pvlLocation': 255,\n",
-       "  'dataRepresentationType': 0,\n",
-       "  'gridDefinitionDescription': 'Latitude/Longitude Grid',\n",
-       "  'gridDefinitionTemplateNumber': 0,\n",
-       "  'Ni': 12,\n",
-       "  'Nj': 7,\n",
-       "  'latitudeOfFirstGridPoint': 90000,\n",
-       "  'latitudeOfFirstGridPointInDegrees': 90.0,\n",
-       "  'longitudeOfFirstGridPoint': 0,\n",
-       "  'longitudeOfFirstGridPointInDegrees': 0.0,\n",
-       "  'resolutionAndComponentFlags': 128,\n",
-       "  'ijDirectionIncrementGiven': 1,\n",
-       "  'earthIsOblate': 0,\n",
-       "  'resolutionAndComponentFlags3': 0,\n",
-       "  'resolutionAndComponentFlags4': 0,\n",
-       "  'uvRelativeToGrid': 0,\n",
-       "  'resolutionAndComponentFlags6': 0,\n",
-       "  'resolutionAndComponentFlags7': 0,\n",
-       "  'resolutionAndComponentFlags8': 0,\n",
-       "  'latitudeOfLastGridPoint': -90000,\n",
-       "  'latitudeOfLastGridPointInDegrees': -90.0,\n",
-       "  'longitudeOfLastGridPoint': 330000,\n",
-       "  'longitudeOfLastGridPointInDegrees': 330.0,\n",
-       "  'iDirectionIncrement': 30000,\n",
-       "  'jDirectionIncrement': 30000,\n",
-       "  'scanningMode': 0,\n",
-       "  'iScansNegatively': 0,\n",
-       "  'jScansPositively': 0,\n",
-       "  'jPointsAreConsecutive': 0,\n",
-       "  'alternativeRowScanning': 0,\n",
-       "  'iScansPositively': 1,\n",
-       "  'jScansNegatively': 1,\n",
-       "  'scanningMode4': 0,\n",
-       "  'scanningMode5': 0,\n",
-       "  'scanningMode6': 0,\n",
-       "  'scanningMode7': 0,\n",
-       "  'scanningMode8': 0,\n",
-       "  'swapScanningAlternativeRows': 0,\n",
-       "  'jDirectionIncrementInDegrees': 30.0,\n",
-       "  'iDirectionIncrementInDegrees': 30.0,\n",
-       "  'numberOfDataPoints': 84,\n",
-       "  'numberOfValues': 84,\n",
-       "  'zeros': '',\n",
-       "  'PVPresent': 0,\n",
-       "  'padding_sec2_2': '',\n",
-       "  'PLPresent': 0,\n",
-       "  'padding_sec2_1': '',\n",
-       "  'deletePV': '1',\n",
-       "  'padding_sec2_3': '',\n",
-       "  'md5Section2': 'e09e4d6171c0ac85da1d256b2f8acf88',\n",
-       "  'lengthOfHeaders': 85,\n",
-       "  'md5Headers': '9160fb809a9d7b1efc95163bf36e6b51',\n",
-       "  'missingValue': 9999,\n",
-       "  'tableReference': 0,\n",
-       "  'section4Length': 54,\n",
-       "  'halfByte': 8,\n",
-       "  'dataFlag': 8,\n",
-       "  'binaryScaleFactor': 3,\n",
-       "  'referenceValue': 240.56417846679688,\n",
-       "  'referenceValueError': 1.52587890625e-05,\n",
-       "  'sphericalHarmonics': 0,\n",
-       "  'complexPacking': 0,\n",
-       "  'integerPointValues': 0,\n",
-       "  'additionalFlagPresent': 0,\n",
-       "  'orderOfSPD': 2,\n",
-       "  'boustrophedonic': 0,\n",
-       "  'hideThis': 0,\n",
-       "  'packingType': 'grid_simple',\n",
-       "  'bitsPerValue': 4,\n",
-       "  'constantFieldHalfByte': 8,\n",
-       "  'bitMapIndicator': 255,\n",
-       "  'numberOfCodedValues': 84,\n",
-       "  'packingError': 4.000007629394531,\n",
-       "  'unpackedError': 1.52587890625e-05,\n",
-       "  'maximum': 320.5641784667969,\n",
-       "  'minimum': 240.56417846679688,\n",
-       "  'average': 279.70703560965404,\n",
-       "  'standardDeviation': 19.67421739058438,\n",
-       "  'skewness': -0.7312302105044429,\n",
-       "  'kurtosis': -0.16904561574741717,\n",
-       "  'isConstant': 0.0,\n",
-       "  'numberOfMissing': 0,\n",
-       "  'dataLength': 5,\n",
-       "  'changeDecimalPrecision': 0,\n",
-       "  'decimalPrecision': 0,\n",
-       "  'bitsPerValueAndRepack': 4,\n",
-       "  'scaleValuesBy': 1.0,\n",
-       "  'offsetValuesBy': 0.0,\n",
-       "  'gridType': 'regular_ll',\n",
-       "  'getNumberOfValues': 84,\n",
-       "  'padding_sec4_1': '',\n",
-       "  'md5Section4': '7ea3331d80a962b5dc99276b76451eac',\n",
-       "  'section5Length': 4,\n",
-       "  '7777': '7777'},\n",
-       " 'ls': {'edition': 1,\n",
-       "  'centre': 'ecmf',\n",
-       "  'typeOfLevel': 'isobaricInhPa',\n",
-       "  'level': 1000,\n",
-       "  'dataDate': 20180801,\n",
-       "  'stepRange': '0',\n",
-       "  'dataType': 'an',\n",
-       "  'shortName': 't',\n",
-       "  'packingType': 'grid_simple',\n",
-       "  'gridType': 'regular_ll'},\n",
-       " 'geography': {'bitmapPresent': 0,\n",
-       "  'latitudeOfFirstGridPointInDegrees': 90.0,\n",
-       "  'longitudeOfFirstGridPointInDegrees': 0.0,\n",
-       "  'latitudeOfLastGridPointInDegrees': -90.0,\n",
-       "  'longitudeOfLastGridPointInDegrees': 330.0,\n",
-       "  'iScansNegatively': 0,\n",
-       "  'jScansPositively': 0,\n",
-       "  'jPointsAreConsecutive': 0,\n",
-       "  'jDirectionIncrementInDegrees': 30.0,\n",
-       "  'iDirectionIncrementInDegrees': 30.0,\n",
-       "  'gridType': 'regular_ll'},\n",
-       " 'mars': {'domain': 'g',\n",
-       "  'levtype': 'pl',\n",
-       "  'levelist': 1000,\n",
-       "  'date': 20180801,\n",
-       "  'time': 1200,\n",
-       "  'step': 0,\n",
-       "  'param': 't',\n",
-       "  'class': 'od',\n",
-       "  'type': 'an',\n",
-       "  'stream': 'oper',\n",
-       "  'expver': '0001'},\n",
-       " 'parameter': {'centre': 'ecmf',\n",
-       "  'paramId': 130,\n",
-       "  'units': 'K',\n",
-       "  'name': 'Temperature',\n",
-       "  'shortName': 't'},\n",
-       " 'statistics': {'max': 320.5641784667969,\n",
-       "  'min': 240.56417846679688,\n",
-       "  'avg': 279.70703560965404,\n",
-       "  'sd': 19.67421739058438,\n",
-       "  'skew': -0.7312302105044429,\n",
-       "  'kurt': -0.16904561574741717,\n",
-       "  'const': 0.0},\n",
-       " 'time': {'dataDate': 20180801,\n",
-       "  'dataTime': 1200,\n",
-       "  'stepUnits': 1,\n",
-       "  'stepType': 'instant',\n",
-       "  'stepRange': '0',\n",
-       "  'startStep': 0,\n",
-       "  'endStep': 0,\n",
-       "  'validityDate': 20180801,\n",
-       "  'validityTime': 1200},\n",
-       " 'vertical': {'typeOfLevel': 'isobaricInhPa', 'level': 1000}}"
+       "<IPython.core.display.HTML object>"
       ]
      },
      "execution_count": 21,
@@ -2227,20 +1988,13 @@
    "source": [
     "fs[0].dump()"
    ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "mpy10",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
-   "name": "mpy10"
+   "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
@@ -2252,7 +2006,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.10.12"
+   "version": "3.8.16"
   }
  },
  "nbformat": 4,
diff --git a/earthkit/data/readers/grib/codes.py b/earthkit/data/readers/grib/codes.py
index 9c947d1..6821553 100644
--- a/earthkit/data/readers/grib/codes.py
+++ b/earthkit/data/readers/grib/codes.py
@@ -561,7 +561,7 @@ class GribField(Base):
 
         Returns
         -------
-        :class:`BoundingBox`
+        :obj:`BoundingBox <data.utils.bbox.BoundingBox>`
         """
         return BoundingBox(
             north=self.handle.get("latitudeOfFirstGridPointInDegrees", default=None),
@@ -767,8 +767,7 @@ class GribField(Base):
         return self.metadata(key)
 
     def dump(self, namespace=None, **kwargs):
-        r"""Generates dump with all the metadata keys belonging to ``namespace``
-        offering a tabbed interface in a Jupyter notebook.
+        r"""Generates dump with all the metadata keys belonging to ``namespace``.
 
         Parameters
         ----------
@@ -778,20 +777,29 @@ class GribField(Base):
             that ecCodes can access without specifying a namespace.
             When ``namespace`` is None all the available namespaces will be used.
         **kwargs: dict, optional
-            Other keyword arguments used for testing only
+            Other keyword arguments:
+
+            print: bool, optional
+                Enables printing the dump to the standard output when not in a Jupyter notebook.
+                Default: False
+            html: bool, optional
+                Enables generating HTML based content in a Jupyter notebook. Default: True
+
 
         Returns
         -------
-        NamespaceDump
-            Dict-like object with one item per namespace. In a Jupyter notebook represented
-            as a tabbed interface to browse the dump contents.
+        html or dict
+            - When in Jupyter notebook returns HTML code providing a tabbed interface to browse the
+              dump content. When ``html`` is False a dict is returned.
+            - dict otherwise. When ``print`` is True also prints the dict to stdout.
+
 
         Examples
         --------
         :ref:`/examples/grib_metadata.ipynb`
 
         """
-        from earthkit.data.utils.summary import format_namespace_dump
+        from earthkit.data.utils.summary import format_info
 
         namespace = _GRIB_NAMESPACES.keys() if namespace is None else [namespace]
         r = [
@@ -803,7 +811,7 @@ class GribField(Base):
             for ns in namespace
         ]
 
-        return format_namespace_dump(
+        return format_info(
             r, selected="parameter", details=self.__class__.__name__, **kwargs
         )
 
diff --git a/earthkit/data/readers/grib/fieldlist.py b/earthkit/data/readers/grib/fieldlist.py
index b7a6b2b..9fa9b45 100644
--- a/earthkit/data/readers/grib/fieldlist.py
+++ b/earthkit/data/readers/grib/fieldlist.py
@@ -13,7 +13,6 @@ from collections import defaultdict
 
 from earthkit.data.core.caching import auxiliary_cache_file
 from earthkit.data.core.index import ScaledIndex
-from earthkit.data.utils.bbox import BoundingBox
 
 from .pandas import PandasMixIn
 from .xarray import XarrayMixIn
@@ -260,7 +259,7 @@ class FieldListMixin(PandasMixIn, XarrayMixIn):
             result.append(s.metadata(*args, **kwargs))
         return result
 
-    def ls(self, n=None, keys=None, extra_keys=None, namespace=None):
+    def ls(self, n=None, keys=None, extra_keys=None, namespace=None, **kwargs):
         r"""Generates a list like summary of fieldlist using a set of metadata keys.
 
         Parameters
@@ -279,11 +278,19 @@ class FieldListMixin(PandasMixIn, XarrayMixIn):
         namespace: str, None
             The :xref:`eccodes_namespace` to choose the ``keys`` from. When it is set ``keys`` and
             ``extra_keys`` are omitted.
+        **kwargs: dict, optional
+            Other keyword arguments:
+
+            print: bool, optional
+                Enables printing the DataFrame to the standard output when not in a Jupyter notebook.
+                Default: False
 
         Returns
         -------
         Pandas DataFrame
             DataFrame with one row per :obj:`GribField <data.readers.grib.codes.GribField>`.
+            If not in a Jupyter notebook and ``print`` is True the DataFrame is printed to
+            the standard output
 
         """
         from earthkit.data.utils.summary import ls
@@ -308,7 +315,7 @@ class FieldListMixin(PandasMixIn, XarrayMixIn):
                     yield (self[i]._attributes(keys))
 
         _keys = GRIB_LS_KEYS if namespace is None else dict(namespace=namespace)
-        return ls(_proc, _keys, n=n, keys=keys, extra_keys=extra_keys)
+        return ls(_proc, _keys, n=n, keys=keys, extra_keys=extra_keys, **kwargs)
 
     def head(self, n=5, **kwargs):
         r"""Generates a list like summary of the first ``n``
@@ -421,10 +428,10 @@ class FieldListMixin(PandasMixIn, XarrayMixIn):
         Returns
         -------
         list
-            List with one :obj:`BoundingBox` per
+            List with one :obj:`BoundingBox <data.utils.bbox.BoundingBox>` per
             :obj:`GribField <data.readers.grib.codes.GribField>`
         """
-        return BoundingBox.multi_merge([s.bounding_box() for s in self])
+        return [s.bounding_box() for s in self]
 
     def statistics(self):
         import numpy as np
diff --git a/earthkit/data/readers/netcdf.py b/earthkit/data/readers/netcdf.py
index 400b607..6c28621 100644
--- a/earthkit/data/readers/netcdf.py
+++ b/earthkit/data/readers/netcdf.py
@@ -397,7 +397,7 @@ class NetCDFReader(Reader):
         return {"base_time": r, "valid_time": r}
 
     def bounding_box(self):
-        return BoundingBox.multi_merge([s.bounding_box() for s in self.get_fields()])
+        return [s.bounding_box() for s in self.get_fields()]
 
 
 def reader(source, path, magic=None, deeper_check=False):
diff --git a/earthkit/data/sources/multi.py b/earthkit/data/sources/multi.py
index be146c0..a059971 100644
--- a/earthkit/data/sources/multi.py
+++ b/earthkit/data/sources/multi.py
@@ -138,7 +138,7 @@ class MultiSource(Source):
         return {k: sorted(v) for k, v in result.items()}
 
     def bounding_box(self):
-        return BoundingBox.multi_merge([s.bounding_box() for s in self.sources])
+        return BoundingBox.union([s.bounding_box() for s in self.sources])
 
 
 source = MultiSource
diff --git a/earthkit/data/utils/bbox.py b/earthkit/data/utils/bbox.py
index 2646822..0952048 100644
--- a/earthkit/data/utils/bbox.py
+++ b/earthkit/data/utils/bbox.py
@@ -21,6 +21,20 @@ def _normalize(lon, minimum):
 
 
 class BoundingBox:
+    r"""Represents a geographic bounding box.
+
+    Parameters
+    ----------
+    north: number
+        Northern latitude (degrees)
+    west: number
+        Western longitude (degrees)
+    south: number
+        Southern latitude (degrees)
+    east: number
+        Eastern longitude (degrees)
+    """
+
     def __init__(self, *, north, west, south, east):
         # Convert to float as these values may come from Numpy
         self.north = min(float(north), 90.0)
@@ -65,14 +79,33 @@ class BoundingBox:
 
     @property
     def width(self):
+        """number: Returns the East-West size (degrees)"""
         return self.east - self.west
 
     @property
     def height(self):
+        """number: Returns the North-South size (degrees)"""
         return self.north - self.south
 
     @classmethod
-    def multi_merge(cls, bboxes):
+    def union(cls, bboxes):
+        """Generates the union of a list of :obj:`BoundingBox` objects.
+
+        Parameters
+        ----------
+        bboxes: list
+            Input  :obj:`BoundingBox` objects.
+
+        Returns
+        -------
+        :obj:`BoundingBox`
+            Union of input objects.
+
+        See Also
+        --------
+        :obj:`union_with`
+
+        """
         north = max(z.north for z in bboxes)
         south = min(z.south for z in bboxes)
 
@@ -141,10 +174,40 @@ class BoundingBox:
             east=origin + east,
         )
 
-    def merge(self, other):
-        return self.multi_merge([self, other])
+    def union_with(self, other):
+        """Generates the union of the current object and ``other``.
+
+        Parameters
+        ----------
+        other: :obj:`BoundingBox`
+            The object to make the union with.
+
+        Returns
+        -------
+        :obj:`BoundingBox`
+            New object containing the union.
+
+        See Also
+        --------
+        :obj:`union`
+
+        """
+        return self.union([self, other])
 
     def add_margins(self, margins):
+        """Generates a new :obj:`BoundingBox` object with adjusted ``margins``.
+
+        Parameters
+        ----------
+        margins: str or number
+            The margin to be added to each side. When it is a ``str`` must specify a percentage
+            in terms of the current size like "50%", while a ``number`` must specify degrees.
+
+        Returns
+        -------
+        :obj:`BoundingBox`
+            New object with adjusted ``margins``.
+        """
         if isinstance(margins, str) and margins[-1] == "%":
             margins = int(margins[:-1]) / 100.0
             margins = max(
diff --git a/earthkit/data/utils/summary.py b/earthkit/data/utils/summary.py
index 1bdfe79..48e6bf0 100644
--- a/earthkit/data/utils/summary.py
+++ b/earthkit/data/utils/summary.py
@@ -47,10 +47,10 @@ def make_unique(x, full=False):
 
 
 def ls(metadata_proc, default_keys, n=None, keys=None, extra_keys=None, **kwargs):
-    # do_print = kwargs.pop("print", False)
+    do_print = kwargs.pop("print", False)
 
     if kwargs:
-        raise TypeError(f"ls: unsupported arguments={kwargs}")
+        raise ValueError(f"ls: unsupported arguments={kwargs}")
 
     _keys = {}
     if isinstance(default_keys, (list, tuple)):
@@ -74,13 +74,22 @@ def ls(metadata_proc, default_keys, n=None, keys=None, extra_keys=None, **kwargs
     if n == 0:
         raise ValueError("n cannot be 0")
 
-    return format_ls(metadata_proc(_keys, n))
+    return format_ls(metadata_proc(_keys, n), do_print)
 
 
-def format_ls(attributes):
+def format_ls(attributes, do_print):
     import pandas as pd
 
     df = pd.DataFrame.from_records(attributes)
+
+    # TODO: this is GRIB specific code, should not be here
+    drop_unwanted_series(df, key="number", axis=1)
+
+    # test whether we're in the Jupyter environment
+    if ipython_active:
+        return df
+    elif do_print:
+        print(df)
     return df
 
 
@@ -92,7 +101,7 @@ def format_describe(attributes, *args, **kwargs):
     if param is None:
         param = kwargs.pop("param", None)
 
-    # do_print = kwargs.pop("print", True)
+    do_print = kwargs.pop("print", True)
 
     df = pd.DataFrame.from_records(attributes, **kwargs)
 
@@ -125,35 +134,36 @@ def format_describe(attributes, *args, **kwargs):
     if no_header:
         df.hide(axis="columns")
 
+    if ipython_active:
+        return df
+    elif do_print:
+        print(df)
     return df
 
 
-class NamespaceDump(dict):
-    def __init__(self, data, **kwargs):
-        d = {item["title"]: item["data"] for item in data}
-        super().__init__(d)
-        self.data = data
-        self._kwargs = kwargs
+def format_info(data, selected=None, details=None, **kwargs):
+    do_print = kwargs.pop("print", True)
+    html = kwargs.pop("html", True)
+    raw = kwargs.pop("_as_raw", False)
+
+    rv = {item["title"]: item["data"] for item in data}
 
-    def _repr_html_(self):
-        if ipython_active:
+    if ipython_active:
+        if html:
+            from earthkit.data.core.ipython import HTML
             from earthkit.data.utils.html import tab, table_from_dict
 
-            if len(self.data) == 1:
-                return table_from_dict(self.data[0]["data"])
-            elif len(self.data) > 1:
-                for item in self.data:
+            if len(data) == 1:
+                return HTML(table_from_dict(data[0]["data"]))
+            elif len(data) > 1:
+                for item in data:
                     item["text"] = table_from_dict(item["data"])
 
-                return tab(self.data, **self._kwargs)
+                return HTML(tab(data, details, selected))
         else:
-            return str(self)
-
-
-def format_namespace_dump(data, selected=None, details=None, **kwargs):
-    raw = kwargs.pop("_as_raw", False)
+            return rv
 
-    if not raw:
-        return NamespaceDump(data, selected=selected, details=details)
+    if do_print:
+        print(data if raw else rv)
     else:
-        return data
+        return data if raw else rv


Test patch:
diff --git a/tests/bufr/test_bufr_summary.py b/tests/bufr/test_bufr_summary.py
index f9b063b..343c78c 100644
--- a/tests/bufr/test_bufr_summary.py
+++ b/tests/bufr/test_bufr_summary.py
@@ -17,10 +17,10 @@ from earthkit.data.testing import earthkit_examples_file
 def test_bufr_ls_invalid_num():
     f = from_source("file", earthkit_examples_file("temp_10.bufr"))
     with pytest.raises(ValueError):
-        f.ls(n=0)
+        f.ls(n=0, print=False)
 
     with pytest.raises(ValueError):
-        f.ls(0)
+        f.ls(0, print=False)
 
 
 def test_bufr_ls_num():
@@ -29,7 +29,7 @@ def test_bufr_ls_num():
     # default keys
 
     # head
-    df = f.ls(n=2)
+    df = f.ls(n=2, print=False)
     ref = {
         "edition": {0: 3, 1: 3},
         "type": {0: 2, 1: 2},
@@ -49,7 +49,7 @@ def test_bufr_ls_num():
     assert ref == df.to_dict()
 
     # t tail
-    df = f.ls(-2)
+    df = f.ls(-2, print=False)
     ref = {
         "edition": {0: 3, 1: 3},
         "type": {0: 2, 1: 2},
@@ -68,7 +68,7 @@ def test_bufr_ls_num():
 
     assert ref == df.to_dict()
 
-    df = f.ls(-2)
+    df = f.ls(-2, print=False)
     assert ref == df.to_dict()
 
 
@@ -76,7 +76,7 @@ def test_bufr_head_num():
     f = from_source("file", earthkit_examples_file("temp_10.bufr"))
 
     # default keys
-    df = f.head(n=2)
+    df = f.head(n=2, print=False)
     ref = {
         "edition": {0: 3, 1: 3},
         "type": {0: 2, 1: 2},
@@ -95,7 +95,7 @@ def test_bufr_head_num():
 
     assert ref == df.to_dict()
 
-    df = f.head(2)
+    df = f.head(2, print=False)
     assert ref == df.to_dict()
 
 
@@ -103,7 +103,7 @@ def test_bufr_tail_num():
     f = from_source("file", earthkit_examples_file("temp_10.bufr"))
 
     # default keys
-    df = f.tail(n=2)
+    df = f.tail(n=2, print=False)
     ref = {
         "edition": {0: 3, 1: 3},
         "type": {0: 2, 1: 2},
@@ -122,5 +122,5 @@ def test_bufr_tail_num():
 
     assert ref == df.to_dict()
 
-    df = f.tail(2)
+    df = f.tail(2, print=False)
     assert ref == df.to_dict()
diff --git a/tests/grib/test_grib_contents.py b/tests/grib/test_grib_contents.py
index d98ad37..e81e140 100644
--- a/tests/grib/test_grib_contents.py
+++ b/tests/grib/test_grib_contents.py
@@ -710,8 +710,11 @@ def test_grib_datetime():
 
 
 def test_bbox():
-    s = from_source("file", earthkit_examples_file("test.grib"))
-    assert s.bounding_box().as_tuple() == (73, -27, 33, 45), s.bounding_box()
+    ds = from_source("file", earthkit_examples_file("test.grib"))
+    bb = ds.bounding_box()
+    assert len(bb) == 2
+    for b in bb:
+        assert b.as_tuple() == (73, -27, 33, 45)
 
 
 def test_grib_projection_ll():
diff --git a/tests/grib/test_grib_summary.py b/tests/grib/test_grib_summary.py
index 6857073..b216908 100644
--- a/tests/grib/test_grib_summary.py
+++ b/tests/grib/test_grib_summary.py
@@ -18,7 +18,7 @@ def test_grib_describe():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
 
     # full contents
-    df = f.describe()
+    df = f.describe(print=False)
     df = df.data
 
     ref = {
@@ -77,7 +77,7 @@ def test_grib_describe():
     assert ref == df.to_dict()
 
     # single param by shortName
-    df = f.describe("t")
+    df = f.describe("t", print=False)
     df = df.data
 
     ref = {
@@ -99,7 +99,7 @@ def test_grib_describe():
     assert ref[0] == df[0].to_dict()
 
     # repeated use
-    df = f.describe(param="t")
+    df = f.describe(param="t", print=False)
     df = df.data
     assert ref[0] == df[0].to_dict()
 
@@ -112,7 +112,7 @@ def test_grib_describe():
     assert ref[0] == df[0].to_dict()
 
     # single param by paramId
-    df = f.describe(130)
+    df = f.describe(130, print=False)
     df = df.data
 
     ref = {
@@ -133,6 +133,14 @@ def test_grib_describe():
 
     assert ref[0] == df[0].to_dict()
 
+    df = f.describe(param=130, print=False)
+    df = df.data
+    assert ref[0] == df[0].to_dict()
+
+    df = f.describe(130)
+    df = df.data
+    assert ref[0] == df[0].to_dict()
+
     df = f.describe(param=130)
     df = df.data
     assert ref[0] == df[0].to_dict()
@@ -143,7 +151,7 @@ def test_grib_ls():
 
     # default keys
     f1 = f.sel(count=[1, 2, 3, 4])
-    df = f1.ls()
+    df = f1.ls(print=False)
 
     ref = {
         "centre": {0: "ecmf", 1: "ecmf", 2: "ecmf", 3: "ecmf"},
@@ -172,7 +180,7 @@ def test_grib_ls():
 
     # extra keys
     f1 = f.sel(count=[1, 2])
-    df = f1.ls(extra_keys=["paramId"])
+    df = f1.ls(extra_keys=["paramId"], print=False)
 
     ref = {
         "centre": {0: "ecmf", 1: "ecmf"},
@@ -196,7 +204,7 @@ def test_grib_ls_keys():
 
     # default keys
     # positive num (=head)
-    df = f.ls(n=2, keys=["shortName", "bitsPerValue", "gridType"])
+    df = f.ls(n=2, keys=["shortName", "bitsPerValue", "gridType"], print=False)
     ref = {
         "shortName": {0: "t", 1: "u"},
         "bitsPerValue": {0: 4, 1: 4},
@@ -206,7 +214,7 @@ def test_grib_ls_keys():
     assert ref == df.to_dict()
 
     # negative num (=tail)
-    df = f.ls(n=-2, keys=["shortName", "bitsPerValue", "gridType"])
+    df = f.ls(n=-2, keys=["shortName", "bitsPerValue", "gridType"], print=False)
     ref = {
         "shortName": {0: "u", 1: "v"},
         "bitsPerValue": {0: 4, 1: 4},
@@ -219,15 +227,15 @@ def test_grib_ls_keys():
 def test_grib_ls_namespace():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
 
-    df = f.ls(n=2, namespace="vertical")
+    df = f.ls(n=2, namespace="vertical", print=False)
     ref = {
         "typeOfLevel": {0: "isobaricInhPa", 1: "isobaricInhPa"},
         "level": {0: 1000, 1: 1000},
     }
     assert ref == df.to_dict()
 
-    df = f.ls(n=2, namespace="vertical", extra_keys="shortName")
-
+    df = f.ls(n=2, namespace="vertical", extra_keys="shortName", print=False)
+    print(df.to_dict())
     ref = {
         "typeOfLevel": {0: "isobaricInhPa", 1: "isobaricInhPa"},
         "level": {0: 1000, 1: 1000},
@@ -239,16 +247,16 @@ def test_grib_ls_namespace():
 def test_grib_ls_invalid_num():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
     with pytest.raises(ValueError):
-        f.ls(n=0)
+        f.ls(n=0, print=False)
 
     with pytest.raises(ValueError):
-        f.ls(0)
+        f.ls(0, print=False)
 
 
 def test_grib_ls_invalid_arg():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
-    with pytest.raises(TypeError):
-        f.ls(invalid=1)
+    with pytest.raises(ValueError):
+        f.ls(invalid=1, print=False)
 
 
 def test_grib_ls_num():
@@ -257,7 +265,7 @@ def test_grib_ls_num():
     # default keys
 
     # positive num (=head)
-    df = f.ls(n=2)
+    df = f.ls(n=2, print=False)
     ref = {
         "centre": {0: "ecmf", 1: "ecmf"},
         "shortName": {0: "t", 1: "u"},
@@ -273,11 +281,11 @@ def test_grib_ls_num():
 
     assert ref == df.to_dict()
 
-    df = f.ls(2)
+    df = f.ls(2, print=False)
     assert ref == df.to_dict()
 
     # negative num (=tail)
-    df = f.ls(n=-2)
+    df = f.ls(n=-2, print=False)
     ref = {
         "centre": {0: "ecmf", 1: "ecmf"},
         "shortName": {0: "u", 1: "v"},
@@ -293,7 +301,7 @@ def test_grib_ls_num():
 
     assert ref == df.to_dict()
 
-    df = f.ls(-2)
+    df = f.ls(-2, print=False)
     assert ref == df.to_dict()
 
 
@@ -301,7 +309,7 @@ def test_grib_head_num():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
 
     # default keys
-    df = f.head(n=2)
+    df = f.head(n=2, print=False)
     ref = {
         "centre": {0: "ecmf", 1: "ecmf"},
         "shortName": {0: "t", 1: "u"},
@@ -317,7 +325,7 @@ def test_grib_head_num():
 
     assert ref == df.to_dict()
 
-    df = f.head(2)
+    df = f.head(2, print=False)
     assert ref == df.to_dict()
 
 
@@ -325,7 +333,7 @@ def test_grib_tail_num():
     f = from_source("file", earthkit_examples_file("tuv_pl.grib"))
 
     # default keys
-    df = f.tail(n=2)
+    df = f.tail(n=2, print=False)
     ref = {
         "centre": {0: "ecmf", 1: "ecmf"},
         "shortName": {0: "u", 1: "v"},
@@ -341,7 +349,7 @@ def test_grib_tail_num():
 
     assert ref == df.to_dict()
 
-    df = f.tail(2)
+    df = f.tail(2, print=False)
     assert ref == df.to_dict()
 
 
@@ -360,7 +368,7 @@ def test_grib_dump():
     )
 
     # default
-    r = f[0].dump(_as_raw=True)
+    r = f[0].dump(print=False, _as_raw=True)
     ref = [
         {
             "title": "ls",
@@ -454,7 +462,7 @@ def test_grib_dump():
             assert d == [x for x in ref if x["title"] == ns][0], ns
 
     # a namespace
-    r = f[0].dump(namespace="mars", _as_raw=True)
+    r = f[0].dump(namespace="mars", print=False, _as_raw=True)
     ref = [
         {
             "title": "mars",
@@ -477,7 +485,7 @@ def test_grib_dump():
     assert r == ref
 
     # namespace reformatted
-    r = f[0].dump(namespace="mars", _as_raw=False)
+    r = f[0].dump(namespace="mars", print=False, _as_raw=False)
     ref = {
         "mars": {
             "domain": "g",
diff --git a/tests/readers/test_grib_reader.py b/tests/readers/test_grib_reader.py
index 0efa613..1953bd8 100644
--- a/tests/readers/test_grib_reader.py
+++ b/tests/readers/test_grib_reader.py
@@ -10,7 +10,6 @@
 #
 
 from earthkit.data import from_source
-from earthkit.data.testing import earthkit_file
 
 
 def test_grib_len():
@@ -41,11 +40,6 @@ def test_dummy_grib():
     assert len(s) == 8
 
 
-def test_bbox():
-    s = from_source("file", earthkit_file("docs/examples/test.grib"))
-    assert s.bounding_box().as_tuple() == (73, -27, 33, 45), s.bounding_box()
-
-
 if __name__ == "__main__":
     from earthkit.data.testing import main
 
diff --git a/tests/readers/test_netcdf_reader.py b/tests/readers/test_netcdf_reader.py
index c27781f..5c4e5c2 100644
--- a/tests/readers/test_netcdf_reader.py
+++ b/tests/readers/test_netcdf_reader.py
@@ -196,8 +196,11 @@ def test_netcdf_to_points_1():
 
 
 def test_bbox():
-    s = from_source("file", earthkit_file("docs/examples/test.nc"))
-    assert s.bounding_box().as_tuple() == (73, -27, 33, 45), s.bounding_box()
+    ds = from_source("file", earthkit_file("docs/examples/test.nc"))
+    bb = ds.bounding_box()
+    assert len(bb) == 2
+    for b in bb:
+        assert b.as_tuple() == (73, -27, 33, 45)
 
 
 def test_netcdf_proj_string_non_cf():
diff --git a/tests/utils/test_bbox.py b/tests/utils/test_bbox.py
new file mode 100644
index 0000000..6b28c2d
--- /dev/null
+++ b/tests/utils/test_bbox.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python3
+
+# (C) Copyright 2020 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+#
+
+
+import pytest
+
+from earthkit.data.utils.bbox import BoundingBox
+
+
+def test_globe():
+    globe1 = BoundingBox(north=90, west=0, east=360, south=-90)
+    assert globe1.width == 360
+    assert globe1.west == 0
+
+    globe2 = BoundingBox(north=90, west=-180, east=180, south=-90)
+    assert globe2.width == 360
+    assert globe2.west == -180
+
+    assert BoundingBox.union([globe1, globe2]).width == 360, BoundingBox.union(
+        [globe1, globe2]
+    ).width
+    assert BoundingBox.union([globe2, globe1]).width == 360, BoundingBox.union(
+        [globe2, globe1]
+    ).width
+
+    b1 = BoundingBox(north=90, west=-180, east=0, south=-90)
+    b2 = BoundingBox(north=90, west=0, east=180, south=-90)
+    b0 = BoundingBox.union([b1, b2])
+    assert b0.width == 360
+
+
+def test_almost_globe():
+    globe1 = BoundingBox(north=90, west=1, east=360, south=-90)
+    globe2 = BoundingBox(north=90, west=-180, east=179, south=-90)
+
+    assert BoundingBox.union([globe1, globe2]).width == 360, BoundingBox.union(
+        [globe1, globe2]
+    ).width
+    assert BoundingBox.union([globe2, globe1]).width == 360, BoundingBox.union(
+        [globe2, globe1]
+    ).width
+
+
+def test_bbox():
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i, south=30, east=10 + i)
+        assert bbox.width == 10, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i, south=30, east=350 + i)
+        assert bbox.width == 350, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i, south=30, east=(10 + i) % 360)
+        assert bbox.width == 10, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i, south=30, east=(350 + i) % 360)
+        assert bbox.width == 350, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i % 360, south=30, east=10 + i)
+        assert bbox.width == 10, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i % 360, south=30, east=350 + i)
+        assert bbox.width == 350, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i % 360, south=30, east=(10 + i) % 360)
+        assert bbox.width == 10, bbox
+
+    for i in range(-365, 365):
+        bbox = BoundingBox(north=90, west=i % 360, south=30, east=(350 + i) % 360)
+        assert bbox.width == 350, bbox
+
+    for i in range(-365, 365):
+        bbox1 = BoundingBox(north=90, west=150 + i, south=30, east=170 + i)
+        bbox2 = BoundingBox(north=90, west=-170 + i, south=30, east=-150 + i)
+
+        bbox = BoundingBox.union([bbox1, bbox2])
+        assert bbox.width == 60, (bbox1, bbox2, bbox, bbox.width)
+
+        bbox = BoundingBox.union([bbox2, bbox1])
+        assert bbox.width == 60, (bbox1, bbox2, bbox, bbox.width)
+
+        merge1 = BoundingBox.union([bbox1, bbox2])
+        merge2 = BoundingBox.union([bbox2, bbox1])
+        assert merge1 == merge2, (bbox1, bbox2, merge1, merge2)
+
+    with pytest.raises(ValueError):
+        BoundingBox(north=-10, west=0, south=30, east=1)
+
+    b0 = BoundingBox(north=89.9746, west=-179.975, south=-89.9746, east=179.975)
+    b1 = BoundingBox(north=89.9746, west=-179.975, south=-89.9746, east=179.975)
+    b2 = BoundingBox(north=89.9746, west=-179.975, south=-89.9746, east=179.975)
+    b3 = BoundingBox.union([b1, b2])
+
+    assert b0 == b3
+
+
+def test_overlapping_bbox_1():
+    for offset in range(-500, 500, 10):
+        one = BoundingBox(north=90, west=offset + 10, east=offset + 20, south=-90)
+        two = BoundingBox(north=90, west=offset + 40, east=offset + 60, south=-90)
+        three = BoundingBox(north=90, west=offset + 15, east=offset + 35, south=-90)
+
+        sets = []
+        sets.append([one, two, three])
+        sets.append([two, one, three])
+        sets.append([one, three, two])
+        sets.append([one, three, two, one, three])
+        sets.append([one, one, one, two, one, two, one, three])
+        for i, s in enumerate(sets):
+            merged = BoundingBox.union(s)
+            expected = BoundingBox(
+                east=offset + 60, west=offset + 10, north=90, south=-90
+            )
+            assert merged.east == expected.east, (
+                i,
+                merged.east,
+                expected.east,
+                merged,
+                s,
+            )
+            assert merged.west == expected.west, (
+                i,
+                merged.west,
+                expected.west,
+                merged,
+                s,
+            )
+
+        four = BoundingBox(north=90, west=offset - 200, east=offset + 10, south=-90)
+        sets = []
+        sets.append([one, two, three, four])
+        sets.append([two, one, four, three])
+        sets.append([one, three, four, two])
+        sets.append([one, three, two, four, one, three])
+        sets.append([one, one, one, two, four, one, two, one, three])
+        for i, s in enumerate(sets):
+            merged = BoundingBox.union(s)
+            expected = BoundingBox(
+                east=offset + 60, west=offset - 200, north=90, south=-90
+            )
+            assert merged.east % 360 == expected.east % 360, (
+                i,
+                offset,
+                merged.east,
+                expected.east,
+                merged,
+                s,
+            )
+            assert merged.west % 360 == expected.west % 360, (
+                i,
+                merged.west,
+                expected.west,
+                merged,
+                s,
+            )
+            assert merged.west < merged.east
+
+
+def xxxtest_overlaps():
+    b1 = BoundingBox(north=90, west=-200, south=-90, east=-130)
+    b2 = BoundingBox(north=90, west=-180, south=-90, east=-90)
+    b0 = b1.overlaps(b2)
+    assert b0
+
+
+def test_overlapping_bbox_2():
+    b1 = BoundingBox(north=90, west=-200, south=-90, east=-130)
+    b2 = BoundingBox(north=90, west=-180, south=-90, east=-90)
+    b0 = BoundingBox.union([b1, b2])
+
+    assert b0.width == 110, b0.width
+
+    b3 = BoundingBox(north=90, west=-210, south=-90, east=-160)
+    b0 = BoundingBox.union([b0, b3])
+    assert b0.width == 120, b0.width
+
+    #      ----------------------
+    #           ---------------------
+    # ---------------------
+
+    for i in range(-365, 365):
+        b1 = BoundingBox(north=90, west=10 + i, south=-90, east=80 + i)
+        b2 = BoundingBox(north=90, west=30 + i, south=-90, east=120 + i)
+        b3 = BoundingBox(north=90, west=0 + i, south=-90, east=50 + i)
+        b0 = BoundingBox.union([b1, b2, b3])
+        assert b0.width == 120, (b0.width, b0)
+
+    # --------------------------------
+    #           ---------------------
+    #     ---------------------
+
+    for i in range(-365, 365):
+        b1 = BoundingBox(north=90, west=-10 + i, south=-90, east=200 + i)
+        b2 = BoundingBox(north=90, west=30 + i, south=-90, east=120 + i)
+        b3 = BoundingBox(north=90, west=0 + i, south=-90, east=50 + i)
+
+        b0 = BoundingBox.union([b1, b2, b3])
+        assert b0.width == 210, (b0.width, b0)
+
+    # --------------------------------
+    #           --------------------------
+    #     ---------------------
+
+    for i in range(-365, 365):
+        b1 = BoundingBox(north=90, west=-10 + i, south=-90, east=200 + i)
+        b2 = BoundingBox(north=90, west=30 + i, south=-90, east=300 + i)
+        b3 = BoundingBox(north=90, west=0 + i, south=-90, east=50 + i)
+
+        b0 = BoundingBox.union([b1, b2, b3])
+
+        assert b0.width == 310, (b0.width, b0)
+
+
+def test_overlapping_bbox_3():
+    for i in range(361):
+        b1 = BoundingBox(north=90, west=-45 - i, south=-90, east=45 - i)
+        b2 = BoundingBox(north=90, west=-45 + i, south=-90, east=45 + i)
+        b0 = BoundingBox.union([b1, b2])
+
+        if i <= 90:
+            assert b0.width == 90 + 2 * i, (i, b0.width, b0)
+
+        if i > 90 and i <= 180:
+            assert b0.width == 3 * 90 - 2 * (i - 90), (i, b0.width, b0)
+
+        if i > 180 and i <= 270:
+            assert b0.width == 90 + 2 * (i - 180), (i, b0.width, b0)
+
+        if i > 270:
+            assert b0.width == 3 * 90 - 2 * (i - 270), (i, b0.width, b0)
+
+
+def test_bbox_union_with():
+    b1 = BoundingBox(north=50, west=-20, south=-30, east=40)
+    b2 = BoundingBox(north=60, west=-10, south=-40, east=55)
+    b = b1.union_with(b2)
+    assert b == BoundingBox(north=60, west=-20, south=-40, east=55)
+
+
+if __name__ == "__main__":
+    from earthkit.data.testing import main
+
+    main(__file__)