Universal Code Hub Upgrade
Script Library
ResearchTemperatureAdvanced

Urban Heat Island — Landsat Surface Temperature vs. NDVI

Quantifies UHI intensity as the LST difference between built-up and surrounding rural pixels, with NDVI regression.

Pipeline (5 steps)
  1. Landsat 8/9 ST scaled to °C, summer composite (JJA)
  2. Urban (GHSL SMOD ≥ 30) vs rural (≤ 13) mask
  3. ΔT = mean(LST_urban) − mean(LST_rural)
  4. Per-pixel linear regression: LST = a·NDVI + b
  5. Hot-spot detection (LST > μ + 2σ)
Datasets
  • landsat8
  • landsat9
  • ghsl-smod
  • sentinel2-l2a
AOI modes
adminassetdraw
Simulated previewPaste the script in Earth Engine for live results
Outputs the script produces
map Surface temperature
map Urban / rural mask
chart LST ~ NDVI regression
table UHI summary table
Open Code Editor ↗
adv-uhi-landsat.js · 37 lines
// ============================================================
// Urban Heat Island — Landsat ST, GHSL urban mask, NDVI regression
// ============================================================
var year = 2024;
function landsatST(coll) {
  return coll.filterBounds(aoi).filterDate(year + '-06-01', year + '-09-01')
    .filter(ee.Filter.lt('CLOUD_COVER', 30))
    .map(function(i){
      var st = i.select('ST_B10').multiply(0.00341802).add(149.0).subtract(273.15).rename('LST_C');
      var ndvi = i.normalizedDifference(['SR_B5','SR_B4']).rename('NDVI');
      return st.addBands(ndvi).copyProperties(i, ['system:time_start']);
    });
}
var collection = landsatST(ee.ImageCollection('LANDSAT/LC08/C02/T1_L2'))
  .merge(landsatST(ee.ImageCollection('LANDSAT/LC09/C02/T1_L2')));
var summer = collection.median();
var lst = summer.select('LST_C'), ndvi = summer.select('NDVI');

var smod = ee.ImageCollection('JRC/GHSL/P2023A/GHS_SMOD').filter(ee.Filter.eq('year', 2020)).first().select('smod_code');
var urban = smod.gte(30), rural = smod.lte(13);

var meanU = lst.updateMask(urban).reduceRegion({reducer: ee.Reducer.mean(), geometry: aoi, scale: 100, bestEffort: true, tileScale: 4});
var meanR = lst.updateMask(rural).reduceRegion({reducer: ee.Reducer.mean(), geometry: aoi, scale: 100, bestEffort: true, tileScale: 4});
print('UHI ΔT (°C):', ee.Number(meanU.get('LST_C')).subtract(meanR.get('LST_C')));

var reg = ndvi.addBands(lst).reduceRegion({
  reducer: ee.Reducer.linearFit(), geometry: aoi, scale: 100, bestEffort: true, tileScale: 4
});
print('LST = a·NDVI + b →', reg);   // scale + offset

Map.centerObject(aoi, 9);
Map.addLayer(lst, {min: 20, max: 45, palette: ['#1e3a8a','#22c55e','#eab308','#ef4444']}, 'LST °C');
Map.addLayer(urban.updateMask(urban), {palette:['#7c2d12']}, 'Urban', false);

Export.image.toDrive({image: lst.clip(aoi), description: 'LST_summer_' + year,
  folder: 'gee_exports', region: aoi, scale: 30, maxPixels: 1e13,
  fileFormat: 'GeoTIFF', formatOptions: {cloudOptimized: true}});
#UHI#LST#Landsat#NDVI regression