Skip to content
Download and process satellite imagery in JavaScript or TypeScript using Sentinel Hub services.
TypeScript JavaScript
Branch: master
Clone or download

README.md


Warning!!!

The interface of the library is not final yet - expect breaking changes.



Installation

$ npm install @sentinel-hub/sentinelhub-js

Usage

Layers

The core data structure is Layer, which corresponds to a layer as returned by OGC WMS GetCapabilities request. If the URL matches one of the Sentinel Hub service URLs, additional capabilities are unlocked.

Basic (WMS-capable) Layer can be initialized like this:

  import { WmsLayer } from '@sentinel-hub/sentinelhub-js';

  const layer = new WmsLayer('https://services.sentinel-hub.com/ogc/wms/<your-instance-id>', '<layer-id>', 'Title', 'Description');

Such layer would only allow WMS requests. However, Layer is also a superclass for multiple dataset-specific subclasses (like S1GRDAWSEULayer - Sentinel-1 GRD data on AWS eu-central-1 Sentinel Hub endpoint) which can be instantiated with their own specific parameters and thus unlock additional powers.

When it comes to Sentinel Hub layers, there are four ways to determine their content:

  • by layerId: ID of the layer, as used by OGC WMS and SentinelHub Configurator
  • by evalscript: custom (javascript) code that will be executed by the service per each pixel and will calculate the (usually RGB/RGBA) values
  • by evalscriptUrl: the URL from which the evalscript can be downloaded from
  • by dataProduct: the structure which contains an ID of a pre-existing product
  import { S1GRDAWSEULayer } from '@sentinel-hub/sentinelhub-js';

  let layerS1;
  layerS1 = new S1GRDAWSEULayer(instanceId, '<layer-id>', null, null, null, 'Title', 'Description');
  layerS1 = new S1GRDAWSEULayer(null, null, myEvalscript, null, null, 'Title', 'Description', 'IW', 'DV', 'HIGH');
  layerS1 = new S1GRDAWSEULayer(null, null, null, myEvalscriptUrl, null, 'Title', 'Description', 'IW', 'DV', 'HIGH');
  layerS1 = new S1GRDAWSEULayer(null, null, null, null, '<data-product-id>', 'Title', 'Description', 'EW', 'DH', 'MEDIUM');

It is also possible to create layers as they are defined in Sentinel Hub configuration instance:

  import { LayersFactory } from '@sentinel-hub/sentinelhub-js';

  const layers = LayersFactory.makeLayers('https://services.sentinel-hub.com/ogc/wms/<your-instance-id>');
    // [ layer1, layer2, ... ] - a list of Layer objects

  const layersIds = layers.map(l => l.layerId);
    // [ '<layer-id-1>', '<layer-id-2>',... ]

Depending on baseUrl, method makeLayers() tries to determine if a specific Layer subclass would be better suited and instantiates it with all applicable parameters.

Alternatively, the list can be filtered to include only some of the layers:

  import { LayersFactory, DATASET_S2L2A } from '@sentinel-hub/sentinelhub-js';

  // this will return only a list of those S2L2A layers whose IDs start with "ABC_":
  const layers = LayersFactory.makeLayers(
    'https://services.sentinel-hub.com/ogc/wms/<your-instance-id>',
    (layerId, dataset) => layerId.startsWith("ABC_") && dataset === DATASET_S2L2A,
  );

Some information about the layer is only accessible to authenticated users. In case of Playground and EO Browser, ReCaptcha auth token is sufficient to fetch layer information (such as evalscript / dataProduct). To avoid updating every layer when auth token changes, we have a global function for updating it:

  import { isAuthTokenSet, setAuthToken } from '@sentinel-hub/sentinelhub-js';

  const before = isAuthTokenSet(); // false
  setAuthToken(newAuthToken);
  const after = isAuthTokenSet(); // true

The process of getting the authentication token is described in Authentication for Processing API.

Fetching images

Maps which correspond to these layers can be fetched via different protocols like WMS and Processing. Not all of the protocols can be used in all cases; for example, Processing can only render layers for which it has access to the evalscript and for which evalscript version 3 is used.

  import { BBox, CRS_EPSG4326, MimeTypes, ApiType } from '@sentinel-hub/sentinelhub-js';

  const bbox = new BBox(CRS_EPSG4326, 18, 20, 20, 22);
  const getMapParams = {
    bbox: bbox,
    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),
    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),
    width: 512,
    height: 512,
    format: MimeTypes.JPEG,
  };

  const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);
  const imageBlob2 = await layer.getMap(getMapParams, ApiType.PROCESSING);

Note that in theory both images should be exactly the same. If there are any (minor) differences, they are a consequence of how service processes requests, not because of different interpretation of getMapParams on the side of this library.

In some cases we can retrieve just the image URL instead of a blob:

  const imageUrl = await layer.getMapUrl(getMapParams, ApiType.WMS);
  const imageUrl2 = await layer.getMapUrl(getMapParams, ApiType.PROCESSING); // exception thrown - Processing API does not support HTTP GET method

Searching for data

Searching for the data is a domain either of a layer or its dataset (if available). This library supports different services, some of which (ProbaV and GIBS for example) specify availability dates per layer and not dataset.

We can always use layer to search for data availability:

  import { OrbitDirection } from '@sentinel-hub/sentinelhub-js';

  const maxCloudCoverPercent = 50;
  const layerS2L2A = new S2L2ALayer(instanceId, 'S2L2A', null, null, null, null, null, maxCloudCoverPercent);
  const { tiles, hasMore } = layerS2L2A.findTiles(bbox, fromDate, toDate, maxCount, offset);
  const flyoversS2L2A = layerS2L2A.findFlyovers(bbox, fromDate, toDate);
  const dates = layerS2L2A.findDatesUTC(bbox, fromDate, toDate);

  const layerS1 = new S1GRDAWSEULayer(
    instanceId, 'LayerS1GRD',
    null, null, null, null, null, null, null, null,
    true, BackscatterCoeff.GAMMA0_ELLIPSOID, OrbitDirection.ASCENDING
  );
  const { tiles: tilesS1 } = layerS1.findTiles(bbox, fromDate, toDate, maxCount, offset);
  const flyoversS1 = layerS1.findFlyovers(bbox, fromDate, toDate);

Backwards compatibility

To make it easier to use this library with legacy code, there are two functions that are implemented on top of the library, which do not require instantiating a Layer subclass.

  const imageBlob1 = await legacyGetMapFromParams(rootUrl, wmsParams);
  const imageBlob2 = await legacyGetMapFromParams(rootUrl, wmsParams, ApiType.PROCESSING); // ApiType.WMS is default

If we already have a WMS GetMap URL, we can use it directly:

  const imageBlob3 = await legacyGetMapFromUrl(fullUrlWithWmsQueryString);
  const imageBlob4 = await legacyGetMapFromUrl(fullUrlWithWmsQueryString, ApiType.PROCESSING);

Authentication for Processing API

Requests to Processing API need to be authenticated. Documentation about authentication is available at Sentinel Hub documentation.

In short, authentication is done by getting an authentication token using OAuth Client's id and secret, and setting it.

To get the OAuth Client's id and secret, a new OAuth Client must be created in User settings on Sentinel Hub Dashboard under OAuth clients. OAuth Client's secret is shown only before the creation process is finished so be mindful to save it.

Getting the authentication token by calling requestAuthToken() with the OAuth Client's id and secret as its parameters and then setting the authentication token:

import { setAuthToken, requestAuthToken } from '@sentinel-hub/sentinelhub-js';

const clientId = /* OAuth Client's id, best to put it in .env file and use it from there */;
const clientSecret = /* OAuth client's secret, best to put it in .env file and use it from there */;
const authToken = await requestAuthToken(clientId, clientSecret);
setAuthToken(authToken);

Examples

This project contains some examples to demonstrate how the library is used. Some preparation is needed before running the examples.

Preparation before running examples

To run the examples, the environment variables must be set. These variables should be put in the .env file in the root folder of this project.

  • CLIENT_ID: OAuth Client's id (optional, authentication is needed for examples that use Processing API)

  • CLIENT_SECRET: OAuth Client's secret (optional, authentication is needed for examples that use Processing API)

  • INSTANCE_ID: id of the configuration instance that will be used in examples

  • S1GRDIW_LAYER_ID: id of the Sentinel-1 GRD IW layer from that instance

  • S1GRDEW_LAYER_ID: id of the Sentinel-1 GRD EW layer from that instance

  • S2L2A_LAYER_ID: id of the Sentinel-2 L2A layer from that instance

  • ... (see .env.example for full list)

Instance can be created with the Configurator on the Sentinel Hub Dashboard. It should contain the layers in the list above for examples to work.

CLIENT_ID and CLIENT_SECRET are needed so that the authentication token can be requested, which is then used in examples that use Processing API. The process of getting those two is described in Authentication for Processing API

Running examples

Node.js

$ npm install
$ npm run build
$ cd example/node
$ node index.js

Storybook

$ npm install
$ npm run build
$ cp .env.example .env
  (... edit .env ...)
$ npm run storybook

Copyright and license

Copyright (c) 2020 Sinergise Ltd. Code released under the MIT License.

You can’t perform that action at this time.