-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #322 from willrayeo/planetscope-veg-indices
Planetscope veg indices
- Loading branch information
Showing
11 changed files
with
490 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
title: 'Max Multitemporal NDVI' | ||
parent: Planetscope | ||
grand_parent: Planet | ||
layout: script | ||
permalink: /planet_scope/max_ndvi/ | ||
nav_exclude: true | ||
scripts: | ||
- - Visualization | ||
- script.js | ||
- - EO Browser | ||
- eob.js | ||
- - Raw Values | ||
- raw.js | ||
examples: | ||
- zoom: '15' | ||
lat: '-16.60556' | ||
lng: '-48.80419' | ||
datasetId: 'ccb1f8f0-e5bf-4c31-afe5-d8803bcbde2a' | ||
fromTime: '2022-11-01T00:00:00.000Z' | ||
toTime: '2023-11-19T23:59:59.999Z' | ||
platform: | ||
- EOB | ||
evalscripturl: https://raw.githubusercontent.com/sentinel-hub/custom-scripts/master/planet_scope/max_ndvi/script.js | ||
additionalQueryParams: | ||
- - themeId | ||
- PLANET_SANDBOX | ||
--- | ||
|
||
## General description | ||
|
||
The script evaluates the NDVI for each scene of the past month and returns the highest NDVI value for every pixel. In short, it returns the highest NDVI values of the past month for every pixel. The script runs on-the-fly, since | ||
it doesn't require preprocessing. It can be used as a cloud free background or an input for further analysis, such as change detection and classification. [Find out more.](https://www.sentinel-hub.com/max_service){:target="_blank"} | ||
Note that multi-temporal processing needs to be enabled for this script to run. | ||
|
||
## Author of the script | ||
|
||
William Ray | ||
|
||
## Description of representative images | ||
|
||
![figure 1](fig/fig1.png) | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//VERSION=3 | ||
|
||
//Basic initialization setup function | ||
function setup() { | ||
return { | ||
//List of all bands, that will be used in the script, either for visualization or for choosing best pixel | ||
input: [{ | ||
bands: [ | ||
"Red", | ||
"NIR", | ||
] | ||
}], | ||
//This can always be the same if one is doing RGB images | ||
output: [ | ||
{ id: "default", bands: 4 }, | ||
{ id: "index", bands: 1, sampleType: "FLOAT32" }, | ||
{ id: "eobrowserStats", bands: 2, sampleType: "FLOAT32" }, | ||
{ id: "dataMask", bands: 1 } | ||
], | ||
mosaicking: "ORBIT" | ||
} | ||
} | ||
|
||
/* | ||
In this function we limit the scenes, which are used for processing. | ||
These are based also on input variables. | ||
E.g. if one sets date "2017-03-01" ("TO date") and cloud coverage filter 30%, | ||
all scenes older than 2017-03-01 with cloud coverage 30% will be checked against | ||
further conditions in this function (in this function it is currently limited to 3 months). | ||
The more scenes there are, longer it will take to process the data. | ||
After 60 seconds of processing, there will be a timeout. | ||
*/ | ||
|
||
function preProcessScenes(collections) { | ||
collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) { | ||
var orbitDateFrom = new Date(orbit.dateFrom) | ||
return orbitDateFrom.getTime() >= (collections.to.getTime() - 3 * 31 * 24 * 3600 * 1000); | ||
}) | ||
return collections | ||
} | ||
|
||
function calcNDVI(sample) { | ||
var denom = sample.Red + sample.NIR; | ||
return ((denom != 0) ? (sample.NIR - sample.Red) / denom : NaN); | ||
} | ||
function evaluatePixel(samples) { | ||
var max = Number.NEGATIVE_INFINITY; | ||
for (let i = 0; i < samples.length; i++) { | ||
var ndvi = calcNDVI(samples[i]); | ||
max = ndvi > max ? ndvi : max; | ||
} | ||
let max_exists = 0; | ||
if (isFinite(max)) { | ||
max_exists = 1; | ||
} | ||
let imgVals; | ||
if (max < -1.1) { imgVals = [0, 0, 0]; } | ||
else if (max < - 0.2) { imgVals = [0.75, 0.75, 0.75]; } | ||
else if (max < - 0.1) { imgVals = [0.86, 0.86, 0.86]; } | ||
else if (max < 0) { imgVals = [1, 1, 0.88]; } | ||
else if (max < 0.025) { imgVals = [1, 0.98, 0.8]; } | ||
else if (max < 0.05) { imgVals = [0.93, 0.91, 0.71]; } | ||
else if (max < 0.075) { imgVals = [0.87, 0.85, 0.61]; } | ||
else if (max < 0.1) { imgVals = [0.8, 0.78, 0.51]; } | ||
else if (max < 0.125) { imgVals = [0.74, 0.72, 0.42]; } | ||
else if (max < 0.15) { imgVals = [0.69, 0.76, 0.38]; } | ||
else if (max < 0.175) { imgVals = [0.64, 0.8, 0.35]; } | ||
else if (max < 0.2) { imgVals = [0.57, 0.75, 0.32]; } | ||
else if (max < 0.25) { imgVals = [0.5, 0.7, 0.28]; } | ||
else if (max < 0.3) { imgVals = [0.44, 0.64, 0.25]; } | ||
else if (max < 0.35) { imgVals = [0.38, 0.59, 0.21]; } | ||
else if (max < 0.4) { imgVals = [0.31, 0.54, 0.18]; } | ||
else if (max < 0.45) { imgVals = [0.25, 0.49, 0.14]; } | ||
else if (max < 0.5) { imgVals = [0.19, 0.43, 0.11]; } | ||
else if (max < 0.55) { imgVals = [0.13, 0.38, 0.07]; } | ||
else if (max < 0.6) { imgVals = [0.06, 0.33, 0.04]; } | ||
else { imgVals = [0, 0.27, 0]; } | ||
return { | ||
default: imgVals.concat(max_exists), | ||
index: [max], | ||
eobrowserStats: [max, max_exists], | ||
dataMask: [max_exists] | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
//VERSION=3 | ||
|
||
//Basic initialization setup function | ||
function setup() { | ||
return { | ||
//List of all bands, that will be used in the script, either for visualization or for choosing best pixel | ||
input: [{ | ||
bands: [ | ||
"Red", | ||
"NIR" | ||
] | ||
}], | ||
//This can always be the same if one is doing RGB images | ||
output: { bands: 1 }, | ||
mosaicking: "ORBIT" | ||
} | ||
} | ||
|
||
/* | ||
In this function we limit the scenes, which are used for processing. | ||
These are based also on input variables. | ||
E.g. if one sets date "2017-03-01" ("TO date") and cloud coverage filter 30%, | ||
all scenes older than 2017-03-01 with cloud coverage 30% will be checked against | ||
further conditions in this function (in this function it is currently limited to 3 months). | ||
The more scenes there are, longer it will take to process the data. | ||
After 60 seconds of processing, there will be a timeout. | ||
*/ | ||
|
||
function preProcessScenes(collections) { | ||
collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) { | ||
var orbitDateFrom = new Date(orbit.dateFrom) | ||
return orbitDateFrom.getTime() >= (collections.to.getTime() - 3 * 31 * 24 * 3600 * 1000); | ||
}) | ||
return collections | ||
} | ||
|
||
function calcNDVI(sample) { | ||
var denom = sample.Red + sample.NIR; | ||
return ((denom != 0) ? (sample.NIR - sample.Red) / denom : NaN); | ||
} | ||
function evaluatePixel(samples) { | ||
var max = Number.NEGATIVE_INFINITY; | ||
for (let i = 0; i < samples.length; i++) { | ||
var ndvi = calcNDVI(samples[i]); | ||
max = ndvi > max ? ndvi : max; | ||
} | ||
return [max]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
//VERSION=3 | ||
|
||
//Basic initialization setup function | ||
function setup() { | ||
return { | ||
//List of all bands, that will be used in the script, either for visualization or for choosing best pixel | ||
input: [{ | ||
bands: [ | ||
"Red", | ||
"NIR" | ||
] | ||
}], | ||
//This can always be the same if one is doing RGB images | ||
output: { bands: 4 }, | ||
mosaicking: "ORBIT" | ||
} | ||
} | ||
|
||
/* | ||
In this function we limit the scenes, which are used for processing. | ||
These are based also on input variables. | ||
E.g. if one sets date "2017-03-01" ("TO date") and cloud coverage filter 30%, | ||
all scenes older than 2017-03-01 with cloud coverage 30% will be checked against | ||
further conditions in this function (in this function it is currently limited to 3 months). | ||
The more scenes there are, longer it will take to process the data. | ||
After 60 seconds of processing, there will be a timeout. | ||
*/ | ||
|
||
function preProcessScenes(collections) { | ||
collections.scenes.orbits = collections.scenes.orbits.filter(function (orbit) { | ||
var orbitDateFrom = new Date(orbit.dateFrom) | ||
return orbitDateFrom.getTime() >= (collections.to.getTime() - 3 * 31 * 24 * 3600 * 1000); | ||
}) | ||
return collections | ||
} | ||
|
||
function calcNDVI(sample) { | ||
var denom = sample.Red + sample.NIR; | ||
return ((denom != 0) ? (sample.NIR - sample.Red) / denom : NaN); | ||
} | ||
function evaluatePixel(samples) { | ||
var max = Number.NEGATIVE_INFINITY; | ||
for (let i = 0; i < samples.length; i++) { | ||
var ndvi = calcNDVI(samples[i]); | ||
max = ndvi > max ? ndvi : max; | ||
} | ||
let max_exists = 0; | ||
if (isFinite(max)) { | ||
max_exists = 1; | ||
} | ||
if (max < -1.1) { return [0, 0, 0, max_exists]; } | ||
else if (max < - 0.2) { return [0.75, 0.75, 0.75, max_exists]; } | ||
else if (max < - 0.1) { return [0.86, 0.86, 0.86, max_exists]; } | ||
else if (max < 0) { return [1, 1, 0.88, max_exists]; } | ||
else if (max < 0.025) { return [1, 0.98, 0.8, max_exists]; } | ||
else if (max < 0.05) { return [0.93, 0.91, 0.71, max_exists]; } | ||
else if (max < 0.075) { return [0.87, 0.85, 0.61, max_exists]; } | ||
else if (max < 0.1) { return [0.8, 0.78, 0.51, max_exists]; } | ||
else if (max < 0.125) { return [0.74, 0.72, 0.42, max_exists]; } | ||
else if (max < 0.15) { return [0.69, 0.76, 0.38, max_exists]; } | ||
else if (max < 0.175) { return [0.64, 0.8, 0.35, max_exists]; } | ||
else if (max < 0.2) { return [0.57, 0.75, 0.32, max_exists]; } | ||
else if (max < 0.25) { return [0.5, 0.7, 0.28, max_exists]; } | ||
else if (max < 0.3) { return [0.44, 0.64, 0.25, max_exists]; } | ||
else if (max < 0.35) { return [0.38, 0.59, 0.21, max_exists]; } | ||
else if (max < 0.4) { return [0.31, 0.54, 0.18, max_exists]; } | ||
else if (max < 0.45) { return [0.25, 0.49, 0.14, max_exists]; } | ||
else if (max < 0.5) { return [0.19, 0.43, 0.11, max_exists]; } | ||
else if (max < 0.55) { return [0.13, 0.38, 0.07, max_exists]; } | ||
else if (max < 0.6) { return [0.06, 0.33, 0.04, max_exists]; } | ||
else { return [0, 0.27, 0, max_exists]; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--- | ||
title: NDVI difference between two dates | ||
parent: Planetscope | ||
grand_parent: Planet | ||
layout: script | ||
permalink: /planet_scope/ndvi_difference/ | ||
nav_exclude: true | ||
scripts: | ||
- - Visualization | ||
- script.js | ||
- - EO Browser | ||
- eob.js | ||
- - Raw Values | ||
- raw.js | ||
examples: | ||
- zoom: '14' | ||
lat: '52.24714' | ||
lng: '9.20886' | ||
datasetId: Planetscope | ||
fromTime: '2023-04-02T00:00:00.000Z' | ||
toTime: '2023-06-01T23:59:59.999Z' | ||
platform: | ||
- EOB | ||
evalscripturl: https://raw.githubusercontent.com/sentinel-hub/custom-scripts/master/planet_scope/ndvi_difference/script.js | ||
additionalQueryParams: | ||
- - themeId | ||
- PLANET_SANDBOX | ||
--- | ||
|
||
## 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`](https://docs.sentinel-hub.com/api/latest/evalscript/v3/#mosaicking) and [`preProcessScenes`](https://docs.sentinel-hub.com/api/latest/evalscript/v3/#preprocessscenes-function-optional) 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](#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](https://www.sentinel-hub.com/faq/#how-configure-your-layers-statistical-info-eo-browser) 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](fig/fig1.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
//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 | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.