Skip to content

Commit

Permalink
Add flash drought example for Soil Water Content (#334)
Browse files Browse the repository at this point in the history
* Add initial flash drought script

* Add raw file

* add flash drought

* add flash drought

* right links on index page

* Add superscript to SWC units

* Add improved formatting, align whitespace

* Refactor to remove map

---------

Co-authored-by: anne <[email protected]>
  • Loading branch information
charliemoriarty and anne authored Nov 11, 2024
1 parent 65721e0 commit 5f05c28
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 1 deletion.
3 changes: 2 additions & 1 deletion planetary-variables/soil-water-content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ Planet's SWC product provides near-daily measurements at spatial resolutions of
- [Soil Water Content Anomaly]({% link planetary-variables/soil-water-content/soil-water-content-anomaly/index.md %})
- [Derived Root-Zone Soil Water Content]({% link planetary-variables/soil-water-content/derived-root-zone-soil-water-content/index.md %})
- [Soil Water Content Backward Average]({% link planetary-variables/soil-water-content/soil-water-content-backward-average/index.md %})
- [Soil Water Content Quality Flags]({% link planetary-variables/soil-water-content/soil-water-content-quality-flags/index.md %})
- [Soil Water Content Quality Flags]({% link planetary-variables/soil-water-content/soil-water-content-quality-flags/index.md %})
- [Flash Drought]({% link planetary-variables/soil-water-content/soil-water-content-flash-drought/index.md %})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Flash Drought
grand_parent: Planetary Variables
parent: Soil Water Content
layout: script
nav_exclude: false
scripts:
- [Visualization, script.js]
- [Raw Values, raw.js]
---
## General description
Droughts that are formed in just a few weeks are called flash droughts. We have published a [series of blog posts](https://www.planet.com/pulse/drying-up-in-a-flash-what-satellite-data-can-tell-us-about-flash-drought-risks-and-the-regions-we-should-be-watching/) 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 m<sup>3</sup>/m<sup>3</sup> and the average decrease in the 14-days backward average SWC was above 0.12 m<sup>3</sup>/m<sup>3</sup> 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](https://www.planet.com/pulse/flash-drought-hotspots-southwestern-united-states/) 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](fig/flash_drought.png)

## 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.

## Useful links
- [Drying Up in a Flash: What Satellite Data Can Tell Us About Flash Drought Risks and the Regions We Should Be Watching](https://www.planet.com/pulse/drying-up-in-a-flash-what-satellite-data-can-tell-us-about-flash-drought-risks-and-the-regions-we-should-be-watching/)
- [Flash Drought Hotspots: Southwestern United States](https://www.planet.com/pulse/flash-drought-hotspots-southwestern-united-states/)
- [Flash Drought Hotspots: Iberian Peninsula](https://www.planet.com/pulse/flash-drought-hotspots-iberian-peninsula/)

## 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](https://doi.org/10.3390/rs10050788)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//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];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//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];
}

0 comments on commit 5f05c28

Please sign in to comment.