Examples
On this page, there are example evalscripts that will help with understanding the basics of writing evalscripts. There are additional examples for getting started on the custom scripts repository.
All the scripts utilize the Sentinel-2 L2A and Analysis Ready PlanetScope data collections. For more examples, follow the Introduction to Custom Scripts on Planet Insights Platform course on Planet University.
Returning a True Color Image
- Code Version 3 must be specified in the custom script header using
//VERSION=3
. - The
setup()
is a mandatory function used to specify the input bands used, the output shape, and the format of your response. In this example, there are three bands in the input; the return also contains three bands. - The
evaluatePixel()
is a mandatory function that performs the required operations per pixel. - Note that the returned object's shape (3 elements) matches the specified shape in the output defined in the
setup()
function.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: ['B02', 'B03', 'B04'],
output: {
bands: 3,
sampleType: 'AUTO',
},
};
}
function evaluatePixel(sample) {
return [sample.B04, sample.B03, sample.B02];
}
//VERSION=3
function setup() {
return {
input: ['blue', 'green', 'red'],
output: {
bands: 3,
sampleType: 'AUTO',
},
};
}
let factor = 1 / 2000;
function evaluatePixel(sample) {
return [factor * sample.red, factor * sample.green, factor * sample.blue];
}
The output of the above script results in the following image:
True Color with Color Correction
Basic color correction can be performed in the return function, like in the example below, which brightens and increases the contrast of the image returned.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
return [
2.5 * sample.B04 - 0.07,
2.5 * sample.B03 - 0.07,
2.5 * sample.B02 - 0.07,
];
return [
2.5 * sample.red - 0.07,
2.5 * sample.green - 0.07,
2.5 * sample.blue - 0.07,
];
Output:
Calculating Raw NDVI Values
Spectral indices can be generated within an evalscript.
- Note that this script only uses the two bands required to calculate NDVI in its input.
- In addition, the index is only a 1-band image, so the output is defined as one band.
- Note that the
sampleType
in the output definition has changed fromAUTO
toFLOAT32
, meaning the output can contain float values (decimals). - Within the
evaluatePixel()
function theindex()
function is applied to these 2 bands to return NDVI values.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: ['B04', 'B08'],
output: {
bands: 1,
sampleType: 'FLOAT32',
},
};
}
function evaluatePixel(samples) {
return [index(samples.B08, samples.B04)];
}
//VERSION=3
function setup() {
return {
input: ['red', 'nir'],
output: {
bands: 1,
sampleType: 'FLOAT32',
},
};
}
function evaluatePixel(samples) {
return [index(samples.nir, samples.red)];
}
Calculating NDVI and Returning an Interpolated Colormap
To visualize output, calculate the spectral index and return interpolated colormaps instead of the raw NDVI values.
- Compared to the previous example, the output is now three bands rather than 1.
- The index function is now defined as a variable using
let NDVI
. - The return is now defined by the
valueInterpolate()
function. This requires three inputs: the input values (in this case, NDVI), the intervals, an array of numbers in ascending order defining intervals, and the output interval for the given value/interval of the intervals array. - In this example, five intervals and five arrays are defined with the RGB values to create the colormap.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: ['B04', 'B08'],
output: {
bands: 3,
sampleType: 'FLOAT32',
},
};
}
function evaluatePixel(samples) {
let NDVI = index(samples.B08, samples.B04);
return valueInterpolate(
NDVI,
[-1, 0, 0.2, 0.5, 1],
[
[0, 0, 0],
[1, 1, 0.88],
[0.57, 0.75, 0.32],
[0.31, 0.54, 0.18],
[0.06, 0.33, 0.04],
],
);
}
//VERSION=3
function setup() {
return {
input: ['red', 'nir'],
output: {
bands: 3,
sampleType: 'FLOAT32',
},
};
}
function evaluatePixel(samples) {
let NDVI = index(samples.nir, samples.red);
return valueInterpolate(
NDVI,
[-1, 0, 0.2, 0.5, 1],
[
[0, 0, 0],
[1, 1, 0.88],
[0.57, 0.75, 0.32],
[0.31, 0.54, 0.18],
[0.06, 0.33, 0.04],
],
);
}
Output:
Masking Out Cloudy Pixels
Data masks may be used, for example, to exclude cloudy pixels from visualizations.
- If visualizing Sentinel-2 L2A data, use the
SCL
band to perform the masking. - In the following example, if the
SCL
band has one of the following values: 8, 9, or 10, those pixels will be black. - Alternatively, if visualizing Analysis Ready Planetscope, utilize the
cloudmask
band.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: ['B04', 'B08', 'SCL'],
output: {
bands: 3,
sampleType: 'AUTO',
},
};
}
function evaluatePixel(samples) {
let NDVI = index(samples.B08, samples.B04);
if ([8, 9, 10].includes(samples.SCL)) {
return [0, 0, 0];
} else {
return valueInterpolate(
NDVI,
[-1, 0, 0.2, 0.5, 1],
[
[0, 0, 0],
[1, 1, 0.88],
[0.57, 0.75, 0.32],
[0.31, 0.54, 0.18],
[0.06, 0.33, 0.04],
],
);
}
}
//VERSION=3
function setup() {
return {
input: ['red', 'nir', 'cloud_mask'],
output: {
bands: 3,
sampleType: 'AUTO',
},
};
}
function evaluatePixel(samples) {
let NDVI = index(samples.nir, samples.red);
if (samples.cloud_mask > 1) {
return [0, 0, 0];
} else {
return valueInterpolate(
NDVI,
[-1, 0, 0.2, 0.5, 1],
[
[0, 0, 0],
[1, 1, 0.88],
[0.57, 0.75, 0.32],
[0.31, 0.54, 0.18],
[0.06, 0.33, 0.04],
],
);
}
}
Output:
Calculating the Mean NDVI Value During a Given Time Period
Multi-temporal analysis can also be performed and will output aggregated products using evalscripts. This example explains how to calculate the mean NDVI value over a given time period.
- By default, evalscripts use
SIMPLE
mosaicking, meaning only one satellite scene will be used. Note that mosaicking is explicitly set toORBIT
in this example so that you can use multiple scenes. - The calculation of NDVI is defined within a function named
calcNDVI()
. - The evalscript then loops through the scenes in the evalscript, performing this function on each of the scenes.
- Lastly, the mean is calculated using the sum (number of scenes) and the cumulative count of NDVI values across those scenes.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: [{ bands: ['B04', 'B08', 'dataMask'] }],
output: {
bands: 1,
},
mosaicking: 'ORBIT',
};
}
function calcNDVI(sample) {
var NDVI = (sample.B08 - sample.B04) / (sample.B08 + sample.B04);
return NDVI;
}
function evaluatePixel(samples) {
var sum = 0;
var count = 0;
for (var i = 0; i < samples.length; i++) {
if (samples[i].dataMask != 0) {
var ndvi = calcNDVI(samples[i]);
sum = sum + ndvi;
count++;
}
}
var average = sum / count;
return [average];
}
//VERSION=3
function setup() {
return {
input: [{ bands: ['red', 'nir', 'dataMask'] }],
output: {
bands: 1,
},
mosaicking: 'ORBIT',
};
}
function calcNDVI(sample) {
var NDVI = (sample.nir - sample.red) / (sample.nir + sample.red);
return NDVI;
}
function evaluatePixel(samples) {
var sum = 0;
var count = 0;
for (var i = 0; i < samples.length; i++) {
if (samples[i].dataMask != 0) {
var ndvi = calcNDVI(samples[i]);
sum = sum + ndvi;
count++;
}
}
var average = sum / count;
return [average];
}
Selecting Scenes Only from Given Dates to Perform Change Detection
It is also possible to select only certain scenes in the evalscript, which is useful when performing change detection, for example. To do this, use the preProcessScenes
function.
- Define your
allowedDates
as a list, filtering scenes to only these dates. - Then calculate NDVI for the two dates that are allowed. The samples array will have two elements.
- To calculate the difference between the two, subtract the first element
samples[0]
from the second elementsamples[1]
.
- Sentinel-2 L2A
- Analysis Ready PlanetScope
//VERSION=3
function setup() {
return {
input: [{ bands: ['B04', 'B08'] }],
output: { bands: 1 },
mosaicking: 'ORBIT',
};
}
function preProcessScenes(collections) {
var allowedDates = ['2023-03-10', '2023-06-13'];
collections.scenes.orbits = collections.scenes.orbits.filter(
function (orbit) {
var orbitDateFrom = orbit.dateFrom.split('T')[0];
return allowedDates.includes(orbitDateFrom);
},
);
return collections;
}
function calcNDVI(sample) {
var NDVI = (sample.B08 - sample.B04) / (sample.B08 + sample.B04);
return NDVI;
}
function evaluatePixel(samples) {
var ndvi_diff = calcNDVI(samples[1]) - calcNDVI(samples[0]);
return [ndvi_diff];
}
//VERSION=3
function setup() {
return {
input: [{ bands: ['red', 'nir'] }],
output: { bands: 1 },
mosaicking: 'ORBIT',
};
}
function preProcessScenes(collections) {
var allowedDates = ['2023-03-10', '2023-06-13'];
collections.scenes.orbits = collections.scenes.orbits.filter(
function (orbit) {
var orbitDateFrom = orbit.dateFrom.split('T')[0];
return allowedDates.includes(orbitDateFrom);
},
);
return collections;
}
function calcNDVI(sample) {
var NDVI = (sample.nir - sample.red) / (sample.nir + sample.red);
return NDVI;
}
function evaluatePixel(samples) {
var ndvi_diff = calcNDVI(samples[1]) - calcNDVI(samples[0]);
return [ndvi_diff];
}
Additional Resources
Custom Scripts Repository
Explore a collection of custom scripts for Sentinel Hub.
Introduction to Custom Scripts on the Planet Insights Platform
Explore the video tutorial that explains what custom scripts are, and how to create them.
Interactive Intro to Evalscripts
Check out the Jupyter notebooks tutorial on GitHub for using evalscripts.