Skip to content

Add NIMBO to an OpenLayers web map

The preview deliberately keeps a reference map beneath NIMBO so that administrative context, roads and place names remain visible. Use the control in the preview to switch between NIMBO + reference map and NIMBO only.

The preview uses the public July 2023 NIMBO sandbox. It adds an OpenStreetMap reference layer only to preserve geographic context; that reference layer is not part of NIMBO.

Already have an OpenLayers map? This is the essential NIMBO configuration, including the optional reference layer shown in the preview:

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';
import XYZ from 'ol/source/XYZ.js';
import { fromLonLat } from 'ol/proj.js';
const NIMBO_DEMO_TMS =
'https://prod-data.nimbo.earth/mapcache/tms/1.0.0/demo_2023_7_1@kermap/{z}/{x}/{-y}.png?kermap_token=750bf73bb0f6cb9639286ea471b05e335dd4595ce1';
const referenceLayer = new TileLayer({
source: new OSM(),
});
const nimboLayer = new TileLayer({
opacity: 0.82,
source: new XYZ({
url: NIMBO_DEMO_TMS,
tileSize: 256,
attributions:
'NIMBO by KERMAP — Contains modified Copernicus Sentinel data 2023.',
}),
});
const map = new Map({
target: 'map',
layers: [referenceLayer, nimboLayer],
view: new View({
center: fromLonLat([2.35, 46.5]),
zoom: 5,
}),
});

Set opacity: 1 and remove referenceLayer when you want to display NIMBO without an underlying reference map. In a production GIS application, a better pattern is often to keep NIMBO fully opaque and add only the required vector boundaries, labels or operational layers above it.

The important OpenLayers-specific detail is {-y} in the URL, which converts the XYZ row into the TMS row expected by NIMBO.

You need:

  • Node.js and npm;
  • a modern web browser;
  • a text editor;
  • approximately ten minutes for the first test.

The project setup below follows the standard OpenLayers application scaffold.

  1. Generate a new application

    Run:

    Terminal window
    npm create ol-app nimbo-openlayers
    cd nimbo-openlayers

    The scaffold creates index.html, main.js, style.css, the OpenLayers dependency and a local development server.

  2. Replace main.js

    Use the following complete code:

    import 'ol/ol.css';
    import './style.css';
    import Map from 'ol/Map.js';
    import View from 'ol/View.js';
    import TileLayer from 'ol/layer/Tile.js';
    import OSM from 'ol/source/OSM.js';
    import XYZ from 'ol/source/XYZ.js';
    import { fromLonLat } from 'ol/proj.js';
    const NIMBO_DEMO_TMS =
    'https://prod-data.nimbo.earth/mapcache/tms/1.0.0/demo_2023_7_1@kermap/{z}/{x}/{-y}.png?kermap_token=750bf73bb0f6cb9639286ea471b05e335dd4595ce1';
    const nimboSource = new XYZ({
    url: NIMBO_DEMO_TMS,
    tileSize: 256,
    minZoom: 0,
    maxZoom: 18,
    attributions:
    'NIMBO by KERMAP — Contains modified Copernicus Sentinel data 2023.',
    });
    nimboSource.on('tileloaderror', (event) => {
    console.error('NIMBO tile failed to load:', event);
    });
    const referenceLayer = new TileLayer({
    source: new OSM(),
    });
    const nimboLayer = new TileLayer({
    source: nimboSource,
    // Keep borders and place names visible in this onboarding example.
    // Use 1 for a fully opaque NIMBO basemap.
    opacity: 0.82,
    });
    const map = new Map({
    target: 'map',
    layers: [referenceLayer, nimboLayer],
    view: new View({
    center: fromLonLat([2.35, 46.5]),
    zoom: 5,
    maxZoom: 18,
    }),
    });
    // Expose the map during development so it can be inspected from the console.
    window.nimboMap = map;
  3. Replace style.css

    html,
    body,
    #map {
    width: 100%;
    height: 100%;
    margin: 0;
    }
  4. Check index.html

    The generated file must contain the map target before the module script:

    <div id="map" aria-label="NIMBO satellite map"></div>
    <script type="module" src="./main.js"></script>
  5. Start the development server

    Run:

    Terminal window
    npm start

    Open the local address shown in the terminal, normally http://localhost:5173/.

You should see the July 2023 NIMBO mosaic across France, with the reference map still visible beneath it. Pan and zoom to confirm that new tiles load continuously. Set the NIMBO layer opacity to 1 to inspect the imagery alone.

2. Understand the OpenLayers source configuration

Section titled “2. Understand the OpenLayers source configuration”

The NIMBO basemap is delivered through an OpenLayers XYZ source inside a TileLayer.

SettingWhy it matters
urlDefines the NIMBO TMS tile template.
{-y}Converts OpenLayers’ top-left XYZ Y coordinate to the bottom-left TMS coordinate expected by NIMBO.
tileSize: 256Matches the dimensions of NIMBO tiles.
minZoom and maxZoomRestrict requests to the documented tile pyramid.
attributionsDisplays the required NIMBO and source-data credit in the default attribution control.
tileloaderrorGives developers a clear signal when authentication, layer naming or tile requests fail.
opacity: 0.82Keeps the optional reference layer visible in this tutorial. Use 1 for NIMBO alone.

OpenLayers XYZ sources normally use an origin at the top-left. The NIMBO endpoint in this tutorial is TMS, whose Y-axis origin is at the bottom-left.

OpenLayers provides the {-y} placeholder for this conversion:

const source = new XYZ({
url: 'https://.../{z}/{x}/{-y}.png?...',
});

Do not change this URL back to {y} unless you also replace the source with a custom tile-grid or tile-URL function designed for that convention.

3. Confirm that the sandbox test succeeded

Section titled “3. Confirm that the sandbox test succeeded”

Open the browser developer tools and inspect the Console and Network panels.

A successful test has the following characteristics:

  • the map fills the page;
  • the July 2023 watermarked basemap appears;
  • the Reference map control reveals borders, roads and place names;
  • the attribution control credits OpenStreetMap and NIMBO/Copernicus;
  • requests target demo_2023_7_1@kermap;
  • panning and zooming load additional PNG tiles;
  • the console does not report repeated tileloaderror events.

4. Switch to a layer from your NIMBO account

Section titled “4. Switch to a layer from your NIMBO account”

Production monthly layer names use this structure:

<YEAR>_<MONTH>_<LAYER_CODE>@kermap

Create a URL builder that preserves the OpenLayers {-y} placeholder:

function buildNimboTmsUrl({ year, month, layerCode, token }) {
if (!Number.isInteger(year) || year < 1) {
throw new Error('year must be a positive integer');
}
if (!Number.isInteger(month) || month < 1 || month > 12) {
throw new Error('month must be an integer between 1 and 12');
}
if (!Number.isInteger(layerCode) || layerCode < 1) {
throw new Error('layerCode must be a positive integer');
}
if (typeof token !== 'string' || token.trim() === '') {
throw new Error('A NIMBO API token is required');
}
const layerName = `${year}_${month}_${layerCode}@kermap`;
return (
`https://prod-data.nimbo.earth/mapcache/tms/1.0.0/${layerName}` +
`/{z}/{x}/{-y}.png?kermap_token=${encodeURIComponent(token.trim())}`
);
}

Update the existing source instead of creating a new map, and update the visible attribution at the same time:

function setNimboLayer({ year, month, layerCode, token }) {
nimboSource.setUrl(
buildNimboTmsUrl({ year, month, layerCode, token }),
);
nimboSource.setAttributions(
`NIMBO by KERMAP — Contains modified Copernicus Sentinel data ${year}.`,
);
}
setNimboLayer({
year: 2023,
month: 7,
layerCode: 1,
token: '<YOUR_TOKEN>',
});

OpenLayers invalidates the source URL and requests the tiles needed for the current view. Replace the example date and layer code with values available to your account. When a user selects another date, call setNimboLayer(...) again.

CodeLayer
1RGB — 10 m
2NIR — 10 m
3NDVI — 10 m
4Radar
5NIMBO HD — 2.5 m
6Traceability

Read Get your NIMBO API token before using authenticated services.

5. Use NIMBO as the basemap for your application

Section titled “5. Use NIMBO as the basemap for your application”

Keep the NIMBO TileLayer below your operational vector or analytical layers:

const map = new Map({
target: 'map',
layers: [
referenceLayer, // optional; remove it when it is not needed
nimboLayer,
parcelsLayer,
assetsLayer,
alertsLayer,
],
view,
});

This lets users interpret their own parcels, assets, boundaries or detections against the same homogeneous monthly background across dates and territories.

For a time selector, keep one XYZ source and call nimboSource.setUrl(...) when the selected month changes. Avoid keeping several invisible NIMBO layers active unless the application genuinely needs them, because every active date or product can generate its own tile requests.

The public sandbox does not consume your account’s GeoCredits. Authenticated production tiles do: each new tile requested from NIMBO consumes 1 GeoCredit.

Consumption depends on the tiles requested by the application, including:

  • viewport size and zoom level;
  • panning and repeated navigation;
  • the number of active NIMBO layers or dates;
  • side-by-side comparison views;
  • browser and application caching.

Read Tiles and GeoCredits before estimating a production allowance.

SymptomLikely causeCorrection
The page is blank#map has no height, or the module did not compile.Keep the full-height CSS and check the Vite terminal and browser console.
Borders and place names are no longer visibleNIMBO is fully opaque and covers the reference map below it.Use the optional reference layer with a lower NIMBO opacity for the demo, or add vector boundaries and labels above a fully opaque NIMBO layer.
The map is centered in the wrong placeLongitude and latitude were passed directly to an EPSG:3857 view.Wrap geographic coordinates with fromLonLat([longitude, latitude]).
Imagery is inverted or repeatedThe TMS Y-axis was not converted.Keep {-y} in the NIMBO URL.
Sandbox tiles failThe demo URL was altered or the sandbox is unavailable.Restore the exact URL and inspect the first failed request.
Authenticated requests return 401 or 403The token is invalid, incomplete or not authorized for the selected layer.Test the token and confirm access in your NIMBO account.
A date displays no imageryThe layer name or month is unavailable.Verify <YEAR>_<MONTH>_<LAYER_CODE>@kermap against the catalog.
Tile errors continue after changing URLOld requests remain in flight or the source cache needs refreshing.Wait for the current cycle, then call nimboSource.refresh() and inspect the new request URL.
The app works locally but is unsafe to publishThe personal token is present in frontend code.Define a protected production integration with NIMBO.

Before deployment, define:

  • the internal, client or public audience;
  • required months, products and geographic coverage;
  • expected sessions, zoom levels and concurrent users;
  • token protection, caching, monitoring and support requirements;
  • attribution, client-facing, OEM and redistribution rights.

A Pro or Pro HD plan covers defined professional and static-deliverable uses. Interactive client-facing applications, SaaS, OEM integration, redistribution and multi-user deployments require an Enterprise agreement.

Create or open your NIMBO account

Discuss an OpenLayers production integration