Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Add swc backward average script #331

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions planetary-variables/soil-water-content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Planet's SWC product provides near-daily measurements at spatial resolutions of
- [Soil Water Content Visualization]({% link planetary-variables/soil-water-content/soil-water-content-visualization/index.md %})
- [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 %})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: Soil Water Content Backward Average
grand_parent: Planetary Variables
parent: Soil Water Content
layout: script
nav_exclude: false
scripts:
- [Visualization, script.js]
- [Raw Values, raw.js]
examples:
- zoom: '11'
lat: '41.1921'
lng: '-93.845'
datasetId: '65f7e4fb-a27a-4fae-8d79-06a59d7e6ede'
fromTime: '2022-05-01T00:00:00.000Z'
toTime: '2022-05-26T23:59:59.999Z'
platform:
- EOB
evalscripturl: https://custom-scripts.sentinel-hub.com/custom-scripts/planetary-variables/soil-water-content/soil-water-content-backward-average/script.js
additionalQueryParams:
- - themeId
- PLANET_SANDBOX
---
## General description
The Soil Water Content Backward Average is a method to reduce data gaps and measurement noise in the Soil Water Content (SWC) data. Depending on the requirements, we can choose a lookback period, for example 20 days. The 20-day backward average of SWC for day n is the average of SWC over the 20 days preceding day n. We compute the backward average using all available measurements within this 20-day period, and therefore, we do have a valid value for every day, except in case of prolonged data unavailability, such as during long frost and snow periods.

## Why it is useful
Our Soil Water Content provides a great reflection of the immediate soil conditions. However, daily measurements often exhibit short-term fluctuations due to rainfall events, evaporation, or changes in soil properties. The Soil Water Content Backward Average is suitable for applications where long-term soil moisture conditions are more relevant than daily fluctuations. The moving average operation reduces day-to-day variations and in the resulting time series, seasonal and longer-term changes can be easily detected. It can be used for monitoring drought risk, yield forecasting and analysis of climate change.


## Useful links
- [SWC Technical specifications](https://developers.planet.com/docs/planetary-variables/soil-water-content-technical-specification/)
- [SWC Data sheet](https://planet.widen.net/s/cv7bfjhhd5)
- [Sentinel Hub documentation about Soil Water Content](https://docs.sentinel-hub.com/api/latest/data/planetary-variables/soil-water-content/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//VERSION=3

const nDays = 20; // The number of days to load data for
const scaleFactor = 1000; // The scale factor for the SWC values

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

function preProcessScenes(collections) {
collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
var orbitDateFrom = new Date(orbit.dateFrom)
// Select all images within the last nDays
return orbitDateFrom.getTime() >= (collections.to.getTime() - (nDays * 24 * 3600 * 1000));
})
return collections
}

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

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

return mean_swc_value;
}

function evaluatePixel(samples) {
// When there are no dates, return no data
if (samples.length == 0) return [NaN, NaN, NaN, 0];

// Extract SWC values and dataMask
var swc = samples.map(sample => sample.SWC / scaleFactor);
var dataMask = samples.map(sample => sample.dataMask);

// Calculate mean SWC value
const mean_swc_val = get_mean_swc_value(swc, dataMask);

return [mean_swc_val];
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//VERSION=3

const nDays = 20; // The number of days to load data for
const scaleFactor = 1000; // The scale factor for the SWC values
const vmin = 0.0; // The minimum value of the colormap
const vmax = 0.4; // The maximum value of the colormap

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

function preProcessScenes(collections) {
collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) {
var orbitDateFrom = new Date(orbit.dateFrom)
// Select all images within the last nDays
return orbitDateFrom.getTime() >= (collections.to.getTime() - (nDays * 24 * 3600 * 1000));
})
return collections
}

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

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

return mean_swc_value;
}


const cmap = [
[0.0, 0xfff7ea],
[0.05, 0xfaedda],
[0.1, 0xede4cb],
[0.15, 0xdedcbd],
[0.2, 0xced3af],
[0.25, 0xbdcba3],
[0.3, 0xaac398],
[0.35, 0x96bc90],
[0.4, 0x80b48a],
[0.45, 0x68ac86],
[0.5, 0x4da484],
[0.55, 0x269c83],
[0.6, 0x009383],
[0.65, 0x008a85],
[0.7, 0x008186],
[0.75, 0x007788],
[0.8, 0x006d8a],
[0.85, 0x00618c],
[0.9, 0x00558d],
[0.95, 0x00478f],
[1.0, 0x003492],
];

// Prepare colormap based on provided min and max values
const visualizer = new ColorRampVisualizer(cmap, vmin, vmax);


function evaluatePixel(samples) {
// When there are no dates, return no data
if (samples.length == 0) return [NaN, NaN, NaN, 0];

// Extract SWC values and dataMask
var swc = samples.map(sample => sample.SWC / scaleFactor);
var dataMask = samples.map(sample => sample.dataMask);

// Calculate mean SWC value
const mean_swc_val = get_mean_swc_value(swc, dataMask);

// Set opacity to 0 if there is no valid data
let opacity = 1;
if (isNaN(mean_swc_val)) {
opacity = 0
}

// Apply colormap
imgVals = visualizer.process(mean_swc_val);

return [...imgVals, opacity];
}