NDVI difference between two dates

//VERSION=3
// Script to extract NDVI difference between the latest acquisition and the acquisition 10-day prior to the latest within a specified time range
// Returns visualized RGB values
function setup() {
    return {
        input: [{
            bands: ["Red", "NIR", "dataMask"],
            units: "DN"
        }],
        output: { bands: 4 },
        mosaicking: Mosaicking.ORBIT
    }

}

function evaluatePixel(samples) {
    // ndvi difference
    let latest = samples[0];
    let prior = samples[1];
    let dataMask = latest.dataMask * prior.dataMask;
    const diff = dataMask === 1 ? index(latest.NIR, latest.Red) - index(prior.NIR, prior.Red) : NaN;

    // visualisation
    const ramps = [
        [-2, 0x8e0152],
        [-1, 0xc51b7d],
        [-0.5, 0xde77ae],
        [0, 0xf7f7f7],
        [0.5, 0x7fbc41],
        [1, 0x4d9221],
        [2, 0x276419]
    ]
    const visualizer = new ColorRampVisualizer(ramps);
    let imgVals = visualizer.process(diff);
    return imgVals.concat(dataMask)
}

function preProcessScenes(collections) {
    // sort from most recent to least recent
    collections.scenes.orbits = collections.scenes.orbits.sort(
        (s1, s2) => new Date(s2.dateFrom) - new Date(s1.dateFrom)
    );

    let scenes = collections.scenes.orbits;
    let latest;
    let closest;
    latest = closest = scenes[0];

    // timestamp of 10-day prior to latest acquisition
    let target = new Date(new Date(latest.dateFrom).getTime() - 10 * 24 * 3600 * 1000);

    // find closet timestamp to the target
    let diff = Number.POSITIVE_INFINITY;
    for (let i = 1; i < scenes.length; i++) {
        current = new Date(scenes[i].dateFrom);
        if (Math.abs(current - target) >= diff) { break; }
        diff = Math.abs(current - target);
        closest = scenes[i];
    }

    // filter collections to keep the latest acquisition and the closest acquisitions to the target
    collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
        var orbitDateFrom = orbit.dateFrom;
        return [latest.dateFrom, closest.dateFrom].includes(orbitDateFrom);
    })
    return collections
}
//VERSION=3
// Script to extract NDVI difference between the latest acquisition and the acquisition 10-day prior to the latest within a specified time range
// To be used on EO Browser. Makes statistics work in EOB
function setup() {
    return {
        input: [{
            bands: ["Red", "NIR", "dataMask"],
            units: "DN"
        }],
        output: [
            { id: "default", bands: 4 },
            { id: "index", bands: 1, sampleType: "FLOAT32" },
            { id: "eobrowserStats", bands: 2, sampleType: "FLOAT32" },
            { id: "dataMask", bands: 1 }
        ],
        mosaicking: Mosaicking.ORBIT
    }

}

function evaluatePixel(samples) {
    // ndvi difference
    let latest = samples[0];
    let prior = samples[1];
    let dataMask = latest.dataMask * prior.dataMask;
    const diff = dataMask === 1 ? index(latest.NIR, latest.Red) - index(prior.NIR, prior.Red) : NaN;

    // visualisation
    const ramps = [
        [-2, 0x8e0152],
        [-1, 0xc51b7d],
        [-0.5, 0xde77ae],
        [0, 0xf7f7f7],
        [0.5, 0x7fbc41],
        [1, 0x4d9221],
        [2, 0x276419]
    ]
    const visualizer = new ColorRampVisualizer(ramps);
    let imgVals = visualizer.process(diff);
    return {
        default: imgVals.concat(dataMask),
        index: [diff],
        eobrowserStats: [diff, dataMask],
        dataMask: [dataMask]
    };
}

function preProcessScenes(collections) {
    // sort from most recent to least recent
    collections.scenes.orbits = collections.scenes.orbits.sort(
        (s1, s2) => new Date(s2.dateFrom) - new Date(s1.dateFrom)
    );

    let scenes = collections.scenes.orbits;
    let latest;
    let closest;
    latest = closest = scenes[0];

    // timestamp of 10-day prior to latest acquisition
    let target = new Date(new Date(latest.dateFrom).getTime() - 10 * 24 * 3600 * 1000);

    // find closet timestamp to the target
    let diff = Number.POSITIVE_INFINITY;
    for (let i = 1; i < scenes.length; i++) {
        current = new Date(scenes[i].dateFrom);
        if (Math.abs(current - target) >= diff) { break; }
        diff = Math.abs(current - target);
        closest = scenes[i];
    }

    // filter collections to keep the latest acquisition and the closest acquisitions to the target
    collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
        var orbitDateFrom = orbit.dateFrom;
        return [latest.dateFrom, closest.dateFrom].includes(orbitDateFrom);
    })
    return collections
}
//VERSION=3
// Script to extract NDVI difference between the latest acquisition and the acquisition 10-day prior to the latest within a specified time range
// Returns raw (Float32) values
function setup() {
  return {
    input: [{
      bands: ["Red", "NIR", "dataMask"],
      units: "DN"
    }],
    output: {
      bands: 1,
      sampleType: SampleType.FLOAT32
    },
    mosaicking: Mosaicking.ORBIT
  }

}

function evaluatePixel(samples) {
  // ndvi difference
  let latest = samples[0];
  let prior = samples[1];
  let combinedMask = latest.dataMask * prior.dataMask;
  const diff = combinedMask === 1 ? index(latest.NIR, latest.Red) - index(prior.NIR, prior.Red) : NaN;
  return [diff];
}

function preProcessScenes(collections) {
  // sort from most recent to least recent
  collections.scenes.orbits = collections.scenes.orbits.sort(
    (s1, s2) => new Date(s2.dateFrom) - new Date(s1.dateFrom)
  );

  let scenes = collections.scenes.orbits;
  let latest;
  let closest;
  latest = closest = scenes[0];

  // timestamp of 10-day prior to latest acquisition
  let target = new Date(new Date(latest.dateFrom).getTime() - 10 * 24 * 3600 * 1000);

  // find closet timestamp to the target
  let diff = Number.POSITIVE_INFINITY;
  for (let i = 1; i < scenes.length; i++) {
    current = new Date(scenes[i].dateFrom);
    if (Math.abs(current - target) >= diff) { break; }
    diff = Math.abs(current - target);
    closest = scenes[i];
  }
  collections.scenes.orbits = [latest, closest];
  return collections
}

Evaluate and Visualize

General description

This script aims to obtain the diffence of NDVI between the latest acquisition and the acquisition 10-day prior to the latest on within a specified time period. Multi-temporal analysis is common in the Earth Observation field. Here we take NDVI as an example and demonstrate how to calculate the difference of NDVI between two acquisitions using mosaicking: ORBIT and preProcessScenes in one single request.

To implement multi-temporal analysis in the Evalscript, we apply ORBIT mosaicking to query daily mosaic in the specified time period. Then, by using the optional preProcessScenes function, we find out the acquisition acquired on the date closest to the date 10-day prior to the latest acquisition and filter the out the other unused acquisitions to save Processing Units. Last but not least, in the evaluatePixel function we initialise a combined mask to ensure the difference of NDVI between two acquisitions exists only if there is data on both dates.

Note: The example script is used to obtain the raw value of NDVI difference. For visualisation purpose, please follow the EO Browser link in the Evaluate and visualize section. The visualisation script contains 4 outputs: default, index, eobrowserStats and dataMask. The default layer is a visualisation layer to visualise NDVI difference in EO Browser. The index layer is the actual value of the NDVI difference. The eobrowserStats and the dataMask layer is configured to activate statistical features on EO Browser. Please see the FAQ for more details.

Author of the script

William Ray

Description of representative images

The following image shows the NDVI difference between the latest acquisition and the acquisition 10-day prior to the latest one during the time period from 2nd of April, 2023 to 2nd June, 2023. NDVI difference example