Agricultural growth stage

//VERSION=3 (auto-converted from 1)
/*
Source: @HarelDan - https://github.com/hareldunn/GIS_Repo/blob/master/Multi-Temporal%20NDVI%20for%20Sentinel%20Hub%20Custom%20Scripts
Visualizing NDVI multi-temporal trends in Sentinel-2 imagery.
will take the current image as baseline and calculate average NDVI for the previous 2 months
Based on:
https://twitter.com/sentinel_hub/status/922813457145221121
https://twitter.com/sentinel_hub/status/1020755996359225344
Script requires multi-temporal processing so parameter TEMPORAL=true should be added to the request.
*/

function setup() {
    return {
        input: [
            {
                bands: ["B04", "B08"],
            },
        ],
        output: { bands: 3 },
        mosaicking: "ORBIT",
    };
}

function calcNDVI(sample) {
    var denom = sample.B04 + sample.B08;
    return denom != 0 ? (sample.B08 - sample.B04) / denom : 0.0;
}
function stretch(val, min, max) {
    return (val - min) / (max - min);
}

function evaluatePixel(samples, scenes) {
    var avg1 = 0;
    var count1 = 0;
    var avg2 = 0;
    var count2 = 0;
    var avg3 = 0;
    var count3 = 0;
    var endMonth = scenes[0].date.getMonth();

    for (var i = 0; i < samples.length; i++) {
        var ndvi = calcNDVI(samples[i]);
        if (scenes[i].date.getMonth() == endMonth) {
            avg3 = avg3 + ndvi;
            count3++;
        } else if (scenes[i].date.getMonth() == endMonth - 1) {
            avg2 = avg2 + ndvi;
            count2++;
        } else {
            avg1 = avg1 + ndvi;
            count1++;
        }
    }
    avg1 = avg1 / count1;
    avg2 = avg2 / count2;
    avg3 = avg3 / count3;
    avg1 = stretch(avg1, 0.1, 0.7);
    avg2 = stretch(avg2, 0.1, 0.7);
    avg3 = stretch(avg3, 0.1, 0.7);

    return [avg1, avg2, avg3];
}

function preProcessScenes(collections) {
    collections.scenes.orbits = collections.scenes.orbits.filter(function (
        orbit
    ) {
        var orbitDateFrom = new Date(orbit.dateFrom);
        return (
            orbitDateFrom.getTime() >=
            collections.to.getTime() - 3 * 31 * 24 * 3600 * 1000
        );
    });
    return collections;
}
//VERSION=3 (auto-converted from 1)
/*
Based on Source: @HarelDan - https://github.com/hareldunn/GIS_Repo/blob/master/Multi-Temporal%20NDVI%20for%20Sentinel%20Hub%20Custom%20Scripts
Adapted to visualize NDVI for Sentinel-2 quarterly cloudless mosaics by András Zlinszky using GitHub Copilot.
Each quarter's NDVI is assigned to an RGB channel:
- Red: NDVI for the oldest quarter
- Green: NDVI for the middle quarter
- Blue: NDVI for the most recent quarter
Script requires multi-temporal processing so parameter TEMPORAL=true should be added to the request.
*/

// How to use this script:
// Open Copernicus Browser, zoom to your area of interest
// Select Sentinel-2 Quarterly Mosaics
// On the date panel, select Time Range mode (calendar icon with from-to arrows on the right side of the date panel)
// Set the time range - it should be 7 months. This is because the date of a Quarterly Mosaic refers to the first date in the 3 month interval that is used for generating data (https://forum.dataspace.copernicus.eu/t/scenes-getmonth-for-quarterly-mosaics/3045/2?u=andras.zlinszky_education). So eg. if your start date is 2022-01-01 and your end date is 2022-07-01, your are actually looking at images from 2022-01-01 to 2022-09-30
//  If it is longer, the algorithm will use only the last 9 months

function setup() {
    return {
        input: [
            {
                bands: ["B04", "B08"],
            },
        ],
        output: { bands: 3 },
        mosaicking: "ORBIT",
    };
}

function calcNDVI(sample) {
    var denom = sample.B04 + sample.B08;
    return denom != 0 ? (sample.B08 - sample.B04) / denom : 0.0;
}

function evaluatePixel(samples, scenes) {
    var ndvi1 = 0; // NDVI for the oldest quarter
    var ndvi2 = 0; // NDVI for the middle quarter
    var ndvi3 = 0; // NDVI for the most recent quarter

    // Get the quarter of the most recent scene
    var endQuarter = Math.floor(scenes[0].date.getMonth() / 3);

    for (var i = 0; i < samples.length; i++) {
        var ndvi = calcNDVI(samples[i]);
        var sceneQuarter = Math.floor(scenes[i].date.getMonth() / 3);

        if (sceneQuarter == endQuarter) {
            ndvi3 = ndvi; // Most recent quarter
        } else if (sceneQuarter == endQuarter - 1 || (endQuarter == 0 && sceneQuarter == 3)) {
            ndvi2 = ndvi; // Middle quarter
        } else if (sceneQuarter == endQuarter - 2 || (endQuarter == 0 && sceneQuarter == 2) || (endQuarter == 1 && sceneQuarter == 3)) {
            ndvi1 = ndvi; // Oldest quarter
        }
    }

    // Stretch NDVI values for visualization
    ndvi1 = stretch(ndvi1, 0.1, 0.7);
    ndvi2 = stretch(ndvi2, 0.1, 0.7);
    ndvi3 = stretch(ndvi3, 0.1, 0.7);

    return [ndvi1, ndvi2, ndvi3];
}

function stretch(val, min, max) {
    return (val - min) / (max - min);
}

function preProcessScenes(collections) {
    // Filter scenes to include only the last three quarters
    collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
        var orbitDateFrom = new Date(orbit.dateFrom);
        return (
            orbitDateFrom.getTime() >=
            collections.to.getTime() - 3 * 3 * 30 * 24 * 3600 * 1000 // Last 9 months
        );
    });
    return collections;
}

Evaluate and Visualize

Author of the script

@HarelDan

Adapted to Sentinel-2 Quarterly Cloudless Mosaics by András Zlinszky and Github Copilot.

General description of the script

Agricultural growth stage is a script visualizing the multi-temporal NDVI trends in Sentinel-2 imagery. It takes the current image as baseline and calculates the average NDVI for the previous 2 months. The script requires multi-temporal processing, so the parameter TEMPORAL=true should be added to the request. A simple stretching is applied to NDVI between 0.1 and 0.7 by default, then the mean NDVI from the first, second and third month is assigned to the Red, Green and Blue channels of the image respectively, creating a composite image. What you see on the composite is

  • how dense and/or vigorous the vegetation is, represented by the brightness of the color from black (no vegetation at all) to white (dense green vegetation all year), with various shades of color in between
  • when the vegetation peak happens and how distinct it is. Vegetation with a single very distict peak will be one of the primary colors (Red, Green, Blue) while vegetation with a longer, more even growth season will be yellow (between Red and Green) or cyan (between Green and Blue). Purple color may indicate two vegetation peaks, one in the first month and another in the last, with a dry period or grassland mowing in between.

The adaptation for Sentinel-2 Quarterly Cloudless mosaics also visualizes multi-temporal NDVI trends in Sentinel-2 imagery, but over an even longer timeframe as each mosaic dataset covers 3 months. It uses the NDVI values from three mosaics: the latest mosaic in the provided time interval, the mosaic from the previous quarter and the mosaic two quarters before the last. This means that the script will integrate information over a period of 9 months, typically a full growing season The NDVI values are similarly stretched between 0.1 and 0.7. Again, the color shades represent

  • how dense or vigorous the vegetation is, represented by the intensity of the color (bright colors or white for dense, healthy vegetation)
  • when the vegetation peak happens and how distinct it is, on a timescale of several months.

How to use

  • In Copernicus Browser, open the calendar panel dropdown (with the dropdown button on the right)
  • Select the time interval view (the calendar icon with arrows on the top right). You will now see two dates, labeled “from” and “until”.
  • Select these dates to cover an interval of three months for the regular script, or 7 months for the quarterly mosaics. This is because each mosaic represents the three months after its start date. Selecting eg. June 1 to August 31 for regular Sentinel-2 images will cover most of the agriculture growth season, and selecting April 01 to October 01 for the Quarterly Sentinel-2 Mosaics will cover the nine months from April 1 to December 31, including the full vegetation season in the temperate northern hemisphere.
  • Select your evalscript of choice from this script website and copy it from the code window above
  • In Copernicus Browser, click the </> icon beside the name of the active layer to open the evalscript code window
  • Select the full text inside the window (eg. with the Ctrl+A hotkey) and paste the evalscript code from the clipboard
  • Wait until the data loads - this may take some time for large areas.

Description of representative images

The Agricultural growth stage script applied to the agricultural fields of Italy (Veneto).

The Agricultural growth stage script applied to agricultural fields of Italy.

References

Based on: source 1, source 2