Lab 29 - Urban Heat Island Analysis

Objective: Measure Surface Urban Heat Island (SUHI) intensity by contrasting urban and rural reference areas.

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

  1. Why do we need separate urban and rural masks instead of using all pixels?
  2. What would a negative SUHI value mean?
  3. How might SUHI vary by time of day or season?
  4. What factors besides land cover contribute to urban heat islands?

Troubleshooting

Problem: Rural mask is empty

Solution: Your AOI may be entirely urban. Expand to include surrounding areas or lower the rural threshold.

Problem: Some tracts have null SUHI

Solution: Tracts with no urban or rural pixels will have null values. Filter these out or use city-wide averages.

Problem: SUHI values seem too high or low

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