// ====================================================================== // URBAN HEAT ISLAND CHANGE ANALYSIS OF CHENNAI // Land-only Chennai Focus Region // Before Year: 2014 // After Year : 2024 // Using Landsat 8/9 Collection 2 Level 2 Data in Google Earth Engine // ====================================================================== // ====================================================================== // SECTION 1: USER CONFIGURATION // ====================================================================== var CONFIG = { // Chennai focus bounding box // Sea will be removed using district land boundary aoiBox: ee.Geometry.Rectangle([80.05, 12.75, 80.35, 13.25]), // Before year summer season beforeStart: '2014-03-01', beforeEnd: '2014-05-31', // After year summer season afterStart: '2024-03-01', afterEnd: '2024-05-31', mapZoom: 11 }; // ====================================================================== // SECTION 2: CREATE LAND-ONLY AOI TO REMOVE SEA AREA // ====================================================================== var districts = ee.FeatureCollection("FAO/GAUL/2015/level2"); var chennaiLand = districts.filter( ee.Filter.and( ee.Filter.eq('ADM1_NAME', 'Tamil Nadu'), ee.Filter.inList('ADM2_NAME', [ 'Chennai', 'Thiruvallur', 'Kancheepuram' ]) ) ); // Final land-only AOI CONFIG.aoi = chennaiLand.geometry().intersection(CONFIG.aoiBox, ee.ErrorMargin(1)); // ====================================================================== // SECTION 3: MAP INITIALIZATION // ====================================================================== Map.centerObject(CONFIG.aoi, CONFIG.mapZoom); Map.setOptions('HYBRID'); print('===================================================='); print('URBAN HEAT ISLAND CHANGE ANALYSIS - CHENNAI'); print('Before Period:', CONFIG.beforeStart, 'to', CONFIG.beforeEnd); print('After Period:', CONFIG.afterStart, 'to', CONFIG.afterEnd); print('Dataset: Landsat 8/9 Collection 2 Level 2'); print('AOI: Land-only Chennai Focus Region'); print('===================================================='); // ====================================================================== // SECTION 4: STUDY AREA // ====================================================================== var studyArea = ee.FeatureCollection([ ee.Feature(CONFIG.aoi, {name: 'Land-only Chennai Focus Region'}) ]); var outline = studyArea.style({ color: 'yellow', fillColor: '00000000', width: 2 }); Map.addLayer(outline, {}, 'Study Area Boundary', true); // ====================================================================== // SECTION 5: CLOUD MASK FUNCTION // ====================================================================== function maskLandsatC2L2(image) { var qa = image.select('QA_PIXEL'); var cloudShadowBitMask = 1 << 4; var cloudsBitMask = 1 << 3; var snowBitMask = 1 << 5; var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0) .and(qa.bitwiseAnd(cloudsBitMask).eq(0)) .and(qa.bitwiseAnd(snowBitMask).eq(0)); return image.updateMask(mask); } // ====================================================================== // SECTION 6: FUNCTION TO CREATE LANDSAT COMPOSITE // ====================================================================== function getLandsatComposite(startDate, endDate) { var landsat8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .filterBounds(CONFIG.aoi) .filterDate(startDate, endDate) .map(maskLandsatC2L2); var landsat9 = ee.ImageCollection('LANDSAT/LC09/C02/T1_L2') .filterBounds(CONFIG.aoi) .filterDate(startDate, endDate) .map(maskLandsatC2L2); var collection = landsat8.merge(landsat9); return collection.median().clip(CONFIG.aoi); } var beforeImage = getLandsatComposite(CONFIG.beforeStart, CONFIG.beforeEnd); var afterImage = getLandsatComposite(CONFIG.afterStart, CONFIG.afterEnd); print('Before Landsat 8 Image Count:', ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .filterBounds(CONFIG.aoi) .filterDate(CONFIG.beforeStart, CONFIG.beforeEnd) .size() ); print('After Landsat 8 Image Count:', ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .filterBounds(CONFIG.aoi) .filterDate(CONFIG.afterStart, CONFIG.afterEnd) .size() ); print('After Landsat 9 Image Count:', ee.ImageCollection('LANDSAT/LC09/C02/T1_L2') .filterBounds(CONFIG.aoi) .filterDate(CONFIG.afterStart, CONFIG.afterEnd) .size() ); // ====================================================================== // SECTION 7: FUNCTION TO CALCULATE NDVI, NDBI, NDWI AND LST // ====================================================================== function calculateUHIProducts(image) { // Surface Reflectance scale factor var red = image.select('SR_B4').multiply(0.0000275).add(-0.2); var green = image.select('SR_B3').multiply(0.0000275).add(-0.2); var nir = image.select('SR_B5').multiply(0.0000275).add(-0.2); var swir = image.select('SR_B6').multiply(0.0000275).add(-0.2); // NDVI = vegetation var ndvi = nir.subtract(red) .divide(nir.add(red)) .rename('NDVI'); // NDBI = built-up var ndbi = swir.subtract(nir) .divide(swir.add(nir)) .rename('NDBI'); // NDWI = water var ndwi = green.subtract(nir) .divide(green.add(nir)) .rename('NDWI'); // Land Surface Temperature // ST_B10 scale factor: // Kelvin = DN * 0.00341802 + 149 // Celsius = Kelvin - 273.15 var lst = image.select('ST_B10') .multiply(0.00341802) .add(149.0) .subtract(273.15) .rename('LST'); return { ndvi: ndvi, ndbi: ndbi, ndwi: ndwi, lst: lst }; } var before = calculateUHIProducts(beforeImage); var after = calculateUHIProducts(afterImage); // ====================================================================== // SECTION 8: VISUALIZATION PARAMETERS // ====================================================================== var trueColorVis = { bands: ['SR_B4', 'SR_B3', 'SR_B2'], min: 7000, max: 18000 }; var lstVis = { min: 25, max: 45, palette: [ '0000ff', '00ffff', '00ff00', 'ffff00', 'ff9900', 'ff0000', '800000' ] }; var ndviVis = { min: -0.2, max: 0.8, palette: [ 'brown', 'yellow', 'lightgreen', 'green', 'darkgreen' ] }; var ndbiVis = { min: -0.4, max: 0.5, palette: [ 'blue', 'white', 'orange', 'red' ] }; var ndwiVis = { min: -0.5, max: 0.5, palette: [ 'brown', 'white', 'cyan', 'blue' ] }; // ====================================================================== // SECTION 9: LAND SURFACE TEMPERATURE MAPS // ====================================================================== Map.addLayer(beforeImage, trueColorVis, 'True Color 2014', false); Map.addLayer(afterImage, trueColorVis, 'True Color 2024', false); Map.addLayer(before.lst, lstVis, 'LST Map 2014', false); Map.addLayer(after.lst, lstVis, 'LST Map 2024', true); // ====================================================================== // SECTION 10: NDVI, NDBI, NDWI MAPS // ====================================================================== Map.addLayer(before.ndvi, ndviVis, 'NDVI Vegetation Map 2014', false); Map.addLayer(after.ndvi, ndviVis, 'NDVI Vegetation Map 2024', false); Map.addLayer(before.ndbi, ndbiVis, 'NDBI Built-up Map 2014', false); Map.addLayer(after.ndbi, ndbiVis, 'NDBI Built-up Map 2024', false); Map.addLayer(before.ndwi, ndwiVis, 'NDWI Water Body Map 2014', false); Map.addLayer(after.ndwi, ndwiVis, 'NDWI Water Body Map 2024', false); // ====================================================================== // SECTION 11: LST CHANGE MAP // ====================================================================== // Positive value = temperature increased from 2014 to 2024 var lstChange = after.lst.subtract(before.lst).rename('LST_Change'); var lstChangeVis = { min: -5, max: 5, palette: [ '0000ff', '00ffff', 'ffffff', 'ffff00', 'ff9900', 'ff0000' ] }; Map.addLayer(lstChange, lstChangeVis, 'LST Change Map 2024 - 2014', false); // ====================================================================== // SECTION 12: NDVI CHANGE AND NDBI CHANGE // ====================================================================== var ndviChange = after.ndvi.subtract(before.ndvi).rename('NDVI_Change'); var ndbiChange = after.ndbi.subtract(before.ndbi).rename('NDBI_Change'); var ndviChangeVis = { min: -0.3, max: 0.3, palette: ['red', 'white', 'green'] }; var ndbiChangeVis = { min: -0.3, max: 0.3, palette: ['blue', 'white', 'red'] }; Map.addLayer(ndviChange, ndviChangeVis, 'NDVI Change Map 2024 - 2014', false); Map.addLayer(ndbiChange, ndbiChangeVis, 'NDBI Change Map 2024 - 2014', false); // ====================================================================== // SECTION 13: UHI ZONE CLASSIFICATION FUNCTION // ====================================================================== function createHeatZones(lstImage) { var percentiles = lstImage.reduceRegion({ reducer: ee.Reducer.percentile([25, 50, 75]), geometry: CONFIG.aoi, scale: 30, maxPixels: 1e13, bestEffort: true }); var p25 = ee.Number(percentiles.get('LST_p25')); var p50 = ee.Number(percentiles.get('LST_p50')); var p75 = ee.Number(percentiles.get('LST_p75')); var zones = ee.Image(0) .where(lstImage.gte(p25), 1) .where(lstImage.gte(p50), 2) .where(lstImage.gte(p75), 3) .rename('Heat_Zone') .clip(CONFIG.aoi); return zones; } var heatZones2014 = createHeatZones(before.lst); var heatZones2024 = createHeatZones(after.lst); var heatZoneVis = { min: 0, max: 3, palette: [ '00ffff', 'ffff00', 'ff9900', 'ff0000' ] }; Map.addLayer(heatZones2014, heatZoneVis, 'UHI Zone Map 2014', false); Map.addLayer(heatZones2024, heatZoneVis, 'UHI Zone Map 2024', false); // ====================================================================== // SECTION 14: HOTSPOT MAPS // ====================================================================== var hotspot2014 = heatZones2014.eq(3).selfMask().rename('Hotspot_2014'); var hotspot2024 = heatZones2024.eq(3).selfMask().rename('Hotspot_2024'); Map.addLayer(hotspot2014, {palette: ['orange']}, 'Very High Heat Hotspots 2014', false); Map.addLayer(hotspot2024, {palette: ['red']}, 'Very High Heat Hotspots 2024', false); // ====================================================================== // SECTION 15: EXTRACT BUILT-UP, VEGETATION AND WATER // ====================================================================== var builtUp2014 = before.ndbi.gt(0).selfMask().rename('Built_up_2014'); var builtUp2024 = after.ndbi.gt(0).selfMask().rename('Built_up_2024'); var vegetation2014 = before.ndvi.gt(0.3).selfMask().rename('Vegetation_2014'); var vegetation2024 = after.ndvi.gt(0.3).selfMask().rename('Vegetation_2024'); var water2014 = before.ndwi.gt(0).selfMask().rename('Water_2014'); var water2024 = after.ndwi.gt(0).selfMask().rename('Water_2024'); Map.addLayer(builtUp2014, {palette: ['orange']}, 'Extracted Built-up Area 2014', false); Map.addLayer(builtUp2024, {palette: ['red']}, 'Extracted Built-up Area 2024', false); Map.addLayer(vegetation2014, {palette: ['lightgreen']}, 'Extracted Vegetation Area 2014', false); Map.addLayer(vegetation2024, {palette: ['green']}, 'Extracted Vegetation Area 2024', false); Map.addLayer(water2014, {palette: ['cyan']}, 'Extracted Water Area 2014', false); Map.addLayer(water2024, {palette: ['blue']}, 'Extracted Water Area 2024', false); // ====================================================================== // SECTION 16: AREA CALCULATION FUNCTION // ====================================================================== var pixelAreaKm2 = ee.Image.pixelArea().divide(1000000); function areaSqKm(mask, bandName) { return mask.unmask(0) .multiply(pixelAreaKm2) .reduceRegion({ reducer: ee.Reducer.sum(), geometry: CONFIG.aoi, scale: 30, maxPixels: 1e13, bestEffort: true }) .get(bandName); } function printArea(mask, bandName, label) { print(label + ' Area sq.km:', areaSqKm(mask.rename(bandName), bandName)); } // ====================================================================== // SECTION 17: HEAT ZONE AREA CALCULATION // ====================================================================== print(''); print('================ HEAT ZONE AREA 2014 ================'); printArea(heatZones2014.eq(0), 'area', 'Low Heat Zone 2014'); printArea(heatZones2014.eq(1), 'area', 'Moderate Heat Zone 2014'); printArea(heatZones2014.eq(2), 'area', 'High Heat Zone 2014'); printArea(heatZones2014.eq(3), 'area', 'Very High Heat Zone 2014'); print(''); print('================ HEAT ZONE AREA 2024 ================'); printArea(heatZones2024.eq(0), 'area', 'Low Heat Zone 2024'); printArea(heatZones2024.eq(1), 'area', 'Moderate Heat Zone 2024'); printArea(heatZones2024.eq(2), 'area', 'High Heat Zone 2024'); printArea(heatZones2024.eq(3), 'area', 'Very High Heat Zone 2024'); print(''); print('================ LAND COVER AREA COMPARISON ================'); printArea(builtUp2014, 'area', 'Built-up Area 2014'); printArea(builtUp2024, 'area', 'Built-up Area 2024'); printArea(vegetation2014, 'area', 'Vegetation Area 2014'); printArea(vegetation2024, 'area', 'Vegetation Area 2024'); printArea(water2014, 'area', 'Water Area 2014'); printArea(water2024, 'area', 'Water Area 2024'); // ====================================================================== // SECTION 18: LST STATISTICS // ====================================================================== function lstStatistics(lstImage, yearLabel) { var stats = lstImage.reduceRegion({ reducer: ee.Reducer.minMax() .combine({ reducer2: ee.Reducer.mean(), sharedInputs: true }), geometry: CONFIG.aoi, scale: 30, maxPixels: 1e13, bestEffort: true }); print('LST Statistics ' + yearLabel + ':', stats); } lstStatistics(before.lst, '2014'); lstStatistics(after.lst, '2024'); var meanLSTChange = lstChange.reduceRegion({ reducer: ee.Reducer.mean(), geometry: CONFIG.aoi, scale: 30, maxPixels: 1e13, bestEffort: true }); print('Mean LST Change 2024 - 2014:', meanLSTChange); // ====================================================================== // SECTION 19: MEAN LST BY LAND COVER TYPE // ====================================================================== function meanLSTOverMask(lstImage, mask, label) { var mean = lstImage.updateMask(mask).reduceRegion({ reducer: ee.Reducer.mean(), geometry: CONFIG.aoi, scale: 30, maxPixels: 1e13, bestEffort: true }); print(label, mean); } print(''); print('================ MEAN TEMPERATURE BY LAND COVER ================'); meanLSTOverMask(before.lst, builtUp2014, 'Mean LST over Built-up Area 2014'); meanLSTOverMask(after.lst, builtUp2024, 'Mean LST over Built-up Area 2024'); meanLSTOverMask(before.lst, vegetation2014, 'Mean LST over Vegetation Area 2014'); meanLSTOverMask(after.lst, vegetation2024, 'Mean LST over Vegetation Area 2024'); meanLSTOverMask(before.lst, water2014, 'Mean LST over Water Area 2014'); meanLSTOverMask(after.lst, water2024, 'Mean LST over Water Area 2024'); // ====================================================================== // SECTION 20: LEGEND // ====================================================================== var legend = ui.Panel({ style: { position: 'bottom-right', padding: '10px', backgroundColor: 'rgba(255,255,255,0.9)' } }); legend.add(ui.Label({ value: 'UHI Legend', style: { fontWeight: 'bold', fontSize: '15px', margin: '0 0 6px 0' } })); function legendRow(color, name) { var colorBox = ui.Label({ style: { backgroundColor: color, padding: '9px', margin: '0 6px 4px 0' } }); var label = ui.Label({ value: name, style: { margin: '0 0 4px 0', fontSize: '12px' } }); return ui.Panel({ widgets: [colorBox, label], layout: ui.Panel.Layout.Flow('horizontal') }); } legend.add(legendRow('0000ff', 'Low Temperature')); legend.add(legendRow('00ffff', 'Low Heat Zone')); legend.add(legendRow('ffff00', 'Moderate Heat Zone')); legend.add(legendRow('ff9900', 'High Heat Zone')); legend.add(legendRow('ff0000', 'Very High Heat / Increase')); legend.add(legendRow('green', 'Vegetation')); legend.add(legendRow('blue', 'Water')); legend.add(legendRow('yellow', 'Study Area Boundary')); Map.add(legend); // ====================================================================== // SECTION 21: TITLE // ====================================================================== var title = ui.Label({ value: 'Urban Heat Island Change Analysis of Chennai from 2014 to 2024', style: { position: 'top-center', fontWeight: 'bold', fontSize: '20px', padding: '8px', backgroundColor: 'rgba(255,255,255,0.85)' } }); Map.add(title); // ====================================================================== // SECTION 22: FINAL OUTPUT NOTE // ====================================================================== print(''); print('================ PROJECT OUTPUTS ================='); print('1. LST Map 2014'); print('2. LST Map 2024'); print('3. UHI Zone Map 2014'); print('4. UHI Zone Map 2024'); print('5. LST Change Map 2024 - 2014'); print('6. NDVI Change Map'); print('7. NDBI Change Map'); print('8. Heat Zone Area Comparison'); print('9. Mean LST over Built-up, Vegetation and Water'); print('=================================================='); // ====================================================================== // SECTION 23: EXPORT OPTIONS // ====================================================================== /* Export.image.toDrive({ image: before.lst, description: 'Chennai_LST_2014_LandOnly', folder: 'GEE_UHI_Project', fileNamePrefix: 'Chennai_LST_2014_LandOnly', region: CONFIG.aoi, scale: 30, crs: 'EPSG:4326', maxPixels: 1e13 }); Export.image.toDrive({ image: after.lst, description: 'Chennai_LST_2024_LandOnly', folder: 'GEE_UHI_Project', fileNamePrefix: 'Chennai_LST_2024_LandOnly', region: CONFIG.aoi, scale: 30, crs: 'EPSG:4326', maxPixels: 1e13 }); Export.image.toDrive({ image: lstChange, description: 'Chennai_LST_Change_2024_2014_LandOnly', folder: 'GEE_UHI_Project', fileNamePrefix: 'Chennai_LST_Change_2024_2014_LandOnly', region: CONFIG.aoi, scale: 30, crs: 'EPSG:4326', maxPixels: 1e13 }); */ // ====================================================================== // END OF SCRIPT // ======================================================================