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 |
|---|---|---|
| S5P | Sentinel-5 Precursor | Earth observation satellite launched by ESA dedicated to monitoring atmospheric chemistry and air quality. |
| NO2 | Nitrogen Dioxide | A respiratory irritant trace gas primarily emitted by fossil fuel combustion and industrial processes. |
| AOD | Aerosol Optical Depth | A measure of atmospheric light extinction caused by airborne particulate matter and aerosol particles. |
| GeoAI | Geospatial Artificial Intelligence | Integration of spatial data science with machine learning algorithms to model geographic phenomena. |
| Exposure Assessment | Environmental Exposure Assessment | Quantitative estimation of population contact intensity with environmental hazards across space and time. |
| Vulnerability Index | Socio-Environmental Vulnerability Index | A composite score reflecting population susceptibility and lack of adaptive capacity to environmental stressors. |
| GHSL | Global Human Settlement Layer | Spatial raster datasets mapping human presence, built-up surfaces, and population density globally. |
| Random Forest | Random Forest Ensemble Classifier/Regressor | An ensemble machine learning method that constructs multiple decision trees to predict complex nonlinear targets. |
| Tropospheric Column Density | Tropospheric Column Density | Total vertically integrated molecular count of a trace gas per unit horizontal surface area in the troposphere. |
| Spatial Autocorrelation | Spatial Autocorrelation | The formal measurement of spatial dependency where geographic proximity implies similarity in feature values. |
| Ecological Fallacy | Ecological Fallacy | A logical error in interpretation occurring when individual health outcomes are inappropriately deduced from aggregate spatial data. |
| Risk Proxy | Environmental Health Risk Proxy | An indirect, spatially continuous indicator used to estimate potential health threats when direct epidemiological surveillance is absent. |
| Confounding Variable | Confounding Factor | An unmeasured or unmodeled variable that influences both environmental exposure and health outcomes simultaneously. |
Ambient nitrogen dioxide (NO2) and aerosol pollution from urban transport and heavy industrial activity present critical global public health risks, strongly linked to acute and chronic respiratory disorders. Satellite remote sensing from TROPOMI aboard Sentinel-5P provides unprecedented daily global observations of atmospheric chemistry, yet translating raw column densities into actionable health risk metrics requires integrating meteorology, population density, and machine learning. This course demonstrates how to harmonize multi-sensor satellite imagery and population grids within Google Earth Engine to map respiratory health risk indicators across major global urban regions.
COPERNICUS/S5P/OFFL/L3_NO2You need two browser tabs open side by side throughout this course.
Course29_Global Air Pollution and Respiratory Health Risk Mapping via GeoAI
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.
// 1. Define Urban Study Area (Example: Northern Italy Industrial Hub / Po Valley)
var roi = ee.Geometry.Polygon([
[[7.5, 44.5], [12.5, 44.5], [12.5, 46.5], [7.5, 46.5]]
]);
// 2. Load and Filter Sentinel-5P Tropospheric NO2
var startPeriod = '2023-05-01';
var endPeriod = '2023-09-30';
var s5p_no2 = ee.ImageCollection('COPERNICUS/S5P/OFFL/L3_NO2')
.filterBounds(roi)
.filterDate(startPeriod, endPeriod)
.select('tropospheric_NO2_column_number_density')
.mean()
.clip(roi);
// 3. Load Population Density from GHSL 2020
var pop = ee.Image('JRC/GHSL/P2023A/GHS_POP/2020')
.select('population_count')
.clip(roi);
// 4. Load Meteorological Covariates (ERA5-Land Monthly)
var era5 = ee.ImageCollection('ECMWF/ERA5_LAND/MONTHLY_AGGR')
.filterBounds(roi)
.filterDate(startPeriod, endPeriod)
.select(['temperature_2m', 'u_component_of_wind_10m', 'v_component_of_wind_10m'])
.mean()
.clip(roi);
// 5. Load MODIS AOD (Aerosol Optical Depth at 0.47um)
var aod = ee.ImageCollection('MODIS/061/MCD19A2_GRANULES')
.filterBounds(roi)
.filterDate(startPeriod, endPeriod)
.select('Optical_Depth_047')
.mean()
.multiply(0.001)
.rename('AOD_470')
.clip(roi);
// 6. Calculate Population-Weighted Exposure Raster
// Exposure = NO2 Concentration * Log(Population Count + 1)
var popExposure = s5p_no2.multiply(pop.add(1).log()).rename('Pop_Weighted_Exposure');
// 7. Visualization Setup
Map.centerObject(roi, 7);
Map.addLayer(pop, {min: 0, max: 1000, palette: ['white', 'yellow', 'red']}, 'GHSL Population Count');
Map.addLayer(s5p_no2, {min: 0.00002, max: 0.00015, palette: ['black', 'blue', 'purple', 'orange', 'red']}, 'Sentinel-5P NO2 Mol/m2');
Map.addLayer(popExposure, {min: 0, max: 0.001, palette: ['green', 'yellow', 'orange', 'red', 'purple']}, 'Population Weighted Exposure');
Add this block after the previous code. It continues the analysis with additional processing and visualization.
// 1. Stack All Predictors into a Multi-Band Composite Image
var composite = ee.Image.cat([
s5p_no2.rename('NO2'),
aod,
pop.rename('Population'),
era5.select('temperature_2m').rename('Temp'),
era5.select('u_component_of_wind_10m').rename('U_Wind'),
era5.select('v_component_of_wind_10m').rename('V_Wind')
]);
// 2. Define Proxy Target Variable: Integrated Respiratory Risk Score (0-100 Normalized)
// Target combines normalized NO2 exposure, aerosol load, and population vulnerability
var normalizedNO2 = s5p_no2.unitScale(0.00001, 0.00020).clamp(0, 1);
var normalizedAOD = aod.unitScale(0.05, 0.8).clamp(0, 1);
var normalizedPop = pop.log().unitScale(0, 8).clamp(0, 1);
var targetRisk = normalizedNO2.multiply(0.5)
.add(normalizedAOD.multiply(0.3))
.add(normalizedPop.multiply(0.2))
.multiply(100)
.rename('Respiratory_Risk_Index');
// 3. Sample Training Points across the Region of Interest
var trainingData = composite.addBands(targetRisk).sample({
region: roi,
scale: 1000,
numPixels: 2000,
seed: 42,
geometries: true
});
// 4. Train a Random Forest Regression Model
var rfRegressor = ee.Classifier.smileRandomForest(100)
.setOutputMode('REGRESSION')
.train({
features: trainingData,
classProperty: 'Respiratory_Risk_Index',
inputProperties: ['NO2', 'AOD_470', 'Population', 'Temp', 'U_Wind', 'V_Wind']
});
// 5. Predict Respiratory Health Risk across the Entire Spatial Domain
var predictedRiskMap = composite.classify(rfRegressor, 'Predicted_Respiratory_Risk');
// 6. Visualization of Machine Learning Risk Prediction
var riskPalette = ['#1a9641', '#a6d96a', '#ffffbf', '#fdae61', '#d7191c'];
Map.addLayer(predictedRiskMap, {min: 10, max: 80, palette: riskPalette}, 'Predicted Respiratory Risk Index');
// 7. Model Feature Importance Verification
var explanation = rfRegressor.explain();
print('Random Forest Model Details:', explanation);
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 Predicted Respiratory Risk Map as a GeoTIFF to Google Drive
Export.image.toDrive({
image: predictedRiskMap,
description: 'Global_Urban_Respiratory_Health_Risk_2023',
folder: 'GEE_Health_GeoAI',
scale: 1000,
region: roi,
maxPixels: 1e9,
fileFormat: 'GeoTIFF'
});
// Export Sampled Health Exposure Table as CSV
Export.table.toDrive({
collection: trainingData,
description: 'Respiratory_Risk_Sample_Points_2023',
folder: 'GEE_Health_GeoAI',
fileFormat: 'CSV'
});
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 | COPERNICUS/S5P/OFFL/L3_NO2 |
| 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" |