Edit

STAC Item with GeoZarr asset

stac14 stacitem8 geozarr1 zarr1 eopf1

Rendering a STAC Item with Web-Optimized GeoZarr assets

Visualize Web-Optimized GeoZarr assets from SpatioTemporal Asset Catalog (STAC) Items.

main.js
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import {register} from 'ol/proj/proj4.js';
import OSM from 'ol/source/OSM.js';
import proj4 from 'proj4';
import STAC from 'ol-stac';

register(proj4); // required to support source reprojection

const background = new TileLayer({
  source: new OSM(),
});

const map = new Map({
  target: 'map',
  layers: [background],
  view: new View({
    center: [0, 0],
    zoom: 0,
  }),
});

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

let layer;
updateLayer();

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

function getStyle() {
  return {
    color: [
      'color',
      [
        'interpolate',
        ['linear'],
        ['band', 1],
        parseFloat(min.value),
        0,
        parseFloat(max.value),
        255,
      ],
      [
        'interpolate',
        ['linear'],
        ['band', 2],
        parseFloat(min.value),
        0,
        parseFloat(max.value),
        255,
      ],
      [
        'interpolate',
        ['linear'],
        ['band', 3],
        parseFloat(min.value),
        0,
        parseFloat(max.value),
        255,
      ],
    ],
  };
}

function updateLayer() {
  if (layer) {
    map.removeLayer(layer);
  }
  layer = new STAC({
    url: getUrl(),
    style: getStyle(),
  });
  layer.on('sourceready', () => {
    const view = map.getView();
    view.fit(layer.getExtent());
  });
  layer.on('error', (event) => {
    alert('An unexpected error occurred: ' + event.error.message);
  });
  map.addLayer(layer);
  return layer;
}

button.addEventListener('click', updateLayer);

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

min.addEventListener('change', () => {
  layer.setStyle(getStyle());
});
max.addEventListener('change', () => {
  layer.setStyle(getStyle());
});
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>STAC Item with GeoZarr asset</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">Min / Max values for visualization (0-1)</label>
          <div class="form-control">
            <input type="range" id="min" name="min" min="0" max="1" value="0" step="0.1" />
            <input type="range" id="max" name="max" min="0" max="1" value="0.5" step="0.1" />
          </div>
        </div>
      </div>
    </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://api.explorer.eopf.copernicus.eu/stac/collections/sentinel-2-l2a/items/S2C_MSIL2A_20260414T114351_N0512_R123_T30VVK_20260414T164110" selected>Sentinel-2 L2A (Highlands)</option>
            <option value="https://api.explorer.eopf.copernicus.eu/stac/collections/sentinel-2-l2a/items/S2C_MSIL2A_20260120T084301_N0511_R064_T36UXA_20260120T122910">Sentinel-2 L2A (Krasnokuts'k)</option>
            <option value="https://api.explorer.eopf.copernicus.eu/stac/collections/sentinel-2-l2a/items/S2B_MSIL2A_20260120T125339_N0511_R138_T27VWL_20260120T131151">Sentinel-2 L2A (Hvolsvöllur)</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": "stac-item-zarr",
  "dependencies": {
    "ol-stac": "1.4.0",
    "proj4": "^2.16.1"
  },
  "devDependencies": {
    "vite": "^3.2.3"
  },
  "scripts": {
    "start": "vite",
    "build": "vite build"
  }
}