What You'll Learn
- Derive LST from Landsat 8/9 L2 thermal data
- Define urban and rural reference masks using built-up datasets
- Calculate Surface Urban Heat Island (SUHI) intensity
- Summarize SUHI by administrative units for spatial equity analysis
Building On Previous Learning
This lab extends Lab 14 by focusing specifically on the urban-rural temperature contrast. You'll use similar LST techniques but add the SUHI calculation.
Why This Matters
Urban Heat Islands have significant impacts:
- Health equity: Low-income neighborhoods often have higher SUHI
- Energy consumption: Higher temperatures increase cooling demand
- Climate adaptation: Cities must plan for increasing heat exposure
- Urban planning: Data guides tree planting and cool roof policies
Before You Start
- Prerequisites: Complete Lab 14 and understand LST derivation.
- Estimated time: 75 minutes
- Materials: Earth Engine account and a city AOI.
Key Terms
- Surface Urban Heat Island (SUHI)
- The difference between urban and rural land surface temperatures, typically positive (urban is warmer).
- Urban Mask
- Areas classified as built-up or developed land, used as the "urban" reference.
- Rural Reference
- Areas with low built-up percentage outside the urban core, used as the baseline temperature.
- GHSL (Global Human Settlement Layer)
- JRC dataset providing global built-up area information.
Step 1: Load Landsat LST Data
Define AOI and calculate median summer LST:
// 1) AOI and date window - Washington DC metro example
var aoi = ee.Geometry.Rectangle([-77.25, 38.70, -76.80, 39.00]);
Map.centerObject(aoi, 10);
// Load Landsat 8 Collection 2 Level 2
var imgs = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterBounds(aoi)
.filterDate('2021-06-01', '2021-09-30')
.filter(ee.Filter.lt('CLOUD_COVER', 20));
// Convert thermal band to Kelvin
function toKelvin(img){
var kelvin = img.select('ST_B10').multiply(0.00341802).add(149.0);
var cloudFree = img.select('QA_PIXEL').bitwiseAnd(1).eq(0);
return kelvin.updateMask(cloudFree).copyProperties(img, img.propertyNames());
}
// Calculate median LST and convert to Celsius
var lst = imgs.map(toKelvin).median().clip(aoi);
var lstC = lst.subtract(273.15).rename('LST_C');
Map.addLayer(lstC, {min: 20, max: 45, palette: ['blue','cyan','yellow','red']}, 'LST (C)');
Step 2: Create Urban/Rural Masks
Use GHSL built-up percentage to define urban and rural areas:
// GHSL built-up: 0-100 (percent built-up)
var ghsl = ee.Image('JRC/GHSL/P2016/BUILT_LDSMT_GLOBE_V1').select('built');
// Urban mask: > 20% built-up
var urban = ghsl.gt(20).selfMask().clip(aoi);
// Rural mask: <= 5% built-up
var rural = ghsl.lte(5).selfMask().clip(aoi);
Map.addLayer(urban, {palette: 'white'}, 'Urban mask', false);
Map.addLayer(rural, {palette: 'green'}, 'Rural mask', false);
Threshold Selection: The 20% and 5% thresholds are examples. Adjust based on your city's development patterns.
Step 3: Calculate SUHI Intensity
Compute city-wide SUHI as the difference between urban and rural mean LST:
// Stack urban and rural masked LST
var stacked = ee.Image.cat(
lstC.updateMask(urban).rename('LST_urban'),
lstC.updateMask(rural).rename('LST_rural')
);
// Calculate means
var stats = stacked.reduceRegion({
reducer: ee.Reducer.mean().repeat(2),
geometry: aoi,
scale: 100,
maxPixels: 1e9
});
// SUHI = urban mean - rural mean
var suhi = ee.Number(stats.get('LST_urban_mean'))
.subtract(ee.Number(stats.get('LST_rural_mean')));
print('Citywide SUHI (C) = urban_mean - rural_mean:', suhi);
Step 4: SUHI by Administrative Units
Calculate SUHI for each Census tract:
// Load Census tracts
var tracts = ee.FeatureCollection('TIGER/2018/Tracts').filterBounds(aoi);
// Calculate LST means by tract for urban and rural areas
var urbMean = lstC.updateMask(urban).rename('LST_u');
var rurMean = lstC.updateMask(rural).rename('LST_r');
var two = urbMean.addBands(rurMean);
var tractStats = two.reduceRegions({
collection: tracts,
reducer: ee.Reducer.mean().repeat(2),
scale: 100,
tileScale: 4
});
// Calculate SUHI for each tract
var tractSuhi = tractStats.map(function(f){
return f.set('SUHI_C', ee.Number(f.get('LST_u_mean'))
.subtract(ee.Number(f.get('LST_r_mean'))));
});
print('SUHI by tract', tractSuhi.limit(5));
// Visualize as choropleth
var suhiImage = tractSuhi.reduceToImage({
properties: ['SUHI_C'],
reducer: ee.Reducer.first()
});
Map.addLayer(suhiImage, {min: 0, max: 8, palette: ['#2c7bb6','#ffff8c','#d7191c']}, 'SUHI by tract');
Map.addLayer(tracts.style({color: 'black', width: 1, fillColor: '00000000'}), {}, 'Tract boundaries', false);
Check Your Understanding
- Why do we need separate urban and rural masks instead of using all pixels?
- What would a negative SUHI value mean?
- How might SUHI vary by time of day or season?
- What factors besides land cover contribute to urban heat islands?
Troubleshooting
Solution: Your AOI may be entirely urban. Expand to include surrounding areas or lower the rural threshold.
Solution: Tracts with no urban or rural pixels will have null values. Filter these out or use city-wide averages.
Solution: Check your urban/rural thresholds and ensure date filtering captures hot summer days.
Pro Tips
- Compare day vs night: Use MODIS for day/night LST comparison
- Consider elevation: Higher areas are naturally cooler - normalize if needed
- Temporal analysis: Compare SUHI across years to track urban growth impacts
- Validate with weather data: Compare to ground-based temperature stations
?? Lab Submission
Subject: Lab 29 - Urban Heat Island - [Your Name]
Submit:
- Map: LST (C) with urban/rural overlays and SUHI choropleth
- Table: SUHI (C) per admin unit with top/bottom 5 tracts
- Short write-up: Method choices and equity insights
- EE script URL and CSV export of tract-level SUHI