Before starting, make sure you understand these terms. They appear throughout all code blocks in this course.
| Term | Full Name | What it means in simple words |
|---|---|---|
| VIIRS | Visible Infrared Imaging Radiometer Suite | A sensor aboard Suomi NPP and NOAA weather satellites capturing visible and infrared imagery, including nighttime radiance. |
| DNB | Day/Night Band | A specialized spectral band on VIIRS capable of detecting faint visible lights at night, including city lights, fires, and ships. |
| NTL | Nighttime Lights | Remote sensing imagery measuring artificial light emissions visible at night, widely used as a proxy for human activity. |
| Radiance | Radiometric Light Intensity | The amount of light emitted from a surface unit, measured by VIIRS in nanoWatts per square centimeter per steradian (nW/cm^2/sr). |
| Stray Light | Stray Light Contamination | Solar glare contamination occurring near satellite orbital terminators, corrected in VCMSLCFG products. |
| Linear Trend | Temporal Linear Regression Slope | A parametric statistical measure quantifying the annual change rate in night light radiance over time. |
| Cloud-Free Coverage | Quality Control Coverage Index | A quality band (cf_cvg) indicating the number of cloud-free observations contributing to a monthly composite pixel. |
| Urban Brightening | Anthropogenic Light Growth | A statistically significant increase in nighttime radiance over time, often tied to urban expansion or infrastructure. |
| Light Dimming | Anthropogenic Light Reduction | A decline in nighttime radiance, indicating population displacement, economic shifts, or energy efficiency policies. |
| Random Forest | Random Forest Ensemble Machine Learning | A supervised machine learning algorithm using decision trees to classify intensity trajectories or predict socioeconomic variables. |
| Spatial Aggregation | Regional Summary Statistics | The process of computing regional mean or sum metrics over administrative boundaries to analyze geographic trends. |
| Anomaly Detection | Nightlight Deviation Analysis | Identifying temporal deviations from baseline night light patterns caused by major events, disasters, or power outages. |
Nighttime satellite imagery provides an unprecedented global lens for monitoring human economic activity, urbanization dynamics, and electrification trends. This course utilizes the NOAA VIIRS Day/Night Band dataset to analyze multi-decadal night light changes across global scales using Google Earth Engine. Students will master time-series filtering, noise suppression, linear trend fitting, and machine learning to map expanding urban centers and changing light regimes.
NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFGYou need two browser tabs open side by side throughout this course.
Course23_Global Night Light Dynamics: VIIRS Time Series & Machine Learning
The prompt is the core skill of this course. A good prompt includes the exact dataset ID, band names, study area coordinates, dates, preprocessing steps, and desired outputs.
Paste the following code into your GEE code editor and click Run. Read the beginner notes below to understand what each line does.
var viirs = ee.ImageCollection('NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG');
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Preprocessing function to filter cloud-free observations and background noise
function preprocessVIIRS(img) {
var cloudFree = img.select('cf_cvg').gte(2);
var radiance = img.select('avg_rad');
// Mask out noise and low-quality pixels (< 0.5 nW/cm^2/sr)
var maskedRadiance = radiance.updateMask(cloudFree).updateMask(radiance.gte(0.5));
return img.addBands(maskedRadiance.rename('cleaned_rad'), null, true);
}
var processedCollection = viirs.map(preprocessVIIRS);
// Generate 2014 and 2023 annual medians
var median2014 = processedCollection.filterDate('2014-01-01', '2014-12-31')
.select('cleaned_rad').median();
var median2023 = processedCollection.filterDate('2023-01-01', '2023-12-31')
.select('cleaned_rad').median();
// Calculate absolute change
var absoluteChange = median2023.subtract(median2014).rename('radiance_change');
// Visualization parameters
var radVis = {min: 0.5, max: 30, palette: ['black', 'purple', 'cyan', 'lime', 'yellow', 'white']};
var diffVis = {min: -10, max: 10, palette: ['blue', 'black', 'red']};
Map.setCenter(15.0, 20.0, 3);
Map.addLayer(median2014, radVis, '2014 Night Lights Median');
Map.addLayer(median2023, radVis, '2023 Night Lights Median');
Map.addLayer(absoluteChange, diffVis, 'Radiance Change (2023 - 2014)');
Add this block after the previous code. It continues the analysis with additional processing and visualization.
var processedCollection = ee.ImageCollection('NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG')
.filterDate('2014-01-01', '2023-12-31')
.map(function(img) {
var date = img.date();
var years = date.difference(ee.Date('2014-01-01'), 'year');
var cleanRad = img.select('avg_rad').updateMask(img.select('cf_cvg').gte(2));
return cleanRad.addBands(ee.Image.constant(years).rename('time'))
.addBands(ee.Image.constant(1).rename('constant'));
});
// Calculate linear trend across time series
var linearFit = processedCollection.select(['time', 'avg_rad']).reduce(ee.Reducer.linearFit());
var slope = linearFit.select('scale').rename('annual_slope');
// Define sample predictor metrics for Random Forest
var meanRad = processedCollection.select('avg_rad').mean().rename('mean_rad');
var maxRad = processedCollection.select('avg_rad').max().rename('max_rad');
var features = ee.Image.cat([meanRad, maxRad, slope]);
// Unsupervised KMeans clustering as target generator for ML training
var clusterer = ee.Clusterer.wekaKMeans(4).train({
features: features.sample({region: Map.getBounds(true), scale: 5000, numPixels: 1000})
});
var clusters = features.cluster(clusterer);
// Visualization parameters
var slopeVis = {min: -0.5, max: 0.5, palette: ['darkblue', 'blue', 'white', 'orange', 'red']};
Map.addLayer(slope, slopeVis, '10-Year Linear Slope (nW/cm^2/sr/yr)');
Map.addLayer(clusters.randomVisualizer(), {}, 'Light Trajectory Clusters');
Add this block at the end of your script to export the final map as a GeoTIFF to your Google Drive. After clicking Run, go to the Tasks tab in GEE and click Run next to each export task.
var exportSlope = linearFit.select('scale').rename('VIIRS_Slope_2014_2023');
Export.image.toDrive({
image: exportSlope,
description: 'VIIRS_Nightlight_Linear_Slope_Global',
folder: 'GEE_Nightlights',
scale: 1000,
region: ee.Geometry.BBox(-180, -60, 180, 70),
maxPixels: 1e13,
fileFormat: 'GeoTIFF'
});
Use DeepSeek to extend the script for each exercise below. Write a follow-up prompt describing what you want to add, then copy the generated code and paste it into your GEE script.
To get the best results from DeepSeek for GEE tasks, always include these elements in your prompts:
| Element | Example |
|---|---|
| Exact dataset ID | NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG |
| Band names | "select band 'B4' for red, 'B8' for NIR" |
| Study area | "rectangle [-6.5, 33.0, -1.5, 36.0] North Morocco" |
| Date range | "filter from 2020-01-01 to 2023-12-31" |
| Output format | "print area in hectares, add a legend, export to Drive" |
| Error fixing | "The code gives error X. Please fix it." |
| Iteration | "Add a time series chart for each year 2018-2023" |