[Git][debian-gis-team/xcube-resampling][master] 6 commits: New upstream version 0.3.2

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sun Mar 22 09:50:25 GMT 2026



Antonio Valentino pushed to branch master at Debian GIS Project / xcube-resampling


Commits:
3fe5570d by Antonio Valentino at 2026-03-22T09:22:18+00:00
New upstream version 0.3.2
- - - - -
f7932033 by Antonio Valentino at 2026-03-22T09:23:00+00:00
Update upstream source from tag 'upstream/0.3.2'

Update to upstream version '0.3.2'
with Debian dir 819cd310d0b392046e5e0b6e3d3cc42a12fbe7f7
- - - - -
245330a0 by Antonio Valentino at 2026-03-22T09:23:50+00:00
New upstream release

- - - - -
678f2de1 by Antonio Valentino at 2026-03-22T09:41:15+00:00
Drop 0004-zarr-compat.patch

- - - - -
455b2abd by Antonio Valentino at 2026-03-22T09:41:18+00:00
Regresh all patches

- - - - -
976a5a17 by Antonio Valentino at 2026-03-22T09:41:18+00:00
Set distribution to unstable

- - - - -


29 changed files:

- CHANGES.md
- CONTRIBUTING.md
- README.md
- debian/changelog
- debian/patches/0001-No-numba.patch
- debian/patches/0002-Absolute-imports.patch
- debian/patches/0003-Fix-package-discovery.patch
- − debian/patches/0004-zarr-compat.patch
- debian/patches/series
- docs/examples/affine.ipynb
- docs/examples/grid_mapping.ipynb
- docs/examples/rectify_sentinel3.ipynb
- docs/index.md
- environment.yml
- examples/affine.ipynb
- examples/grid_mapping.ipynb
- examples/rectify_sentinel3.ipynb
- examples/resample_in_space.ipynb
- pyproject.toml
- tests/gridmapping/test_cfconv.py
- tests/gridmapping/test_dataset.py
- tests/test_reproject.py
- tests/test_utils.py
- xcube_resampling/affine.py
- xcube_resampling/gridmapping/base.py
- xcube_resampling/gridmapping/cfconv.py
- xcube_resampling/reproject.py
- xcube_resampling/utils.py
- xcube_resampling/version.py


Changes:

=====================================
CHANGES.md
=====================================
@@ -1,3 +1,23 @@
+## Changes in 0.3.2
+
+- Change inconsistent license classifier in `pyproject.toml` 
+- Removed the dependency on `zarr`. As part of this change, 
+  `xcube_resampling.grid_mapping.cfconv.add_spatial_ref` has been removed, as it 
+  only loosely fit the scope of the **xcube-resampling** repository.
+- Added `xcube_resampling.utils.get_utm_crs` to return the UTM CRS for a given 
+  longitude and latitude pair.
+- In `xcube_resampling.reproject_dataset`, the dataset is now clipped when the target
+  grid-mapping covers less than 80% of the source grid-mapping, improving performance
+  for reprojected cutouts.
+- In `xcube_resampling.affine_transform_dataset`, resampling is now performed per 
+  spatial slice, ensuring that interpolation is applied only along the spatial axes 
+  and no longer across non-spatial dimensions (e.g., time).
+- When `prevent_nan_propagation=True`, computation is now fully lazy. Previously, the 
+  implementation checked for NaN values in the source array, which triggered loading
+  of the entire dataset into memory.
+- Enhanced `GridMapping` API documentation.
+
+
 ## Changes in 0.3.1
 
 - Fixed a bug in `xcube_resampling.rectify_dataset` that occurred when processing  


=====================================
CONTRIBUTING.md
=====================================
@@ -42,7 +42,7 @@ sort imports statements according to the default settings of
 
 0. Future imports
 1. Python standard library imports, e.g., `os`, `typing`, etc
-2. 3rd-party imports, e.g., `xarray`, `zarr`, etc
+2. 3rd-party imports, e.g., `xarray`, `pyproj`, etc
 3. 1st-party library module imports using absolute paths, 
    e.g., `from xcube_resampling.a.b.c import d`. 
 4. 1st-party library module imports from local modules: 


=====================================
README.md
=====================================
@@ -25,6 +25,6 @@ All methods work seamlessly with chunked (lazily loaded) [xarray.Datasets](https
 
 ### ⚡ Lightweight & Independent
 The package is independent of the core *xcube* framework and has minimal dependencies:
-`affine, dask, dask-image, numba, numpy, pyproj, xarray, zarr`.
+`affine, dask, dask-image, numba, numpy, pyproj, xarray`.
 
 Find out more in the [xcube-resampling Documentation](https://xcube-dev.github.io/xcube-resampling/).


=====================================
debian/changelog
=====================================
@@ -1,3 +1,12 @@
+xcube-resampling (0.3.2-1) unstable; urgency=medium
+
+  * New upstream release.
+  * debian/patches:
+    - Drop 0004-zarr-compat.patch, no longer needed.
+    - Refresh remaining patchres.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Sun, 22 Mar 2026 09:31:36 +0000
+
 xcube-resampling (0.3.1-2) unstable; urgency=medium
 
   * Team upload.


=====================================
debian/patches/0001-No-numba.patch
=====================================
@@ -13,7 +13,7 @@ Forwarded: not-needed
  create mode 100644 xcube_resampling/_dummy_numba.py
 
 diff --git a/pyproject.toml b/pyproject.toml
-index 2bc586e..c347c0e 100644
+index 3750f90..89d8d9b 100644
 --- a/pyproject.toml
 +++ b/pyproject.toml
 @@ -44,7 +44,7 @@ dependencies = [


=====================================
debian/patches/0002-Absolute-imports.patch
=====================================
@@ -9,10 +9,10 @@ Forwarded: not-needed
  2 files changed, 2 insertions(+), 2 deletions(-)
 
 diff --git a/tests/gridmapping/test_dataset.py b/tests/gridmapping/test_dataset.py
-index ec2bcf0..4fbc307 100644
+index bd167bd..4dc50bf 100644
 --- a/tests/gridmapping/test_dataset.py
 +++ b/tests/gridmapping/test_dataset.py
-@@ -28,7 +28,7 @@ import xarray as xr
+@@ -27,7 +27,7 @@ import xarray as xr
  
  from xcube_resampling.gridmapping import GridMapping
  


=====================================
debian/patches/0003-Fix-package-discovery.patch
=====================================
@@ -8,10 +8,10 @@ Forwarded: not-needed
  1 file changed, 1 insertion(+), 4 deletions(-)
 
 diff --git a/pyproject.toml b/pyproject.toml
-index c347c0e..affafad 100644
+index 89d8d9b..44fecd3 100644
 --- a/pyproject.toml
 +++ b/pyproject.toml
-@@ -82,10 +82,7 @@ version = {attr = "xcube_resampling.version.__version__"}
+@@ -81,10 +81,7 @@ version = {attr = "xcube_resampling.version.__version__"}
  readme = {file = "README.md", content-type = "text/markdown"}
  
  [tool.setuptools.packages.find]


=====================================
debian/patches/0004-zarr-compat.patch deleted
=====================================
@@ -1,56 +0,0 @@
-From: Antonio Valentino <antonio.valentino at tiscali.it>
-Date: Sun, 8 Mar 2026 11:51:50 +0000
-Subject: zarr-compat
-
-Fix compatibility with the lates versions of zarr.
----
- tests/gridmapping/test_cfconv.py       | 5 +++--
- xcube_resampling/gridmapping/cfconv.py | 3 ++-
- 2 files changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/tests/gridmapping/test_cfconv.py b/tests/gridmapping/test_cfconv.py
-index eeb5dc1..8f2e35d 100644
---- a/tests/gridmapping/test_cfconv.py
-+++ b/tests/gridmapping/test_cfconv.py
-@@ -27,6 +27,7 @@ import numpy as np
- import pyproj
- import xarray as xr
- import zarr
-+import zarr.storage
- 
- # noinspection PyProtectedMember
- from xcube_resampling.gridmapping.cfconv import (
-@@ -403,10 +404,10 @@ class TestAddSpatialRef(unittest.TestCase):
- 
-     def setUp(self):
-         # Create an in-memory Zarr group
--        self.store = zarr.MemoryStore()
-+        self.store = zarr.storage.MemoryStore()
-         self.group = zarr.group(store=self.store, overwrite=True)
-         # Create a dummy dataset array
--        self.group.zeros("data", shape=(3, 3), chunks=(3, 3), dtype=np.float32)
-+        self.group.zeros(name="data", shape=(3, 3), chunks=(3, 3), dtype=np.float32)
-         # Add _ARRAY_DIMENSIONS attribute to simulate xarray
-         self.group["data"].attrs["_ARRAY_DIMENSIONS"] = ["y", "x"]
- 
-diff --git a/xcube_resampling/gridmapping/cfconv.py b/xcube_resampling/gridmapping/cfconv.py
-index 4a3a19a..9ecb19f 100644
---- a/xcube_resampling/gridmapping/cfconv.py
-+++ b/xcube_resampling/gridmapping/cfconv.py
-@@ -28,6 +28,7 @@ import pyproj
- import xarray as xr
- import zarr
- import zarr.convenience
-+import zarr.storage
- 
- from .assertions import assert_instance
- from .base import CRS_WGS84
-@@ -318,7 +319,7 @@ def _find_dataset_tile_size(
- 
- 
- def add_spatial_ref(
--    dataset_store: zarr.convenience.StoreLike,
-+    dataset_store: zarr.storage.StoreLike,
-     crs: pyproj.CRS,
-     crs_var_name: str = "spatial_ref",
-     xy_dim_names: tuple[str, str] | None = None,


=====================================
debian/patches/series
=====================================
@@ -1,4 +1,3 @@
 0001-No-numba.patch
 0002-Absolute-imports.patch
 0003-Fix-package-discovery.patch
-0004-zarr-compat.patch


=====================================
docs/examples/affine.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
docs/examples/grid_mapping.ipynb
=====================================
@@ -1,35 +1,46 @@
 {
  "cells": [
   {
-   "metadata": {},
    "cell_type": "markdown",
-   "source": "# GridMapping instance"
+   "metadata": {},
+   "source": [
+    "# GridMapping instance"
+   ]
   },
   {
+   "cell_type": "code",
+   "execution_count": 1,
    "metadata": {
-    "tags": [],
     "ExecuteTime": {
      "end_time": "2026-02-20T08:24:39.414833251Z",
      "start_time": "2026-02-20T08:24:38.912662773Z"
-    }
+    },
+    "tags": []
    },
-   "cell_type": "code",
    "outputs": [],
-   "execution_count": 1,
    "source": [
     "import dask.array as da\n",
     "import numpy as np\n",
     "import pyproj as pp\n",
-    "import xarray as xr"
+    "import xarray as xr\n",
+    "from zarr.storage import ZipStore"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 2,
    "metadata": {
     "tags": []
    },
    "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 51.1 ms, sys: 4.03 ms, total: 55.1 ms\n",
+      "Wall time: 53.6 ms\n"
+     ]
+    },
     {
      "data": {
       "text/html": [
@@ -48,9 +59,7 @@
        "</symbol>\n",
        "</defs>\n",
        "</svg>\n",
-       "<style>/* CSS stylesheet for displaying xarray objects in jupyterlab.\n",
-       " *\n",
-       " */\n",
+       "<style>/* CSS stylesheet for displaying xarray objects in notebooks */\n",
        "\n",
        ":root {\n",
        "  --xr-font-color0: var(\n",
@@ -129,6 +138,8 @@
        "  display: block !important;\n",
        "  min-width: 300px;\n",
        "  max-width: 700px;\n",
+       "  line-height: 1.6;\n",
+       "  padding-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-text-repr-fallback {\n",
@@ -139,8 +150,11 @@
        ".xr-header {\n",
        "  padding-top: 6px;\n",
        "  padding-bottom: 6px;\n",
-       "  margin-bottom: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-header {\n",
        "  border-bottom: solid 1px var(--xr-border-color);\n",
+       "  margin-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-header > div,\n",
@@ -151,46 +165,62 @@
        "}\n",
        "\n",
        ".xr-obj-type,\n",
-       ".xr-array-name {\n",
+       ".xr-obj-name {\n",
        "  margin-left: 2px;\n",
        "  margin-right: 10px;\n",
        "}\n",
        "\n",
-       ".xr-obj-type {\n",
+       ".xr-obj-type,\n",
+       ".xr-group-box-contents > label {\n",
        "  color: var(--xr-font-color2);\n",
+       "  display: block;\n",
        "}\n",
        "\n",
        ".xr-sections {\n",
        "  padding-left: 0 !important;\n",
        "  display: grid;\n",
        "  grid-template-columns: 150px auto auto 1fr 0 20px 0 20px;\n",
+       "  margin-block-start: 0;\n",
+       "  margin-block-end: 0;\n",
        "}\n",
        "\n",
        ".xr-section-item {\n",
        "  display: contents;\n",
        "}\n",
        "\n",
-       ".xr-section-item input {\n",
-       "  display: inline-block;\n",
+       ".xr-section-item > input,\n",
+       ".xr-group-box-contents > input,\n",
+       ".xr-array-wrap > input {\n",
+       "  display: block;\n",
        "  opacity: 0;\n",
        "  height: 0;\n",
+       "  margin: 0;\n",
        "}\n",
        "\n",
-       ".xr-section-item input + label {\n",
+       ".xr-section-item > input + label,\n",
+       ".xr-var-item > input + label {\n",
        "  color: var(--xr-disabled-color);\n",
-       "  border: 2px solid transparent !important;\n",
        "}\n",
        "\n",
-       ".xr-section-item input:enabled + label {\n",
+       ".xr-section-item > input:enabled + label,\n",
+       ".xr-var-item > input:enabled + label,\n",
+       ".xr-array-wrap > input:enabled + label,\n",
+       ".xr-group-box-contents > input:enabled + label {\n",
        "  cursor: pointer;\n",
        "  color: var(--xr-font-color2);\n",
        "}\n",
        "\n",
-       ".xr-section-item input:focus + label {\n",
-       "  border: 2px solid var(--xr-font-color0) !important;\n",
+       ".xr-section-item > input:focus-visible + label,\n",
+       ".xr-var-item > input:focus-visible + label,\n",
+       ".xr-array-wrap > input:focus-visible + label,\n",
+       ".xr-group-box-contents > input:focus-visible + label {\n",
+       "  outline: auto;\n",
        "}\n",
        "\n",
-       ".xr-section-item input:enabled + label:hover {\n",
+       ".xr-section-item > input:enabled + label:hover,\n",
+       ".xr-var-item > input:enabled + label:hover,\n",
+       ".xr-array-wrap > input:enabled + label:hover,\n",
+       ".xr-group-box-contents > input:enabled + label:hover {\n",
        "  color: var(--xr-font-color0);\n",
        "}\n",
        "\n",
@@ -198,11 +228,25 @@
        "  grid-column: 1;\n",
        "  color: var(--xr-font-color2);\n",
        "  font-weight: 500;\n",
+       "  white-space: nowrap;\n",
+       "}\n",
+       "\n",
+       ".xr-section-summary > em {\n",
+       "  font-weight: normal;\n",
+       "}\n",
+       "\n",
+       ".xr-span-grid {\n",
+       "  grid-column-end: -1;\n",
        "}\n",
        "\n",
        ".xr-section-summary > span {\n",
        "  display: inline-block;\n",
-       "  padding-left: 0.5em;\n",
+       "  padding-left: 0.3em;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label > span {\n",
+       "  display: inline-block;\n",
+       "  padding-left: 0.6em;\n",
        "}\n",
        "\n",
        ".xr-section-summary-in:disabled + label {\n",
@@ -230,9 +274,9 @@
        "}\n",
        "\n",
        ".xr-section-summary,\n",
-       ".xr-section-inline-details {\n",
+       ".xr-section-inline-details,\n",
+       ".xr-group-box-contents > label {\n",
        "  padding-top: 4px;\n",
-       "  padding-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-section-inline-details {\n",
@@ -240,15 +284,81 @@
        "}\n",
        "\n",
        ".xr-section-details {\n",
-       "  display: none;\n",
        "  grid-column: 1 / -1;\n",
+       "  margin-top: 4px;\n",
        "  margin-bottom: 5px;\n",
        "}\n",
        "\n",
+       ".xr-section-summary-in ~ .xr-section-details {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
        ".xr-section-summary-in:checked ~ .xr-section-details {\n",
        "  display: contents;\n",
        "}\n",
        "\n",
+       ".xr-children {\n",
+       "  display: inline-grid;\n",
+       "  grid-template-columns: 100%;\n",
+       "  grid-column: 1 / -1;\n",
+       "  padding-top: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box {\n",
+       "  display: inline-grid;\n",
+       "  grid-template-columns: 0px 30px auto;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-vline {\n",
+       "  grid-column-start: 1;\n",
+       "  border-right: 0.2em solid;\n",
+       "  border-color: var(--xr-border-color);\n",
+       "  width: 0px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-hline {\n",
+       "  grid-column-start: 2;\n",
+       "  grid-row-start: 1;\n",
+       "  height: 1em;\n",
+       "  width: 26px;\n",
+       "  border-bottom: 0.2em solid;\n",
+       "  border-color: var(--xr-border-color);\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents {\n",
+       "  grid-column-start: 3;\n",
+       "  padding-bottom: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > label::before {\n",
+       "  content: \"📂\";\n",
+       "  padding-right: 0.3em;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label::before {\n",
+       "  content: \"📁\";\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label {\n",
+       "  padding-bottom: 0px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked ~ .xr-sections {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input + label > span {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-ellipsis {\n",
+       "  font-size: 1.4em;\n",
+       "  font-weight: 900;\n",
+       "  color: var(--xr-font-color2);\n",
+       "  letter-spacing: 0.15em;\n",
+       "  cursor: default;\n",
+       "}\n",
+       "\n",
        ".xr-array-wrap {\n",
        "  grid-column: 1 / -1;\n",
        "  display: grid;\n",
@@ -479,19 +589,19 @@
        "</style><pre class='xr-text-repr-fallback'><xarray.Dataset> Size: 72MB\n",
        "Dimensions:        (y: 1890, x: 1189)\n",
        "Coordinates:\n",
-       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    lon            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Dimensions without coordinates: y, x\n",
        "Data variables:\n",
        "    quality_flags  (y, x) uint32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    rtoa_8         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Attributes:\n",
        "    Conventions:   CF-1.4\n",
        "    product_type:  C2RCC_OLCI\n",
        "    start_date:    04-JUL-2018 09:21:55.677316\n",
-       "    stop_date:     04-JUL-2018 09:23:18.811790</pre><div class='xr-wrap' style='display:none'><div class='xr-header'><div class='xr-obj-type'>xarray.Dataset</div></div><ul class='xr-sections'><li class='xr-section-item'><input id='section-9d97c6b2-969f-4ad5-b3b6-26921f3b0af9' class='xr-section-summary-in' type='checkbox' disabled ><label for='section-9d97c6b2-969f-4ad5-b3b6-26921f3b0af9' class='xr-section-summary'  title='Expand/collapse section'>Dimensions:</label><div class='xr-section-inline-details'><ul class='xr-dim-list'><li><span>y</span>: 1890</li><li><span>x</span>: 1189</li></ul></div><div class='xr-section-details'></div></li><li class='xr-section-item'><input id='section-6dc015e2-4abe-42af-b979-873b2de0b39a' class='xr-section-summary-in' type='checkbox'  checked><label for='section-6dc015e2-4abe-42af-b979-873b2de0b39a' class='xr-section-summary' >Coordinates: <span>(2)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>lat</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-deddca9e-bf07-482d-ab99-f08cb1e2342c' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-deddca9e-bf07-482d-ab99-f08cb1e2342c' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-d71f1250-32d5-48e8-b9a9-e34f1f53b6fa' class='xr-var-data-in' type='checkbox'><label for='data-d71f1250-32d5-48e8-b9a9-e34f1f53b6fa' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>latitude coordinate</dd><dt><span>standard_name :</span></dt><dd>latitude</dd><dt><span>units :</span></dt><dd>degrees_north</dd></dl></div><div class='xr-var-data'><table>\n",
+       "    stop_date:     04-JUL-2018 09:23:18.811790</pre><div class='xr-wrap' style='display:none'><div class='xr-header'><div class='xr-obj-type'>xarray.Dataset</div></div><ul class='xr-sections'><li class='xr-section-item'><input id='section-062422a0-035f-4ce5-943c-5b4dbc028b5f' class='xr-section-summary-in' type='checkbox' disabled /><label for='section-062422a0-035f-4ce5-943c-5b4dbc028b5f' class='xr-section-summary'>Dimensions:</label><div class='xr-section-inline-details'><ul class='xr-dim-list'><li><span>y</span>: 1890</li><li><span>x</span>: 1189</li></ul></div></li><li class='xr-section-item'><input id='section-e3031f42-545c-4fbb-a655-37b9c3ef17a0' class='xr-section-summary-in' type='checkbox' checked /><label for='section-e3031f42-545c-4fbb-a655-37b9c3ef17a0' class='xr-section-summary' title='Expand/collapse section'>Coordinates: <span>(2)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>lon</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-35676472-5380-4e7b-9e2e-3a31525aa90e' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-35676472-5380-4e7b-9e2e-3a31525aa90e' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0f67f2a6-ab2d-40b9-a8b6-e1cd6f436b08' class='xr-var-data-in' type='checkbox'><label for='data-0f67f2a6-ab2d-40b9-a8b6-e1cd6f436b08' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>longitude coordinate</dd><dt><span>standard_name :</span></dt><dd>longitude</dd><dt><span>units :</span></dt><dd>degrees_east</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -546,12 +656,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>lon</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-d96693ec-542c-4e7d-a4a9-c2797ff03b5d' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-d96693ec-542c-4e7d-a4a9-c2797ff03b5d' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-4e1c7824-f0a8-49f5-aa27-d4cfaae78e67' class='xr-var-data-in' type='checkbox'><label for='data-4e1c7824-f0a8-49f5-aa27-d4cfaae78e67' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>longitude coordinate</dd><dt><span>standard_name :</span></dt><dd>longitude</dd><dt><span>units :</span></dt><dd>degrees_east</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>lat</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-29092c87-8faf-4f5f-871a-3f266fa3b4bd' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-29092c87-8faf-4f5f-871a-3f266fa3b4bd' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0f54ca8a-f8bd-4a71-bb34-e547807f62fd' class='xr-var-data-in' type='checkbox'><label for='data-0f54ca8a-f8bd-4a71-bb34-e547807f62fd' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>latitude coordinate</dd><dt><span>standard_name :</span></dt><dd>latitude</dd><dt><span>units :</span></dt><dd>degrees_north</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -606,12 +716,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-71e5c3c7-6911-4566-88c1-6adcb98ff152' class='xr-section-summary-in' type='checkbox'  checked><label for='section-71e5c3c7-6911-4566-88c1-6adcb98ff152' class='xr-section-summary' >Data variables: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>quality_flags</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>uint32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-6e582c1e-8520-4a49-85b2-63c1d24727b5' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-6e582c1e-8520-4a49-85b2-63c1d24727b5' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-ef89196d-1f87-4884-b181-ef2a3527615d' class='xr-var-data-in' type='checkbox'><label for='data-ef89196d-1f87-4884-b181-ef2a3527615d' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>flag_coding_name :</span></dt><dd>quality_flags</dd><dt><span>flag_descriptions :</span></dt><dd></dd><dt><span>flag_masks :</span></dt><dd>[-2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]</dd><dt><span>flag_meanings :</span></dt><dd>land coastline fresh_inland_water tidal_region bright straylight_risk invalid cosmetic duplicated sun_glint_risk dubious saturated_Oa01 saturated_Oa02 saturated_Oa03 saturated_Oa04 saturated_Oa05 saturated_Oa06 saturated_Oa07 saturated_Oa08 saturated_Oa09 saturated_Oa10 saturated_Oa11 saturated_Oa12 saturated_Oa13 saturated_Oa14 saturated_Oa15 saturated_Oa16 saturated_Oa17 saturated_Oa18 saturated_Oa19 saturated_Oa20 saturated_Oa21</dd><dt><span>long_name :</span></dt><dd>Classification and quality flags</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-0f4b7342-dc11-4071-9513-ecc6a913bdaa' class='xr-section-summary-in' type='checkbox' checked /><label for='section-0f4b7342-dc11-4071-9513-ecc6a913bdaa' class='xr-section-summary' title='Expand/collapse section'>Data variables: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>quality_flags</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>uint32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-fcfc621f-417d-45fe-ba8c-87ef35a46fa0' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-fcfc621f-417d-45fe-ba8c-87ef35a46fa0' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-03a9c2f3-59b9-46fb-b2dd-118dcaf1f38d' class='xr-var-data-in' type='checkbox'><label for='data-03a9c2f3-59b9-46fb-b2dd-118dcaf1f38d' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>flag_coding_name :</span></dt><dd>quality_flags</dd><dt><span>flag_descriptions :</span></dt><dd></dd><dt><span>flag_masks :</span></dt><dd>[-2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]</dd><dt><span>flag_meanings :</span></dt><dd>land coastline fresh_inland_water tidal_region bright straylight_risk invalid cosmetic duplicated sun_glint_risk dubious saturated_Oa01 saturated_Oa02 saturated_Oa03 saturated_Oa04 saturated_Oa05 saturated_Oa06 saturated_Oa07 saturated_Oa08 saturated_Oa09 saturated_Oa10 saturated_Oa11 saturated_Oa12 saturated_Oa13 saturated_Oa14 saturated_Oa15 saturated_Oa16 saturated_Oa17 saturated_Oa18 saturated_Oa19 saturated_Oa20 saturated_Oa21</dd><dt><span>long_name :</span></dt><dd>Classification and quality flags</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -666,12 +776,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_3</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-1e2330d7-255c-4fb5-a607-0b30307ae071' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-1e2330d7-255c-4fb5-a607-0b30307ae071' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-2152fc0c-c484-45d6-8373-712029aede62' class='xr-var-data-in' type='checkbox'><label for='data-2152fc0c-c484-45d6-8373-712029aede62' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>2.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>442.5</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_8</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-99c3a43f-00b8-4308-81f5-32b9e117f3f3' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-99c3a43f-00b8-4308-81f5-32b9e117f3f3' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-18add4ff-560b-4ef9-9835-92fb7411a1da' class='xr-var-data-in' type='checkbox'><label for='data-18add4ff-560b-4ef9-9835-92fb7411a1da' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>7.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>665.0</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -726,12 +836,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_6</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-034898b4-a379-4180-9e4b-4dc873db74d2' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-034898b4-a379-4180-9e4b-4dc873db74d2' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-ec125ddd-5246-499d-b1e9-79b56f641602' class='xr-var-data-in' type='checkbox'><label for='data-ec125ddd-5246-499d-b1e9-79b56f641602' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>5.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>560.0</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_6</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-7dacbf9a-830c-4c84-9d91-e55921a2dcb4' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-7dacbf9a-830c-4c84-9d91-e55921a2dcb4' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-c4ba4b7f-03b6-4f0a-b1a6-5f3f9b4a3680' class='xr-var-data-in' type='checkbox'><label for='data-c4ba4b7f-03b6-4f0a-b1a6-5f3f9b4a3680' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>5.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>560.0</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -786,12 +896,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_8</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-a883ef5f-62e6-45ef-9987-e49b7ea06da2' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-a883ef5f-62e6-45ef-9987-e49b7ea06da2' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-e664eba8-20d0-4aec-919d-646c5041023b' class='xr-var-data-in' type='checkbox'><label for='data-e664eba8-20d0-4aec-919d-646c5041023b' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>7.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>665.0</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_3</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-7d8edf6d-ed30-40fa-97d1-5474cf97bb9a' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-7d8edf6d-ed30-40fa-97d1-5474cf97bb9a' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-03399361-6593-4cb9-b7b2-0e106f3f3cac' class='xr-var-data-in' type='checkbox'><label for='data-03399361-6593-4cb9-b7b2-0e106f3f3cac' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>2.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>442.5</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -846,25 +956,25 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-10d64c3c-c0f8-4d57-8c3a-1814c98ddd2b' class='xr-section-summary-in' type='checkbox' disabled ><label for='section-10d64c3c-c0f8-4d57-8c3a-1814c98ddd2b' class='xr-section-summary'  title='Expand/collapse section'>Indexes: <span>(0)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'></ul></div></li><li class='xr-section-item'><input id='section-2e4d819d-ead3-46d1-84db-e7355c379e2e' class='xr-section-summary-in' type='checkbox'  checked><label for='section-2e4d819d-ead3-46d1-84db-e7355c379e2e' class='xr-section-summary' >Attributes: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><dl class='xr-attrs'><dt><span>Conventions :</span></dt><dd>CF-1.4</dd><dt><span>product_type :</span></dt><dd>C2RCC_OLCI</dd><dt><span>start_date :</span></dt><dd>04-JUL-2018 09:21:55.677316</dd><dt><span>stop_date :</span></dt><dd>04-JUL-2018 09:23:18.811790</dd></dl></div></li></ul></div></div>"
+       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-32fb75ff-1086-4dc8-a8b9-8d614295f245' class='xr-section-summary-in' type='checkbox' checked /><label for='section-32fb75ff-1086-4dc8-a8b9-8d614295f245' class='xr-section-summary' title='Expand/collapse section'>Attributes: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><dl class='xr-attrs'><dt><span>Conventions :</span></dt><dd>CF-1.4</dd><dt><span>product_type :</span></dt><dd>C2RCC_OLCI</dd><dt><span>start_date :</span></dt><dd>04-JUL-2018 09:21:55.677316</dd><dt><span>stop_date :</span></dt><dd>04-JUL-2018 09:23:18.811790</dd></dl></div></li></ul></div></div>"
       ],
       "text/plain": [
        "<xarray.Dataset> Size: 72MB\n",
        "Dimensions:        (y: 1890, x: 1189)\n",
        "Coordinates:\n",
-       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    lon            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Dimensions without coordinates: y, x\n",
        "Data variables:\n",
        "    quality_flags  (y, x) uint32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    rtoa_8         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Attributes:\n",
        "    Conventions:   CF-1.4\n",
        "    product_type:  C2RCC_OLCI\n",
@@ -872,19 +982,21 @@
        "    stop_date:     04-JUL-2018 09:23:18.811790"
       ]
      },
-     "execution_count": 3,
+     "execution_count": 2,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "ds = xr.open_zarr(\"./inputdata/S3-OLCI-L2A.zarr.zip\", consolidated=False)\n",
+    "%%time\n",
+    "store = ZipStore(\"inputdata/S3-OLCI-L2A.zarr.zip\", mode=\"r\")\n",
+    "ds = xr.open_zarr(store, consolidated=False)\n",
     "ds"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 3,
    "metadata": {
     "tags": []
    },
@@ -905,7 +1017,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 4,
+     "execution_count": 3,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -917,7 +1029,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 4,
    "metadata": {
     "tags": []
    },
@@ -928,7 +1040,7 @@
        "True"
       ]
      },
-     "execution_count": 5,
+     "execution_count": 4,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -939,7 +1051,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 5,
    "metadata": {
     "tags": []
    },
@@ -950,7 +1062,7 @@
        "False"
       ]
      },
-     "execution_count": 6,
+     "execution_count": 5,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -961,7 +1073,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 6,
    "metadata": {
     "tags": []
    },
@@ -985,7 +1097,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 7,
+     "execution_count": 6,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -997,7 +1109,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 7,
    "metadata": {
     "tags": []
    },
@@ -1012,7 +1124,7 @@
        "- bounds: (12.0, 0.0, 18.0, 84.0)"
       ]
      },
-     "execution_count": 8,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1024,7 +1136,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 8,
    "metadata": {
     "tags": []
    },
@@ -1036,7 +1148,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 9,
    "metadata": {
     "tags": []
    },
@@ -1045,8 +1157,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 454 ms, sys: 10.1 ms, total: 464 ms\n",
-      "Wall time: 463 ms\n"
+      "CPU times: user 662 ms, sys: 15.4 ms, total: 677 ms\n",
+      "Wall time: 675 ms\n"
      ]
     }
    ],
@@ -1057,7 +1169,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 10,
    "metadata": {
     "tags": []
    },
@@ -1095,7 +1207,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1106,7 +1218,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -1194,9 +1306,9 @@
        "  <polygon points=\"24.9485979497544,14.948597949754403 100.4406614418179,14.948597949754403 100.4406614418179,134.9485979497544 24.9485979497544,134.9485979497544\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"62.694630\" y=\"154.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"120.440661\" y=\"74.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.440661,74.948598)\">1890</text>\n",
-       "  <text x=\"7.474299\" y=\"147.474299\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.474299,147.474299)\">2</text>\n",
+       "  <text x=\"62.69462969578615\" y=\"154.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"120.4406614418179\" y=\"74.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.4406614418179,74.9485979497544)\">1890</text>\n",
+       "  <text x=\"7.4742989748772\" y=\"147.4742989748772\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.4742989748772,147.4742989748772)\">2</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
@@ -1206,7 +1318,7 @@
        "dask.array<rechunk-merge, shape=(2, 1890, 1189), dtype=float64, chunksize=(2, 512, 512), chunktype=numpy.ndarray>"
       ]
      },
-     "execution_count": 12,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1218,7 +1330,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1230,7 +1342,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -1318,9 +1430,9 @@
        "  <polygon points=\"24.9485979497544,14.948597949754403 100.4406614418179,14.948597949754403 100.4406614418179,134.9485979497544 24.9485979497544,134.9485979497544\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"62.694630\" y=\"154.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"120.440661\" y=\"74.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.440661,74.948598)\">1890</text>\n",
-       "  <text x=\"7.474299\" y=\"147.474299\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.474299,147.474299)\">2</text>\n",
+       "  <text x=\"62.69462969578615\" y=\"154.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"120.4406614418179\" y=\"74.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.4406614418179,74.9485979497544)\">1890</text>\n",
+       "  <text x=\"7.4742989748772\" y=\"147.4742989748772\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.4742989748772,147.4742989748772)\">2</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
@@ -1330,7 +1442,7 @@
        "dask.array<transpose, shape=(2, 1890, 1189), dtype=float64, chunksize=(2, 512, 512), chunktype=numpy.ndarray>"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1344,15 +1456,15 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 955 ms, sys: 107 ms, total: 1.06 s\n",
-      "Wall time: 343 ms\n"
+      "CPU times: user 1.43 s, sys: 313 ms, total: 1.74 s\n",
+      "Wall time: 556 ms\n"
      ]
     }
    ],
@@ -1363,7 +1475,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -1399,7 +1511,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 16,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1410,7 +1522,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1421,7 +1533,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -1431,7 +1543,7 @@
        " dask.array<transpose, shape=(1890, 1189), dtype=float64, chunksize=(512, 512), chunktype=numpy.ndarray>)"
       ]
      },
-     "execution_count": 18,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1450,15 +1562,15 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 1.69 s, sys: 64.8 ms, total: 1.76 s\n",
-      "Wall time: 570 ms\n"
+      "CPU times: user 2.27 s, sys: 204 ms, total: 2.48 s\n",
+      "Wall time: 824 ms\n"
      ]
     }
    ],
@@ -1470,7 +1582,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
@@ -1506,7 +1618,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 19,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1517,7 +1629,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -1536,7 +1648,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 21,
+     "execution_count": 20,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1547,7 +1659,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -1566,7 +1678,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 21,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1592,7 +1704,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.13.5"
+   "version": "3.14.3"
   }
  },
  "nbformat": 4,


=====================================
docs/examples/rectify_sentinel3.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
docs/index.md
=====================================
@@ -18,7 +18,7 @@ All methods work seamlessly with chunked (lazily loaded) [xarray.Datasets](https
 
 ### ⚡ Lightweight & Independent
 The package is independent of the core *xcube* framework and has minimal dependencies:
-`affine, dask, dask-image, numba, numpy, pyproj, xarray, zarr`.
+`affine, dask, dask-image, numba, numpy, pyproj, xarray`.
 
 
 ## Overview


=====================================
environment.yml
=====================================
@@ -12,7 +12,6 @@ dependencies:
   - numpy >=1.16
   - pyproj >=3.0
   - xarray >=2024.7
-  - zarr >=2.11,<3  # until we can ensure zarr 3 compatibility
   # Development Dependencies - Tools
   - black
   - isort
@@ -28,4 +27,5 @@ dependencies:
   # Development Dependencies - Demos
   - jupyterlab
   - matplotlib
+  - zarr
 


=====================================
examples/affine.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
examples/grid_mapping.ipynb
=====================================
@@ -1,35 +1,46 @@
 {
  "cells": [
   {
-   "metadata": {},
    "cell_type": "markdown",
-   "source": "# GridMapping instance"
+   "metadata": {},
+   "source": [
+    "# GridMapping instance"
+   ]
   },
   {
+   "cell_type": "code",
+   "execution_count": 1,
    "metadata": {
-    "tags": [],
     "ExecuteTime": {
      "end_time": "2026-02-20T08:24:39.414833251Z",
      "start_time": "2026-02-20T08:24:38.912662773Z"
-    }
+    },
+    "tags": []
    },
-   "cell_type": "code",
    "outputs": [],
-   "execution_count": 1,
    "source": [
     "import dask.array as da\n",
     "import numpy as np\n",
     "import pyproj as pp\n",
-    "import xarray as xr"
+    "import xarray as xr\n",
+    "from zarr.storage import ZipStore"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 2,
    "metadata": {
     "tags": []
    },
    "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 51.1 ms, sys: 4.03 ms, total: 55.1 ms\n",
+      "Wall time: 53.6 ms\n"
+     ]
+    },
     {
      "data": {
       "text/html": [
@@ -48,9 +59,7 @@
        "</symbol>\n",
        "</defs>\n",
        "</svg>\n",
-       "<style>/* CSS stylesheet for displaying xarray objects in jupyterlab.\n",
-       " *\n",
-       " */\n",
+       "<style>/* CSS stylesheet for displaying xarray objects in notebooks */\n",
        "\n",
        ":root {\n",
        "  --xr-font-color0: var(\n",
@@ -129,6 +138,8 @@
        "  display: block !important;\n",
        "  min-width: 300px;\n",
        "  max-width: 700px;\n",
+       "  line-height: 1.6;\n",
+       "  padding-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-text-repr-fallback {\n",
@@ -139,8 +150,11 @@
        ".xr-header {\n",
        "  padding-top: 6px;\n",
        "  padding-bottom: 6px;\n",
-       "  margin-bottom: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-header {\n",
        "  border-bottom: solid 1px var(--xr-border-color);\n",
+       "  margin-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-header > div,\n",
@@ -151,46 +165,62 @@
        "}\n",
        "\n",
        ".xr-obj-type,\n",
-       ".xr-array-name {\n",
+       ".xr-obj-name {\n",
        "  margin-left: 2px;\n",
        "  margin-right: 10px;\n",
        "}\n",
        "\n",
-       ".xr-obj-type {\n",
+       ".xr-obj-type,\n",
+       ".xr-group-box-contents > label {\n",
        "  color: var(--xr-font-color2);\n",
+       "  display: block;\n",
        "}\n",
        "\n",
        ".xr-sections {\n",
        "  padding-left: 0 !important;\n",
        "  display: grid;\n",
        "  grid-template-columns: 150px auto auto 1fr 0 20px 0 20px;\n",
+       "  margin-block-start: 0;\n",
+       "  margin-block-end: 0;\n",
        "}\n",
        "\n",
        ".xr-section-item {\n",
        "  display: contents;\n",
        "}\n",
        "\n",
-       ".xr-section-item input {\n",
-       "  display: inline-block;\n",
+       ".xr-section-item > input,\n",
+       ".xr-group-box-contents > input,\n",
+       ".xr-array-wrap > input {\n",
+       "  display: block;\n",
        "  opacity: 0;\n",
        "  height: 0;\n",
+       "  margin: 0;\n",
        "}\n",
        "\n",
-       ".xr-section-item input + label {\n",
+       ".xr-section-item > input + label,\n",
+       ".xr-var-item > input + label {\n",
        "  color: var(--xr-disabled-color);\n",
-       "  border: 2px solid transparent !important;\n",
        "}\n",
        "\n",
-       ".xr-section-item input:enabled + label {\n",
+       ".xr-section-item > input:enabled + label,\n",
+       ".xr-var-item > input:enabled + label,\n",
+       ".xr-array-wrap > input:enabled + label,\n",
+       ".xr-group-box-contents > input:enabled + label {\n",
        "  cursor: pointer;\n",
        "  color: var(--xr-font-color2);\n",
        "}\n",
        "\n",
-       ".xr-section-item input:focus + label {\n",
-       "  border: 2px solid var(--xr-font-color0) !important;\n",
+       ".xr-section-item > input:focus-visible + label,\n",
+       ".xr-var-item > input:focus-visible + label,\n",
+       ".xr-array-wrap > input:focus-visible + label,\n",
+       ".xr-group-box-contents > input:focus-visible + label {\n",
+       "  outline: auto;\n",
        "}\n",
        "\n",
-       ".xr-section-item input:enabled + label:hover {\n",
+       ".xr-section-item > input:enabled + label:hover,\n",
+       ".xr-var-item > input:enabled + label:hover,\n",
+       ".xr-array-wrap > input:enabled + label:hover,\n",
+       ".xr-group-box-contents > input:enabled + label:hover {\n",
        "  color: var(--xr-font-color0);\n",
        "}\n",
        "\n",
@@ -198,11 +228,25 @@
        "  grid-column: 1;\n",
        "  color: var(--xr-font-color2);\n",
        "  font-weight: 500;\n",
+       "  white-space: nowrap;\n",
+       "}\n",
+       "\n",
+       ".xr-section-summary > em {\n",
+       "  font-weight: normal;\n",
+       "}\n",
+       "\n",
+       ".xr-span-grid {\n",
+       "  grid-column-end: -1;\n",
        "}\n",
        "\n",
        ".xr-section-summary > span {\n",
        "  display: inline-block;\n",
-       "  padding-left: 0.5em;\n",
+       "  padding-left: 0.3em;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label > span {\n",
+       "  display: inline-block;\n",
+       "  padding-left: 0.6em;\n",
        "}\n",
        "\n",
        ".xr-section-summary-in:disabled + label {\n",
@@ -230,9 +274,9 @@
        "}\n",
        "\n",
        ".xr-section-summary,\n",
-       ".xr-section-inline-details {\n",
+       ".xr-section-inline-details,\n",
+       ".xr-group-box-contents > label {\n",
        "  padding-top: 4px;\n",
-       "  padding-bottom: 4px;\n",
        "}\n",
        "\n",
        ".xr-section-inline-details {\n",
@@ -240,15 +284,81 @@
        "}\n",
        "\n",
        ".xr-section-details {\n",
-       "  display: none;\n",
        "  grid-column: 1 / -1;\n",
+       "  margin-top: 4px;\n",
        "  margin-bottom: 5px;\n",
        "}\n",
        "\n",
+       ".xr-section-summary-in ~ .xr-section-details {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
        ".xr-section-summary-in:checked ~ .xr-section-details {\n",
        "  display: contents;\n",
        "}\n",
        "\n",
+       ".xr-children {\n",
+       "  display: inline-grid;\n",
+       "  grid-template-columns: 100%;\n",
+       "  grid-column: 1 / -1;\n",
+       "  padding-top: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box {\n",
+       "  display: inline-grid;\n",
+       "  grid-template-columns: 0px 30px auto;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-vline {\n",
+       "  grid-column-start: 1;\n",
+       "  border-right: 0.2em solid;\n",
+       "  border-color: var(--xr-border-color);\n",
+       "  width: 0px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-hline {\n",
+       "  grid-column-start: 2;\n",
+       "  grid-row-start: 1;\n",
+       "  height: 1em;\n",
+       "  width: 26px;\n",
+       "  border-bottom: 0.2em solid;\n",
+       "  border-color: var(--xr-border-color);\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents {\n",
+       "  grid-column-start: 3;\n",
+       "  padding-bottom: 4px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > label::before {\n",
+       "  content: \"📂\";\n",
+       "  padding-right: 0.3em;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label::before {\n",
+       "  content: \"📁\";\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked + label {\n",
+       "  padding-bottom: 0px;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input:checked ~ .xr-sections {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-contents > input + label > span {\n",
+       "  display: none;\n",
+       "}\n",
+       "\n",
+       ".xr-group-box-ellipsis {\n",
+       "  font-size: 1.4em;\n",
+       "  font-weight: 900;\n",
+       "  color: var(--xr-font-color2);\n",
+       "  letter-spacing: 0.15em;\n",
+       "  cursor: default;\n",
+       "}\n",
+       "\n",
        ".xr-array-wrap {\n",
        "  grid-column: 1 / -1;\n",
        "  display: grid;\n",
@@ -479,19 +589,19 @@
        "</style><pre class='xr-text-repr-fallback'><xarray.Dataset> Size: 72MB\n",
        "Dimensions:        (y: 1890, x: 1189)\n",
        "Coordinates:\n",
-       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    lon            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Dimensions without coordinates: y, x\n",
        "Data variables:\n",
        "    quality_flags  (y, x) uint32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    rtoa_8         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Attributes:\n",
        "    Conventions:   CF-1.4\n",
        "    product_type:  C2RCC_OLCI\n",
        "    start_date:    04-JUL-2018 09:21:55.677316\n",
-       "    stop_date:     04-JUL-2018 09:23:18.811790</pre><div class='xr-wrap' style='display:none'><div class='xr-header'><div class='xr-obj-type'>xarray.Dataset</div></div><ul class='xr-sections'><li class='xr-section-item'><input id='section-9d97c6b2-969f-4ad5-b3b6-26921f3b0af9' class='xr-section-summary-in' type='checkbox' disabled ><label for='section-9d97c6b2-969f-4ad5-b3b6-26921f3b0af9' class='xr-section-summary'  title='Expand/collapse section'>Dimensions:</label><div class='xr-section-inline-details'><ul class='xr-dim-list'><li><span>y</span>: 1890</li><li><span>x</span>: 1189</li></ul></div><div class='xr-section-details'></div></li><li class='xr-section-item'><input id='section-6dc015e2-4abe-42af-b979-873b2de0b39a' class='xr-section-summary-in' type='checkbox'  checked><label for='section-6dc015e2-4abe-42af-b979-873b2de0b39a' class='xr-section-summary' >Coordinates: <span>(2)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>lat</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-deddca9e-bf07-482d-ab99-f08cb1e2342c' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-deddca9e-bf07-482d-ab99-f08cb1e2342c' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-d71f1250-32d5-48e8-b9a9-e34f1f53b6fa' class='xr-var-data-in' type='checkbox'><label for='data-d71f1250-32d5-48e8-b9a9-e34f1f53b6fa' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>latitude coordinate</dd><dt><span>standard_name :</span></dt><dd>latitude</dd><dt><span>units :</span></dt><dd>degrees_north</dd></dl></div><div class='xr-var-data'><table>\n",
+       "    stop_date:     04-JUL-2018 09:23:18.811790</pre><div class='xr-wrap' style='display:none'><div class='xr-header'><div class='xr-obj-type'>xarray.Dataset</div></div><ul class='xr-sections'><li class='xr-section-item'><input id='section-062422a0-035f-4ce5-943c-5b4dbc028b5f' class='xr-section-summary-in' type='checkbox' disabled /><label for='section-062422a0-035f-4ce5-943c-5b4dbc028b5f' class='xr-section-summary'>Dimensions:</label><div class='xr-section-inline-details'><ul class='xr-dim-list'><li><span>y</span>: 1890</li><li><span>x</span>: 1189</li></ul></div></li><li class='xr-section-item'><input id='section-e3031f42-545c-4fbb-a655-37b9c3ef17a0' class='xr-section-summary-in' type='checkbox' checked /><label for='section-e3031f42-545c-4fbb-a655-37b9c3ef17a0' class='xr-section-summary' title='Expand/collapse section'>Coordinates: <span>(2)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>lon</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-35676472-5380-4e7b-9e2e-3a31525aa90e' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-35676472-5380-4e7b-9e2e-3a31525aa90e' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0f67f2a6-ab2d-40b9-a8b6-e1cd6f436b08' class='xr-var-data-in' type='checkbox'><label for='data-0f67f2a6-ab2d-40b9-a8b6-e1cd6f436b08' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>longitude coordinate</dd><dt><span>standard_name :</span></dt><dd>longitude</dd><dt><span>units :</span></dt><dd>degrees_east</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -546,12 +656,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>lon</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-d96693ec-542c-4e7d-a4a9-c2797ff03b5d' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-d96693ec-542c-4e7d-a4a9-c2797ff03b5d' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-4e1c7824-f0a8-49f5-aa27-d4cfaae78e67' class='xr-var-data-in' type='checkbox'><label for='data-4e1c7824-f0a8-49f5-aa27-d4cfaae78e67' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>longitude coordinate</dd><dt><span>standard_name :</span></dt><dd>longitude</dd><dt><span>units :</span></dt><dd>degrees_east</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>lat</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float64</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-29092c87-8faf-4f5f-871a-3f266fa3b4bd' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-29092c87-8faf-4f5f-871a-3f266fa3b4bd' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-0f54ca8a-f8bd-4a71-bb34-e547807f62fd' class='xr-var-data-in' type='checkbox'><label for='data-0f54ca8a-f8bd-4a71-bb34-e547807f62fd' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>long_name :</span></dt><dd>latitude coordinate</dd><dt><span>standard_name :</span></dt><dd>latitude</dd><dt><span>units :</span></dt><dd>degrees_north</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -606,12 +716,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-71e5c3c7-6911-4566-88c1-6adcb98ff152' class='xr-section-summary-in' type='checkbox'  checked><label for='section-71e5c3c7-6911-4566-88c1-6adcb98ff152' class='xr-section-summary' >Data variables: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>quality_flags</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>uint32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-6e582c1e-8520-4a49-85b2-63c1d24727b5' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-6e582c1e-8520-4a49-85b2-63c1d24727b5' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-ef89196d-1f87-4884-b181-ef2a3527615d' class='xr-var-data-in' type='checkbox'><label for='data-ef89196d-1f87-4884-b181-ef2a3527615d' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>flag_coding_name :</span></dt><dd>quality_flags</dd><dt><span>flag_descriptions :</span></dt><dd></dd><dt><span>flag_masks :</span></dt><dd>[-2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]</dd><dt><span>flag_meanings :</span></dt><dd>land coastline fresh_inland_water tidal_region bright straylight_risk invalid cosmetic duplicated sun_glint_risk dubious saturated_Oa01 saturated_Oa02 saturated_Oa03 saturated_Oa04 saturated_Oa05 saturated_Oa06 saturated_Oa07 saturated_Oa08 saturated_Oa09 saturated_Oa10 saturated_Oa11 saturated_Oa12 saturated_Oa13 saturated_Oa14 saturated_Oa15 saturated_Oa16 saturated_Oa17 saturated_Oa18 saturated_Oa19 saturated_Oa20 saturated_Oa21</dd><dt><span>long_name :</span></dt><dd>Classification and quality flags</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-0f4b7342-dc11-4071-9513-ecc6a913bdaa' class='xr-section-summary-in' type='checkbox' checked /><label for='section-0f4b7342-dc11-4071-9513-ecc6a913bdaa' class='xr-section-summary' title='Expand/collapse section'>Data variables: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'><li class='xr-var-item'><div class='xr-var-name'><span>quality_flags</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>uint32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-fcfc621f-417d-45fe-ba8c-87ef35a46fa0' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-fcfc621f-417d-45fe-ba8c-87ef35a46fa0' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-03a9c2f3-59b9-46fb-b2dd-118dcaf1f38d' class='xr-var-data-in' type='checkbox'><label for='data-03a9c2f3-59b9-46fb-b2dd-118dcaf1f38d' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>flag_coding_name :</span></dt><dd>quality_flags</dd><dt><span>flag_descriptions :</span></dt><dd></dd><dt><span>flag_masks :</span></dt><dd>[-2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]</dd><dt><span>flag_meanings :</span></dt><dd>land coastline fresh_inland_water tidal_region bright straylight_risk invalid cosmetic duplicated sun_glint_risk dubious saturated_Oa01 saturated_Oa02 saturated_Oa03 saturated_Oa04 saturated_Oa05 saturated_Oa06 saturated_Oa07 saturated_Oa08 saturated_Oa09 saturated_Oa10 saturated_Oa11 saturated_Oa12 saturated_Oa13 saturated_Oa14 saturated_Oa15 saturated_Oa16 saturated_Oa17 saturated_Oa18 saturated_Oa19 saturated_Oa20 saturated_Oa21</dd><dt><span>long_name :</span></dt><dd>Classification and quality flags</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -666,12 +776,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_3</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-1e2330d7-255c-4fb5-a607-0b30307ae071' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-1e2330d7-255c-4fb5-a607-0b30307ae071' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-2152fc0c-c484-45d6-8373-712029aede62' class='xr-var-data-in' type='checkbox'><label for='data-2152fc0c-c484-45d6-8373-712029aede62' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>2.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>442.5</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_8</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-99c3a43f-00b8-4308-81f5-32b9e117f3f3' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-99c3a43f-00b8-4308-81f5-32b9e117f3f3' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-18add4ff-560b-4ef9-9835-92fb7411a1da' class='xr-var-data-in' type='checkbox'><label for='data-18add4ff-560b-4ef9-9835-92fb7411a1da' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>7.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>665.0</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -726,12 +836,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_6</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-034898b4-a379-4180-9e4b-4dc873db74d2' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-034898b4-a379-4180-9e4b-4dc873db74d2' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-ec125ddd-5246-499d-b1e9-79b56f641602' class='xr-var-data-in' type='checkbox'><label for='data-ec125ddd-5246-499d-b1e9-79b56f641602' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>5.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>560.0</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_6</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-7dacbf9a-830c-4c84-9d91-e55921a2dcb4' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-7dacbf9a-830c-4c84-9d91-e55921a2dcb4' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-c4ba4b7f-03b6-4f0a-b1a6-5f3f9b4a3680' class='xr-var-data-in' type='checkbox'><label for='data-c4ba4b7f-03b6-4f0a-b1a6-5f3f9b4a3680' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>5.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>560.0</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -786,12 +896,12 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_8</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-a883ef5f-62e6-45ef-9987-e49b7ea06da2' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-a883ef5f-62e6-45ef-9987-e49b7ea06da2' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-e664eba8-20d0-4aec-919d-646c5041023b' class='xr-var-data-in' type='checkbox'><label for='data-e664eba8-20d0-4aec-919d-646c5041023b' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>7.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>665.0</dd></dl></div><div class='xr-var-data'><table>\n",
+       "</table></div></li><li class='xr-var-item'><div class='xr-var-name'><span>rtoa_3</span></div><div class='xr-var-dims'>(y, x)</div><div class='xr-var-dtype'>float32</div><div class='xr-var-preview xr-preview'>dask.array<chunksize=(512, 512), meta=np.ndarray></div><input id='attrs-7d8edf6d-ed30-40fa-97d1-5474cf97bb9a' class='xr-var-attrs-in' type='checkbox' ><label for='attrs-7d8edf6d-ed30-40fa-97d1-5474cf97bb9a' title='Show/Hide attributes'><svg class='icon xr-icon-file-text2'><use xlink:href='#icon-file-text2'></use></svg></label><input id='data-03399361-6593-4cb9-b7b2-0e106f3f3cac' class='xr-var-data-in' type='checkbox'><label for='data-03399361-6593-4cb9-b7b2-0e106f3f3cac' title='Show/Hide data repr'><svg class='icon xr-icon-database'><use xlink:href='#icon-database'></use></svg></label><div class='xr-var-attrs'><dl class='xr-attrs'><dt><span>bandwidth :</span></dt><dd>10.0</dd><dt><span>long_name :</span></dt><dd>Top-of-atmosphere reflectance</dd><dt><span>spectral_band_index :</span></dt><dd>2.0</dd><dt><span>units :</span></dt><dd>1</dd><dt><span>wavelength :</span></dt><dd>442.5</dd></dl></div><div class='xr-var-data'><table>\n",
        "    <tr>\n",
        "        <td>\n",
        "            <table style=\"border-collapse: collapse;\">\n",
@@ -846,25 +956,25 @@
        "  <polygon points=\"0.0,0.0 75.4920634920635,0.0 75.4920634920635,120.0 0.0,120.0\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"37.746032\" y=\"140.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"95.492063\" y=\"60.000000\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.492063,60.000000)\">1890</text>\n",
+       "  <text x=\"37.74603174603175\" y=\"140.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"95.4920634920635\" y=\"60.0\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,95.4920634920635,60.0)\">1890</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
-       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-10d64c3c-c0f8-4d57-8c3a-1814c98ddd2b' class='xr-section-summary-in' type='checkbox' disabled ><label for='section-10d64c3c-c0f8-4d57-8c3a-1814c98ddd2b' class='xr-section-summary'  title='Expand/collapse section'>Indexes: <span>(0)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><ul class='xr-var-list'></ul></div></li><li class='xr-section-item'><input id='section-2e4d819d-ead3-46d1-84db-e7355c379e2e' class='xr-section-summary-in' type='checkbox'  checked><label for='section-2e4d819d-ead3-46d1-84db-e7355c379e2e' class='xr-section-summary' >Attributes: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><dl class='xr-attrs'><dt><span>Conventions :</span></dt><dd>CF-1.4</dd><dt><span>product_type :</span></dt><dd>C2RCC_OLCI</dd><dt><span>start_date :</span></dt><dd>04-JUL-2018 09:21:55.677316</dd><dt><span>stop_date :</span></dt><dd>04-JUL-2018 09:23:18.811790</dd></dl></div></li></ul></div></div>"
+       "</table></div></li></ul></div></li><li class='xr-section-item'><input id='section-32fb75ff-1086-4dc8-a8b9-8d614295f245' class='xr-section-summary-in' type='checkbox' checked /><label for='section-32fb75ff-1086-4dc8-a8b9-8d614295f245' class='xr-section-summary' title='Expand/collapse section'>Attributes: <span>(4)</span></label><div class='xr-section-inline-details'></div><div class='xr-section-details'><dl class='xr-attrs'><dt><span>Conventions :</span></dt><dd>CF-1.4</dd><dt><span>product_type :</span></dt><dd>C2RCC_OLCI</dd><dt><span>start_date :</span></dt><dd>04-JUL-2018 09:21:55.677316</dd><dt><span>stop_date :</span></dt><dd>04-JUL-2018 09:23:18.811790</dd></dl></div></li></ul></div></div>"
       ],
       "text/plain": [
        "<xarray.Dataset> Size: 72MB\n",
        "Dimensions:        (y: 1890, x: 1189)\n",
        "Coordinates:\n",
-       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    lon            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    lat            (y, x) float64 18MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Dimensions without coordinates: y, x\n",
        "Data variables:\n",
        "    quality_flags  (y, x) uint32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
-       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "    rtoa_8         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_6         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
+       "    rtoa_3         (y, x) float32 9MB dask.array<chunksize=(512, 512), meta=np.ndarray>\n",
        "Attributes:\n",
        "    Conventions:   CF-1.4\n",
        "    product_type:  C2RCC_OLCI\n",
@@ -872,19 +982,21 @@
        "    stop_date:     04-JUL-2018 09:23:18.811790"
       ]
      },
-     "execution_count": 3,
+     "execution_count": 2,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "ds = xr.open_zarr(\"./inputdata/S3-OLCI-L2A.zarr.zip\", consolidated=False)\n",
+    "%%time\n",
+    "store = ZipStore(\"inputdata/S3-OLCI-L2A.zarr.zip\", mode=\"r\")\n",
+    "ds = xr.open_zarr(store, consolidated=False)\n",
     "ds"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 3,
    "metadata": {
     "tags": []
    },
@@ -905,7 +1017,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 4,
+     "execution_count": 3,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -917,7 +1029,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 4,
    "metadata": {
     "tags": []
    },
@@ -928,7 +1040,7 @@
        "True"
       ]
      },
-     "execution_count": 5,
+     "execution_count": 4,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -939,7 +1051,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 5,
    "metadata": {
     "tags": []
    },
@@ -950,7 +1062,7 @@
        "False"
       ]
      },
-     "execution_count": 6,
+     "execution_count": 5,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -961,7 +1073,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 6,
    "metadata": {
     "tags": []
    },
@@ -985,7 +1097,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 7,
+     "execution_count": 6,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -997,7 +1109,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 7,
    "metadata": {
     "tags": []
    },
@@ -1012,7 +1124,7 @@
        "- bounds: (12.0, 0.0, 18.0, 84.0)"
       ]
      },
-     "execution_count": 8,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1024,7 +1136,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 8,
    "metadata": {
     "tags": []
    },
@@ -1036,7 +1148,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 9,
    "metadata": {
     "tags": []
    },
@@ -1045,8 +1157,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 454 ms, sys: 10.1 ms, total: 464 ms\n",
-      "Wall time: 463 ms\n"
+      "CPU times: user 662 ms, sys: 15.4 ms, total: 677 ms\n",
+      "Wall time: 675 ms\n"
      ]
     }
    ],
@@ -1057,7 +1169,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 10,
    "metadata": {
     "tags": []
    },
@@ -1095,7 +1207,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1106,7 +1218,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -1194,9 +1306,9 @@
        "  <polygon points=\"24.9485979497544,14.948597949754403 100.4406614418179,14.948597949754403 100.4406614418179,134.9485979497544 24.9485979497544,134.9485979497544\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"62.694630\" y=\"154.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"120.440661\" y=\"74.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.440661,74.948598)\">1890</text>\n",
-       "  <text x=\"7.474299\" y=\"147.474299\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.474299,147.474299)\">2</text>\n",
+       "  <text x=\"62.69462969578615\" y=\"154.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"120.4406614418179\" y=\"74.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.4406614418179,74.9485979497544)\">1890</text>\n",
+       "  <text x=\"7.4742989748772\" y=\"147.4742989748772\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.4742989748772,147.4742989748772)\">2</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
@@ -1206,7 +1318,7 @@
        "dask.array<rechunk-merge, shape=(2, 1890, 1189), dtype=float64, chunksize=(2, 512, 512), chunktype=numpy.ndarray>"
       ]
      },
-     "execution_count": 12,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1218,7 +1330,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1230,7 +1342,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -1318,9 +1430,9 @@
        "  <polygon points=\"24.9485979497544,14.948597949754403 100.4406614418179,14.948597949754403 100.4406614418179,134.9485979497544 24.9485979497544,134.9485979497544\" style=\"fill:#ECB172A0;stroke-width:0\"/>\n",
        "\n",
        "  <!-- Text -->\n",
-       "  <text x=\"62.694630\" y=\"154.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
-       "  <text x=\"120.440661\" y=\"74.948598\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.440661,74.948598)\">1890</text>\n",
-       "  <text x=\"7.474299\" y=\"147.474299\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.474299,147.474299)\">2</text>\n",
+       "  <text x=\"62.69462969578615\" y=\"154.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" >1189</text>\n",
+       "  <text x=\"120.4406614418179\" y=\"74.9485979497544\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(-90,120.4406614418179,74.9485979497544)\">1890</text>\n",
+       "  <text x=\"7.4742989748772\" y=\"147.4742989748772\" font-size=\"1.0rem\" font-weight=\"100\" text-anchor=\"middle\" transform=\"rotate(45,7.4742989748772,147.4742989748772)\">2</text>\n",
        "</svg>\n",
        "        </td>\n",
        "    </tr>\n",
@@ -1330,7 +1442,7 @@
        "dask.array<transpose, shape=(2, 1890, 1189), dtype=float64, chunksize=(2, 512, 512), chunktype=numpy.ndarray>"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1344,15 +1456,15 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 955 ms, sys: 107 ms, total: 1.06 s\n",
-      "Wall time: 343 ms\n"
+      "CPU times: user 1.43 s, sys: 313 ms, total: 1.74 s\n",
+      "Wall time: 556 ms\n"
      ]
     }
    ],
@@ -1363,7 +1475,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -1399,7 +1511,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 16,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1410,7 +1522,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1421,7 +1533,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -1431,7 +1543,7 @@
        " dask.array<transpose, shape=(1890, 1189), dtype=float64, chunksize=(512, 512), chunktype=numpy.ndarray>)"
       ]
      },
-     "execution_count": 18,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1450,15 +1562,15 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "CPU times: user 1.69 s, sys: 64.8 ms, total: 1.76 s\n",
-      "Wall time: 570 ms\n"
+      "CPU times: user 2.27 s, sys: 204 ms, total: 2.48 s\n",
+      "Wall time: 824 ms\n"
      ]
     }
    ],
@@ -1470,7 +1582,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
@@ -1506,7 +1618,7 @@
        "       shape=(1890, 1189)))"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 19,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1517,7 +1629,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -1536,7 +1648,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 21,
+     "execution_count": 20,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1547,7 +1659,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -1566,7 +1678,7 @@
        "- Prime Meridian: Greenwich"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 21,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1592,7 +1704,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.13.5"
+   "version": "3.14.3"
   }
  },
  "nbformat": 4,


=====================================
examples/rectify_sentinel3.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
examples/resample_in_space.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
pyproject.toml
=====================================
@@ -23,7 +23,7 @@ classifiers = [
   "Development Status :: 5 - Production/Stable",
   "Intended Audience :: Science/Research",
   "Intended Audience :: Developers",
-  "License :: OSI Approved :: Apache Software License",
+  "License :: OSI Approved :: MIT License",
   "Programming Language :: Python :: 3",
   "Programming Language :: Python :: 3.10",
   "Programming Language :: Python :: 3.11",
@@ -48,7 +48,6 @@ dependencies = [
   "numpy>=1.16",
   "pyproj>=3.0",
   "xarray>=2024.7",
-  "zarr>=2.11,<3"
 ]
 
 [project.optional-dependencies]


=====================================
tests/gridmapping/test_cfconv.py
=====================================
@@ -26,7 +26,6 @@ import warnings
 import numpy as np
 import pyproj
 import xarray as xr
-import zarr
 
 # noinspection PyProtectedMember
 from xcube_resampling.gridmapping.cfconv import (
@@ -34,7 +33,6 @@ from xcube_resampling.gridmapping.cfconv import (
     GridMappingProxy,
     _find_potential_coord_vars,
     _is_potential_coord_var,
-    add_spatial_ref,
     get_dataset_grid_mapping_proxies,
 )
 
@@ -397,41 +395,3 @@ class XarrayDecodeCfTest(unittest.TestCase):
         self.assertEqual(("y", "x"), lon.dims)
         self.assertEqual(("y", "x"), lat.dims)
         return noise, crs, lon, lat
-
-
-class TestAddSpatialRef(unittest.TestCase):
-
-    def setUp(self):
-        # Create an in-memory Zarr group
-        self.store = zarr.MemoryStore()
-        self.group = zarr.group(store=self.store, overwrite=True)
-        # Create a dummy dataset array
-        self.group.zeros("data", shape=(3, 3), chunks=(3, 3), dtype=np.float32)
-        # Add _ARRAY_DIMENSIONS attribute to simulate xarray
-        self.group["data"].attrs["_ARRAY_DIMENSIONS"] = ["y", "x"]
-
-    def test_add_spatial_ref_creates_variable(self):
-        crs = pyproj.CRS.from_epsg(4326)
-        add_spatial_ref(self.store, crs, crs_var_name="spatial_ref_test")
-
-        # The spatial_ref_test variable should exist
-        self.assertIn("spatial_ref_test", self.group)
-        spatial_ref = self.group["spatial_ref_test"]
-        self.assertEqual(spatial_ref.shape, ())
-        self.assertTrue(spatial_ref.attrs)  # CRS attributes added
-        self.assertIn("_ARRAY_DIMENSIONS", spatial_ref.attrs)
-        self.assertEqual(spatial_ref.attrs["_ARRAY_DIMENSIONS"], [])
-
-    def test_add_grid_mapping_attribute(self):
-        crs = pyproj.CRS.from_epsg(4326)
-        add_spatial_ref(
-            self.store,
-            crs,
-            crs_var_name="spatial_ref_test",
-            xy_dim_names=("x", "y"),
-        )
-
-        # Original array should have grid_mapping pointing to spatial_ref_test
-        self.assertEqual(
-            self.group["data"].attrs.get("grid_mapping"), "spatial_ref_test"
-        )


=====================================
tests/gridmapping/test_dataset.py
=====================================
@@ -19,7 +19,6 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
-import os.path
 import unittest
 
 import numpy as np
@@ -80,31 +79,6 @@ class DatasetGridMappingTest(unittest.TestCase):
         self.assertTrue(result.is_regular)
         self.assertEqual(result.crs.to_string(), "EPSG:4326")
 
-    def test_from_real_olci(self):
-        olci_l2_path = os.path.join(
-            os.path.dirname(__file__),
-            "..",
-            "..",
-            "examples",
-            "inputdata",
-            "S3-OLCI-L2A.zarr.zip",
-        )
-
-        dataset = xr.open_zarr(olci_l2_path, consolidated=False)
-        gm = GridMapping.from_dataset(dataset)
-        self.assertEqual((1189, 1890), gm.size)
-        self.assertEqual((512, 512), gm.tile_size)
-        self.assertEqual(GEO_CRS, gm.crs)
-        self.assertEqual((0.00447132, 0.00255235), gm.xy_res)
-        self.assertEqual(False, gm.is_regular)
-        self.assertEqual(False, gm.is_lon_360)
-        self.assertEqual(False, gm.is_j_axis_up)
-        self.assertEqual((2, 1890, 1189), gm.xy_coords.shape)
-        self.assertEqual(("coord", "y", "x"), gm.xy_coords.dims)
-
-        gm = gm.to_regular()
-        self.assertEqual((1636, 2132), gm.size)
-
     def test_from_sentinel_2(self):
         dataset = create_s2plus_dataset()
         tol = 1e-6


=====================================
tests/test_reproject.py
=====================================
@@ -252,9 +252,9 @@ class ReprojectDatasetTest(unittest.TestCase):
 
         target_ds = reproject_dataset(source_ds, target_gm, interp_methods="triangular")
         self.assertCountEqual(["temperature", "onedim_data"], list(target_ds.data_vars))
-        self.assertAlmostEqual(target_ds.temperature.values[0, 0, 0], 6216.15, places=2)
+        self.assertAlmostEqual(target_ds.temperature.values[0, 0, 0], 6216.14, places=2)
         self.assertAlmostEqual(
-            target_ds.temperature.values[0, -1, -1], 2848.51, places=2
+            target_ds.temperature.values[0, -1, -1], 2848.50, places=2
         )
         self.assertEqual(
             [2, 5, 5],
@@ -267,9 +267,9 @@ class ReprojectDatasetTest(unittest.TestCase):
 
         target_ds = reproject_dataset(source_ds, target_gm, interp_methods=1)
         self.assertCountEqual(["temperature", "onedim_data"], list(target_ds.data_vars))
-        self.assertAlmostEqual(target_ds.temperature.values[0, 0, 0], 6216.15, places=2)
+        self.assertAlmostEqual(target_ds.temperature.values[0, 0, 0], 6216.14, places=2)
         self.assertAlmostEqual(
-            target_ds.temperature.values[0, -1, -1], 2848.51, places=2
+            target_ds.temperature.values[0, -1, -1], 2848.50, places=2
         )
         self.assertEqual(
             [2, 5, 5],


=====================================
tests/test_utils.py
=====================================
@@ -26,6 +26,7 @@ from xcube_resampling.utils import (
     bbox_overlap,
     clip_dataset_by_bbox,
     get_spatial_coords,
+    get_utm_crs,
     reproject_bbox,
     resolution_meters_to_degrees,
 )
@@ -60,6 +61,22 @@ class TestUtils(unittest.TestCase):
             get_spatial_coords(ds)
         self.assertIn("No standard spatial coordinates found", str(context.exception))
 
+    def test_get_utm_crs(self):
+        crs = get_utm_crs(10, 55.5)
+        self.assertEqual("EPSG:32632", crs.to_string())
+        crs = get_utm_crs(5, 60)
+        self.assertEqual("EPSG:32632", crs.to_string())
+        crs = get_utm_crs(2.9, 60)
+        self.assertEqual("EPSG:32631", crs.to_string())
+        crs = get_utm_crs(8, 75)
+        self.assertEqual("EPSG:32631", crs.to_string())
+        crs = get_utm_crs(20, 75)
+        self.assertEqual("EPSG:32633", crs.to_string())
+        crs = get_utm_crs(22, 75)
+        self.assertEqual("EPSG:32635", crs.to_string())
+        crs = get_utm_crs(40, 75)
+        self.assertEqual("EPSG:32637", crs.to_string())
+
     def test_select_variables(self):
         ds = xr.Dataset(
             {


=====================================
xcube_resampling/affine.py
=====================================
@@ -22,6 +22,7 @@
 import math
 from collections.abc import Iterable, Sequence
 
+import dask
 import dask.array as da
 import numpy as np
 import xarray as xr
@@ -203,6 +204,16 @@ def resample_dataset(
         data_array = xr.DataArray(data_array)
         new_data_array = None
         if data_array.dims[-2:] == yx_dims:
+            assert len(data_array.dims) in (
+                2,
+                3,
+            ), f"Data variable {var_name} has {len(data_array.dims)} dimensions."
+
+            data_array_expanded = False
+            if len(data_array.dims) == 2:
+                data_array = data_array.expand_dims({"dummy": 1})
+                data_array_expanded = True
+
             if isinstance(data_array.data, np.ndarray):
                 is_numpy_array = True
                 array = da.asarray(data_array.data)
@@ -229,8 +240,13 @@ def resample_dataset(
             )
             if is_numpy_array:
                 resampled_array = resampled_array.compute()
+            if data_array_expanded:
+                resampled_array = resampled_array[0, :, :]
+                dims = yx_dims
+            else:
+                dims = data_array.dims
             new_data_array = xr.DataArray(
-                data=resampled_array, dims=data_array.dims, attrs=data_array.attrs
+                data=resampled_array, dims=dims, attrs=data_array.attrs
             )
         elif yx_dims[0] not in data_array.dims and yx_dims[1] not in data_array.dims:
             new_data_array = data_array
@@ -326,9 +342,8 @@ def _upscale(
     fill_value: FloatInt,
 ) -> da.Array:
     (i_scale, _, i_off), (_, j_scale, j_off) = affine_matrix
-    offset = (array.ndim - 2) * (0,) + (j_off, i_off)
-    scale = (array.ndim - 2) * (1,) + (j_scale, i_scale)
-    matrix = np.diag(scale)
+    offset = (j_off, i_off)
+    matrix = np.diag((j_scale, i_scale))
     if interp_method > 1:
         raise ValueError(
             "interp_methods must be one of 0, 1, 'nearest', 'bilinear'. "
@@ -339,18 +354,18 @@ def _upscale(
     kwargs = dict(
         offset=offset,
         order=interp_method,
-        output_shape=output_shape,
-        output_chunks=output_chunks,
+        output_shape=output_shape[-2:],
+        output_chunks=output_chunks[-2:],
         mode="constant",
         cval=fill_value,
     )
-    if prevent_nan_propagation and interp_method > 0:
-        # Prevent NaNs from spreading to nearby pixels during interpolation
-        mask = da.isnan(array)
-        # First check if there are NaN values ar all
-        if da.any(mask):
+
+    def _transform_slice(slice_2d: da.Array):
+        if prevent_nan_propagation and interp_method > 0:
+            # Prevent NaNs from spreading to nearby pixels during interpolation
+            mask = da.isnan(slice_2d)
             # 1. replace NaN by zero
-            filled_im = da.where(mask, 0.0, array)
+            filled_im = da.where(mask, 0.0, slice_2d)
             # 2. transform the zero-filled image
             scaled_im = ndinterp.affine_transform(filled_im, matrix, **kwargs)
             # 3. transform the inverted mask
@@ -360,5 +375,7 @@ def _upscale(
             return da.where(
                 da.isclose(scaled_norm, 0.0), np.nan, scaled_im / scaled_norm
             )
+        else:
+            return ndinterp.affine_transform(slice_2d, matrix, **kwargs)
 
-    return ndinterp.affine_transform(array, matrix, **kwargs)
+    return da.stack([_transform_slice(array[i]) for i in range(array.shape[0])])


=====================================
xcube_resampling/gridmapping/base.py
=====================================
@@ -62,17 +62,27 @@ class GridMapping(abc.ABC):
     This class cannot be instantiated directly. Use one of its factory methods
     to create instances:
 
-    * `regular`
-    * `regular_from_bbox`
-    * `from_dataset`
-    * `from_coords`
+    - `regular`
+    - `regular_from_bbox`
+    - `from_dataset`
+    - `from_coords`
 
     Some instance methods can be used to derive new instances:
 
-    * `derive`
-    * `scale`
-    * `transform`
-    * `to_regular`
+    - `derive`
+    - `scale`
+    - `transform`
+    - `to_regular`
+
+    Notes:
+
+    - `x` : eastward axis in CRS coordinates
+    - `y` : northward axis in CRS coordinates
+    - `i` : eastward axis in pixel space
+    - `j` : northward axis in pixel space
+
+    References:
+        [CF Conventions — Coordinate types](https://cfconventions.org/Data/cf-conventions/cf-conventions-1.13/cf-conventions.html#coordinate-types)
     """
 
     def __init__(
@@ -277,8 +287,14 @@ class GridMapping(abc.ABC):
 
     @property
     def x_coords(self):
-        """The 1D or 2D x-coordinate array of
-        shape (width,) or (height, width).
+        """Return the eastward (x) coordinate values.
+
+        The coordinates are provided as either a 1D or 2D array:
+        - Shape (width,) for rectilinear grids
+        - Shape (height, width) for curvilinear grids
+
+        See Also:
+            [CF Conventions, Longitude Coordinate](https://cfconventions.org/Data/cf-conventions/cf-conventions-1.13/cf-conventions.html#longitude-coordinate)
         """
         return self._get_computed_attribute("_x_coords", self._new_x_coords)
 
@@ -290,8 +306,14 @@ class GridMapping(abc.ABC):
 
     @property
     def y_coords(self):
-        """The 1D or 2D y-coordinate array of
-        shape (width,) or (height, width).
+        """Return the northward (y) coordinate values.
+
+        The coordinates are provided as either a 1D or 2D array:
+        - Shape (width,) for rectilinear grids
+        - Shape (height, width) for curvilinear grids
+
+        See Also:
+            [CF Conventions, Latitude Coordinate](https://cfconventions.org/Data/cf-conventions/cf-conventions-1.13/cf-conventions.html#latitude-coordinate)
         """
         return self._get_computed_attribute("_y_coords", self._new_y_coords)
 
@@ -303,8 +325,12 @@ class GridMapping(abc.ABC):
 
     @property
     def xy_coords(self) -> xr.DataArray:
-        """The x,y coordinates as data array of shape (2, height, width).
-        Coordinates are given in units of the CRS.
+        """Return the spatial coordinates as a single DataArray.
+
+        The coordinates are provided as a 3D array of shape `(2, height, width)`,
+        where the first axis corresponds to the x and y coordinates stacked in order:
+        `(x_coords, y_coords)`. All values are expressed in the units of the
+        dataset's CRS (coordinate reference system).
         """
         xy_coords = self._get_computed_attribute("_xy_coords", self._new_xy_coords)
         _assert_valid_xy_coords(xy_coords)
@@ -312,7 +338,7 @@ class GridMapping(abc.ABC):
 
     @property
     def xy_coords_chunks(self) -> tuple[int, int, int]:
-        """Get the chunks for the *xy_coords* array."""
+        """Return the chunks for the `xy_coords` array."""
         return 2, self.tile_height, self.tile_width
 
     @abc.abstractmethod
@@ -337,15 +363,15 @@ class GridMapping(abc.ABC):
 
     @property
     def xy_var_names(self) -> tuple[str, str]:
-        """The variable names of the x,y coordinates as
-        tuple (x_var_name, y_var_name).
+        """The variable names of `x_coords` and `y_coords` as tuple
+        `(x_var_name, y_var_name)`.
         """
         return self._xy_var_names
 
     @property
     def xy_dim_names(self) -> tuple[str, str]:
-        """The dimension names of the x,y coordinates as
-        tuple (x_dim_name, y_dim_name).
+        """The dimension names of `x_coords` and `y_coords` as
+        tuple `(x_dim_name, y_dim_name)`.
         """
         return self._xy_dim_names
 
@@ -356,37 +382,37 @@ class GridMapping(abc.ABC):
 
     @property
     def x_min(self) -> FloatInt:
-        """Minimum x-coordinate in CRS units."""
+        """Minimum eastward (x) coordinate value in CRS units."""
         return round(self._xy_bbox[0] + self.x_res / 2, ndigits=7)
 
     @property
     def y_min(self) -> FloatInt:
-        """Minimum y-coordinate in CRS units."""
+        """Minimum northward (y) coordinate value in CRS units."""
         return round(self._xy_bbox[1] + self.y_res / 2, ndigits=7)
 
     @property
     def x_max(self) -> FloatInt:
-        """Maximum x-coordinate in CRS units."""
+        """Maximum eastward (x) coordinate value in CRS units."""
         return round(self._xy_bbox[2] - self.x_res / 2, ndigits=7)
 
     @property
     def y_max(self) -> FloatInt:
-        """Maximum y-coordinate in CRS units."""
+        """Maximum northward (y) coordinate value in CRS units."""
         return round(self._xy_bbox[3] - self.y_res / 2, ndigits=7)
 
     @property
     def xy_res(self) -> tuple[FloatInt, FloatInt]:
-        """Pixel size in x and y direction."""
+        """Pixel size in eastward (x) and northward (y) direction `(x_res, y_res)`."""
         return self._xy_res
 
     @property
     def x_res(self) -> FloatInt:
-        """Pixel size in CRS units per pixel in x-direction."""
+        """Pixel size in CRS units per pixel in eastward (x) direction."""
         return self._xy_res[0]
 
     @property
     def y_res(self) -> FloatInt:
-        """Pixel size in CRS units per pixel in y-direction."""
+        """Pixel size in CRS units per pixel in northward (y) direction."""
         return self._xy_res[1]
 
     @property
@@ -400,8 +426,8 @@ class GridMapping(abc.ABC):
 
     @property
     def is_lon_360(self) -> bool | None:
-        """Check whether *x_max* is greater than 180 degrees.
-        Effectively tests whether the range *x_min*, *x_max* crosses
+        """Check whether `x_max` is greater than 180 degrees.
+        Effectively tests whether the range `x_min`, `x_max` crosses
         the anti-meridian at 180 degrees.
         Works only for geographical coordinate reference systems.
         """
@@ -409,29 +435,30 @@ class GridMapping(abc.ABC):
 
     @property
     def is_regular(self) -> bool | None:
-        """Do the x,y coordinates for a regular grid?
-        A regular grid has a constant delta in both
-        x- and y-directions of the x- and y-coordinates.
+        """Check whether the x (eastward) and y (northward) coordinates form a regular
+        grid.
 
-        Returns: None, if this property cannot be determined,
-            True or False otherwise.
+        A regular grid is defined as having constant spacing (delta) along
+        the x- and y-axes individually.
         """
+
         return self._is_regular
 
     @property
     def is_j_axis_up(self) -> bool | None:
-        """Does the positive image j-axis point up?
-        By default, the positive image j-axis points down.
+        """Indicate whether the positive image j-axis points upward.
 
-        Returns: None, if this property cannot be determined,
-            True or False otherwise.
+        Here, the j-axis refers to the y-coordinate in pixel space. By convention,
+        the positive j-axis usually points downward in image coordinates, so
+        `True` indicates that the axis has been flipped to point upward.
         """
         return self._is_j_axis_up
 
     @property
     def ij_to_xy_transform(self) -> AffineTransformMatrix:
         """The affine transformation matrix from image to CRS coordinates.
-        Defined only for grid mappings with rectified x,y coordinates.
+        Defined only for grid mappings with regular x (eastward) and y (northward)
+        coordinates.
         """
         self._assert_regular()
         if self.is_j_axis_up:
@@ -448,20 +475,22 @@ class GridMapping(abc.ABC):
     @property
     def xy_to_ij_transform(self) -> AffineTransformMatrix:
         """The affine transformation matrix from CRS to image coordinates.
-        Defined only for grid mappings with rectified x,y coordinates.
+        Defined only for grid mappings with regular x (eastward) and y (northward)
+        coordinates.
         """
         self._assert_regular()
         return _from_affine(~_to_affine(self.ij_to_xy_transform))
 
     def ij_transform_to(self, other: "GridMapping") -> AffineTransformMatrix:
         """Get the affine transformation matrix that transforms
-        image coordinates of *other* into image coordinates
+        image coordinates of `other` grid-mapping into image coordinates
         of this image geometry.
 
-        Defined only for grid mappings with rectified x,y coordinates.
+        Defined only for grid mappings with regular x (eastward) and y (northward)
+        coordinates.
 
         Args:
-            other: The other image geometry
+            other: The other grid-mapping
 
         Returns:
             Affine transformation matrix
@@ -474,13 +503,14 @@ class GridMapping(abc.ABC):
 
     def ij_transform_from(self, other: "GridMapping") -> AffineTransformMatrix:
         """Get the affine transformation matrix that transforms
-        image coordinates of this image geometry to image
-        coordinates of *other*.
+        image coordinates of this grid-mapping to image
+        coordinates of *other* grid-mapping.
 
-        Defined only for grid mappings with rectified x,y coordinates.
+        Defined only for grid mappings with regular x (eastward) and y (northward)
+        coordinates.
 
         Args:
-            other: The other image geometry
+            other: The other grid-mapping
 
         Returns:
             Affine transformation matrix


=====================================
xcube_resampling/gridmapping/cfconv.py
=====================================
@@ -20,16 +20,12 @@
 # DEALINGS IN THE SOFTWARE.
 
 import warnings
-from collections.abc import Hashable, MutableMapping
+from collections.abc import Hashable
 from typing import Any
 
-import numpy as np
 import pyproj
 import xarray as xr
-import zarr
-import zarr.convenience
 
-from .assertions import assert_instance
 from .base import CRS_WGS84
 from .helpers import get_dataset_chunks
 
@@ -315,44 +311,3 @@ def _find_dataset_tile_size(
     if tile_width is not None and tile_height is not None:
         return tile_width, tile_height
     return None
-
-
-def add_spatial_ref(
-    dataset_store: zarr.convenience.StoreLike,
-    crs: pyproj.CRS,
-    crs_var_name: str = "spatial_ref",
-    xy_dim_names: tuple[str, str] | None = None,
-):
-    """Helper function that allows adding a spatial reference to an
-    existing Zarr dataset.
-
-    Args:
-        dataset_store: The dataset's existing Zarr store or path.
-        crs: The spatial coordinate reference system.
-        crs_var_name: The name of the variable that will hold the
-            spatial reference. Defaults to "spatial_ref".
-        xy_dim_names: The names of the x and y dimensions. Defaults to
-            ("x", "y").
-    """
-    assert_instance(dataset_store, (MutableMapping, str), name="group_store")
-    assert_instance(crs_var_name, str, name="crs_var_name")
-    x_dim_name, y_dim_name = xy_dim_names or ("x", "y")
-
-    spatial_attrs = crs.to_cf()
-    spatial_attrs["_ARRAY_DIMENSIONS"] = []  # Required by xarray
-    group = zarr.open(dataset_store, mode="r+")
-    spatial_ref = group.array(crs_var_name, 0, shape=(), dtype=np.uint8, fill_value=0)
-    spatial_ref.attrs.update(**spatial_attrs)
-
-    for item_name, item in group.items():
-        if item_name != crs_var_name:
-            dims = item.attrs.get("_ARRAY_DIMENSIONS")
-            if (
-                dims
-                and len(dims) >= 2
-                and dims[-2] == y_dim_name
-                and dims[-1] == x_dim_name
-            ):
-                item.attrs["grid_mapping"] = crs_var_name
-
-    zarr.convenience.consolidate_metadata(dataset_store)


=====================================
xcube_resampling/reproject.py
=====================================
@@ -41,6 +41,7 @@ from .gridmapping import GridMapping
 from .gridmapping.helpers import from_lon_360
 from .utils import (
     SourceTileIndexing,
+    _clip_if_needed,
     _get_fill_value,
     _get_spatial_interp_method_str,
     _get_spatial_interp_methods,
@@ -127,6 +128,11 @@ def reproject_dataset(
     transformer = pyproj.Transformer.from_crs(
         target_gm.crs, source_gm.crs, always_xy=True
     )
+    source_ds, source_gm, is_empty = _clip_if_needed(
+        source_ds, source_gm, target_gm, fill_values, transformer
+    )
+    if is_empty:
+        return source_ds
 
     # If source has higher resolution than target, downscale first, then reproject
     interp_methods = _get_spatial_interp_methods(source_ds, interp_methods)
@@ -233,7 +239,7 @@ def _downscale_source_dataset(
     interp_methods: Mapping[Hashable, SpatialInterpMethod],
     agg_methods: SpatialAggMethods | None,
     prevent_nan_propagations: PreventNaNPropagations,
-) -> (xr.Dataset, GridMapping):
+) -> tuple[xr.Dataset, GridMapping]:
     if all(v in (0, "nearest") for v in interp_methods.values()):
         return source_ds, source_gm
 
@@ -362,7 +368,7 @@ def _get_src_bboxes_indices(
     transformer: pyproj.Transformer,
     source_gm: GridMapping,
     target_gm: GridMapping,
-) -> (SourceTileIndexing, da.Array, da.Array):
+) -> tuple[SourceTileIndexing, da.Array, da.Array]:
     num_tiles_x = math.ceil(target_gm.width / target_gm.tile_width)
     num_tiles_y = math.ceil(target_gm.height / target_gm.tile_height)
 


=====================================
xcube_resampling/utils.py
=====================================
@@ -52,7 +52,7 @@ from .gridmapping import GridMapping
 from .gridmapping.helpers import _normalize_crs
 
 
-def get_spatial_coords(ds: xr.Dataset) -> (str, str):
+def get_spatial_coords(ds: xr.Dataset) -> tuple[str, str]:
     """
     Identify the names of horizontal spatial coordinate in an xarray dataset.
 
@@ -86,6 +86,39 @@ def get_spatial_coords(ds: xr.Dataset) -> (str, str):
     return x_coord, y_coord
 
 
+def get_utm_crs(lon: float, lat: float) -> pyproj.CRS:
+    """
+    Returns the UTM CRS for a given latitude and longitude as a pyproj.CRS object.
+
+    Args:
+        lon: Longitude in decimal degrees.
+        lat: Latitude in decimal degrees.
+
+    Returns:
+        CRS object corresponding to the correct UTM zone.
+    """
+    # Standard UTM zone calculation
+    zone_number = int((lon + 180) // 6) + 1
+
+    # Norway exception (Western Norway and Svalbard)
+    if 56.0 <= lat <= 64.0 and 3.0 <= lon <= 12.0:
+        zone_number = 32  # Western Norway
+    if 72.0 <= lat <= 84.0:
+        # Svalbard zones (special UTM divisions)
+        if 0 <= lon < 9:
+            zone_number = 31
+        elif 9 <= lon < 21:
+            zone_number = 33
+        elif 21 <= lon < 33:
+            zone_number = 35
+        elif 33 <= lon < 42:
+            zone_number = 37
+
+    # Northern or Southern hemisphere
+    epsg = 32600 + zone_number if lat >= 0 else 32700 + zone_number
+    return pyproj.CRS.from_epsg(epsg)
+
+
 def clip_dataset_by_bbox(
     ds: xr.Dataset,
     bbox: Sequence[FloatInt],


=====================================
xcube_resampling/version.py
=====================================
@@ -19,4 +19,4 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
-__version__ = "0.3.1"
+__version__ = "0.3.2"



View it on GitLab: https://salsa.debian.org/debian-gis-team/xcube-resampling/-/compare/12b4ac307d149c914203ff1aa530a2ed467caafa...976a5a17895136032501e8a57aa3078907894748

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/xcube-resampling/-/compare/12b4ac307d149c914203ff1aa530a2ed467caafa...976a5a17895136032501e8a57aa3078907894748
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260322/e68a2d19/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list