"""
Carbon Emission Maps - Visualization of CO2 emissions across Delhi
Similar to AQI maps but showing emission sources and concentrations
"""
import folium
import os
import time
import random
import pandas as pd
from pathlib import Path
# Delhi center coordinates
DELHI_LAT = 28.7041
DELHI_LON = 77.1025
# Emission hotspots with sector-based CO2 values (in tonnes/day per zone)
# Categories: Industrial, Transport Hub, Residential, Power, Commercial
EMISSION_HOTSPOTS = [
# Industrial Areas (Highest emissions)
{"name": "Bawana Industrial Area", "lat": 28.8, "lon": 77.03, "emission": 45.2, "sector": "Industry", "icon": "🏭"},
{"name": "Okhla Industrial Area", "lat": 28.5365, "lon": 77.2803, "emission": 42.8, "sector": "Industry", "icon": "🏭"},
{"name": "Mundka Industrial", "lat": 28.6794, "lon": 77.0284, "emission": 38.5, "sector": "Industry", "icon": "🏭"},
{"name": "Wazirpur Industrial", "lat": 28.6967, "lon": 77.1658, "emission": 36.2, "sector": "Industry", "icon": "🏭"},
{"name": "Narela Industrial", "lat": 28.85, "lon": 77.1, "emission": 34.8, "sector": "Industry", "icon": "🏭"},
{"name": "Patparganj Industrial", "lat": 28.612, "lon": 77.292, "emission": 32.5, "sector": "Industry", "icon": "🏭"},
# Transport Hubs
{"name": "IGI Airport", "lat": 28.5562, "lon": 77.1, "emission": 48.5, "sector": "Aviation", "icon": "✈️"},
{"name": "ISBT Kashmere Gate", "lat": 28.6682, "lon": 77.2284, "emission": 28.4, "sector": "Transport", "icon": "🚌"},
{"name": "ISBT Anand Vihar", "lat": 28.6508, "lon": 77.3152, "emission": 26.8, "sector": "Transport", "icon": "🚌"},
{"name": "New Delhi Railway Station", "lat": 28.6421, "lon": 77.2194, "emission": 22.5, "sector": "Transport", "icon": "🚂"},
{"name": "ITO Junction", "lat": 28.6294, "lon": 77.241, "emission": 21.2, "sector": "Transport", "icon": "🚗"},
{"name": "Connaught Place", "lat": 28.6315, "lon": 77.2167, "emission": 24.6, "sector": "Transport", "icon": "🚗"},
{"name": "Chandni Chowk", "lat": 28.656, "lon": 77.231, "emission": 23.8, "sector": "Transport", "icon": "🚗"},
# Power Plants / Substations
{"name": "Badarpur Power Plant", "lat": 28.48, "lon": 77.35, "emission": 52.3, "sector": "Power", "icon": "⚡"},
{"name": "Rajghat Power House", "lat": 28.6567, "lon": 77.2519, "emission": 38.7, "sector": "Power", "icon": "⚡"},
{"name": "Indraprastha Power", "lat": 28.6125, "lon": 77.2372, "emission": 35.4, "sector": "Power", "icon": "⚡"},
# Residential Dense Areas
{"name": "Rohini Residential", "lat": 28.7019, "lon": 77.0984, "emission": 18.5, "sector": "Residential", "icon": "🏠"},
{"name": "Dwarka Sector-8", "lat": 28.5656, "lon": 77.067, "emission": 16.8, "sector": "Residential", "icon": "🏠"},
{"name": "Jahangirpuri", "lat": 28.716, "lon": 77.1829, "emission": 15.2, "sector": "Residential", "icon": "🏠"},
{"name": "Shahdara", "lat": 28.681, "lon": 77.305, "emission": 17.4, "sector": "Residential", "icon": "🏠"},
{"name": "Lajpat Nagar", "lat": 28.5697, "lon": 77.253, "emission": 14.8, "sector": "Residential", "icon": "🏠"},
{"name": "Punjabi Bagh", "lat": 28.673, "lon": 77.1374, "emission": 15.6, "sector": "Residential", "icon": "🏠"},
{"name": "RK Puram", "lat": 28.5505, "lon": 77.1849, "emission": 14.2, "sector": "Residential", "icon": "🏠"},
# Commercial Areas
{"name": "Nehru Place", "lat": 28.5485, "lon": 77.2513, "emission": 19.8, "sector": "Commercial", "icon": "🏢"},
{"name": "Saket Mall Area", "lat": 28.521, "lon": 77.219, "emission": 17.2, "sector": "Commercial", "icon": "🏢"},
{"name": "Gurgaon Border", "lat": 28.495, "lon": 77.08, "emission": 22.4, "sector": "Commercial", "icon": "🏢"},
]
# Sector colors for visualization
SECTOR_COLORS = {
"Industry": "#dc2626", # Red
"Aviation": "#f97316", # Orange
"Transport": "#eab308", # Yellow
"Power": "#8b5cf6", # Purple
"Residential": "#22c55e", # Green
"Commercial": "#3b82f6", # Blue
}
def get_emission_color(emission_value):
"""Get color based on emission level (tonnes/day)."""
if emission_value < 15:
return "#22c55e" # Green - Low
elif emission_value < 25:
return "#84cc16" # Lime
elif emission_value < 35:
return "#eab308" # Yellow
elif emission_value < 45:
return "#f97316" # Orange
else:
return "#dc2626" # Red - High
def add_delhi_boundary_to_map(m):
"""Helper to fetch and add Delhi boundary to a map."""
import requests
try:
geojson_url = "https://raw.githubusercontent.com/datameet/Municipal_Spatial_Data/master/Delhi/Delhi_Boundary.geojson"
folium.GeoJson(
geojson_url,
name="Delhi Boundary",
style_function=lambda x: {
'fillColor': 'none',
'color': 'black',
'weight': 3,
'dashArray': '5, 5',
'opacity': 0.6
}
).add_to(m)
return True
except Exception as e:
print(f"Error adding boundary: {e}")
return False
def generate_emission_heatmap_html():
"""Generates a Folium map with Grid Heatmap for CO2 emissions (Clipped to Delhi)."""
m = folium.Map(
location=[DELHI_LAT, DELHI_LON],
zoom_start=10,
control_scale=True
)
import requests
from shapely.geometry import shape, Point
from shapely.ops import unary_union
add_delhi_boundary_to_map(m)
delhi_polygon = None
has_boundary = False
try:
geojson_url = "https://raw.githubusercontent.com/datameet/Municipal_Spatial_Data/master/Delhi/Delhi_Boundary.geojson"
resp = requests.get(geojson_url)
data = resp.json()
features = data.get('features', [])
shapes = [shape(f['geometry']) for f in features]
delhi_polygon = unary_union(shapes)
has_boundary = True
except Exception as e:
print(f"Error processing boundary for clipping: {e}")
# Grid config
lat_min, lat_max = 28.40, 28.88
lon_min, lon_max = 76.85, 77.35
step = 0.015 # Approx 1.5km grid size
def get_color(value):
"""Blue-to-red emission heatmap colors."""
if value < 15: return "#eff6ff" # Very Light Blue
if value < 25: return "#bfdbfe" # Light Blue
if value < 35: return "#93c5fd" # Medium Blue
if value < 45: return "#60a5fa" # Blue
if value < 55: return "#3b82f6" # Strong Blue
if value < 65: return "#f97316" # Orange
return "#dc2626" # Red
lat = lat_min
while lat < lat_max:
lon = lon_min
while lon < lon_max:
center_point = Point(lon + step/2, lat + step/2)
if has_boundary and not delhi_polygon.contains(center_point):
lon += step
continue
# Calculate emission based on nearby hotspots
base_emission = 20.0 # Baseline
# Add contribution from nearby hotspots
for hotspot in EMISSION_HOTSPOTS:
dist = ((lat - hotspot['lat'])**2 + (lon - hotspot['lon'])**2)**0.5
if dist < 0.05: # ~5km radius of influence
contribution = hotspot['emission'] * (1 - dist/0.05) * 0.5
base_emission += contribution
# Add random variation
val = base_emission + random.uniform(-5, 5)
val = max(10, min(80, val)) # Clamp
color = get_color(val)
folium.Rectangle(
bounds=[[lat, lon], [lat + step, lon + step]],
color=None,
fill=True,
fill_color=color,
fill_opacity=0.6,
tooltip=f"CO₂: {val:.1f} t/day"
).add_to(m)
lon += step
lat += step
return m.get_root().render()
def generate_emission_hotspots_html():
"""Generates a map with sector-wise emission sources."""
m = folium.Map(location=[DELHI_LAT, DELHI_LON], zoom_start=11)
add_delhi_boundary_to_map(m)
for spot in EMISSION_HOTSPOTS:
emission = spot['emission']
sector = spot['sector']
color = SECTOR_COLORS.get(sector, "#6b7280")
# Size based on emission level
radius = 8 + (emission / 5)
folium.CircleMarker(
location=[spot['lat'], spot['lon']],
radius=radius,
popup=f"{spot['icon']} {spot['name']}
Sector: {sector}
CO₂: {emission} t/day",
color=color,
fill=True,
fill_color=color,
fill_opacity=0.7
).add_to(m)
# Add emission value label
folium.Marker(
location=[spot['lat'], spot['lon']],
icon=folium.DivIcon(
html=f'