HackatonSmall / modules /weather_utils.py
Crocolil's picture
Upload folder using huggingface_hub
4e9208d verified
Raw
History Blame Contribute Delete
6.32 kB
import requests
from datetime import date, datetime, timedelta
from modules.weather import Weather
def fetch_json(url: str, timeout: int = 10) -> dict:
resp = requests.get(url, timeout=timeout)
resp.raise_for_status()
data = resp.json()
if data.get("error"):
raise RuntimeError(f"Open-Meteo API error: {data.get('reason', data)}")
if "daily" not in data:
raise RuntimeError(f"Unexpected Open-Meteo response: {data}")
return data
def did_or_will_rain(
target_date,
latitude,
longitude,
forecast_threshold=50,
):
"""
Returns True if:
- it rained on a past date
- rain is forecast on a future date above the threshold
Parameters
----------
target_date : str | date | datetime
Date to check ("YYYY-MM-DD" or date object)
latitude : float
longitude : float
forecast_threshold : int
Minimum rain probability (%) for future dates
Returns
-------
bool
"""
# Normalize date
if isinstance(target_date, str):
target_date = datetime.strptime(target_date, "%Y-%m-%d").date()
elif isinstance(target_date, datetime):
target_date = target_date.date()
today = date.today()
# Historical date
if target_date < today:
url = (
"https://archive-api.open-meteo.com/v1/archive"
f"?latitude={latitude}"
f"&longitude={longitude}"
f"&start_date={target_date}"
f"&end_date={target_date}"
"&daily=precipitation_sum"
"&timezone=auto"
)
data = fetch_json(url)
rain_mm = data["daily"]["precipitation_sum"][0]
return rain_mm > 0
# Today / future date
else:
url = (
"https://api.open-meteo.com/v1/forecast"
f"?latitude={latitude}"
f"&longitude={longitude}"
"&daily=precipitation_probability_max,precipitation_sum"
"&forecast_days=16"
"&timezone=auto"
)
data = fetch_json(url)
dates = data["daily"]["time"]
target_date_str = target_date.isoformat()
if target_date_str not in dates:
raise ValueError("Date outside forecast range")
idx = dates.index(target_date_str)
probability = data["daily"]["precipitation_probability_max"][idx]
precipitation = data["daily"]["precipitation_sum"][idx]
return (
probability >= forecast_threshold
or precipitation > 0
)
def weather_comment(code: int) -> str:
WMO_CODES = {
0: "☀️ Sunny",
1: "🌤️ Mainly clear",
2: "⛅ Partly cloudy",
3: "☁️ Cloudy",
45: "🌫️ Foggy",
48: "🌫️ Icy fog",
51: "🌦️ Light drizzle",
53: "🌦️ Moderate drizzle",
55: "🌧️ Dense drizzle",
56: "🌨️ Light freezing drizzle",
57: "🌨️ Heavy freezing drizzle",
61: "🌧️ Slight rain",
63: "🌧️ Moderate rain",
65: "🌧️ Heavy rain",
66: "🌨️ Light freezing rain",
67: "🌨️ Heavy freezing rain",
71: "❄️ Slight snowfall",
73: "❄️ Moderate snowfall",
75: "❄️ Heavy snowfall",
77: "🌨️ Snow grains",
80: "🌦️ Slight rain showers",
81: "🌧️ Moderate rain showers",
82: "⛈️ Violent rain showers",
85: "🌨️ Slight snow showers",
86: "🌨️ Heavy snow showers",
95: "⛈️ Thunderstorm",
96: "⛈️ Thunderstorm with slight hail",
99: "⛈️ Thunderstorm with heavy hail",
}
return WMO_CODES.get(code, "Unknown weather")
def weather_values(date, latitude, longitude):
# return temperature, wind, precipitation, and a comment (cloudy/sunny/rainy/windy)
url = (
"https://api.open-meteo.com/v1/forecast"
f"?latitude={latitude}"
f"&longitude={longitude}"
"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max,windspeed_10m_max,weathercode"
"&forecast_days=16"
"&timezone=auto"
)
data = fetch_json(url)
dates = data["daily"]["time"]
# date_str = date.isoformat() if isinstance(date, date) else date
date_str = date.isoformat()
if date_str not in dates:
raise ValueError("Date outside forecast range")
idx = dates.index(date_str)
temp_max = data["daily"]["temperature_2m_max"][idx]
temp_min = data["daily"]["temperature_2m_min"][idx]
precipitation = data["daily"]["precipitation_sum"][idx]
precipitation_probability = data["daily"]["precipitation_probability_max"][idx]
windspeed = data["daily"]["windspeed_10m_max"][idx]
weathercode = data["daily"]["weathercode"][idx]
comment = weather_comment(weathercode)
return Weather(
temp_max=temp_max,
temp_min=temp_min,
precipitation=precipitation,
precipitation_probability=precipitation_probability,
wind_speed=windspeed,
comment=comment,
)
def last_rained_date(latitude, longitude, days_back=15):
url = (
"https://archive-api.open-meteo.com/v1/archive"
f"?latitude={latitude}"
f"&longitude={longitude}"
f"&start_date={(date.today() - timedelta(days=days_back + 1 )).isoformat()}"
f"&end_date={(date.today() - timedelta(days=1)).isoformat()}" # YESTERDAY
"&daily=precipitation_sum"
"&timezone=auto"
)
data = fetch_json(url)
dates = data["daily"]["time"]
precipitation = data["daily"]["precipitation_sum"]
for d, p in zip(reversed(dates), reversed(precipitation)):
if p > 5: # Consider it rained if precipitation > 5mm
return date.fromisoformat(d)
return None # No rain in the past `days_back` days
# Marseille
LAT = 43.2965
LON = 5.3698
# Did it rain yesterday?
print(did_or_will_rain("2026-06-03", LAT, LON))
# Will it rain tomorrow?
print(did_or_will_rain("2026-06-04", LAT, LON))
# Require at least 70% confidence
print(
did_or_will_rain(
"2026-06-05",
LAT,
LON,
forecast_threshold=70,
))