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 |
|---|---|---|
| LST | Land Surface Temperature | The radiometric temperature of the land surface emitted by Earth, derived from thermal infrared satellite sensors. |
| UHI | Urban Heat Island | A phenomenon where urban areas experience significantly higher temperatures than surrounding rural context due to human structure and reduced vegetation. |
| HVI | Heat Vulnerability Index | A spatial composite metric combining thermal exposure, population sensitivity, and adaptive capacity factors. |
| Environmental Exposure | Environmental Exposure | The contact between a human population and an environmental hazard, such as extreme surface heat, over a specific geographic area and duration. |
| NDVI | Normalized Difference Vegetation Index | A standardized spectral index calculating green vegetation density using red and near-infrared reflectance bands. |
| NDBI | Normalized Difference Built-up Index | A spectral index sensitive to impervious surface cover and urban structures, calculated from shortwave infrared and near-infrared bands. |
| Thermal Emissivity | Land Surface Emissivity | The efficiency of a surface in emitting thermal radiation relative to a black body at the same temperature. |
| Population Exposure | Population Exposure Assessment | The intersection of population density maps with environmental hazards to estimate total human count impacted by severe conditions. |
| Random Forest Regression | Random Forest Regression | An ensemble machine learning algorithm that builds multiple decision trees to predict continuous targets like localized thermal anomalies. |
| Spatial Autocorrelation | Spatial Autocorrelation | The spatial clustering property where geographic features located close to each other exhibit similar thermal or vulnerability values. |
| Health Risk Proxy | Health Risk Proxy Variable | An environmental or socio-demographic metric used to represent potential health risk when direct epidemiological outcome data are unavailable. |
| Modifiable Areal Unit Problem | Modifiable Areal Unit Problem (MAUP) | A statistical bias occurring when point or raster data are aggregated into different administrative boundaries or spatial resolutions. |
Rapid urbanization and climate change have exacerbated severe extreme heat events in cities worldwide, threatening public health and overburdening medical infrastructure. This course demonstrates how to use Google Earth Engine and GeoAI workflows to combine Landsat thermal imagery, land cover spectral indices, and high-resolution WorldPop grid layers. Students will build transferable, spatial heat vulnerability indices and machine learning models to map urban heat exposure across global metropolitan areas, transforming satellite observations into actionable health risk intelligence.
LANDSAT/LC08/C02/T1_L2You need two browser tabs open side by side throughout this course.
Course28_Global Urban Heat Exposure and Health Risk Mapping with 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.
var cityROI = ee.Geometry.Polygon([
[[-112.15, 33.35], [-111.85, 33.35], [-111.85, 33.65], [-112.15, 33.65]]
]);
function maskLandsatSR(image) {
var qa = image.select('QA_PIXEL');
var cloudBitMask = 1 << 3;
var shadowBitMask = 1 << 4;
var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
.and(qa.bitwiseAnd(shadowBitMask).eq(0));
return image.updateMask(mask);
}
var landsatCollection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterBounds(cityROI)
.filterDate('2023-06-01', '2023-08-31')
.filter(ee.Filter.lt('CLOUD_COVER', 15))
.map(maskLandsatSR);
var medianImage = landsatCollection.median().clip(cityROI);
var lstCelsius = medianImage.select('ST_B10')
.multiply(0.00341802).add(149.0).subtract(273.15)
.rename('LST');
var ndvi = medianImage.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI');
var ndbi = medianImage.normalizedDifference(['SR_B6', 'SR_B5']).rename('NDBI');
var processedImage = ee.Image.cat([lstCelsius, ndvi, ndbi]);
Map.centerObject(cityROI, 11);
Map.addLayer(processedImage.select('NDVI'), {min: -0.1, max: 0.7, palette: ['blue', 'white', 'green']}, 'NDVI');
Map.addLayer(processedImage.select('NDBI'), {min: -0.3, max: 0.3, palette: ['white', 'orange', 'red']}, 'NDBI');
Map.addLayer(processedImage.select('LST'), {min: 25, max: 50, palette: ['blue', 'yellow', 'orange', 'red']}, 'Land Surface Temperature (C)');
Add this block after the previous code. It continues the analysis with additional processing and visualization.
var popImage = ee.ImageCollection('WorldPop/GP/100m/pop')
.filterBounds(cityROI)
.filter(ee.Filter.date('2020-01-01', '2020-12-31'))
.mean()
.clip(cityROI)
.rename('Population');
function minMaxNormalize(img, region, scale) {
var stats = img.reduceRegion({
reducer: ee.Reducer.minMax(),
geometry: region,
scale: scale,
maxPixels: 1e9
});
var minVal = ee.Number(stats.get(img.bandNames().get(0).aside(function(b){ return ee.String(b).cat('_min'); })));
var maxVal = ee.Number(stats.get(img.bandNames().get(0).aside(function(b){ return ee.String(b).cat('_max'); })));
return img.subtract(minVal).divide(maxVal.subtract(minVal)).clamp(0, 1);
}
var lstNorm = minMaxNormalize(processedImage.select('LST'), cityROI, 30).rename('LST_norm');
var ndbiNorm = minMaxNormalize(processedImage.select('NDBI'), cityROI, 30).rename('NDBI_norm');
var ndviNorm = minMaxNormalize(processedImage.select('NDVI'), cityROI, 30).rename('NDVI_norm');
var popNorm = minMaxNormalize(popImage.select('Population'), cityROI, 100).rename('Pop_norm');
var compositeVulnerability = lstNorm.multiply(0.35)
.add(ndbiNorm.multiply(0.25))
.add(popNorm.multiply(0.25))
.add(ee.Image(1).subtract(ndviNorm).multiply(0.15))
.rename('Heat_Vulnerability_Index');
var trainingPoints = compositeVulnerability.sample({
region: cityROI,
scale: 30,
numPixels: 1000,
seed: 42,
geometries: true
});
var predictors = ee.Image.cat([processedImage.select(['NDVI', 'NDBI']), popImage]);
var classifier = ee.Classifier.smileRandomForest(50).train({
features: trainingPoints,
classProperty: 'Heat_Vulnerability_Index',
inputProperties: ['NDVI', 'NDBI', 'Population']
});
Map.addLayer(popImage, {min: 0, max: 200, palette: ['white', 'purple']}, 'Population Density');
Map.addLayer(compositeVulnerability, {min: 0.2, max: 0.8, palette: ['blue', 'yellow', 'orange', 'darkred']}, 'Heat Vulnerability Index (HVI)');
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.image.toDrive({
image: compositeVulnerability,
description: 'Global_City_Heat_Vulnerability_Index_2023',
folder: 'GEE_Public_Health_GeoAI',
scale: 30,
region: cityROI,
fileFormat: 'GeoTIFF',
maxPixels: 1e9
});
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 | LANDSAT/LC08/C02/T1_L2 |
| 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" |