1+ """
2+ Prediction API endpoints for fire spread simulation.
3+ """
4+
5+ from datetime import datetime , timezone
6+ from typing import List
7+
8+ from fastapi import APIRouter , HTTPException
9+
10+ # Local API schemas
11+ from app .schemas .prediction import (
12+ SpreadParameters as ApiSpreadParameters ,
13+ SpreadResult as ApiSpreadResult ,
14+ Isochrone as ApiIsochrone ,
15+ SpreadConfidence as ApiSpreadConfidence ,
16+ Point as ApiPoint ,
17+ )
18+
19+ # Algorithms engine from shared package
20+ try :
21+ from packages .algorithms .src .spread_modeling import (
22+ FireSpreadEngine ,
23+ SpreadParameters as AlgoSpreadParameters ,
24+ SpreadResult as AlgoSpreadResult ,
25+ )
26+ except Exception as e :
27+ # Fallback: make import error visible when endpoint is hit
28+ FireSpreadEngine = None # type: ignore
29+ AlgoSpreadParameters = None # type: ignore
30+ AlgoSpreadResult = None # type: ignore
31+
32+ router = APIRouter ()
33+
34+
35+ def _map_api_to_algo (params : ApiSpreadParameters ) -> AlgoSpreadParameters : # type: ignore
36+ ignition_points = [(p .latitude , p .longitude ) for p in params .ignition_points ]
37+ cond = params .conditions
38+ return AlgoSpreadParameters (
39+ ignition_points = ignition_points ,
40+ wind_speed = cond .wind_speed_mps ,
41+ wind_direction = cond .wind_direction_deg ,
42+ temperature = cond .temperature_c ,
43+ humidity = cond .relative_humidity ,
44+ fuel_moisture = cond .fuel_moisture ,
45+ fuel_model = cond .fuel_model ,
46+ simulation_hours = params .simulation_hours ,
47+ time_step_minutes = int (params .time_step_minutes ),
48+ monte_carlo_runs = params .monte_carlo_runs ,
49+ )
50+
51+
52+ def _map_algo_to_api (result : AlgoSpreadResult , duration_hours : float ) -> ApiSpreadResult : # type: ignore
53+ # Convert perimeter points
54+ perimeter_points : List [ApiPoint ] = [
55+ ApiPoint (latitude = lat , longitude = lon , altitude = 0.0 ) for (lat , lon ) in result .perimeter
56+ ]
57+
58+ # Convert isochrones (engine returns tuples list in geometry)
59+ api_isochrones : List [ApiIsochrone ] = []
60+ for iso in result .isochrones :
61+ geom = [ApiPoint (latitude = lat , longitude = lon , altitude = 0.0 ) for (lat , lon ) in iso .get ("geometry" , [])]
62+ api_isochrones .append (
63+ ApiIsochrone (
64+ hours_from_start = int (iso .get ("hours_from_start" , 0 )),
65+ geometry = geom ,
66+ area_hectares = float (iso .get ("area_hectares" , 0.0 )),
67+ perimeter_km = float (iso .get ("perimeter_km" , 0.0 )),
68+ )
69+ )
70+
71+ conf_value = float (result .confidence )
72+ api_conf = ApiSpreadConfidence (
73+ overall_confidence = conf_value ,
74+ weather_confidence = conf_value ,
75+ fuel_confidence = conf_value ,
76+ terrain_confidence = conf_value ,
77+ confidence_factors = "heuristic" ,
78+ )
79+
80+ return ApiSpreadResult (
81+ simulation_id = result .simulation_id ,
82+ created_at = datetime .now (tz = timezone .utc ),
83+ isochrones = api_isochrones ,
84+ perimeter = perimeter_points ,
85+ total_area_hectares = float (result .total_area_hectares ),
86+ max_spread_rate_mph = float (result .max_spread_rate_mph ),
87+ simulation_duration_hours = float (duration_hours ),
88+ statistics = {k : float (v ) for k , v in (result .statistics or {}).items ()},
89+ confidence = api_conf ,
90+ )
91+
92+
93+ @router .post ("/simulate" , response_model = ApiSpreadResult )
94+ async def simulate_spread (body : ApiSpreadParameters ) -> ApiSpreadResult :
95+ """Run a fire spread simulation using the shared algorithms engine."""
96+ if FireSpreadEngine is None or AlgoSpreadParameters is None :
97+ raise HTTPException (status_code = 500 , detail = "Algorithms package not available on PYTHONPATH" )
98+
99+ try :
100+ engine = FireSpreadEngine ()
101+ algo_params = _map_api_to_algo (body )
102+ algo_result = engine .simulate_spread (algo_params )
103+ return _map_algo_to_api (algo_result , duration_hours = body .simulation_hours )
104+ except HTTPException :
105+ raise
106+ except Exception as e :
107+ raise HTTPException (status_code = 500 , detail = f"Simulation failed: { e } " )
0 commit comments