Session météo (kadi.weather.session)
WeatherSession est le point d'entrée principal du module kadi.weather.
Elle orchestre les composants internes et expose une API simple pour toutes
les fonctionnalités météo et agronomiques.
Méthodes
forecast(days)
Récupère les prévisions météo court-terme depuis Open-Meteo.
prevision = session.forecast(days=5)
Paramètres :
| Nom |
Type |
Défaut |
Description |
days |
int |
Depuis config.py |
Nombre de jours (maximum 16) |
Retour : dict
| Clé |
Type |
Description |
location |
dict |
{'name', 'lat', 'lon'} |
data |
list[dict] |
Liste des jours avec temperature_min, temperature_max, precipitation |
data_source |
str |
Source utilisée ('open-meteo' ou 'cache') |
last_updated |
str |
Horodatage ISO de la mise à jour |
historical(metric, months_back)
Retourne les séries météo historiques depuis CHIRPS et Open-Meteo.
# Seulement les précipitations sur 6 mois
df_pluie = session.historical(metric="precipitation", months_back=6)
# Toutes les variables sur 10 ans
df_complet = session.historical(months_back=120)
Paramètres :
| Nom |
Type |
Défaut |
Description |
metric |
str |
'all' |
Filtre : 'temperature', 'precipitation', 'humidity', 'all' |
months_back |
int |
120 |
Nombre de mois d'historique |
Retour : pd.DataFrame — Données indexées par date.
onset()
Détecte la date de démarrage de la saison des pluies selon la zone climatique.
onset = session.onset()
print(f"Démarrage : {onset['onset_date']}")
print(f"Méthode : {onset['method']}") # 'Sivakumar' ou 'Walter-Anyadike'
Retour : dict avec onset_date, method, confidence, zone.
L'algorithme utilisé dépend de la zone automatiquement détectée :
- Nord (> 9.5° N) : Sivakumar — saison unimodale
- Sud (< 7.5° N) : Walter-Anyadike — saison bimodale
cessation()
Détermine la date de fin des pluies utiles.
cessation = session.cessation()
print(f"Fin saison : {cessation['cessation_date']}")
Retour : dict avec cessation_date, method, confidence.
growing_degree_days(crop, start_date, end_date)
Calcule l'accumulation de degrés-jours de croissance depuis la date de semis.
Les GDD mesurent l'énergie thermique disponible pour le développement de la plante.
gdd = session.growing_degree_days(
crop="maize",
start_date="2026-05-15", # Date de semis
end_date="2026-09-30", # Optionnel — jusqu'à aujourd'hui si None
)
print(f"GDD accumulés : {gdd['gdd_accumulated']:.1f} °C·jour")
print(f"Stade phéno : {gdd['phenology_stage']}")
print(f"Floraison dans : {gdd['days_to_flowering']} jours")
Paramètres :
| Nom |
Type |
Défaut |
Description |
crop |
str |
requis |
Code de la culture ('maize', 'rice', etc.) |
start_date |
str |
requis |
Date de semis au format 'YYYY-MM-DD' |
end_date |
str |
None |
Date de fin (aujourd'hui si None) |
Retour : dict avec gdd_accumulated, phenology_stage, crop,
start_date, days_to_flowering, days_to_maturity.
rain_probability(days_ahead, min_rainfall_mm)
Calcule la probabilité de pluie en combinant les prévisions Open-Meteo et
les fréquences historiques (chaînes de Markov).
prob = session.rain_probability(days_ahead=3, min_rainfall_mm=1.0)
print(f"Demain : {prob['tomorrow'] * 100:.0f}%")
print(f"Recommandation : {prob['recommendation']}")
print(f"Message : {prob['message']}")
Paramètres :
| Nom |
Type |
Défaut |
Description |
days_ahead |
int |
1 |
Horizon de prévision en jours |
min_rainfall_mm |
float |
1.0 |
Seuil de pluie significative en mm |
Retour : dict avec tomorrow (probabilité J+1), message,
recommendation.
drought_index(method, window_months)
Calcule un indice de sécheresse sur les données historiques.
drought = session.drought_index(method="spi", window_months=3)
print(f"SPI 3 mois : {drought['spi_3month']:.2f}")
print(f"Sévérité : {drought['drought_severity']}")
Méthodes disponibles :
| Méthode |
Description |
'spi' |
Standardized Precipitation Index (Z-score sur les précipitations) |
'markov' |
Probabilité de persistance de la sécheresse (Markov) |
'hurst' |
Exposant de Hurst — mémoire longue de la sécheresse |
'combined' |
Combinaison pondérée des 3 méthodes |
Sévérités SPI :
| SPI |
Sévérité |
| > -0.5 |
no_drought |
| -0.5 à -1.0 |
mild |
| -1.0 à -1.5 |
moderate |
| < -1.5 |
severe |
water_balance(crop, soil_type)
Simule le bilan hydrique quotidien du sol selon la méthode FAO-56, en
calculant l'évapotranspiration de référence (ET0) par Hargreaves-Samani.
bilan = session.water_balance(crop="maize", soil_type="ferrugineux")
print(bilan.tail(7)[["precipitation", "ET0", "deficit_eau", "reserve_utile"]])
Paramètres :
| Nom |
Type |
Défaut |
Description |
crop |
str |
'maize' |
Culture de référence (influence le Kc) |
soil_type |
str |
'ferrugineux' |
Type de sol béninois |
Types de sols supportés : 'ferrugineux', 'vertisol', 'hydromorphe',
'sableux'.
Colonnes du DataFrame retourné :
| Colonne |
Description |
precipitation |
Précipitations observées (mm) |
ET0 |
Évapotranspiration de référence (mm) |
ETc |
Évapotranspiration de la culture (ET0 × Kc) |
deficit_eau |
Déficit hydrique journalier (mm) |
reserve_utile |
Eau disponible dans le sol (mm) |
runoff |
Ruissellement journalier (mm) |
et0_hargreaves(tmin, tmax, day_of_year)
Calcule l'évapotranspiration de référence (ET0) pour un jour donné avec la
méthode Hargreaves-Samani.
et0 = session.et0_hargreaves(tmin=22.0, tmax=35.0, day_of_year=180)
print(f"ET0 : {et0:.2f} mm/jour")
WeatherSession
Session météorologique : point d'entrée principal pour l'utilisateur.
Gère le setup, le cache et expose l'API fonctionnelle complète.
Source code in kadi/weather/session.py
| class WeatherSession:
"""
Session météorologique : point d'entrée principal pour l'utilisateur.
Gère le setup, le cache et expose l'API fonctionnelle complète.
"""
def __init__(self, latitude: float, longitude: float, name: str = None, cache_dir: str = None):
"""
Initialise une nouvelle session pour une localisation.
:param latitude: Latitude (en degrés décimaux).
:param longitude: Longitude (en degrés décimaux).
:param name: Nom de la localité (optionnel).
:param cache_dir: Dossier pour le cache (optionnel).
"""
self.location = Location(latitude, longitude, name)
self.cache_dir = cache_dir
# Initialisation de la gestion des données
self.weather_data = WeatherData(self.location, cache_dir)
# Composants métiers (initialisés paresseusement ou lors de _init_all_components)
self.phenology: Optional[Phenology] = None
self.hydrology: Optional[Hydrology] = None
self.risk_indicators: Optional[RiskIndicators] = None
def _ensure_data(self, require_forecast=False, require_historical=False):
"""
S'assure que les données nécessaires sont chargées.
"""
if require_forecast and self.weather_data.forecast_data is None:
self.weather_data.fetch_forecast()
if require_historical and self.weather_data.historical_data is None:
self.weather_data.fetch_historical()
def _ensure_components(self, component: str):
"""
S'assure que le composant demandé est initialisé.
"""
if component == 'phenology' and self.phenology is None:
self._ensure_data(require_historical=True)
hist = self.weather_data.historical_data
self.phenology = Phenology(self.location, hist['precipitation'], hist[['temperature_min', 'temperature_max']])
elif component == 'hydrology' and self.hydrology is None:
self._ensure_data(require_historical=True)
hist = self.weather_data.historical_data
self.hydrology = Hydrology(self.location, hist['precipitation'], hist[['temperature_min', 'temperature_max']])
elif component == 'risk' and self.risk_indicators is None:
self._ensure_data(require_forecast=True, require_historical=True)
self.risk_indicators = RiskIndicators(self.location, self.weather_data.historical_data['precipitation'], self.weather_data.forecast_data)
def forecast(self, days: int = None) -> dict:
"""
Récupère la prévision météorologique court-terme.
:param days: Nombre de jours de prévision (défaut depuis CONFIG).
:return: Dictionnaire des prévisions.
"""
if days is None:
days = CONFIG["weather"]["forecast_days_default"]
if days > CONFIG["weather"]["max_forecast_days"]:
days = CONFIG["weather"]["max_forecast_days"]
df = self.weather_data.fetch_forecast(days=days)
# Format de retour selon cahier des charges
return {
'location': {'name': self.location.name, 'lat': self.location.latitude, 'lon': self.location.longitude},
'data': df.reset_index().to_dict(orient='records'),
'data_source': self.weather_data.data_source,
'last_updated': pd.Timestamp.now().isoformat()
}
def historical(self, metric: str = 'all', months_back: int = 120) -> pd.DataFrame:
"""
Retourne les séries historiques.
:param metric: Filtre de colonne ('temperature', 'precipitation', 'humidity', 'all').
:param months_back: Nombre de mois d'historique.
:return: DataFrame historique.
"""
df = self.weather_data.fetch_historical(months_back=months_back)
if metric != 'all':
cols = [c for c in df.columns if metric in c]
if cols:
return df[cols]
return df
def growing_degree_days(self, crop: str, start_date: str, end_date: str = None) -> dict:
"""
Calcule l'accumulation des degrés-jours.
:param crop: Nom de la culture.
:param start_date: Date de semis (YYYY-MM-DD).
:param end_date: Date de fin (optionnel).
:return: Résultat du cumul GDD.
"""
self._ensure_components('phenology')
return self.phenology.growing_degree_days(crop, start_date, end_date)
def onset(self) -> dict:
"""
Détecte la date de démarrage de la saison agricole.
:return: Résultat de l'onset.
"""
self._ensure_components('phenology')
return self.phenology.onset()
def cessation(self) -> dict:
"""
Détermine la date de fin des pluies utiles.
:return: Résultat de cessation.
"""
self._ensure_components('phenology')
return self.phenology.cessation()
def drought_index(self, method: str = 'spi', window_months: int = 3) -> dict:
"""
Calcule l'indice de sécheresse.
:param method: 'spi', 'markov', 'hurst', 'combined'.
:param window_months: Fenêtre temporelle en mois.
:return: Indicateurs de sécheresse.
"""
self._ensure_components('risk')
return self.risk_indicators.drought_index(method, window_months)
def rain_probability(self, days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict:
"""
Prévoit la probabilité de pluie.
:param days_ahead: Nombre de jours futurs.
:param min_rainfall_mm: Seuil minimum.
:return: Probabilité et recommandations.
"""
self._ensure_components('risk')
return self.risk_indicators.rain_probability(days_ahead, min_rainfall_mm)
def water_balance(self, crop: str = 'maize', soil_type: str = 'ferrugineux') -> pd.DataFrame:
"""
Simule le bilan hydrique quotidien (FAO-56).
:param crop: Type de culture.
:param soil_type: Type de sol.
:return: DataFrame avec le bilan hydrique.
"""
self._ensure_components('hydrology')
self.hydrology.crop = crop
self.hydrology.soil_type = soil_type
self.hydrology.soil_params = self.hydrology.get_soil_params(soil_type)
return self.hydrology.compute_water_balance()
def et0_hargreaves(self, tmin: float, tmax: float, day_of_year: int) -> float:
"""
Calcule l'ETo par Hargreaves-Samani.
:param tmin: Temp. min.
:param tmax: Temp. max.
:param day_of_year: Jour de l'année.
:return: ETo en mm/jour.
"""
self._ensure_components('hydrology')
return self.hydrology.et0_hargreaves(tmin, tmax, day_of_year)
def _init_all_components(self) -> None:
"""
Initialise toutes les classes composantes.
"""
self._ensure_components('phenology')
self._ensure_components('hydrology')
self._ensure_components('risk')
|
__init__
__init__(latitude: float, longitude: float, name: str = None, cache_dir: str = None)
Initialise une nouvelle session pour une localisation.
:param latitude: Latitude (en degrés décimaux).
:param longitude: Longitude (en degrés décimaux).
:param name: Nom de la localité (optionnel).
:param cache_dir: Dossier pour le cache (optionnel).
Source code in kadi/weather/session.py
| def __init__(self, latitude: float, longitude: float, name: str = None, cache_dir: str = None):
"""
Initialise une nouvelle session pour une localisation.
:param latitude: Latitude (en degrés décimaux).
:param longitude: Longitude (en degrés décimaux).
:param name: Nom de la localité (optionnel).
:param cache_dir: Dossier pour le cache (optionnel).
"""
self.location = Location(latitude, longitude, name)
self.cache_dir = cache_dir
# Initialisation de la gestion des données
self.weather_data = WeatherData(self.location, cache_dir)
# Composants métiers (initialisés paresseusement ou lors de _init_all_components)
self.phenology: Optional[Phenology] = None
self.hydrology: Optional[Hydrology] = None
self.risk_indicators: Optional[RiskIndicators] = None
|
forecast
forecast(days: int = None) -> dict
Récupère la prévision météorologique court-terme.
:param days: Nombre de jours de prévision (défaut depuis CONFIG).
:return: Dictionnaire des prévisions.
Source code in kadi/weather/session.py
| def forecast(self, days: int = None) -> dict:
"""
Récupère la prévision météorologique court-terme.
:param days: Nombre de jours de prévision (défaut depuis CONFIG).
:return: Dictionnaire des prévisions.
"""
if days is None:
days = CONFIG["weather"]["forecast_days_default"]
if days > CONFIG["weather"]["max_forecast_days"]:
days = CONFIG["weather"]["max_forecast_days"]
df = self.weather_data.fetch_forecast(days=days)
# Format de retour selon cahier des charges
return {
'location': {'name': self.location.name, 'lat': self.location.latitude, 'lon': self.location.longitude},
'data': df.reset_index().to_dict(orient='records'),
'data_source': self.weather_data.data_source,
'last_updated': pd.Timestamp.now().isoformat()
}
|
historical
historical(metric: str = 'all', months_back: int = 120) -> pd.DataFrame
Retourne les séries historiques.
:param metric: Filtre de colonne ('temperature', 'precipitation', 'humidity', 'all').
:param months_back: Nombre de mois d'historique.
:return: DataFrame historique.
Source code in kadi/weather/session.py
| def historical(self, metric: str = 'all', months_back: int = 120) -> pd.DataFrame:
"""
Retourne les séries historiques.
:param metric: Filtre de colonne ('temperature', 'precipitation', 'humidity', 'all').
:param months_back: Nombre de mois d'historique.
:return: DataFrame historique.
"""
df = self.weather_data.fetch_historical(months_back=months_back)
if metric != 'all':
cols = [c for c in df.columns if metric in c]
if cols:
return df[cols]
return df
|
growing_degree_days
growing_degree_days(crop: str, start_date: str, end_date: str = None) -> dict
Calcule l'accumulation des degrés-jours.
:param crop: Nom de la culture.
:param start_date: Date de semis (YYYY-MM-DD).
:param end_date: Date de fin (optionnel).
:return: Résultat du cumul GDD.
Source code in kadi/weather/session.py
| def growing_degree_days(self, crop: str, start_date: str, end_date: str = None) -> dict:
"""
Calcule l'accumulation des degrés-jours.
:param crop: Nom de la culture.
:param start_date: Date de semis (YYYY-MM-DD).
:param end_date: Date de fin (optionnel).
:return: Résultat du cumul GDD.
"""
self._ensure_components('phenology')
return self.phenology.growing_degree_days(crop, start_date, end_date)
|
onset
Détecte la date de démarrage de la saison agricole.
:return: Résultat de l'onset.
Source code in kadi/weather/session.py
| def onset(self) -> dict:
"""
Détecte la date de démarrage de la saison agricole.
:return: Résultat de l'onset.
"""
self._ensure_components('phenology')
return self.phenology.onset()
|
cessation
Détermine la date de fin des pluies utiles.
:return: Résultat de cessation.
Source code in kadi/weather/session.py
| def cessation(self) -> dict:
"""
Détermine la date de fin des pluies utiles.
:return: Résultat de cessation.
"""
self._ensure_components('phenology')
return self.phenology.cessation()
|
drought_index
drought_index(method: str = 'spi', window_months: int = 3) -> dict
Calcule l'indice de sécheresse.
:param method: 'spi', 'markov', 'hurst', 'combined'.
:param window_months: Fenêtre temporelle en mois.
:return: Indicateurs de sécheresse.
Source code in kadi/weather/session.py
| def drought_index(self, method: str = 'spi', window_months: int = 3) -> dict:
"""
Calcule l'indice de sécheresse.
:param method: 'spi', 'markov', 'hurst', 'combined'.
:param window_months: Fenêtre temporelle en mois.
:return: Indicateurs de sécheresse.
"""
self._ensure_components('risk')
return self.risk_indicators.drought_index(method, window_months)
|
rain_probability
rain_probability(days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict
Prévoit la probabilité de pluie.
:param days_ahead: Nombre de jours futurs.
:param min_rainfall_mm: Seuil minimum.
:return: Probabilité et recommandations.
Source code in kadi/weather/session.py
| def rain_probability(self, days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict:
"""
Prévoit la probabilité de pluie.
:param days_ahead: Nombre de jours futurs.
:param min_rainfall_mm: Seuil minimum.
:return: Probabilité et recommandations.
"""
self._ensure_components('risk')
return self.risk_indicators.rain_probability(days_ahead, min_rainfall_mm)
|
water_balance
water_balance(crop: str = 'maize', soil_type: str = 'ferrugineux') -> pd.DataFrame
Simule le bilan hydrique quotidien (FAO-56).
:param crop: Type de culture.
:param soil_type: Type de sol.
:return: DataFrame avec le bilan hydrique.
Source code in kadi/weather/session.py
| def water_balance(self, crop: str = 'maize', soil_type: str = 'ferrugineux') -> pd.DataFrame:
"""
Simule le bilan hydrique quotidien (FAO-56).
:param crop: Type de culture.
:param soil_type: Type de sol.
:return: DataFrame avec le bilan hydrique.
"""
self._ensure_components('hydrology')
self.hydrology.crop = crop
self.hydrology.soil_type = soil_type
self.hydrology.soil_params = self.hydrology.get_soil_params(soil_type)
return self.hydrology.compute_water_balance()
|
et0_hargreaves
et0_hargreaves(tmin: float, tmax: float, day_of_year: int) -> float
Calcule l'ETo par Hargreaves-Samani.
:param tmin: Temp. min.
:param tmax: Temp. max.
:param day_of_year: Jour de l'année.
:return: ETo en mm/jour.
Source code in kadi/weather/session.py
| def et0_hargreaves(self, tmin: float, tmax: float, day_of_year: int) -> float:
"""
Calcule l'ETo par Hargreaves-Samani.
:param tmin: Temp. min.
:param tmax: Temp. max.
:param day_of_year: Jour de l'année.
:return: ETo en mm/jour.
"""
self._ensure_components('hydrology')
return self.hydrology.et0_hargreaves(tmin, tmax, day_of_year)
|