V3 Transition Guide

In November 2020, we finally disabled support for evalscripts version 1 (V1) and version 2 (V2). All the scripts V1 and V2, which had not been converted by users, were automatically converted to version 3 (V3). Note that simple scripts were not automatically converted and we still support them, but they are now being processed with a V3 processing chain. With this, we concluded the transition to evalscript V3. If you have any questions or issues with converted evalscripts, check this user guide or ask us in the Sentinel Hub forum.

This guide contains all the information you need to transition to V3 smoothly. It provides detailed instructions and examples for the use cases where automatic conversion to evalscript V3 is not possible. At the end, we provide a short overview of the structural differences between all the evalscript versions in case you are not sure which version of evalscript you currently use.

Evalscript, custom script and script are used as synonyms in this tutorial. By data products we refer to the preconfigured evalscripts, which are available to the user in the Configuration Utility. By OGC request we mean WMS, WMTS and WFS requests.

How To Convert Your Scripts?

In most cases, transition to V3 can be accomplished by simply converting the syntax of your old evalscript to evalscript V3. However, OGC requests (i.e. WMS, WTS and WCS request) are processed slightly differently when using evalscripts V3, compared to OGC requests using evalscritps V1 and V2. If you use parameters BGCOLOR, TRANSPARENT or the suffix depth of the FORMAT parameter in your OGC requests, you will need to adjust the automatically converted evalscripts to ensure that the results obtained from SH remain the same after the conversion. In the following chapters, we are providing detailed step-by-step instructions for conversion to V3 in these use cases:

  • If you specify background color in your OGC requests (i.e. you use BGCOLOR parameter in your OGC requests), check use case 1.
  • If you request images with transparent background in your OGC requests (i.e. you use TRANSPARENT parameter in your OGC requests), check use case 2.
  • If you request 16bit or 32bit data, check use case 4 (16bit) and use case 5 (32bit) examples. In evalscript V3, sampleType is used to control bit depth of requested data.
  • If you use data products with simple evalscript and request 16bit or 32bit data you will need to set the custom script (instead of data product) for your layer. Check use case 6.
  • If you use data products note that you will get an additional dataMask band after swapping to suggested V3 product. If this is problematic for your use case, you will need to set the custom script (instead of data product) for your layer. Check use case 7.

Note: These use cases are not mutually exclusive, more of them can apply for your request.

General notes

  • To test the conversion before using it in production, we suggest that you duplicate the layer first, then convert the evalscript in the duplicated layer and compare the results you get using both (old and V3) layers.

  • Simple scripts will not be deprecated and do not need to be converted. However, they are now versioned, so a tag //VERSION=3 can be added at the beginning. Simple scripts without the tag will be processed with the latest processing pipeline (which is currently V1 for simple scripts but it will change to V3 on November 1, 2020). Adding the //VERSION=3 at the top of your script allows you to check if your scripts will behave as expected after switching to processing pipeline V3. By using the //VERSION=3 tag you also ensure that your script will always (even if we introduce evalscript V4 in the future) be processed with our V3 processing pipeline. Adding tags //VERSION=1 or //VERSION=2 is not an option as V1 and V2 processing pipelines will stop working.

  • In case you are requesting UINT16 data with OGC services, the values obtained from SH can slightly differ after the transition to V3. This is because we now use values with double precision for processing. This effect shall not exceed value of 1 per pixel. During our tests, we noticed this effect for a bit less than 0.2% pixels.

  • In the vast majority of cases there is no need to change existing URLs of OGC requests, only evalscripts need to be adjusted. But if you use the same layer in various OGC requests with different settings for BGCOLOR, TRANSPARENCY, and depth, you will need to duplicate the layer for each of the OGC requests. Each of these layers will then need to be converted to V3 and the value of LAYERS parameter in your OGC request URL will need to be adjusted.

  • If you use your layer for FIS requests, you will need to ensure that sampleType: "FLOAT32" or sampleType: "AUTO" is used after the conversion to V3. Related to the previous item, if you e.g. use the same layer in WMS requests with FORMAT=image/tiff;depth=16 and in FIS request, you will need to duplicate the layer and then adjust custom scripts for each of them accordingly.

  • Note that default settings for parameters in OGC were: BGCOLOR=#FFFFFF (white color), TRANSPARENCY=False, FORMAT=image/tiff;depth=16. In evalscripts V3 the defualt value of sampleType parameter is AUTO. dataMask is not returned by default; when used for setting a background color, its default value is 0, which results in black color.

  • On the EOCloud endpoint, the conversion will not happen. V3 scripts won't work there, and you do not need to implement any changes for the time being.

  • For Python users who use our sentinelhub-py package to download the data, we recommend:

    • Check which MimeType you use as image_format in your python code and then convert your scripts following the corresponding use case below. This is how MimeType types map to the values of FORMAT parameter:
      • TIFF => tiff
      • TIFF_d8 => tiff;depth=8
      • TIFF_d16 => tiff;depth=16
      • TIFF_d32f => tiff;depth=32f
    • If you created the configuration from Python template, you might need to switch to using custom scripts instead of products for layers NDVI and NDWI. See use case 6 and use case 7.
  • Here's how V1 and V2 mosaicking relate to V3 mosaicking:

temporal in
OGC query parameter or evalscript
V1/V2 mosaickingV3 mosaicking
false (default)N/ASIMPLE (default)
trueSCENE (default)ORBIT
trueTILETILE

Note: OGC temporal query parameter works only with V1 and V2 scripts.

Use Cases and Examples

Use Case 0: Converting syntax of evalscripts to V3

Let's suppose you have an NDVI script in V1 and a false color composite script in V2 and convert them to V3.

Example of converting NDVI evalscript V1 -> V3

Before: evalscript V1
function evaluatePixel(samples) {
let ndvi = index(samples[0].B08, samples[0].B04)
if (ndvi<-0.2) return [0,0,0];
else if (ndvi<-0.1) return [1,0,0];
else if (ndvi<0) return [0.5,0.6,0,0];
else if (ndvi<0.1) return [0.4,0,0];
else if (ndvi<0.2) return [1,1,0.2];
else if (ndvi<0.3) return [0.8,0.8,0.2];
else if (ndvi<0.4) return [0.4,0.4,0];
else if (ndvi<0.5) return [0.2,1,1];
else if (ndvi<0.6) return [0.2,0.8,0.8];
else if (ndvi<0.7) return [0,0.4,0.4];
else if (ndvi<0.8) return [0.2,1,0.2];
else if (ndvi<0.9) return [0.2,0.8,0.2];
else return [0,0.4,0];
}
function setup(ds) {
setInputComponents([ds.B04, ds.B08]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3
function evaluatePixel(samples) {
let ndvi = index(samples.B08, samples.B04)
if (ndvi<-0.2) return [0,0,0];
else if (ndvi<-0.1) return [1,0,0];
else if (ndvi<0) return [0.5,0.6,0,0];
else if (ndvi<0.1) return [0.4,0,0];
else if (ndvi<0.2) return [1,1,0.2];
else if (ndvi<0.3) return [0.8,0.8,0.2];
else if (ndvi<0.4) return [0.4,0.4,0];
else if (ndvi<0.5) return [0.2,1,1];
else if (ndvi<0.6) return [0.2,0.8,0.8];
else if (ndvi<0.7) return [0,0.4,0.4];
else if (ndvi<0.8) return [0.2,1,0.2];
else if (ndvi<0.9) return [0.2,0.8,0.2];
else return [0,0.4,0];
}
function setup() {
return {
input: [{
bands: [
"B04",
"B08"
]
}],
output: { bands: 3 } }
}

Example of converting False color evalscript V2 -> V3

Before: evalscript V2
//VERSION=2
function evaluatePixel(samples) {
return {
default: [samples[0].B08, samples[0].B04, samples[0].B03]
}
}
function setup(ds) {
return {
components: [ds.B08, ds.B04, ds.B03],
output: [
{
id: "default",
sampleType: SampleType.AUTO,
componentCount: 3
}
]
}
}
After: evalscript V3
//VERSION=3
function evaluatePixel(samples) {
return [samples.B08, samples.B04, samples.B03]
}
function setup() {
return {
input: [{
bands: [
"B08",
"B04",
"B03"
]
}],
output: { bands: 3 } }
}

Use Case 1: Background color

If you use BGCOLOR in your OGC requests you need to do the following:
1 – add dataMask to the evalscript and use it to assign the background color to the no data pixels. See the examples here.

Example

WMS Request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&crs=EPSG%3A3857&layers=AGRICULTURE&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512&format=image%2Fjpeg&transparent=false&bgcolor=0x643c28&

Before: evalscript V1
function evaluatePixel(samples) {
return [samples[0].B11, samples[0].B08, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B11, ds.B08, ds.B02]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3 (auto-converted from 1) + using dataMask to specify bgcolor
function evaluatePixel(samples) {
if (samples.dataMask == 1){
return [samples.B11, samples.B08, samples.B02];
} else {
return [100/255, 60/255, 40/255]
}
}
function setup() {
return {
input: [{
bands: [
"B11",
"B08",
"B02",
"dataMask"
]
}],
output: {
bands: 3
}
}
}

Use Case 2: Transparency (and png format)

If you use TRANSPARENT=TRUE in your OGC requests and you are using either a V1 or V2 script, you need to do the following:
1 – add dataMask to the evalscript and return it as an additional band
2 – increase the number of output bands in function setup() -> output -> bands

If you use TRANSPARENT=TRUE in your OGC requests and you are using a simple script add dataMask to the returned array. See the example here.

Example

WMS Request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&format=image%2Fpng&crs=EPSG%3A3857&layers=AGRICULTURE&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512&transparent=true

Before: evalscript V1
function evaluatePixel(samples) {
return [samples[0].B11, samples[0].B08, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B11, ds.B08, ds.B02]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3 (auto-converted from 1) + adding dataMask for transparency
function evaluatePixel(samples) {
return [samples.B11, samples.B08, samples.B02, samples.dataMask];
}
function setup() {
return {
input: [{
bands: [
"B11",
"B08",
"B02",
"dataMask"
]
}],
output: {
bands: 4
}
}
}

Use Case 3: FORMAT=image/tiff;depth=8

If you use FORMAT=image/tiff;depth=8 in your OGC requests you need to do the following:
1 – if you use a simple evalscript re-write it to an advanced one, since simple evalscripts do not support sampleType parameter.
2 – add sampleType: "UINT8" to the setup () function -> output.
3 – scale the values to use the whole UINT8 range.
If you need to ensure that the no-data pixel values stay as they were, do also:
4 – add dataMask to the evalscript and use it to assign white color to the no data pixels

Note: Since SampleType is not specified in evalscript V3 the default value AUTO will be used.

Example

WMS Request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&format=image%2Ftiff;depth=8&crs=EPSG%3A3857&layers=AGRICULTURE&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512

Before: evalscript V1
function evaluatePixel(samples) {
return [samples[0].B11, samples[0].B08, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B11, ds.B08, ds.B02]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3
function evaluatePixel(samples) {
var factor = 255
if (samples.dataMask==0)return [255, 255, 255]
return [samples.B11*factor,
samples.B08*factor,
samples.B02*factor];
}
function setup() {
return {
input: [{
bands: [
"B11",
"B08",
"B02",
"dataMask"
]
}],
output: {
bands: 4
}
}
}

Use Case 4: FORMAT=image/tiff;depth=16 or FORMAT is not specified

If you use FORMAT=image/tiff;depth=16 or FORMAT=image/tiff in your OGC requests or if FORMAT parameter is not specified, you need to do the following:
1 – if you use a simple evalscript re-write it to an advanced one, since simple evalscripts do not support sampleType parameter.
2 – add sampleType: "UINT16" to the setup () function -> output.
3 – scale the values to use the whole UINT16 range.
If you need to ensure that the no-data pixel values stay as they were, do also:
4 – add dataMask to the evalscript and use it to assign white color to the no data pixels

Note: In the example below the input values are representing S2 reflectance from the interval [0,1], thus we scale with factor 65535. This will ensure that the results you get from SH will remain the same after the conversion, because scaling with 65535 was done on SH side for V1 and V2 evalscripts. However, if you do not need to ensure this backwards compatibility, some other scaling factors might be better for your use case.

Example

WMS Request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&format=image%2Ftiff&crs=EPSG%3A3857&layers=AGRICULTURE&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512&transparent=false

Before: evalscript V1
function evaluatePixel(samples) {
return [samples[0].B11, samples[0].B08, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B11, ds.B08, ds.B02]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3 (auto-converted from 1) + dataMask to control background color + scaling to UINT16 range
function evaluatePixel(samples) {
var factor = 65535
if (samples.dataMask == 0) return [65535, 65535, 65535]
return [samples.B11*factor,
samples.B08*factor,
samples.B02*factor];
}
function setup() {
return {
input: [{
bands: [
"B11",
"B08",
"B02",
"dataMask"
]
}],
output: {
bands: 3,
sampleType:"UINT16"
}
}
}

Use Case 5: FORMAT=image/tiff;depth=32F

If you use FORMAT=image/tiff;depth=32F in your OGC requests, you need to do the following:
1 – if you use a simple evalscript, you will have to re-write it to an advanced one, as simple evalscripts do not support sampleType parameter.
2 – add sampleType: »FLOAT32« to the setup () function -> output -> sampleType.
If you need to ensure that also the values for no data pixels stay exactly as they were, do:
3 – add dataMask to the evalscript and use it to assign white color to the no data pixels.

Note: Requesting depth=32F is commonly used by our Python users who specify this by image_format=MimeType.TIFF_d32f.

Example

WMS Request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&format=image%2Ftiff;depth=32f&crs=EPSG%3A3857&layers=AGRICULTURE&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512

Before: evalscript V1
function evaluatePixel(samples) {
return [samples[0].B11, samples[0].B08, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B11, ds.B08, ds.B02]);
setOutputComponentCount(3);
}
After: evalscript V3
//VERSION=3 (auto-converted from 1) + adding SampleType
function evaluatePixel(samples) {
if (samples.dataMask==0) return[1, 1, 1]
return [samples.B11, samples.B08, samples.B02];
}
function setup() {
return {
input: [{
bands: [
"B11",
"B08",
"B02",
"dataMask"
]
}],
output: {
bands: 3,
sampleType: "FLOAT32"
}
}
}

Use Case 6: Data product and FORMAT=image/tiff;depth=32F or FORMAT=image/tiff;depth=16

If you are using a data product and FORMAT=image/tiff;depth=32F or FORMAT=image/tiff;depth=16 in your OGC requests, you will need to switch from using a data product to using a custom script, because our V3 data products use sampleType: "AUTO". Do the following:
1 - Open the layer and click the "Edit" icon close to the custom script.
2 - Click "Copy script to editor" and then "Set custom script". Click "Save layer".
The layer now uses V1 or V2 evalscript instead of the data product. To convert it to V3 follow the instructions for the Use case 4 if you use FORMAT=image/tiff;depth=16 or for the Use case 5 if you use FORMAT=image/tiff;depth=32F.

Example

WMS request

https://services.sentinel-hub.com/ogc/wms/<instance_id>?version=1.1.1&service=WMS&request=GetMap&crs=EPSG%3A3857&layers=DEM___RAW&format=image%2Ftiff;depth=32f&bbox=313086.06785608194%2C939258.2035682461%2C469629.101784123%2C1095801.2374962876&time=2019-11-07/2019-11-07&width=512&height=512

Before: simple evalscript
return [DEM]
After: evalscript V3
//VERSION=3
function evaluatePixel(samples) {
return [samples.DEM];
}
function setup() {
return {
input: [{
bands: [
"DEM"
]
}],
output: {
bands: 1,
sampleType: "FLOAT32"
}
}
}

Use Case 7: Use custom script V3 instead of transparent V3 data product

If you use data product and the corresponding transparent V3 data product does not meet your needs, you will have to switch to using a custom script. To do so:

  • Click on the edit icon close to the custom script to open Custom script editing dialog
  • Now use "Copy custom script to the editor" option. This will copy the evalscript to the text field on your right.
  • Remove dataMask from the script and reduce the number of output bands for 1.
  • Save the layer.

Differences Between Versions

Simple scripts

Simple scripts are easier to use but provide limited set of functionalities comparing to advanced scripts. You can recognize simple scripts by checking if they do not define functions setup() and evaluatePixel(), or by checking whether bands are being called simply without the preceding object (usually called sample), which are all requirements of advanced scripts.

The following example of a simple script for Sentinel-2 data will return a true color image.

return [2.5 * B04, 2.5 * B03, 2.5 * B02]

The following example of a simple script for Sentinel-2 data will return a discrete visualization of NDVI.

let ndvi = (B08 - B04) / (B08 + B04);
if (ndvi<-0.2) return [0,0,0];
else if (ndvi<-0.1) return [1,0,0];
else if (ndvi<0) return [0.5,0.6,0,0];
else if (ndvi<0.1) return [0.4,0,0];
else if (ndvi<0.2) return [1,1,0.2];
else if (ndvi<0.3) return [0.8,0.8,0.2];
else if (ndvi<0.4) return [0.4,0.4,0];
else if (ndvi<0.5) return [0.2,1,1];
else if (ndvi<0.6) return [0.2,0.8,0.8];
else if (ndvi<0.7) return [0,0.4,0.4];
else if (ndvi<0.8) return [0.2,1,0.2];
else if (ndvi<0.9) return [0.2,0.8,0.2];
else return [0,0.4,0];

Advanced scripts

Advanced scripts require setup() and evaluatePixel() functions to be set up. In setup(), we specify the input bands and the number of output bands; 1 usually used for FIS, metadata or grayscale visualizations, 3 usually used for RGB outputs and 4 usually used for RGB + transparency. In function evaluatePixel(), we specify the output result of the request (e.g. a visualization). We also have to specify which samples we want - for example, samples.B04 will return pixel values of the band 4. V1 and V2 scripts also require the specific acquisition to be set (samples[0].B04 would return pixel values for the first available acquisition of band 4). Which acquisition is chosen as "first available", depends on mosaicking order and type. In V3, the latter is not required, unless MOSAICKING parameter is set.

The following examples all return Sentinel-2 true color results.

V1 scripts

In V1 setup() function, we specify setInputComponents(), where the input components (bands) are set, and setOutputComponentCount() we set the number of output bands.

function evaluatePixel(samples) {
return [samples[0].B04, samples[0].B03, samples[0].B02];
}
function setup(ds) {
setInputComponents([ds.B04, ds.B03, ds.B02]);
setOutputComponentCount(3);
}

V2 scripts

In V2, //VERSION=2 needs to be set at the beginning, so that the script recognizes the version. The structure of the script is similar to V1, but a bit different. Instead of setInputComponents(), we now have components and instead of setOutputComponentCount() we now have output, where we specify componentCount.

//VERSION=2
function evaluatePixel(samples) {
return {
default: [samples[0].B04, samples[0].B03, samples[0].B02]
}
}
function setup(ds) {
return {
components: [ds.B04, ds.B03, ds.B02],
output: [
{
id: "default",
sampleType: SampleType.AUTO,
componentCount: 3
}
]
}
}

V3 scripts

In V3, //VERSION=3 needs to be set at the beginning, so that the script recognizes the version. The structure of the script is similar to V2, but instead of components, we now have input, which includes the specified bands and output, where we specify the number of bands. V3 scripts are much more powerful than the previous versions. V1 and V2 supported multitemporal scripting but did not have support for dataMask or data fusion for example. V3 evalscript is documented very well on our documentation. You can find detailed explanations and examples there.

//VERSION=3
function evaluatePixel(samples) {
return [samples.B04, samples.B03, samples.B02];
}
function setup() {
return {
input: [{
bands: [
"B02",
"B03",
"B04"
]
}],
output: { bands: 3 } }
}