Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,48 @@
---
title: Forest Vitality Change Index - Gain Visualization (FVCI-Gain)
parent: Analysis Ready Planetscope
grand_parent: Planet
layout: script
nav_exclude: true
scripts:
- [Visualization, script.js]
additionalQueryParams:
- - themeId
- PLANET_SANDBOX
---

## Evaluate and visualize
- [EO Browser](https://apps.sentinel-hub.com/eo-browser/?zoom=14&lat=44.74116&lng=-0.68441&themeId=PLANET_SANDBOX&visualizationUrl=U2FsdGVkX18GOLfPT%2FOdeWMNJprEadk9%2FIkChCyr9gwge8vm%2BhkdI6vUYWV0tSp8HlOY%2FAeyWpXjS2z2APPpQqFJfSOhtNTTIuEJDM5zvCuN3HhjP6yDpS1%2BPAlYCZlf&evalscript=Ly9WRVJTSU9OPTMKLyogCiAqIEZvcmVzdCBWaXRhbGl0eSBDaGFuZ2UgSW5kZXggLSBHYWluIFZpc3VhbGl6YXRpb24KICogVGhpcyBzY3JpcHQgdmlzdWFsaXplcyBvbmx5IHBvc2l0aXZlIGNoYW5nZXMgKGdhaW5zKSBpbiBmb3Jlc3Qgdml0YWxpdHkKICogYmV0d2VlbiB0d28gdGltZXBvaW50cyBiYXNlZCBvbiB2ZWdldGF0aW9uIGluZGljZXMuCiAqIAogKiBQYXJhbWV0ZXJzOgogKiAtIFZJX1RZUEU6IFZlZ2V0YXRpb24gaW5kZXggdHlwZSAoIk5EVkkiIG9yICJTQVZJIikKICogLSBDQ19NSU46IE1pbmltdW0gY2Fub3B5IGNvdmVyIHBlcmNlbnRhZ2UKICogLSBWSV9NSU4sIFZJX01BWDogUmFuZ2UgZm9yIGNsaXBwaW5nIHZlZ2V0YXRpb24gaW5kZXggdmFsdWVzCiAqIC0gQ0hBTkdFX1RIUkVTSE9MRDogTWluaW11bSAlIGNoYW5nZSB0byBjbGFzc2lmeSBhcyBnYWluCiAqIC0gQ0xBU1NfSU5URVJWQUw6IEludGVydmFsIGZvciBjbGFzc2lmeWluZyBjaGFuZ2UgbWFnbml0dWRlCiAqLwoKZnVuY3Rpb24gc2V0dXAoKSB7CiAgcmV0dXJuIHsKICAgIGlucHV0OiBbCiAgICAgIHsKICAgICAgICBkYXRhc291cmNlOiAiYXJwcyIsCiAgICAgICAgYmFuZHM6IFsicmVkIiwgIm5pciIsICJkYXRhTWFzayIsICJjbG91ZF9tYXNrIl0sCiAgICAgIH0sCiAgICAgIHsKICAgICAgICBkYXRhc291cmNlOiAiY2Fub3B5X2NvdmVyIiwKICAgICAgICBiYW5kczogWyJDQyIsICJkYXRhTWFzayJdLAogICAgICB9LAogICAgXSwKICAgIG91dHB1dDogWwogICAgICB7IGJhbmRzOiA0IH0sIC8vIFJHQkEgZm9yIGdhaW4KICAgIF0sCiAgICBtb3NhaWNraW5nOiAiT1JCSVQiLAogIH07Cn0KCi8vIENvbmZpZ3VyYXRpb24gcGFyYW1ldGVycwpjb25zdCBWSV9UWVBFID0gIk5EVkkiOyAvLyBDaG9vc2UgIk5EVkkiIG9yICJTQVZJIgpjb25zdCBDQ19NSU4gPSAyNTsgICAgICAvLyBNaW5pbXVtIGNhbm9weSBjb3ZlciBwZXJjZW50YWdlIHRvIGNvbnNpZGVyIHZhbGlkIGZvcmVzdApjb25zdCBWSV9NSU4gPSAwLjE1OyAgICAvLyBNaW5pbXVtIHZlZ2V0YXRpb24gaW5kZXggdmFsdWUgZm9yIGNsaXBwaW5nCmNvbnN0IFZJX01BWCA9IDAuODU7ICAgIC8vIE1heGltdW0gdmVnZXRhdGlvbiBpbmRleCB2YWx1ZSBmb3IgY2xpcHBpbmcKY29uc3QgQ0hBTkdFX1RIUkVTSE9MRCA9IDIuNTsgLy8gTWluaW11bSAlIGNoYW5nZSB0byBjbGFzc2lmeSBhcyBnYWluCmNvbnN0IENMQVNTX0lOVEVSVkFMID0gNTsgLy8gSW50ZXJ2YWwgZm9yIGNsYXNzaWZ5aW5nIGNoYW5nZSBpbnRvIHBhbGV0dGVzCmNvbnN0IElOVkFMSURfVkkgPSAtOTk5OTsgIC8vIFZhbHVlIGZvciBpbnZhbGlkIHZlZ2V0YXRpb24gaW5kZXgKY29uc3QgVklfU0NBTEUgPSA5OTsgICAgICAvLyBTY2FsZSBmYWN0b3IgZm9yIFZJIHZhbHVlcyAocmVzdWx0aW5nIGluIHJhbmdlIDEtMTAwKQpjb25zdCBTQVZJX0wgPSAwLjU7ICAgICAgIC8vIFNvaWwgYWRqdXN0bWVudCBmYWN0b3IgZm9yIFNBVkkKCi8vIENvbG9yIHBhbGV0dGUgLSBmb3Igdml0YWxpdHkgZ2FpbnMgKGZyb20gc21hbGwgdG8gc2lnbmlmaWNhbnQpCmNvbnN0IGdhaW5QYWxldHRlID0gWwogIFsxLjAsIDEuMCwgMC42XSwgIC8vIDAgLSBzbWFsbGVzdCBnYWluCiAgWzEuMCwgMS4wLCAwLjVdLAogIFsxLjAsIDEuMCwgMC40XSwKICBbMC45LCAxLjAsIDAuM10sCiAgWzAuOCwgMS4wLCAwLjJdLAogIFswLjcsIDEuMCwgMC4xXSwKICBbMC42LCAxLjAsIDAuMF0sCiAgWzAuNCwgMS4wLCAwLjBdLAogIFswLjIsIDEuMCwgMC4wXSwKICBbMC4wLCAxLjAsIDAuMF0sCiAgWzAuMCwgMC45LCAwLjBdLAogIFswLjAsIDAuOCwgMC4wXSwKICBbMC4wLCAwLjYsIDAuMF0sCiAgWzAuMCwgMC40LCAwLjBdLAogIFswLjAsIDAuMiwgMC4wXSwKICBbMC42LCAwLjAsIDAuNl0sCiAgWzAuNywgMC4wLCAwLjddLAogIFswLjgsIDAuMCwgMC44XSwKICBbMC45LCAwLjAsIDAuOV0sCiAgWzEuMCwgMC4wLCAxLjBdLCAgLy8gMTkgLSBzaWduaWZpY2FudCBnYWluCl07CgovLyBDYWxjdWxhdGUgdmVnZXRhdGlvbiBpbmRleCAoVkkpIGJhc2VkIG9uIHR5cGUKZnVuY3Rpb24gY2FsY3VsYXRlVkkobmlyLCByZWQpIHsKICBpZiAobmlyID09PSB1bmRlZmluZWQgfHwgcmVkID09PSB1bmRlZmluZWQpIHJldHVybiBJTlZBTElEX1ZJOwogIGlmIChuaXIgKyByZWQgPT09IDApIHJldHVybiBJTlZBTElEX1ZJOyAvLyBBdm9pZCBkaXZpc2lvbiBieSB6ZXJvCiAgCiAgaWYgKFZJX1RZUEUgPT09ICJORFZJIikgewogICAgcmV0dXJuIChuaXIgLSByZWQpIC8gKG5pciArIHJlZCk7CiAgfSBlbHNlIGlmIChWSV9UWVBFID09PSAiU0FWSSIpIHsKICAgIHJldHVybiAoKG5pciAtIHJlZCkgLyAobmlyICsgcmVkICsgU0FWSV9MKSkgKiAoMSArIFNBVklfTCk7CiAgfSBlbHNlIHsKICAgIHJldHVybiBJTlZBTElEX1ZJOwogIH0KfQoKLy8gQ2xpcCBWSSB0byBtaW4vbWF4IHJhbmdlIGFuZCBzY2FsZSB0byAxLTEwMCByYW5nZQpmdW5jdGlvbiBjbGlwQW5kU2NhbGVWSSh2aSkgewogIGNvbnN0IGNsaXBwZWRWSSA9IE1hdGgubWF4KFZJX01JTiwgTWF0aC5taW4odmksIFZJX01BWCkpOwogIHJldHVybiBNYXRoLnJvdW5kKCgoY2xpcHBlZFZJIC0gVklfTUlOKSAvIChWSV9NQVggLSBWSV9NSU4pKSAqIFZJX1NDQUxFKSArIDE7Cn0KCi8vIEZpbHRlciBzY2VuZXMgdG8gb25seSBpbmNsdWRlIGZpcnN0IGFuZCBsYXN0IG9mIHNwZWNpZmllZCB0aW1lIHJhbmdlCmZ1bmN0aW9uIHByZVByb2Nlc3NTY2VuZXMoY29sbGVjdGlvbnMpIHsKICAvLyBIZWxwZXIgdG8ga2VlcCBvbmx5IGZpcnN0IGFuZCBsYXN0IG9yYml0IGlmIHBvc3NpYmxlCiAgZnVuY3Rpb24gZmlsdGVyT3JiaXRzKG9yYml0cywgbGFiZWwpIHsKICAgIGlmIChBcnJheS5pc0FycmF5KG9yYml0cykgJiYgb3JiaXRzLmxlbmd0aCA%2BPSAyKSB7CiAgICAgIHJldHVybiBbb3JiaXRzWzBdLCBvcmJpdHNbb3JiaXRzLmxlbmd0aCAtIDFdXTsKICAgIH0gZWxzZSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihgRlZDSSByZXF1aXJlcyBhdCBsZWFzdCAyICR7bGFiZWx9IHNjZW5lcyBmb3IgY2hhbmdlIGRldGVjdGlvbmApOwogICAgfQogIH0KCiAgY29sbGVjdGlvbnMuYXJwcy5zY2VuZXMub3JiaXRzID0gZmlsdGVyT3JiaXRzKGNvbGxlY3Rpb25zLmFycHMuc2NlbmVzLm9yYml0cywgImFycHMiKTsKICBjb2xsZWN0aW9ucy5jYW5vcHlfY292ZXIuc2NlbmVzLm9yYml0cyA9IGZpbHRlck9yYml0cyhjb2xsZWN0aW9ucy5jYW5vcHlfY292ZXIuc2NlbmVzLm9yYml0cywgImNhbm9weSBjb3ZlciIpOwoKICByZXR1cm4gY29sbGVjdGlvbnM7Cn0KCi8vIE1haW4gZnVuY3Rpb24gdG8gZXZhbHVhdGUgZWFjaCBwaXhlbApmdW5jdGlvbiBldmFsdWF0ZVBpeGVsKHNhbXBsZXMpIHsKICAvLyBDaGVjayBmb3IgdW5kZWZpbmVkIGlucHV0cwogIGlmICghc2FtcGxlcy5hcnBzIHx8ICFzYW1wbGVzLmNhbm9weV9jb3ZlciB8fCAKICAgICAgc2FtcGxlcy5hcnBzLmxlbmd0aCA8IDIgfHwgc2FtcGxlcy5jYW5vcHlfY292ZXIubGVuZ3RoIDwgMikgewogICAgcmV0dXJuIFswLCAwLCAwLCAwXTsgLy8gVHJhbnNwYXJlbnQKICB9CgogIGNvbnN0IHBzX2FyZF9UMSA9IHNhbXBsZXMuYXJwc1sxXTsKICBjb25zdCBwc19hcmRfVDIgPSBzYW1wbGVzLmFycHNbMF07CiAgY29uc3QgY2Fub3B5X1QxID0gc2FtcGxlcy5jYW5vcHlfY292ZXJbMV07CiAgY29uc3QgY2Fub3B5X1QyID0gc2FtcGxlcy5jYW5vcHlfY292ZXJbMF07CgogIC8vIENoZWNrIGRhdGEgbWFza3MKICBpZiAocHNfYXJkX1QxLmRhdGFNYXNrID09PSAwIHx8IHBzX2FyZF9UMi5kYXRhTWFzayA9PT0gMCkgewogICAgcmV0dXJuIFswLCAwLCAwLCAwXTsKICB9CgogIC8vIENoZWNrIGNsb3VkIG1hc2tzCiAgaWYgKHBzX2FyZF9UMS5jbG91ZF9tYXNrICE9PSAxIHx8IHBzX2FyZF9UMi5jbG91ZF9tYXNrICE9PSAxKSB7CiAgICByZXR1cm4gWzAsIDAsIDAsIDBdOwogIH0KCiAgLy8gQ2hlY2sgY2Fub3B5IGNvdmVyIHRocmVzaG9sZHMKICBpZiAoY2Fub3B5X1QxLkNDIDw9IENDX01JTiB8fCBjYW5vcHlfVDIuQ0MgPD0gQ0NfTUlOKSB7CiAgICByZXR1cm4gWzAsIDAsIDAsIDBdOwogIH0KCiAgLy8gQ2FsY3VsYXRlIFZJIGZvciBUMSBhbmQgVDIKICBjb25zdCB2aV9UMSA9IGNhbGN1bGF0ZVZJKHBzX2FyZF9UMS5uaXIsIHBzX2FyZF9UMS5yZWQpOwogIGNvbnN0IHZpX1QyID0gY2FsY3VsYXRlVkkocHNfYXJkX1QyLm5pciwgcHNfYXJkX1QyLnJlZCk7CiAgCiAgLy8gQ2hlY2sgZm9yIGludmFsaWQgVkkgdmFsdWVzCiAgaWYgKHZpX1QxID09PSBJTlZBTElEX1ZJIHx8IHZpX1QyID09PSBJTlZBTElEX1ZJKSB7CiAgICByZXR1cm4gWzAsIDAsIDAsIDBdOwogIH0KCiAgLy8gQ2xpcCBhbmQgc2NhbGUgVkkgdmFsdWVzCiAgY29uc3QgdmkxMDBfVDEgPSBjbGlwQW5kU2NhbGVWSSh2aV9UMSk7CiAgY29uc3QgdmkxMDBfVDIgPSBjbGlwQW5kU2NhbGVWSSh2aV9UMik7CgogIC8vIENhbGN1bGF0ZSBwZXJjZW50YWdlIGNoYW5nZSBkaXJlY3RseQogIGNvbnN0IGNoYW5nZVBlcmNlbnQgPSB2aTEwMF9UMiAtIHZpMTAwX1QxOwoKICAvLyBDbGFzc2lmeSBjaGFuZ2UgaW50byBnYWluCiAgaWYgKGNoYW5nZVBlcmNlbnQgPiBDSEFOR0VfVEhSRVNIT0xEKSB7CiAgICBjb25zdCBjbGFzc0luZGV4ID0gTWF0aC5taW4oCiAgICAgIDE5LAogICAgICBNYXRoLmZsb29yKChjaGFuZ2VQZXJjZW50IC0gQ0hBTkdFX1RIUkVTSE9MRCkgLyBDTEFTU19JTlRFUlZBTCkKICAgICk7CiAgICBjb25zdCBjb2xvciA9IGdhaW5QYWxldHRlW2NsYXNzSW5kZXhdOwogICAgcmV0dXJuIFsuLi5jb2xvciwgMV07IC8vIEZ1bGx5IG9wYXF1ZSBnYWluCiAgfSBlbHNlIHsKICAgIHJldHVybiBbMCwgMCwgMCwgMF07IC8vIEZ1bGx5IHRyYW5zcGFyZW50IGlmIG5vIHNpZ25pZmljYW50IGNoYW5nZQogIH0KfQo%3D&datasetId=3f605f75-86c4-411a-b4ae-01c896f0e54e&fromTime=2023-03-20T00%3A00%3A00.000Z&toTime=2023-07-14T23%3A59%3A59.999Z&demSource3D=%22MAPZEN%22&dataFusion=%5B%7B%22id%22%3A%22CUSTOM%22%2C%22alias%22%3A%22arps%22%2C%22additionalParameters%22%3A%7B%22collectionId%22%3A%223f605f75-86c4-411a-b4ae-01c896f0e54e%22%2C%22subType%22%3Anull%2C%22locationId%22%3A%22aws-eu-central-1%22%7D%7D%2C%7B%22id%22%3A%22CUSTOM%22%2C%22alias%22%3A%22canopy_cover%22%2C%22additionalParameters%22%3A%7B%22collectionId%22%3A%22ca501757-cf8e-43a8-b1a4-1aa59ae22425%22%2C%22subType%22%3A%22BYOC%22%2C%22locationId%22%3A%22aws-eu-central-1%22%7D%2C%22timespan%22%3A%5B%222023-03-21T00%3A00%3A00.000Z%22%2C%222023-07-21T23%3A59%3A59.999Z%22%5D%7D%5D#custom-script){:target="_blank"}

The example data is using Planet Sandox data. This data is restricted to Sentinel Hub users with active paid plans. If you are already a Planet Customer, see [here](https://community.planet.com/sentinel-hub-81/access-new-tools-for-analyzing-your-planet-data-on-sentinel-hub-732) on how to get access.

## General description

The FVCI-Gain script visualizes positive changes in forest vitality between two time periods. This specialized visualization focuses exclusively on areas showing improvement in forest health, making it easier to identify regeneration, growth, or recovery patterns. The script combines Analysis Ready PlanetScope imagery with the Canopy Cover layer from the Forest Carbon Monitoring dataset to ensure analysis is focused on forest areas only.

## Details of the script

The FVCI-Gain processing workflow includes:
1. Filter pixels based on canopy cover percentage (>25%) for both time periods
2. Calculate vegetation index (users can choose between NDVI or SAVI) for both time periods
3. Clip and scale both indices to a standardized range
4. Calculate the difference between the time periods
5. Identify pixels with positive change exceeding the threshold (>2.5%)
6. Classify positive changes into 20 intensity levels
7. Apply a color palette transitioning from yellow through green to purple to visualize different gain intensities

The script utilizes the first and last PlanetScope ARD acquisitions and Canopy Cover products in the specified time range.

## Description of representative images

The FVCI-Gain visualization uses a color palette specifically designed to highlight forest vitality improvements:
- Light yellow to yellow: Minor improvements in forest health
- Yellow-green to green: Moderate improvements
- Dark green: Significant improvements
- Purple to magenta: Major improvements/regeneration

Areas with no significant improvement or insufficient canopy cover remain transparent.

A visualization of FVCI-Gain between March and July 2023 over Cestas, France:

![FVCI-Gain over Cestas](fig/fig1.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//VERSION=3
/*
* Forest Vitality Change Index - Gain Visualization
* This script visualizes only positive changes (gains) in forest vitality
* between two timepoints based on vegetation indices.
*
* Parameters:
* - VI_TYPE: Vegetation index type ("NDVI" or "SAVI")
* - CC_MIN: Minimum canopy cover percentage
* - VI_MIN, VI_MAX: Range for clipping vegetation index values
* - CHANGE_THRESHOLD: Minimum % change to classify as gain
* - CLASS_INTERVAL: Interval for classifying change magnitude
*/

function setup() {
return {
input: [
{
datasource: "arps",
bands: ["red", "nir", "dataMask", "cloud_mask"],
},
{
datasource: "canopy_cover",
bands: ["CC", "dataMask"],
},
],
output: [
{ bands: 4 }, // RGBA for gain
],
mosaicking: "ORBIT",
};
}

// Configuration parameters
const VI_TYPE = "NDVI"; // Choose "NDVI" or "SAVI"
const CC_MIN = 25; // Minimum canopy cover percentage to consider valid forest
const VI_MIN = 0.15; // Minimum vegetation index value for clipping
const VI_MAX = 0.85; // Maximum vegetation index value for clipping
const CHANGE_THRESHOLD = 2.5; // Minimum % change to classify as gain
const CLASS_INTERVAL = 5; // Interval for classifying change into palettes
const INVALID_VI = -9999; // Value for invalid vegetation index
const VI_SCALE = 99; // Scale factor for VI values (resulting in range 1-100)
const SAVI_L = 0.5; // Soil adjustment factor for SAVI

// Color palette - for vitality gains (from small to significant)
const gainPalette = [
[1.0, 1.0, 0.6], // 0 - smallest gain
[1.0, 1.0, 0.5],
[1.0, 1.0, 0.4],
[0.9, 1.0, 0.3],
[0.8, 1.0, 0.2],
[0.7, 1.0, 0.1],
[0.6, 1.0, 0.0],
[0.4, 1.0, 0.0],
[0.2, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.9, 0.0],
[0.0, 0.8, 0.0],
[0.0, 0.6, 0.0],
[0.0, 0.4, 0.0],
[0.0, 0.2, 0.0],
[0.6, 0.0, 0.6],
[0.7, 0.0, 0.7],
[0.8, 0.0, 0.8],
[0.9, 0.0, 0.9],
[1.0, 0.0, 1.0], // 19 - significant gain
];

// Calculate vegetation index (VI) based on type
function calculateVI(nir, red) {
if (nir === undefined || red === undefined) return INVALID_VI;
if (nir + red === 0) return INVALID_VI; // Avoid division by zero

if (VI_TYPE === "NDVI") {
return (nir - red) / (nir + red);
} else if (VI_TYPE === "SAVI") {
return ((nir - red) / (nir + red + SAVI_L)) * (1 + SAVI_L);
} else {
return INVALID_VI;
}
}

// Clip VI to min/max range and scale to 1-100 range
function clipAndScaleVI(vi) {
const clippedVI = Math.max(VI_MIN, Math.min(vi, VI_MAX));
return Math.round(((clippedVI - VI_MIN) / (VI_MAX - VI_MIN)) * VI_SCALE) + 1;
}

// Filter scenes to only include first and last of specified time range
function preProcessScenes(collections) {
// Helper to keep only first and last orbit if possible
function filterOrbits(orbits, label) {
if (Array.isArray(orbits) && orbits.length >= 2) {
return [orbits[0], orbits[orbits.length - 1]];
} else {
throw new Error(`FVCI requires at least 2 ${label} scenes for change detection`);
}
}

collections.arps.scenes.orbits = filterOrbits(collections.arps.scenes.orbits, "arps");
collections.canopy_cover.scenes.orbits = filterOrbits(collections.canopy_cover.scenes.orbits, "canopy cover");

return collections;
}

// Main function to evaluate each pixel
function evaluatePixel(samples) {
// Check for undefined inputs
if (!samples.arps || !samples.canopy_cover ||
samples.arps.length < 2 || samples.canopy_cover.length < 2) {
return [0, 0, 0, 0]; // Transparent
}

const ps_ard_T1 = samples.arps[1];
const ps_ard_T2 = samples.arps[0];
const canopy_T1 = samples.canopy_cover[1];
const canopy_T2 = samples.canopy_cover[0];

// Check data masks
if (ps_ard_T1.dataMask === 0 || ps_ard_T2.dataMask === 0) {
return [0, 0, 0, 0];
}

// Check cloud masks
if (ps_ard_T1.cloud_mask !== 1 || ps_ard_T2.cloud_mask !== 1) {
return [0, 0, 0, 0];
}

// Check canopy cover thresholds
if (canopy_T1.CC <= CC_MIN || canopy_T2.CC <= CC_MIN) {
return [0, 0, 0, 0];
}

// Calculate VI for T1 and T2
const vi_T1 = calculateVI(ps_ard_T1.nir, ps_ard_T1.red);
const vi_T2 = calculateVI(ps_ard_T2.nir, ps_ard_T2.red);

// Check for invalid VI values
if (vi_T1 === INVALID_VI || vi_T2 === INVALID_VI) {
return [0, 0, 0, 0];
}

// Clip and scale VI values
const vi100_T1 = clipAndScaleVI(vi_T1);
const vi100_T2 = clipAndScaleVI(vi_T2);

// Calculate percentage change directly
const changePercent = vi100_T2 - vi100_T1;

// Classify change into gain
if (changePercent > CHANGE_THRESHOLD) {
const classIndex = Math.min(
19,
Math.floor((changePercent - CHANGE_THRESHOLD) / CLASS_INTERVAL)
);
const color = gainPalette[classIndex];
return [...color, 1]; // Fully opaque gain
} else {
return [0, 0, 0, 0]; // Fully transparent if no significant change
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading