🛰️ GeoAI DisasterMapper
Automated Building Damage Detection & Climate Vulnerability Analysis
import streamlit as st import torch import numpy as np from PIL import Image import matplotlib.pyplot as plt import matplotlib.patches as mpatches from matplotlib.colors import ListedColormap import io import os import sys sys.path.append(os.path.dirname(__file__)) from utils.inference import ( load_model, run_inference, get_damage_stats, prediction_to_colored_image, export_geojson, LABELS, COLORS ) from utils.climate import ( get_climate_data, parse_climate_data, calculate_anomaly, plot_climate_trends, get_climate_summary ) # ── Page config ────────────────────────────────────────────────────────────── st.set_page_config( page_title = "GeoAI Disaster Mapper", page_icon = "🛰️", layout = "wide", initial_sidebar_state = "expanded" ) # ── Custom CSS ──────────────────────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ── Header ──────────────────────────────────────────────────────────────────── st.markdown("""
🛰️ GeoAI DisasterMapper
Automated Building Damage Detection & Climate Vulnerability Analysis
⚙️ Configuration
', unsafe_allow_html=True) # Model path model_path = st.text_input( "Model Path", value="model/best_model_resnet50.pth", help="Path to trained U-Net model" ) st.markdown("---") st.markdown('📍 Location
', unsafe_allow_html=True) # Disaster presets disaster_presets = { "Custom" : (0.0, 0.0, None), "Morocco Earthquake 2023" : (31.07, -8.41, "2023-09-08"), "Turkey Earthquake 2023" : (37.17, 37.03, "2023-02-06"), "Nepal Earthquake 2015" : (27.73, 85.33, "2015-04-25"), "Palu Tsunami 2018" : (-0.90, 119.88, "2018-09-28"), "Hurricane Michael 2018" : (30.19, -85.68, "2018-10-10"), } selected_preset = st.selectbox("Disaster Preset", list(disaster_presets.keys())) preset_lat, preset_lon, preset_date = disaster_presets[selected_preset] col1, col2 = st.columns(2) with col1: lat = st.number_input("Latitude", value=preset_lat, format="%.4f") with col2: lon = st.number_input("Longitude", value=preset_lon, format="%.4f") disaster_date = st.text_input( "Disaster Date (YYYY-MM-DD)", value=preset_date if preset_date else "" ) st.markdown("---") st.markdown('🌡️ Climate Settings
', unsafe_allow_html=True) start_year = st.slider("Climate Data Start Year", 2000, 2020, 2010) end_year = st.slider("Climate Data End Year", 2015, 2024, 2023) st.markdown("---") st.markdown("""Upload Satellite Images
', unsafe_allow_html=True) col1, col2 = st.columns(2) with col1: st.markdown("**Before Disaster**") pre_file = st.file_uploader( "Upload pre-disaster image", type=['png', 'jpg', 'jpeg', 'tif', 'tiff'], key="pre" ) if pre_file: pre_image = Image.open(pre_file) st.image(pre_image, caption="Before Disaster", use_column_width=True) with col2: st.markdown("**After Disaster**") post_file = st.file_uploader( "Upload post-disaster image", type=['png', 'jpg', 'jpeg', 'tif', 'tiff'], key="post" ) if post_file: post_image = Image.open(post_file) st.image(post_image, caption="After Disaster", use_column_width=True) st.markdown("---") if pre_file and post_file: if st.button("🔍 RUN DAMAGE DETECTION"): with st.spinner("Loading model and running inference..."): try: # Load model if not os.path.exists(model_path): st.error(f"Model not found at: {model_path}") st.stop() device = 'cuda' if torch.cuda.is_available() else 'cpu' model = load_model(model_path, device) # Run inference pre_image = Image.open(pre_file) post_image = Image.open(post_file) pred = run_inference(model, pre_image, post_image, device) # Get stats stats = get_damage_stats(pred) st.success("Inference complete ✓") # ── Damage metrics ──────────────────────────────────── st.markdown('Damage Statistics
', unsafe_allow_html=True) metric_cols = st.columns(5) damage_colors_hex = { 'background' : '#64748b', 'no-damage' : '#2ecc71', 'minor' : '#f1c40f', 'major' : '#e67e22', 'destroyed' : '#e74c3c' } for i, label in enumerate(LABELS): with metric_cols[i]: pct = stats.get(label, {}).get('percent', 0) st.markdown(f"""Damage Map
', unsafe_allow_html=True) colored_pred = prediction_to_colored_image(pred) fig, axes = plt.subplots(1, 3, figsize=(18, 6)) fig.patch.set_facecolor('#0a0e1a') for ax in axes: ax.set_facecolor('#0a0e1a') ax.axis('off') axes[0].imshow(np.array(pre_image.convert('RGB'))) axes[0].set_title('Before Disaster', color='white', fontsize=12, pad=10) axes[1].imshow(np.array(post_image.convert('RGB'))) axes[1].set_title('After Disaster', color='white', fontsize=12, pad=10) axes[2].imshow(np.array(post_image.convert('RGB'))) axes[2].imshow(colored_pred, alpha=0.6) axes[2].set_title('Damage Prediction Overlay', color='white', fontsize=12, pad=10) # Legend patches = [ mpatches.Patch(color=COLORS[i], label=LABELS[i]) for i in range(len(LABELS)) ] fig.legend( handles=patches, loc='lower center', ncol=5, facecolor='#1a2035', labelcolor='white', fontsize=10, bbox_to_anchor=(0.5, -0.05) ) plt.tight_layout() buf = io.BytesIO() plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor='#0a0e1a') buf.seek(0) plt.close() st.image(buf, use_column_width=True) # ── GeoJSON export ──────────────────────────────────── st.markdown('Export Results
', unsafe_allow_html=True) geojson_path = export_geojson(pred) if geojson_path: with open(geojson_path, 'r') as f: geojson_data = f.read() st.download_button( label = "⬇️ Download GeoJSON", data = geojson_data, file_name = "damage_map.geojson", mime = "application/json" ) st.markdown("""Climate Vulnerability Analysis
', unsafe_allow_html=True) st.markdown("""Climate Summary
', unsafe_allow_html=True) mcols = st.columns(4) with mcols[0]: st.metric( "Avg Temperature", f"{summary['avg_temp']}°C" ) with mcols[1]: st.metric( "Max Temperature", f"{summary['max_temp']}°C" ) with mcols[2]: st.metric( "Avg Rainfall", f"{summary['avg_rainfall']} mm/day" ) with mcols[3]: warming = summary.get('warming_rate', 0) or 0 st.metric( "Warming Rate", f"{warming:+.3f}°C/year", delta=f"{'↑ Warming' if warming > 0 else '↓ Cooling'}" ) # Pre vs post disaster if disaster_date and 'temp_change' in summary: st.markdown('Pre vs Post Disaster Climate
', unsafe_allow_html=True) pcols = st.columns(3) with pcols[0]: st.metric( "Pre-disaster Avg Temp", f"{summary['pre_disaster_avg_temp']}°C" ) with pcols[1]: st.metric( "Post-disaster Avg Temp", f"{summary['post_disaster_avg_temp']}°C" ) with pcols[2]: change = summary['temp_change'] st.metric( "Temperature Change", f"{change:+.2f}°C", delta=f"{'Warmer' if change > 0 else 'Cooler'} after disaster" ) # ── Climate charts ──────────────────────────────────── st.markdown('Climate Trends
', unsafe_allow_html=True) location_name = selected_preset if selected_preset != "Custom" \ else f"{lat}°N, {lon}°E" chart_buf = plot_climate_trends( df, location_name, disaster_date if disaster_date else None ) st.image(chart_buf, use_column_width=True) # Download CSV csv = df.to_csv(index=False) st.download_button( label = "⬇️ Download Climate Data (CSV)", data = csv, file_name = "climate_data.csv", mime = "text/csv" ) # Vulnerability assessment st.markdown('Vulnerability Assessment
', unsafe_allow_html=True) warming_rate = summary.get('warming_rate') or 0 if warming_rate > 0.02: risk_level = "🔴 HIGH" risk_color = "#e74c3c" risk_msg = f"Region is warming at {warming_rate:.3f}°C/year — significantly above global average." elif warming_rate > 0.01: risk_level = "🟠 MEDIUM" risk_color = "#e67e22" risk_msg = f"Region shows moderate warming trend of {warming_rate:.3f}°C/year." else: risk_level = "🟡 LOW-MEDIUM" risk_color = "#f1c40f" risk_msg = f"Region shows relatively stable temperature trend." st.markdown(f"""Model Architecture
', unsafe_allow_html=True) col1, col2 = st.columns(2) with col1: st.markdown("""Training Curves
', unsafe_allow_html=True) curve_path = "outputs/training_curves.png" cm_path = "outputs/confusion_matrix.png" if os.path.exists(curve_path): st.image(curve_path, caption="Training Loss & Accuracy Curves", use_column_width=True) else: st.info("Place training_curves.png in outputs/ folder to display here.") if os.path.exists(cm_path): st.image(cm_path, caption="Confusion Matrix", use_column_width=True) else: st.info("Place confusion_matrix.png in outputs/ folder to display here.") st.markdown('Project Pipeline
', unsafe_allow_html=True) st.markdown(""" ``` xBD Dataset (USA/Guatemala disasters) ↓ Preprocessing → 256x256 chips with augmentation ↓ U-Net ResNet50 Training (Google Colab T4 GPU) ↓ Inference on unseen test disasters ↓ Apply to target disaster location ↓ GeoJSON damage map export ↓ NASA POWER climate vulnerability overlay ``` """) # ── Footer ──────────────────────────────────────────────────────────────────── st.markdown("---") st.markdown("""