from fastapi import FastAPI
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
import folium
import pandas as pd
import joblib
import os
import requests
app = FastAPI()
# ===============================
# Serve static files
# ===============================
app.mount("/static", StaticFiles(directory="."), name="static")
# ===============================
# Load ML Model
# ===============================
model = None
model_paths = [
"wildfire_model_cyprus.pkl",
"models/wildfire_model_cyprus.pkl",
]
for path in model_paths:
if os.path.exists(path):
model = joblib.load(path)
print(f"✅ Model loaded from: {path}")
break
MAP_FILE = "cyprus_wildfire_risk_map.html"
# ===============================
# Cyprus Cities
# ===============================
cities = {
"Nicosia": (35.1856, 33.3823),
"Limassol": (34.7071, 33.0226),
"Larnaca": (34.9182, 33.6233),
"Paphos": (34.7754, 32.4218),
"Famagusta": (35.1167, 33.9500),
"Kyrenia": (35.3417, 33.3167),
"Morphou": (35.1989, 32.9931),
"Lapta": (35.3389, 33.1611),
"Alsancak": (35.3444, 33.1936),
"Dikmen": (35.2681, 33.3408),
"Değirmenlik": (35.2897, 33.4356),
"Geçitkale": (35.2606, 33.7922),
"Iskele": (35.2722, 33.9458),
"Geroskipou": (34.7567, 32.4547),
"Chloraka": (34.8075, 32.4064),
"Ypsonas": (34.6889, 32.9556),
"Kato Polemidia": (34.6967, 33.0111),
"Aradippou": (34.9517, 33.5928),
"Athienou": (35.0608, 33.5419),
"Polis": (35.0367, 32.4250),
"Peyia": (34.8833, 32.3833),
"Kathikas": (34.9175, 32.4686),
"Ayia Napa": (34.9889, 34.0018),
"Paralimni": (35.0394, 33.9819),
"Deryneia": (35.0648, 33.9566),
"Kakopetria": (34.9894, 32.9033),
"Platres": (34.8633, 32.8575),
}
# ===============================
# Home Page
# ===============================
@app.get("/", response_class=HTMLResponse)
def home():
return """
Cyprus Wildfire Risk
🔥 Cyprus Wildfire Risk Map
View Map
"""
# ===============================
# Generate Map
# ===============================
@app.post("/generate-map")
def generate_map():
if model is None:
return {"error": "Model not loaded"}
m = folium.Map(location=[35.1, 33.4], zoom_start=8)
for city, (lat, lon) in cities.items():
# ===============================
# REAL WEATHER API CALL
# ===============================
url = (
"https://api.open-meteo.com/v1/forecast"
f"?latitude={lat}"
f"&longitude={lon}"
"&hourly=temperature_2m,relative_humidity_2m,wind_speed_100m,precipitation"
"&forecast_days=7"
"&timezone=auto"
)
try:
response = requests.get(url, timeout=8)
data = response.json()
hourly = data["hourly"]
# 3-day averages
temp = sum(hourly["temperature_2m"][:72]) / 72
humidity = sum(hourly["relative_humidity_2m"][:72]) / 72
wind = sum(hourly["wind_speed_100m"][:72]) / 72
# 7-day rainfall
rainfall = sum(hourly["precipitation"][:168])
except Exception as e:
print("Weather API failed:", e)
temp = 30
humidity = 50
wind = 12
rainfall = 5
# ===============================
# Prepare ML Input
# ===============================
X = pd.DataFrame({
"temp_c": [temp],
"humidity": [humidity],
"wind_speed": [wind],
"precipitation": [0],
"temp_3d_avg": [temp],
"humidity_3d_avg": [humidity],
"precipitation_7d": [rainfall],
"wind_3d_avg": [wind]
})
prediction = model.predict(X)[0]
# ===============================
# Risk Classification
# ===============================
if prediction == 1 or (temp > 32 and humidity < 35):
color = "red"
risk = "High Risk"
elif temp > 28 and humidity < 45 and rainfall < 10:
color = "orange"
risk = "Medium Risk"
else:
color = "green"
risk = "Low Risk"
# ===============================
# Add marker to map
# ===============================
folium.CircleMarker(
location=[lat, lon],
radius=7,
color=color,
fill=True,
fill_color=color,
fill_opacity=0.8,
popup=f"""
{city}
Temperature: {temp:.1f} °C
Humidity: {humidity:.1f}%
Wind Speed: {wind:.1f} km/h
Rainfall (7d): {rainfall:.1f} mm
{risk}
"""
).add_to(m)
m.save(MAP_FILE)
return {"status": "map generated", "view": "/view-map"}
# ===============================
# View Map
# ===============================
@app.get("/view-map")
def view_map():
if os.path.exists(MAP_FILE):
return FileResponse(MAP_FILE)
return {"error": "Generate map first"}