Before starting, make sure you are comfortable with these terms. They appear throughout all code blocks in this course.
| Term | Full Name | What it means in simple words |
|---|---|---|
| GEE | Google Earth Engine | A free cloud platform where you write JavaScript code to analyze satellite images without downloading anything to your computer |
| GFC | Global Forest Change | The name of the Hansen dataset -- a global map of where forests existed in 2000 and where they were lost or gained since then |
| Landsat | Land Remote Sensing Satellite | A family of NASA/USGS satellites that have been photographing Earth since 1972 at 30m resolution. The Hansen GFC dataset is built entirely from Landsat images |
| MODIS | Moderate Resolution Imaging Spectroradiometer | A sensor on NASA's Terra and Aqua satellites that scans the entire Earth every 1-2 days at 250m-1km resolution. Used here for fire detection |
| FIRMS | Fire Information for Resource Management System | A NASA system that processes MODIS and VIIRS data daily to detect active fires anywhere on Earth. Each fire detection is called a "hotspot" |
| VIIRS | Visible Infrared Imaging Radiometer Suite | A newer NASA sensor (on Suomi-NPP satellite) that detects fires with higher resolution than MODIS. FIRMS combines both MODIS and VIIRS data |
| GEDI | Global Ecosystem Dynamics Investigation | A NASA lidar instrument on the International Space Station that measures tree height by firing laser pulses at the forest and timing the return signal |
| ETH | ETH Zurich (EidgenΓΆssische Technische Hochschule) | A leading Swiss university. Their canopy height dataset (Lang et al. 2023) was produced by training a deep learning model on Sentinel-2 images + GEDI measurements |
| WDPA | World Database on Protected Areas | A global database of national parks, nature reserves, and other protected zones, maintained by IUCN and UNEP. Available as a vector dataset in GEE |
| FAO GAUL | Food & Agriculture Organization Global Administrative Unit Layers | Official country and province boundary polygons from the UN. Used in GEE to filter analysis by country or region |
| Band | -- | A single layer of data in a satellite image. Like a channel -- a raster image can have many bands, each storing a different variable (e.g. loss year, canopy cover) |
| Pixel | -- | The smallest unit of a raster image. In the Hansen dataset, each pixel covers 30x30 meters on the ground |
| Mask | -- | Hiding certain pixels from analysis. A masked pixel is treated as "no data" and does not appear on the map or in calculations |
| Reducer | -- | A GEE operation that summarizes many pixel values into one number -- for example, summing all pixel areas to get total hectares |
| Scale | -- | The pixel size (in meters) used for a calculation. Larger scale = faster but less precise. Smaller scale = slower but more accurate |
| Confidence | -- | In FIRMS, a 0-100 score indicating how certain the algorithm is that a pixel is actually on fire. Values β₯50 are considered reliable detections |
The Hansen Global Forest Change (GFC) dataset, produced by the University of Maryland and available free in GEE, is the world's most widely used annual deforestation product. It covers 2000 to 2023 at 30m resolution using Landsat imagery -- a family of NASA/USGS satellites that have been photographing Earth continuously since 1972.
Think of it as a global before-and-after photo album: the dataset records exactly where forest existed in the year 2000, and tracks pixel by pixel where trees were lost or gained in each subsequent year up to 2023.
UMD/hansen/global_forest_change_2023_v1_11lossyear)Course04_Forest_Hansen_DeepSeek[-6.5, 33.0, -1.5, 36.0]. You can also narrow to just the Rif: [-6.0, 34.5, -4.5, 35.8], or expand to the full Atlas range.The Hansen dataset is a single multi-band image. Before prompting DeepSeek, students must understand what each band represents:
| Band Name | Type | Values | Use Case |
|---|---|---|---|
treecover2000 | Continuous | 0-100 (%) | Canopy cover in year 2000 |
loss | Binary | 0 or 1 | Pixel experienced forest loss (2001-2023) |
lossyear | Integer | 1-23 | Year of loss (1=2001, 23=2023) |
gain | Binary | 0 or 1 | Forest gain between 2000-2012 |
datamask | Integer | 0,1,2 | Valid land (1), water (2) |
first_b30 | Reflectance | 0-255 | Landsat band (for visualization) |
treecover2000 >= 10% for North Morocco (sparse semi-arid forests). Loss pixels where lossyear == 20 means forest loss occurred in 2020 (the encoding is: value 1 = 2001, value 2 = 2002 ... value 23 = 2023). This integer encoding makes filtering by year very simple.datamask? This band tells GEE which pixels are usable. Value 0 = no data (outside coverage), value 1 = valid land pixel, value 2 = water body. We always filter to datamask == 1 so that ocean and river pixels are excluded from our forest calculations.The prompt is the core skill of this course. Write it precisely: include the exact dataset ID, band names, threshold, study area, desired outputs.
treecover2000.gte(30))ee.Image.pixelArea()ui.Chart.image.byClassIf DeepSeek output is cut short, type: "Continue the code from the legend panel". If a chart is missing, type: "Add the annual loss bar chart by year 2001 to 2023".
The lossyear band encodes the year of loss as an integer (1=2001 ... 23=2023). To visualize a gradient of loss years, use a sequential palette:
// Study area: North Morocco (Rif Mountains & Middle Atlas)
var studyArea = ee.Geometry.Rectangle([-6.5, 33.0, -1.5, 36.0]);
Map.setCenter(-4.0, 34.5, 8);
var hansen = ee.Image('UMD/hansen/global_forest_change_2023_v1_11');
var lossYear = hansen.select('lossyear');
var loss = hansen.select('loss');
var treeCover = hansen.select('treecover2000');
// Define forest mask (>= 10% canopy cover -- semi-arid Moroccan threshold)
var forestMask = treeCover.gte(10);
// Mask loss to forest pixels only
var forestLoss = loss.updateMask(loss).updateMask(forestMask);
var lossYearMasked = lossYear.updateMask(loss).updateMask(forestMask);
// Visualize loss year with temporal gradient
Map.addLayer(treeCover.updateMask(forestMask).clip(studyArea),
{min: 10, max: 100, palette: ['#A8D5A2','#2D6A4F','#1B4332']},
'Tree Cover 2000');
Map.addLayer(lossYearMasked.clip(studyArea),
{min: 1, max: 23, palette: ['#FFFFB2','#FED976','#FEB24C','#FD8D3C','#FC4E2A','#E31A1C','#B10026']},
'Forest Loss Year (2001-2023)');updateMask(condition) -- hides all pixels where the condition is false. For example, updateMask(forestMask) keeps only pixels that were forest in 2000 and removes everything else from the map and calculations.selfMask() -- a shortcut that hides pixels with a value of 0. Instead of writing updateMask(image.gt(0)), you can just write .selfMask(). Used here so that non-loss pixels (value 0) do not appear as a colored layer on the map.Beyond loss detection, you can analyze forest structure. Ask DeepSeek to add the Global Canopy Height Model (Lang et al. 2023) available in GEE:
// ETH Global Canopy Height Model (Lang et al. 2023, Sentinel-2 based, 10m)
// Correct GEE asset: users/nlang/ETH_GlobalCanopyHeight_2020_10m_v1
var canopyHeight = ee.Image('users/nlang/ETH_GlobalCanopyHeight_2020_10m_v1').select('b1');
Map.addLayer(canopyHeight.clip(studyArea),
{min: 0, max: 40, palette: ['#F7FCF0','#A8D5B5','#52B788','#2D6A4F','#1B4332']},
'Canopy Height (m) 2020');
// Mean height of lost forest pixels
var heightInLoss = canopyHeight.updateMask(forestLoss);
var meanHeight = heightInLoss.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: studyArea,
scale: 100,
maxPixels: 1e9
});
print('Mean canopy height in lost forest (m):', meanHeight.get('b1'));reduceRegion() -- a GEE function that takes all pixel values inside a geometry and computes a summary statistic (here: mean). Think of it as: "calculate the average height of all pixels inside my study area". The result is a dictionary you read with .get('band_name').A key deliverable: an annual bar chart of hectares of forest lost per year. This is what policy-makers and NGOs want to see. Ask DeepSeek to generate it with a loop over years 2001-2023:
// Single-pass approach: count pixels per loss year using frequencyHistogram
// Much faster -- avoids "Too many concurrent aggregations" error
var lossPixelCount = lossYearMasked.reduceRegion({
reducer: ee.Reducer.frequencyHistogram(),
geometry: studyArea,
scale: 100, // use 100m for speed; change to 30 for exact area
maxPixels: 1e10,
bestEffort: true
});
// Each pixel at 100m = 1 ha (100x100m = 10,000 m2 = 1 ha)
// At scale 30m: divide pixel count by ~11.11 to get ha (900m2/10000)
var pixelAreaHa = ee.Number(100).multiply(100).divide(10000); // = 1 ha at scale 100
var histogram = ee.Dictionary(lossPixelCount.get('lossyear'));
// Convert pixel counts to hectares and build FeatureCollection for chart
var years = ee.List.sequence(1, 23);
var annualLossFc = ee.FeatureCollection(years.map(function(y) {
y = ee.Number(y);
var yearKey = y.int().format('%d');
var count = ee.Number(histogram.get(yearKey, 0));
var ha = count.multiply(pixelAreaHa);
return ee.Feature(null, {year: y.add(2000), area_ha: ha});
}));
var chart = ui.Chart.feature.byFeature({
features: annualLossFc,
xProperty: 'year',
yProperties: ['area_ha']
}).setChartType('ColumnChart')
.setOptions({
title: 'Annual Forest Loss - North Morocco (2001-2023)',
hAxis: {title: 'Year', format: '####'},
vAxis: {title: 'Forest Loss (ha)'},
colors: ['#B10026'],
bar: {groupWidth: '80%'},
legend: {position: 'none'},
backgroundColor: '#FFFFFF'
});
print(chart);reduceRegion calls, triggering GEE's "Too many concurrent aggregations" error. The histogram approach computes all 23 years in a single pass -- faster, cheaper, and error-free. Scale is set to 100m for speed; areas are still in hectares since 100x100m = 1 ha exactly.frequencyHistogram() -- a special GEE reducer that counts how many pixels have each unique value. For example, applied to the lossyear band it returns a dictionary like {1: 3200, 2: 4100, ..., 23: 5800} where each key is a year code and each value is the pixel count for that year. One calculation instead of 23.bestEffort: true -- tells GEE "if the calculation is too heavy at scale 100m, automatically use a coarser scale to avoid a timeout error". This is useful for large study areas.scale: 100 -- each pixel in this calculation is 100x100 meters = 10,000 mΒ² = exactly 1 hectare. This makes the area conversion trivial: pixel count = hectares. At scale 30m (Hansen native resolution) each pixel is only 900 mΒ², so you would need to divide by 11.11 to get hectares.ee.Image.pixelArea() -- an alternative approach that creates an image where every pixel's value equals its area in square meters. Multiplying it by a binary mask then summing gives total area. Here we avoid it by using scale 100m directly.A key driver of forest loss is fire. Ask DeepSeek to overlay FIRMS MODIS fire alerts with Hansen loss to identify fire-driven deforestation:
>= 50 as a balanced threshold to keep reliable detections while removing noise.// ---- Fire & Deforestation Linkage ----
// Requires: studyArea, forestMask, lossYear, loss (defined in main script)
// Recent loss: 2020-2023 = lossyear values 20-23
var recentLoss = lossYear.gte(20).and(loss.eq(1)).updateMask(forestMask);
// Load FIRMS MODIS fire detections (confidence >= 50)
var fireMask = ee.ImageCollection('FIRMS')
.filterBounds(studyArea)
.filterDate('2020-01-01', '2023-12-31')
.select('confidence')
.map(function(img) { return img.gte(50).selfMask(); });
// Fire frequency raster: how many days each pixel had a detection
var fireFrequency = fireMask.sum().rename('fire_frequency').clip(studyArea);
var fireBinary = fireFrequency.gt(0).rename('fire_detected');
// Intersect: pixels that are both recently lost AND had fire
var fireLoss = recentLoss.updateMask(fireBinary).rename('fire_driven_loss');
// Visualise
Map.addLayer(recentLoss.selfMask().clip(studyArea),
{palette: ['#FD8D3C']}, 'Recent Forest Loss (2020-2023)');
Map.addLayer(fireFrequency.selfMask(),
{min: 1, max: 20, palette: ['#FFEDA0','#FC4E2A','#800026']},
'Fire Frequency 2020-2023');
Map.addLayer(fireLoss.selfMask().clip(studyArea),
{palette: ['#FF0000']}, 'Fire-Driven Forest Loss');
// Compute areas and percentage (scale 100m = 1 ha per pixel)
var pixelAreaHa = ee.Number(100).multiply(100).divide(10000);
var recentLossHa = ee.Number(
recentLoss.reduceRegion({reducer: ee.Reducer.sum(), geometry: studyArea,
scale: 100, maxPixels: 1e10, bestEffort: true}).get('lossyear')
).multiply(pixelAreaHa);
var fireLossHa = ee.Number(
fireLoss.reduceRegion({reducer: ee.Reducer.sum(), geometry: studyArea,
scale: 100, maxPixels: 1e10, bestEffort: true}).get('fire_driven_loss')
).multiply(pixelAreaHa);
print('Recent forest loss 2020-2023 (ha):', recentLossHa);
print('Fire-driven loss (ha):', fireLossHa);
print('Fire-driven loss (%):', fireLossHa.divide(recentLossHa).multiply(100));
// Bar chart: fire detections per year
var fireByYear = ee.FeatureCollection([2020,2021,2022,2023].map(function(yr) {
var count = ee.ImageCollection('FIRMS').filterBounds(studyArea)
.filterDate(yr+'-01-01', yr+'-12-31').select('confidence')
.map(function(img){ return img.gte(50).selfMask(); }).sum()
.reduceRegion({reducer: ee.Reducer.sum(), geometry: studyArea,
scale: 1000, maxPixels: 1e9, bestEffort: true});
return ee.Feature(null, {year: yr, fire_detections: count.get('confidence')});
}));
print(ui.Chart.feature.byFeature({
features: fireByYear, xProperty: 'year', yProperties: ['fire_detections']
}).setChartType('ColumnChart').setOptions({
title: 'MODIS Fire Detections per Year -- North Morocco',
hAxis: {title: 'Year', format: '####'},
vAxis: {title: 'Fire Pixel Count'},
colors: ['#E31A1C'], bar: {groupWidth: '70%'}, legend: {position: 'none'}
}));'2021-07-01' to '2021-10-31' to focus on the major 2021 Rif fires. The bar chart uses scale: 1000m to stay within computation limits.// ---- Legend ----
// Covers all map layers: forest cover, annual loss, gain, fire
var legend = ui.Panel({style: {position: 'bottom-left', padding: '10px', width: '240px'}});
legend.add(ui.Label('Forest Change & Fire Legend', {fontWeight: 'bold', fontSize: '13px', margin: '0 0 6px 0'}));
var items = [
{color: '#2D6A4F', label: 'Forest cover 2000 (β₯10% canopy)'},
{color: '#FFFFB2', label: 'Forest loss: early (2001-2007)'},
{color: '#FD8D3C', label: 'Forest loss: mid (2008-2015)'},
{color: '#B10026', label: 'Forest loss: recent (2016-2023)'},
{color: '#3B82F6', label: 'Forest gain (2000-2012)'},
{color: '#FC4E2A', label: 'Fire detections (FIRMS)'},
{color: '#FF0000', label: 'Fire-driven loss (overlap)'}
];
items.forEach(function(item) {
var row = ui.Panel({layout: ui.Panel.Layout.Flow('horizontal'), style: {margin: '2px 0'}});
row.add(ui.Label({style: {backgroundColor: item.color, padding: '7px',
margin: '0 6px 0 0', borderRadius: '3px'}}));
row.add(ui.Label(item.label, {fontSize: '11px', color: '#374151'}));
legend.add(row);
});
Map.add(legend);ui.Panel -- a GEE interface element that creates a floating box on the map. You can add text labels, color swatches, and other widgets inside it. The position style property places it at a corner of the map (e.g. 'bottom-left', 'top-right').ui.Label -- adds a text or colored box element inside a panel. When you set backgroundColor without text, it renders as a colored square -- this is how the legend color swatches are made.Map.add(legend) -- attaches the panel to the GEE map. Without this line the legend exists in memory but is never displayed.// ---- Export Maps to Google Drive ----
// Export 1: Annual loss year map (full time series 2001-2023)
Export.image.toDrive({
image: lossYearMasked.rename('loss_year'),
description: 'Hansen_LossYear_NorthMorocco',
folder: 'GEE_ForestLoss',
region: studyArea,
scale: 30,
crs: 'EPSG:4326',
maxPixels: 1e13
});
// Export 2: Recent fire-driven loss (2020-2023)
Export.image.toDrive({
image: fireLoss.rename('fire_driven_loss'),
description: 'FireDrivenLoss_NorthMorocco_2020_2023',
folder: 'GEE_ForestLoss',
region: studyArea,
scale: 100,
crs: 'EPSG:4326',
maxPixels: 1e10
});
// Export 3: Fire frequency raster
Export.image.toDrive({
image: fireFrequency.rename('fire_frequency'),
description: 'FIRMS_FireFrequency_NorthMorocco_2020_2023',
folder: 'GEE_ForestLoss',
region: studyArea,
scale: 500,
crs: 'EPSG:4326',
maxPixels: 1e10
});Export.image.toDrive() -- saves a GEE image as a GeoTIFF file to your Google Drive. Running this line does not export immediately -- it creates a task.EPSG:4326 is geographic coordinates (latitude/longitude in degrees), the standard for GIS files that need to work in any software (QGIS, ArcGIS, etc.).[-6.0, 34.5, -4.5, 35.8]) and Middle Atlas ([-5.5, 32.5, -3.5, 34.5]).// =====================================================
// Course 04: Forest Loss Mapping with Hansen GFC
// Generated by DeepSeek AI (March 2026)
// Study area: North Morocco (Rif Mountains & Middle Atlas)
// Dataset: UMD/hansen/global_forest_change_2023_v1_11
// =====================================================
// ---- 1. Study Area ----
var studyArea = ee.Geometry.Rectangle([-6.5, 33.0, -1.5, 36.0]);
Map.setCenter(-4.0, 34.5, 8);
Map.setOptions('SATELLITE');
// ---- 2. Load Hansen GFC Dataset ----
var hansen = ee.Image('UMD/hansen/global_forest_change_2023_v1_11');
var treeCover = hansen.select('treecover2000');
var loss = hansen.select('loss');
var lossYear = hansen.select('lossyear');
var gain = hansen.select('gain');
var dataMask = hansen.select('datamask');
// ---- 3. Define Forest Mask (>= 10% canopy cover -- semi-arid Moroccan threshold) ----
// Use 30% for dense tropical forests (Congo, Amazon)
var CANOPY_THRESHOLD = 10;
var forestMask = treeCover.gte(CANOPY_THRESHOLD).and(dataMask.eq(1));
// ---- 4. Forest Loss (masked to forest pixels) ----
var forestLoss = loss.updateMask(loss).updateMask(forestMask);
var lossYearMasked = lossYear.updateMask(loss).updateMask(forestMask);
var forestGain = gain.updateMask(gain);
// ---- 5. Visualisation Layers ----
// Tree cover 2000
Map.addLayer(treeCover.updateMask(forestMask).clip(studyArea),
{min: CANOPY_THRESHOLD, max: 100, palette: ['#A8D5A2','#52B788','#2D6A4F','#1B4332']},
'Tree Cover 2000');
// Annual loss year (2001-2023) -- yellow to dark red temporal gradient
Map.addLayer(lossYearMasked.clip(studyArea),
{min: 1, max: 23, palette: ['#FFFFB2','#FED976','#FEB24C','#FD8D3C','#FC4E2A','#E31A1C','#B10026']},
'Forest Loss Year (2001-2023)');
// Gain layer (blue)
Map.addLayer(forestGain.clip(studyArea),
{palette: ['#3B82F6']},
'Forest Gain 2000-2012');
// ---- 6. Total Forest Loss Area (hectares) ----
var totalLossArea = forestLoss.multiply(ee.Image.pixelArea())
.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: studyArea,
scale: 30,
maxPixels: 1e13
});
var totalHa = ee.Number(totalLossArea.get('loss')).divide(10000);
print('Total forest loss 2001-2023 (ha):', totalHa);
// ---- 7. Annual Loss by Year (single-pass histogram chart) ----
// frequencyHistogram counts pixels per lossyear value in one reduceRegion call
var lossPixelCount = lossYearMasked.reduceRegion({
reducer: ee.Reducer.frequencyHistogram(),
geometry: studyArea,
scale: 100,
maxPixels: 1e10,
bestEffort: true
});
var pixelAreaHa = ee.Number(100).multiply(100).divide(10000); // 1 ha at scale 100m
var histogram = ee.Dictionary(lossPixelCount.get('lossyear'));
var years = ee.List.sequence(1, 23);
var annualLossFc = ee.FeatureCollection(years.map(function(y) {
y = ee.Number(y);
var yearKey = y.int().format('%d');
var count = ee.Number(histogram.get(yearKey, 0));
var ha = count.multiply(pixelAreaHa);
return ee.Feature(null, {year: y.add(2000), area_ha: ha});
}));
var chart = ui.Chart.feature.byFeature({
features: annualLossFc,
xProperty: 'year',
yProperties: ['area_ha']
}).setChartType('ColumnChart')
.setOptions({
title: 'Annual Forest Loss - North Morocco (2001-2023)',
hAxis: {title: 'Year', format: '####'},
vAxis: {title: 'Forest Loss (ha)'},
colors: ['#B10026'],
bar: {groupWidth: '80%'},
legend: {position: 'none'},
backgroundColor: '#FFFFFF'
});
print(chart);
// ---- 8. Legend Panel ----
var legend = ui.Panel({style: {position: 'bottom-left', padding: '10px', width: '220px'}});
legend.add(ui.Label('Forest Change Legend', {fontWeight: 'bold', fontSize: '13px', margin: '0 0 6px 0'}));
var items = [
{color: '#2D6A4F', label: 'Forest cover 2000 (β₯30%)'},
{color: '#FFFFB2', label: 'Loss: early (2001-2007)'},
{color: '#FD8D3C', label: 'Loss: mid (2008-2015)'},
{color: '#B10026', label: 'Loss: recent (2016-2023)'},
{color: '#3B82F6', label: 'Forest gain (2000-2012)'}
];
items.forEach(function(item) {
var row = ui.Panel({layout: ui.Panel.Layout.Flow('horizontal'), style: {margin: '2px 0'}});
row.add(ui.Label({style: {backgroundColor: item.color, padding: '7px', margin: '0 6px 0 0', borderRadius: '3px'}}));
row.add(ui.Label(item.label, {fontSize: '11px', color: '#374151'}));
legend.add(row);
});
Map.add(legend);
// ---- 10. Click Inspector ----
var inspector = ui.Panel({style: {position: 'top-right', width: '240px', padding: '10px'}});
inspector.add(ui.Label('Click Inspector', {fontWeight: 'bold', fontSize: '13px'}));
var infoLabel = ui.Label('Click on the map to inspect', {fontSize: '11px', color: '#6B7280'});
inspector.add(infoLabel);
Map.add(inspector);
Map.onClick(function(coords) {
var point = ee.Geometry.Point([coords.lon, coords.lat]);
var vals = hansen.select(['treecover2000','loss','lossyear','gain']).reduceRegion({
reducer: ee.Reducer.first(),
geometry: point,
scale: 30
});
vals.evaluate(function(v) {
var ly = v.lossyear ? (2000 + v.lossyear) : 'None';
infoLabel.setValue(
'Canopy 2000: ' + (v.treecover2000 || 0) + '%\n' +
'Loss: ' + (v.loss ? 'Yes (' + ly + ')' : 'No') + '\n' +
'Gain: ' + (v.gain ? 'Yes' : 'No')
);
});
});
// ---- 11. Export Annual Loss Map ----
Export.image.toDrive({
image: lossYearMasked.rename('loss_year'),
description: 'Hansen_Forest_Loss_Year_NorthMorocco',
folder: 'GEE_ForestLoss',
region: studyArea,
scale: 30,
crs: 'EPSG:4326',
maxPixels: 1e13
});scale: 30) over a large Congo Basin rectangle is computationally heavy. If GEE times out, reduce to scale: 100 or narrow the study area. Ask DeepSeek: "Reduce the computation scale to 100m for the chart only."| Goal | What to add to your prompt |
|---|---|
| Semi-arid forests | "Lower canopy threshold to 10% for sparse forest cover" |
| Country-level stats | "Use FAO GAUL Level 1 to compute loss by country" |
| Protected areas | "Mask to WDPA protected areas and compute % loss inside" |
| Deforestation drivers | "Overlay fire (FIRMS), roads (OSM), and cropland (Dynamic World)" |
| Time animation | "Create a GEE video export showing loss accumulation year by year" |
| Forest fragmentation | "Compute patch size distribution using connectedPixelCount" |
| Regeneration | "Show gain areas and compare with loss areas from 2001-2012" |