Flash Drought

//VERSION=3

const nDaysBackwardAverage = 14; // The number of days that is used to calculate the swc backward average
const nDaysPrevious = 28; // The number of days to look back to compare the current value with the previous value
const scaleFactor = 1000; // The scale factor for the SWC values
const droughtThreshold = 0.15; // The SWC value under which conditions are considered drought-like
const differenceThreshold = -0.12; // Threshold signifying a rapid drop in SWC from the previous average value

function setup() {
  return {
    input: ["SWC", "dataMask"],
    output: { bands: 4 },
    mosaicking: "ORBIT",
  };
}

const daysToLoad = nDaysBackwardAverage + nDaysPrevious; // The number of days looking back to load data for

function preProcessScenes(collections) {
  const millisecondsBack = daysToLoad * 24 * 3600 * 1000;

  collections.scenes.orbits = collections.scenes.orbits.filter(function (
    orbit
  ) {
    const orbitDateFrom = new Date(orbit.dateFrom);
    return (
      orbitDateFrom.getTime() >= collections.to.getTime() - millisecondsBack
    );
  });
  return collections;
}

function getMeanSWCValue(samples) {
  // Get the sum of all SWC values
  let validDateCount = 0;
  let sum = 0;
  for (let i = 0; i < samples.length; i++) {
    if (samples[i].dataMask) {
      sum += samples[i].swc;
      validDateCount += 1;
    }
  }

  // Calculate the mean SWC value
  let meanSWCValue = NaN;
  if (validDateCount > 0) {
    meanSWCValue = sum / validDateCount;
  }

  return meanSWCValue;
}

function evaluatePixel(samples) {
  // When there are no dates, return no data
  if (samples.length == 0) return [NaN, NaN, NaN, 0];
  // When the search interval of the request body is too short to perform the calculation, return no data
  if (samples.length < daysToLoad) return [NaN, NaN, NaN, 0];

  // Extract samples for the two time periods
  const initialSamples = samples.slice(0, nDaysBackwardAverage)
  const previousSamples = samples.slice(nDaysPrevious, nDaysBackwardAverage + nDaysPrevious)

  // Calculate mean SWC value
  const meanSWC = getMeanSWCValue(initialSamples);
  const meanSWCPrevious = getMeanSWCValue(previousSamples);

  const swcDifference = meanSWC - meanSWCPrevious;
  const isFlashDrought =
    meanSWC < droughtThreshold && swcDifference < differenceThreshold ? 1 : 0;

  let opacity = 0;
  if (isFlashDrought) {
    opacity = 1;
  }

  return [isFlashDrought, 0, 0, opacity];
}
//VERSION=3

const nDaysBackwardAverage = 14; // The number of days that is used to calculate the swc backward average
const nDaysPrevious = 28; // The number of days to look back to compare the current value with the previous value
const scaleFactor = 1000; // The scale factor for the SWC values
const droughtThreshold = 0.15; // The SWC value under which conditions are considered drought-like
const differenceThreshold = -0.12; // Threshold signifying a rapid drop in SWC from the previous average value

function setup() {
  return {
    input: ["SWC", "dataMask"],
    output: { bands: 1 },
    mosaicking: "ORBIT",
  };
}

const daysToLoad = nDaysBackwardAverage + nDaysPrevious; // The number of days looking back to load data for

function preProcessScenes(collections) {
  const millisecondsBack = daysToLoad * 24 * 3600 * 1000;

  collections.scenes.orbits = collections.scenes.orbits.filter(function (
    orbit
  ) {
    const orbitDateFrom = new Date(orbit.dateFrom);
    return (
      orbitDateFrom.getTime() >= collections.to.getTime() - millisecondsBack
    );
  });
  return collections;
}

function getMeanSWCValue(samples) {
  // Get the sum of all SWC values
  let validDateCount = 0;
  let sum = 0;
  for (let i = 0; i < samples.length; i++) {
    if (samples[i].dataMask) {
      sum += samples[i].swc;
      validDateCount += 1;
    }
  }

  // Calculate the mean SWC value
  let meanSWCValue = NaN;
  if (validDateCount > 0) {
    meanSWCValue = sum / validDateCount;
  }

  return meanSWCValue;
}

function evaluatePixel(samples) {
  // When there are no dates, return no data
  if (samples.length == 0) return [NaN];
  // When the search interval of the request body is too short to perform the calculation, return no data
  if (samples.length < daysToLoad) return [NaN];

  // Extract samples for the two time periods
  const initialSamples = samples.slice(0, nDaysBackwardAverage)
  const previousSamples = samples.slice(nDaysPrevious, nDaysBackwardAverage + nDaysPrevious)

  // Calculate mean SWC value
  const meanSWC = getMeanSWCValue(initialSamples);
  const meanSWCPrevious = getMeanSWCValue(previousSamples);

  const swcDifference = meanSWC - meanSWCPrevious;
  const isFlashDrought =
    meanSWC < droughtThreshold && swcDifference < differenceThreshold ? 1 : 0;

  return [isFlashDrought];
}

General description

Droughts that are formed in just a few weeks are called flash droughts. We have published a series of blog posts about this fast-emerging and increasingly impactful climate phenomenon. This custom script uses the same principles as described in the blogs and can be used to highlight areas with potential flash droughts.

Method

Based on the work of Eswar et al. (2018), we developed a methodology that highlights areas that reach a certain threshold for dryness and changes in SWC over a 28-day period. Specific thresholds for dryness and changes in SWC are set; by default, areas are highlighted where the 14-days backward average SWC was under 0.15 m3/m3 and the average decrease in the 14-days backward average SWC was above 0.12 m3/m3 compared to 28 days ago. Visualizing the areas that meet those criteria over time show these rapidly drying regions in red, and as conditions continue to change, they fade away.

Description of representative images

One of the blog posts describes the flash drought in Southwestern United States in 2023. The example below shows the flash drought in a part of the Southwest on 22 April 2023. Flash Drought Southwestern U.S.A. 22 April 2023

Usage of the script

The amount of days to calculate the backward average is 14, but can be changed by setting nDaysBackwardAverage. By default, the backward average SWC value is compared to the backward average SWC value of 28 days ago, which can be changed by setting nDaysPrevious. If you have a collection with SWC data, the first day on which this flash drought script can be applied is day 42 (nDaysBackwardAverage + nDaysPrevious). Be also aware that you set the timespan correctly in the EO Browser or Requests Builder (the difference between the start and end date must be at least nDaysBackwardAverage + nDaysPrevious), otherwise no data will be returned.

References

Eswar R., Das N.N., Poulsen C., Behrangi A., Swigart J., Svoboda M., Entekhabi D., Yueh S., Doorn B., Entin J. (2018). SMAP Soil Moisture Change as an Indicator of Drought Conditions. Remote Sensing, 10(5), 788. https://doi.org/10.3390/rs10050788