""" app.py — Hugging Face Spaces entry point for the Multi-Hazard Warning System. This is a self-contained Gradio application that loads the quantized MTL model and runs inference directly (no separate FastAPI backend needed). Layout: Left panel: Location search, lat/lon sliders, date picker, Analyze button Center: Interactive Folium map (embedded HTML) Right panel: Output tabs (Grad-CAM, AQI Chart, Raw Data) Bottom bar: Risk level badge, confidence score, prediction timestamp """ import base64 import logging import sys import os from datetime import datetime, date from pathlib import Path # ---- Ensure project root is on Python path ---- PROJECT_ROOT = Path(__file__).resolve().parent if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) # Force CPU for HF Spaces free tier os.environ["CUDA_VISIBLE_DEVICES"] = "" import gradio as gr import numpy as np from src.inference.predict import Predictor from src.inference.visualize import ( create_folium_map, create_aqi_forecast_chart, ) from src.training.config import OUTPUTS_DIR logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(name)s | %(levelname)s | %(message)s", ) logger = logging.getLogger(__name__) # ---- Initialize Predictor ---- predictor = None def get_predictor(): """Lazy-initialize the predictor singleton.""" global predictor if predictor is None: logger.info("Initializing predictor for Gradio dashboard...") predictor = Predictor() return predictor # ---- CSS ---- CSS_PATH = Path(__file__).parent / "frontend" / "assets" / "style.css" custom_css = "" if CSS_PATH.exists(): custom_css = CSS_PATH.read_text() from geopy.geocoders import Nominatim def geocode_location(location_name: str): if not location_name or not location_name.strip(): return 37.5, -120.3 try: geolocator = Nominatim(user_agent="multi_hazard_app") loc = geolocator.geocode(location_name) if loc: return loc.latitude, loc.longitude except Exception as e: logger.error(f"Geocoding failed: {e}") return 37.5, -120.3 def analyze_region(latitude: float, longitude: float, target_date: str): """ Main prediction handler for the Gradio interface. Called when the user clicks "Analyze Region". Runs the full prediction pipeline and returns all UI components. """ try: pred = get_predictor() # Run prediction result = pred.predict(latitude, longitude, target_date) # 1. Folium map HTML risk_map = np.array(result.get("risk_map", np.zeros((128, 128)))) folium_html = create_folium_map( latitude=latitude, longitude=longitude, risk_map=risk_map, risk_score=result["risk_score"], aqi_forecast=result["aqi_forecast"], gradcam_b64=result.get("gradcam_base64"), ) # Wrap in an iframe-styled container for better display map_display = f"""
""" # 2. Grad-CAM image gradcam_b64 = result.get("gradcam_base64", "") heatmap_b64 = result.get("heatmap_base64", "") gradcam_html = f"""Satellite + Grad-CAM Overlay
Risk Heatmap
🔴 Red/Yellow = High fire risk regions identified by the model
""" # 3. AQI forecast chart aqi_chart = create_aqi_forecast_chart( result["aqi_forecast"], result["forecast_hours"], ) # 4. Raw data table raw_data_html = _create_raw_data_table(result) # 5. Risk badge risk_badge = _create_risk_badge( result["risk_level"], result["risk_score"], result.get("prediction_timestamp", ""), result.get("inference_time_ms", 0), result.get("peak_aqi_value", 0), result.get("peak_aqi_hour", 0), ) return map_display, gradcam_html, aqi_chart, raw_data_html, risk_badge except Exception as e: logger.error(f"Analysis failed: {e}", exc_info=True) error_html = f"""{str(e)}
Please try again or check server logs.
| Date | Temp (°C) | Humidity (%) | Wind Speed (km/h) | Wind Dir (°) | Precipitation (mm) | PM2.5 (µg/m³) |
|---|
Multi-Task Learning · Wildfire Risk Prediction · AQI Forecasting (24–72h)
ResNet-50 (CNN) + BiLSTM · Grad-CAM Explainability · Deployed on Hugging Face Spaces
ℹ️ Quick Presets
Delhi: 28.6, 77.2
California: 37.5, -120.3
Australia: -33.8, 151.2
Amazon: -3.4, -60.5
Click 'Analyze Region' to generate " "Grad-CAM visualization
", elem_id="gradcam-output", ) with gr.TabItem("📊 AQI Forecast", id="aqi-tab"): aqi_output = gr.Plot( label="72-Hour AQI Forecast", elem_id="aqi-output", ) with gr.TabItem("📋 Raw Data", id="data-tab"): data_output = gr.HTML( value="Input data table will appear here
", elem_id="data-output", ) # ---- Bottom Status Bar ---- status_bar = gr.HTML( value="""Fetching data and running model inference...
", visible=True) ) def _reset_loading(): return ( gr.Button(value="🔍 Analyze Region", interactive=True), gr.HTML(value="", visible=False) ) analyze_btn.click( fn=_set_loading, outputs=[analyze_btn, loading_status] ).then( fn=analyze_region, inputs=[lat_slider, lon_slider, date_picker], outputs=[map_output, gradcam_output, aqi_output, data_output, status_bar], api_name="analyze", ).then( fn=_reset_loading, outputs=[analyze_btn, loading_status] ) return app # ============================================================ # MAIN # ============================================================ if __name__ == "__main__": logger.info("Launching Multi-Hazard Warning System Dashboard...") app = build_app() app.launch( server_name="0.0.0.0", server_port=7860, share=False, )