Copy for LLM[View as Markdown](https://docs.planet.com/develop/evalscripts/examples/) # 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](https://custom-scripts.sentinel-hub.com/). note All the scripts utilize the [Sentinel-2 L2A](https://docs.planet.com/data/public-data/copernicus/sentinel-2.md) and Analysis Ready PlanetScope data collections. For more examples, follow the Introduction to Custom Scripts on Planet Insights Platform course on [Planet University](https://university.planet.com/introduction-to-custom-scripts-on-the-planet-insights-platform). ## 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](/assets/images/true_color-4033ef6262741d4aa5c5208e7936f955.webp) ### 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**: ![True color correction](/assets/images/true_color_corrected-ede929910a41a51e1c5e8eadfae14ed7.webp) ## 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 from `AUTO` to `FLOAT32`, meaning the output can contain float values (decimals). * Within the `evaluatePixel()` function the `index()` 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**: ![NDVI interpolated colormap](/assets/images/NDVI-6aa93e24b884cd076f1d2298cffc9fa9.webp) ## 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**: ![NDVI cloud mask](/assets/images/NDVI_cloudmask-b80779bd15fd39ff61b30736ad165e6e.webp) ## 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 to `ORBIT` 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]; } ``` ## Comparing Two Dates to Perform Change Detection A common task is to compare imagery from two dates — for example, to compute the change in NDVI between a "before" and an "after" acquisition. The recommended way to do this is with [data fusion](https://docs.planet.com/develop/evalscripts/data-fusion.md): request the same data collection twice, each with its own time range, and give each input an `id` (here `before` and `after`). This has two advantages: * **The dates live in the request, not in the evalscript.** You can run the same registered evalscript for any pair of dates by changing only the request body — no code edit or re-upload. * **It avoids extra input processing.** Each input is filtered to a single date by its `timeRange`, so only the two scenes you need are read, rather than loading the full stack and discarding scenes in the script. In the evalscript, the two inputs are accessed by their `id` as keys of the `samples` object. With `SIMPLE` mosaicking each key is an array holding a single mosaic, so the date's data is `samples.before[0]` and `samples.after[0]`. * Sentinel-2 L2A * Analysis Ready PlanetScope ``` //VERSION=3 function setup() { return { input: [ { datasource: 'before', bands: ['B04', 'B08', 'dataMask'] }, { datasource: 'after', bands: ['B04', 'B08', 'dataMask'] }, ], output: { bands: 1, sampleType: 'FLOAT32' }, mosaicking: 'SIMPLE', }; } function calcNDVI(sample) { return (sample.B08 - sample.B04) / (sample.B08 + sample.B04); } function evaluatePixel(samples) { var before = samples.before[0]; var after = samples.after[0]; if (before.dataMask === 0 || after.dataMask === 0) { return [NaN]; } return [calcNDVI(after) - calcNDVI(before)]; } ``` ``` //VERSION=3 function setup() { return { input: [ { datasource: 'before', bands: ['red', 'nir', 'dataMask'] }, { datasource: 'after', bands: ['red', 'nir', 'dataMask'] }, ], output: { bands: 1, sampleType: 'FLOAT32' }, mosaicking: 'SIMPLE', }; } function calcNDVI(sample) { return (sample.nir - sample.red) / (sample.nir + sample.red); } function evaluatePixel(samples) { var before = samples.before[0]; var after = samples.after[0]; if (before.dataMask === 0 || after.dataMask === 0) { return [NaN]; } return [calcNDVI(after) - calcNDVI(before)]; } ``` The request body declares the two inputs and matches each `id` to a `datasource` in the evalscript. The example below uses the [Analysis-Ready PlanetScope Sandbox Data](https://docs.planet.com/data/imagery/arps/sandbox.md) collection over a cropland area in Iowa, comparing bare soil in spring to peak crop growth in summer. ``` { "input": { "bounds": { "bbox": [-93.84, 41.16, -93.78, 41.22], "properties": { "crs": "http://www.opengis.net/def/crs/EPSG/0/4326" } }, "data": [ { "type": "byoc-3f605f75-86c4-411a-b4ae-01c896f0e54e", "id": "before", "dataFilter": { "timeRange": { "from": "2022-04-26T00:00:00Z", "to": "2022-04-26T23:59:59Z" } } }, { "type": "byoc-3f605f75-86c4-411a-b4ae-01c896f0e54e", "id": "after", "dataFilter": { "timeRange": { "from": "2022-08-05T00:00:00Z", "to": "2022-08-05T23:59:59Z" } } } ] }, "output": { "width": 512, "height": 512, "responses": [ { "identifier": "default", "format": { "type": "image/tiff" } } ] } } ``` tip To compare two dates of the **same** data collection, set the same collection `type` for both inputs and only change the `timeRange`, as shown above. To compare across **different** collections (for example, Sentinel-2 against Landsat), give each input its own `type`. ## Additional Resources [📓Custom Scripts Repository](https://custom-scripts.sentinel-hub.com/) [Explore a collection of custom scripts for Sentinel Hub.](https://custom-scripts.sentinel-hub.com/) [🎓Introduction to Custom Scripts on the Planet Insights Platform](https://university.planet.com/introduction-to-custom-scripts-on-the-planet-insights-platform) [Explore the video tutorial that explains what custom scripts are, and how to create them.](https://university.planet.com/introduction-to-custom-scripts-on-the-planet-insights-platform) [📓Interactive Intro to Evalscripts](https://github.com/planetlabs/notebooks/tree/master/jupyter-notebooks/workflows/introduction_to_evalscripts) [Check out the Jupyter notebooks tutorial on GitHub for using evalscripts.](https://github.com/planetlabs/notebooks/tree/master/jupyter-notebooks/workflows/introduction_to_evalscripts)