Practical Course 23

Global Night Light Dynamics: VIIRS Time Series & Machine Learning

Quantify human activity, urbanization, and economic trends globally using VIIRS nighttime lights and machine learning.
Global ~2 hours Intermediate
Dataset: NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG VIIRS Day/Night Band Monthly Composites V1 (Stray Light Corrected)

📖 Key Terms for Beginners

Before starting, make sure you understand these terms. They appear throughout all code blocks in this course.

TermFull NameWhat it means in simple words
VIIRSVisible Infrared Imaging Radiometer SuiteA sensor aboard Suomi NPP and NOAA weather satellites capturing visible and infrared imagery, including nighttime radiance.
DNBDay/Night BandA specialized spectral band on VIIRS capable of detecting faint visible lights at night, including city lights, fires, and ships.
NTLNighttime LightsRemote sensing imagery measuring artificial light emissions visible at night, widely used as a proxy for human activity.
RadianceRadiometric Light IntensityThe amount of light emitted from a surface unit, measured by VIIRS in nanoWatts per square centimeter per steradian (nW/cm^2/sr).
Stray LightStray Light ContaminationSolar glare contamination occurring near satellite orbital terminators, corrected in VCMSLCFG products.
Linear TrendTemporal Linear Regression SlopeA parametric statistical measure quantifying the annual change rate in night light radiance over time.
Cloud-Free CoverageQuality Control Coverage IndexA quality band (cf_cvg) indicating the number of cloud-free observations contributing to a monthly composite pixel.
Urban BrighteningAnthropogenic Light GrowthA statistically significant increase in nighttime radiance over time, often tied to urban expansion or infrastructure.
Light DimmingAnthropogenic Light ReductionA decline in nighttime radiance, indicating population displacement, economic shifts, or energy efficiency policies.
Random ForestRandom Forest Ensemble Machine LearningA supervised machine learning algorithm using decision trees to classify intensity trajectories or predict socioeconomic variables.
Spatial AggregationRegional Summary StatisticsThe process of computing regional mean or sum metrics over administrative boundaries to analyze geographic trends.
Anomaly DetectionNightlight Deviation AnalysisIdentifying temporal deviations from baseline night light patterns caused by major events, disasters, or power outages.

Contents

  1. Introduction to VIIRS Nighttime Light Sensors and Data Products
  2. Filtering and Preprocessing VIIRS Monthly Stray Light Corrected Composites
  3. Filtering Cloud-Free Quality Layers and Removing Background Noise
  4. Building Multi-Year Annual Median Nightlight Composites
  5. Computing Spatial Light Metrics across Global Regions
  6. Executing Pixel-Wise Temporal Linear Regression across Global Datasets
  7. Extracting Trend Slopes and Statistical Intercepts
  8. Applying Random Forest to Classify Urban Dynamic Zones
  9. Visualizing Brightening and Dimming Hotspots Globally
  10. Exporting VIIRS Analysis Products to Cloud Storage
1
Introduction
10 min

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.

GEE Asset ID
NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG
Dataset Name
VIIRS Day/Night Band Monthly Composites V1 (Stray Light Corrected)
Study Area
Global
AI Tool
DeepSeek (free, no API key needed)
🌍
What you will build: By the end of this course you will have a working GEE script that analyzes Global Night Light Dynamics: VIIRS Time Series & Machine Learning, computes statistics, visualizes results on an interactive map, and exports data to Google Drive.

2
Setup: GEE + DeepSeek
5 min

You need two browser tabs open side by side throughout this course.

1
Open chat.deepseek.com -- free account, no API key needed
2
Open code.earthengine.google.com in a second tab
3
Create a new GEE script named Course23_Global Night Light Dynamics: VIIRS Time Series & Machine Learning
💡
Workflow: Write a prompt in DeepSeek describing your task with exact dataset IDs and requirements. Copy the generated GEE JavaScript. Paste into GEE code editor. Click Run. Inspect results. Iterate.

3
Crafting the DeepSeek Prompt
15 min

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.

📋 Main Prompt -- copy and paste this into DeepSeek
Act as an expert remote sensing scientist and Google Earth Engine developer. Write a complete, optimized Earth Engine JavaScript script analyzing VIIRS nighttime lights (dataset ID 'NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG') for global light dynamics between 2014 and 2023. Filter the image collection using the 'avg_rad' band and apply quality filtering with 'cf_cvg' (> 2). Calculate annual median composites for 2014 and 2023, compute absolute radiance differences, and execute linear regression ('ee.Reducer.linearFit()') over the 10-year monthly sequence to measure trend slope. Include visualization parameters for radiance and slope values, add layer legends, and generate Export functions to Drive.
✨
After pasting the prompt: Wait 20-30 seconds for DeepSeek to generate the full GEE script. Copy the entire code block it produces. If the output is cut short, type "Continue the code" to get the rest.

4
VIIRS Collection Filtering, Noise Masking, and Annual Compositing
10 min

Paste the following code into your GEE code editor and click Run. Read the beginner notes below to understand what each line does.

VIIRS Collection Filtering, Noise Masking, and Annual Compositing
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)');
🔑
Beginner note: This script loads the monthly stray light corrected VIIRS collection and applies quality control. The function screens out pixels with fewer than 2 cloud-free coverage days (cf_cvg) and removes background noise below 0.5 nW/cm^2/sr. Annual medians for 2014 and 2023 are generated to mitigate seasonal fluctuations, followed by a pixel difference calculation to reveal global light changes over a decade.

5
Time Series Trend Fitting and Machine Learning Classification
10 min

Add this block after the previous code. It continues the analysis with additional processing and visualization.

Time Series Trend Fitting and Machine Learning Classification
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');
🔑
Beginner note: This block implements pixel-wise linear regression over a 10-year VIIRS time series (2014-2023). It calculates a temporal scale band representing annual change rates. It then combines summary metrics (mean, max, slope) into a multi-band predictor image and applies spatial clustering to categorize global light change trajectories automatically.

6
Export Results to Google Drive
5 min

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.

Export to Google Drive
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'
});
🔑
Beginner note -- Export.image.toDrive(): This function does not export immediately. It creates a task in the GEE Tasks tab that you must manually start by clicking Run. The file will appear in your Google Drive folder (GEE_Courses) as a GeoTIFF that you can open in QGIS or ArcGIS.

7
Exercises Using DeepSeek Prompts
Homework

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.

Regional Urban Light Expansion Analysis

  1. Select an urban metropolitan region of choice using an administrative geometry filter.
  2. Modify the temporal range to compare 2017 versus 2023 median composites.
  3. Calculate total sum of lights (SOL) for both years within the urban boundary.
  4. Determine the percentage increase in total light radiance.

Cloud-Free Mask Thresholding Impact Evaluation

  1. Adjust the cloud-free coverage threshold (cf_cvg) from 2 to 5 in Code Block 1.
  2. Observe the reduction in spatial coverage in high-cloud equatorial regions.
  3. Compare mean monthly radiance stability with and without cloud masking.
  4. Document how strict cloud thresholds affect continuous trend detection.

Linear Regression vs. Annual Difference Comparison

  1. Export the annual slope computed in Code Block 2 for a specific region.
  2. Compare the slope values against simple 2014 to 2023 difference imagery.
  3. Identify regions where episodic spikes skew simple difference images relative to linear trends.
  4. Summarize why robust time series metrics are preferred over two-date comparisons.

Cross-Analysis of Light Growth with WorldPop Data

  1. Import the WorldPop global dataset ('WorldPop/GP/100m/pop') for 2020.
  2. Overlay the annual slope image with high-density population clusters.
  3. Identify regions experiencing population growth without proportional light increase.
  4. Discuss potential drivers such as dark sky infrastructure or energy access gaps.

8
Prompt Engineering Tips
Reference

To get the best results from DeepSeek for GEE tasks, always include these elements in your prompts:

ElementExample
Exact dataset IDNOAA/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"
🧠
Best practice: Start with the main prompt, run the code, then send follow-up prompts to add features one at a time. This gives you cleaner, more reliable code than asking for everything at once.
Course 23 - Global Night Light Dynamics: VIIRS Time Series & Machine Learning | GeoAI for Climate Change | Dr. Ayoub Lahlouh | UM6P/CUS | 2025/2026