Detecting trends on NDVI time-series with Statistical API

Hi everyone,

I’m looking to make a function using the NDVI from S2, in order to get a very simple kind of harvest monitoring, where I would like to translate the NDVI values into information, such as, which state is the vegetation at (greening, browning). In the end, the user will have an idea of NDVI trending, in order to know if harvest period is coming, for example.
For now I’m using the max ndvi function present in the custom scripts repository for the last 2/ months, aggregated in 15 days - does anyone has any suggestion on how to integrate such function on the script? Would this be possible on statistical api?

Thanks in advance!

Base script:

curl -X POST 
 -H 'Content-Type: application/json' 
 -d '{
  "input": {
    "bounds": {
      "bbox": [
    "data": [
        "dataFilter": {},
        "type": "sentinel-2-l2a"
  "aggregation": {
    "timeRange": {
      "from": "2021-10-25T00:00:00Z",
      "to": "2022-01-25T23:59:59Z"
    "aggregationInterval": {
      "of": "P15D",
      "lastIntervalBehavior": "SHORTEN"
    "width": 256,
    "height": 169.666,
    "evalscript": "//VERSION=3\nfunction setup() {\n  return {\n    input: [{\n      bands: [\n        \"B04\",\n        \"B08\",\n        \"SCL\",\n        \"dataMask\"\n      ]\n    }],\n    output: [\n      {\n        id: \"data\",\n        bands: 3\n      },\n      {\n        id: \"scl\",\n        sampleType: \"INT8\",\n        bands: 1\n      },\n      {\n        id: \"dataMask\",\n        bands: 1\n      }]\n  };\n}\n\nfunction evaluatePixel(samples) {\n    let index = (samples.B08 - samples.B04) / (samples.B08+samples.B04);\n    return {\n        data: [index, samples.B08, samples.B04],\n        dataMask: [samples.dataMask],\n        scl: [samples.SCL]\n    };\n}\n"
  "calculations": {
    "default": {}

Yes, it is possible to do what you want using evalscript, and it works very well together with statistical api.

The following evalscript will calculate maxNDVI from all samples available to evalscript. In case of 15day aggregation period with StatAPI, this will be all orbits available in any given aggregation period.

max_ndvi_evalscript = '''
function setup() {
  return {
    input: [{
      bands: ["B04", "B08", "dataMask"],
      units: "DN"
    output: [
        id: "data",
        bands: ["monthly_max_ndvi"],
        sampleType: "FLOAT32"
        id: "dataMask",
        bands: 1
    mosaicking: "ORBIT",

function evaluatePixel(samples) {
    var max = 0;
    var hasData = 0;
    for (var i=0;i<samples.length;i++) {
      if (samples[i].dataMask == 1 && samples[i].B04+samples[i].B08 != 0 ){
        hasData = 1
        var ndvi = (samples[i].B08 - samples[i].B04)/(samples[i].B08 + samples[i].B04);
        max = ndvi > max ? ndvi : max;
    return {
        data: [max],
        dataMask: [hasData]

This evalscript was used to produce a map of max NDVI values (monthly aggregates) over districts in Chad:

1 Like

Thanks @batic !

I could make the script work with any problem, my question is, if instead of visualizing the trend over time, as you showed in the images, if it would be possible to build some sort of function where a trend is identified statistically, and as an output (where in the trend is the aoi at a specific date: growing, stagnant or withering)?
In similarity with scripts like the Agriculture growth stage (which only visualizes the different stages, but there are no numerical outputs)

I understand now.

The evalscript that currently calculates max NDVI would have to be changed: as you can see, in the evaluatePixel above you get a list of available scenes (and obviously their values). You can use this time series to implement math to discern patterns (e.g. growth of ndvi, decline of ndvi, stagnation … are values of derivative of ndvi over time).

I don’t have anything like that at hand, would encourage you to try it yourself. Something like that would for sure be a nice contribution to custom-scripts.