Script conversion to version 3

I have a formula to be converted into version=3
but did not manage to control the var val function.
This way it works but better to have it in VERSION=3

var val = [[B11 - B10] - [0.389*[B12 - B10]]];
return colorBlend(val, [-0.003,0], [[0,0,0],[1,1,1]]);

VERSION=3 could be:

//VERSION=3
function setup() {
return {
input: [“B10”,“B11”,“B12”],
output: { bands: 1 }
};
}
function evaluatePixel(sample) {
let MCI =[[B11 - B10] - [0.389*[B12 - B10]]];
var val = MCI
return colorBlend(val, [-0.003,0], [[0,0,0],[1,1,1]]);
}

BUT
Failed to evaluate script!
evalscript.js:9: ReferenceError: B11 is not defined
let MCI =[[B11 - B10] - [0.389*[B12 - B10]]];
^
ReferenceError: B11 is not defined
at evaluatePixel (evalscript.js:9:12)
at executeForSingleScene (:1119:17)

Thanks a lot for an advice.

Hey,

the script is almost good, there are just a few things to change.
I added the explanation at the beginning (which makes this reply so long; sorry for that) and the evalscript that should work properly at the end.


Explanation

The first thing is the way to access the bands’ data.

With the evalscript version 3, when the setup() and evaluatePixel() are used, the variables that hold the bands’ data are not globally available anymore. This is also what the error is about (can’t find the definition of the variable).

Simplified, the way setup() and evaluatePixel() functions work is that in the input array in the setup() function you define which bands’ data is sent to the evaluatePixel() function inside of the sample parameter. In the evaluatePixel() that data can then be used by writing sample.<band name> (e.g. sample.B11).

The solution is to change B11 to sample.B11 (and the same for other bands).

Documentation of setup() function and its parameters is available here.
Documentation for evaluatePixel() function and its parameters is available here.


The second thing is a specific of the colorBlend() function in combination with the number of the bands in the output object (bands property of the output object) in the setup() function.

This isn’t a problem with the original evalscript because the service that executes the evalscript doesn’t have a way to limit the number of the output bands with the basic version of the script.

In your case with the provided parameters, the colorBlend() function returns an array of 3 values (which are most often then used as values for red, green and blue channels in PNG and JPEG (and optionally in TIFF) images).

The statement: return colorBlend(val, [-0.003,0], [[0,0,0],[1,1,1]]);
Quick explanation of the colorBlend() function and its parameters:

  • val - the calculated value that is sent to the function
  • [-0.003,0] and [[0,0,0],[1,1,1]]
    • when the val is smaller than or equal to -0.003, the colorBlend() function returns [0,0,0] (representing black color)
    • when the val is greater than or equal to 0, the colorBlend() function returns [1,1,1] (representing white color)
    • when the val is between -0.003 and 0, the colorBlend() function calculates the values of the array (between [0,0,0] and [1,1,1] representing different colors - but since the blending colors are black and white, the mix is always a shade of gray)

In the setup() function in your script, the bands property of the output object is set to 1, which instructs the service that executes the evalscript to only return 1 band, even if you return more in the evaluatePixel() function.
This can cause incorrect visualization when the data is requested in PNG or JPEG format (as images), because the service then uses the same value for all 3 color channels (red, green, blue) creating a grayscale image.
When the data is requested in TIFF format, the consequence is that the TIFF contains only 1 band (which most image viewers will display the same as PNG and JPEG images mentioned above - in grayscale).

The solution is to set the number of the bands to 3 (output: { bands: 3 }).

The documentation of the colorBlend() function is available here.


The third thing is an update to the colorBlend() function.

The colorBlend() function is deprecated and is replaced with valueInterpolate, which works similarly.

So the statement return colorBlend(val, [-0.003,0], [[0,0,0],[1,1,1]]); can be replaced with return valueInterpolate(val, [-0.003,0], [[0,0,0],[1,1,1]]);

The documentation for the interpolateValue() function is available here.


The fourth thing is the calculation of the MCI value.

If I understand correctly, the result of the calculation [[B11 - B10] - [0.389*[B12 - B10]]] should be one number.

If that is correct, it’s by luck that this worked correctly before and that it also works correctly after all the mentioned changes.
Evalscripts are basically javascript code, so most of javascript rules also apply to evalscripts.
The problem is that in javascript, square brackets ([]) are used mainly for arrays of elements.
For defining the operation priority in the calculations, the normal (round) brackets (()) should be used.

So, the solution is to change [] to () in let MCI =[[B11 - B10] - [0.389*[B12 - B10]]];


Last thing is just a cleanup / improvement.

The var val = MCI; statement is not needed.

With the removal of var val = MCI;, the return valueInterpolate(val, [-0.003, 0], [[0, 0, 0], [1, 1, 1]]); can be changed to return valueInterpolate(MCI, [-0.003, 0], [[0, 0, 0], [1, 1, 1]]);


The evalscript that should produce the same results as your original script:

//VERSION=3
function setup() {
  return {
    input: ["B10","B11","B12"],
    output: { bands: 3 }
  };
}
function evaluatePixel(sample) {
  let MCI = ((sample.B11 - sample.B10) - (0.389 * (sample.B12 - sample.B10)));
  return valueInterpolate(MCI, [-0.003, 0], [[0, 0, 0], [1, 1, 1]]);
}

It also seems to give the same visualization in EO Browser for Sentinel-3 OLCI

Dear Ziga,
that was of great help. Many thanks. I followed your explanations step by step and used your links, too.
And your are right, in EO-Browser scripts need not to be Version=3 but just a simplified script-version can make it equally, such as:
for S-3 MCI
var val = [[B11 - B10] - [0.389*[B12 - B10]]];
return colorBlend(val, [-0.003,0], [[0,0,0],[1,1,1]]);
or
for S-2 FAI
return [[B08 - [B04 + [B11 - B04] * 0.19]]*20]
for which I composed successfully:
//VERSION=3
function setup() {
return {
input: [“B04”,“B08”,“B11”],
output: { bands: 1 }
};
}
function evaluatePixel(sample) {
let FAI = (sample.B08 - (sample.B04 + (sample.B11 - sample.B04) * 0.19))*20;
return [FAI];
}
Of course parameters -0.003, respectively 20 need to be adapted.
Regards
Juerg

1 Like