Edit

GeoZarr

zarr3 geozarr3 sentinel-22

A GeoZarr source displaying Sentinel-2 imagery.

The GeoZarr source can be used to read groups of multi-band data from Zarr files following the GeoZarr specification. The Venice scene uses the first version of the spec, which contains an OGC TileMatrixSet definition. The Crete scene uses the latest version of the spec and follows the Multiscales convention.

To try different scenes, browse to the Sentinel-2 Level-2A catalog of the EOPF Explorer, find a Zarr store, and paste the url after selecting "Custom..." from the dropdown above.

In this example, a true-color composite is rendered from Sentinel-2 L2A bands B02 (blue), B03 (green), and B04 (red) reflectance values. Reflectance values from 0 to 0.5 are stretched to values between 0 and 255 for the RGB color channels; and values that are all zero are made transparent (with 0 in the alpha channel). A gamma correction is also made to brighten the image.

main.js
import Map from 'ol/Map.js';
import {
  getView,
  withExtentCenter,
  withHigherResolutions,
  withLowerResolutions,
  withZoom,
} from 'ol/View.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import GeoZarr from 'ol/source/GeoZarr.js';
import OSM from 'ol/source/OSM.js';

const select = document.getElementById('url-select');
const input = document.getElementById('custom-url');
const button = document.getElementById('load-url');

function getUrl() {
  return select.value === 'custom' ? input.value : select.value;
}

select.addEventListener('change', () => {
  if (select.value === 'custom') {
    input.style.display = '';
    input.focus();
  } else {
    input.style.display = 'none';
  }
});

let map;

function render() {
  const url = getUrl();
  if (!url) {
    return;
  }

  const source = new GeoZarr({
    url: url,
    group: 'measurements/reflectance',
    bands: ['b04', 'b03', 'b02'],
  });

  if (map) {
    map.setTarget(null);
  }

  map = new Map({
    layers: [
      new TileLayer({
        source: new OSM(),
      }),
      new TileLayer({
        style: {
          gamma: 1.5,
          color: [
            'color',
            ['interpolate', ['linear'], ['band', 1], 0, 0, 0.5, 255],
            ['interpolate', ['linear'], ['band', 2], 0, 0, 0.5, 255],
            ['interpolate', ['linear'], ['band', 3], 0, 0, 0.5, 255],
            [
              'case',
              ['==', ['+', ['band', 1], ['band', 2], ['band', 3]], 0],
              0,
              1,
            ],
          ],
        },
        source,
      }),
    ],
    target: 'map',
    view: getView(
      source,
      withLowerResolutions(1),
      withHigherResolutions(2),
      withExtentCenter(),
      withZoom(2),
    ),
  });
}

button.addEventListener('click', render);

render();
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>GeoZarr</title>
    <link rel="stylesheet" href="node_modules/ol/ol.css">
    <style>
      .map {
        width: 100%;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <div class="row mt-2">
      <div class="col-auto">
        <div class="input-group">
          <label class="input-group-text" for="url-select">URL</label>
          <select class="form-select" id="url-select">
            <option value="https://s3.explorer.eopf.copernicus.eu/esa-zarr-sentinel-explorer-fra/tests-output/sentinel-2-l2a/S2A_MSIL2A_20251107T100231_N0511_R122_T32TQR_20251107T115310.zarr">Sentinel-2 L2A (Venice)</option>
            <option value="https://s3.explorer.eopf.copernicus.eu/esa-zarr-sentinel-explorer-fra/tests-output/sentinel-2-l2a-staging/S2B_MSIL2A_20251115T091139_N0511_R050_T35SLU_20251115T111807.zarr">Sentinel-2 L2A (Crete)</option>
            <option value="custom">Custom...</option>
          </select>
          <input type="text" class="form-control" id="custom-url" placeholder="Enter Zarr URL" style="display: none; width: 300px;">
          <button class="btn btn-outline-secondary" id="load-url" type="button">Load</button>
        </div>
      </div>
    </div>

    <script type="module" src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "geozarr",
  "dependencies": {
    "ol": "10.7.1-dev"
  },
  "devDependencies": {
    "vite": "^3.2.3"
  },
  "scripts": {
    "start": "vite",
    "build": "vite build"
  }
}