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. GeoZarrs following the Multiscales convention are supported. Legacy multiscales GeoZarrs with an OGC TileMatrixSet definition also work.
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. A gamma correction is also made to brighten the image.
To try different scenes, browse to the Sentinel-2 Level-2A catalog of the EOPF Explorer, find a Zarr store, and paste the url including the group path (e.g. store.zarr/measurements/reflectance) after selecting "Custom..." from the dropdown above. Note that only GeoZarrs with bands 'b02', 'b03', and 'b04' will work without changes to the source code.
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,
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],
],
},
source,
}),
],
target: 'map',
view: getView(
source,
withLowerResolutions(1),
withHigherResolutions(2),
withExtentCenter(),
withZoom(2),
),
});
}
button.addEventListener('click', render);
render();
<!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/S2C_MSIL2A_20260120T084301_N0511_R064_T36UXA_20260120T122910.zarr/measurements/reflectance">Sentinel-2 L2A (Krasnokuts'k)</option>
<option value="https://s3.explorer.eopf.copernicus.eu/esa-zarr-sentinel-explorer-fra/tests-output/sentinel-2-l2a/S2B_MSIL2A_20260120T125339_N0511_R138_T27VWL_20260120T131151.zarr/measurements/reflectance">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>
{
"name": "geozarr",
"dependencies": {
"ol": "10.9.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}