Aller au contenu

Façade météo (kadi.weather.session)

Weather est le point d'entrée principal du module kadi.weather. Elle orchestre les composants internes (Location, WeatherLoader, Phenology, Hydrology, Risk) et expose une API simple et unifiée pour toutes les fonctionnalités météorologiques et agronomiques.


Initialisation

import kadi as kd

weather = kd.Weather(lat=9.3, lon=2.3, name="Parakou")

Paramètres :

Nom Type Obligatoire Description
lat float oui Latitude en degrés décimaux
lon float oui Longitude en degrés décimaux
name str non Nom de la localité
cache str non Répertoire de cache local

Les paramètres latitude, longitude et cache_dir sont reconnus mais dépréciés depuis la v1.2.0. Utilisez lat, lon et cache à la place.

Attributs publics :

Attribut Type Description
location Location Localisation associée
loader WeatherLoader Gestionnaire de données météorologiques
phenology Phenology Composant phénologique (chargé à la demande)
hydrology Hydrology Composant hydrologique (chargé à la demande)
risk Risk Composant indicateurs de risque (chargé à la demande)
cache str Répertoire de cache

Méthodes

forecast(days)

Récupère les prévisions météorologiques court-terme depuis Open-Meteo.

prevision = weather.forecast(days=5)

print(prevision["location"])
# {'name': 'Parakou', 'lat': 9.3, 'lon': 2.3}

for jour in prevision["data"][:3]:
    print(jour)

Paramètres :

Nom Type Défaut Description
days int Depuis CONFIG Nombre de jours de prévision (maximum 16)

Retour : dict

Clé Type Description
location dict {'name', 'lat', 'lon'}
data list[dict] Liste des jours avec variables météo
source str Source utilisée
last_updated str Horodatage ISO de la dernière mise à jour

historical(metric, months, source)

Retourne les séries météorologiques historiques depuis CHIRPS et Open-Meteo.

# Toutes les variables sur 10 ans (défaut)
df = weather.historical()

# Seulement les précipitations sur 6 mois
df_pluie = weather.historical(metric="precipitation", months=6)

# Toutes les variables avec source explicite
df_complet = weather.historical(months=120, source="both")

Paramètres :

Nom Type Défaut Description
metric str 'all' Filtre : 'temperature', 'precipitation', 'humidity', 'all'
months int 120 Nombre de mois d'historique
source str None Source précipitations : 'chirps', 'openmeteo', 'both'. Si None, utilise CONFIG.

Le paramètre months_back est déprécié depuis la v1.2.0. Utilisez months à la place.

Retour : pd.DataFrame avec DatetimeIndex.


onset()

Détecte la date de démarrage de la saison agricole selon la zone climatique.

debut = weather.onset()

print(f"Démarrage S1 : {debut['onset_1']}")
print(f"Démarrage S2 : {debut['onset_2']}")   # None en zone Nord (unimodale)
print(f"Algorithme   : {debut['algorithm']}")
print(f"Confiance    : {debut['confidence']}")

Retour : dict

Clé Type Description
onset_date str Alias de onset_1 (rétrocompatibilité)
onset_1 str Date de début de la première saison (YYYY-MM-DD)
onset_2 str Date de début de la deuxième saison, None en zone Nord
algorithm str Algorithme utilisé
zone str Zone climatique détectée
confidence float Indice de confiance

L'algorithme dépend de la zone climatique de la localisation :

Zone Régime Algorithme
Nord (> 9.5° N) Unimodal Sivakumar
Sud et Centre Bimodal Walter-Anyadike

cessation()

Détermine la date de fin des pluies utiles.

fin = weather.cessation()

print(f"Fin S1 : {fin['cessation_1']}")
print(f"Fin S2 : {fin['cessation_2']}")   # None en zone Nord
print(f"Durée  : {fin['duration_days']} jours")

Retour : dict

Clé Type Description
cessation_date str Alias de cessation_1 (rétrocompatibilité)
cessation_1 str Date de fin de la première saison (YYYY-MM-DD)
cessation_2 str Date de fin de la deuxième saison, None en zone Nord
duration_days int Durée de la saison en jours (zone Nord)
total_rainfall float Cumul annuel de précipitations (mm)
zone str Zone climatique

gdd(crop, start, end)

Calcule l'accumulation des degrés-jours de croissance (GDD) depuis la date de semis. Les GDD mesurent l'énergie thermique disponible pour le développement de la plante.

resultat = weather.gdd(
    crop="maize",
    start="2026-05-15",
    end="2026-09-30",   # Optionnel : aujourd'hui si None
)

print(f"GDD accumulés   : {resultat['gdd_accumulated']:.1f} degC.jour")
print(f"Stade actuel    : {resultat['phenology_stage']}")
print(f"Avancement      : {resultat['pct_cycle']} %")

Paramètres :

Nom Type Défaut Description
crop str requis Culture : 'maize', 'rice', 'manioc', 'sorghum', 'tomato'
start str ou pd.Timestamp requis Date de semis (YYYY-MM-DD)
end str ou pd.Timestamp None Date de fin (aujourd'hui si None)

Retour : dict

Clé Type Description
gdd_accumulated float Cumul GDD sur la période
crop str Nom de la culture
gdd_total_cycle int GDD total requis pour le cycle complet
pct_cycle int Pourcentage du cycle accompli
phenology_stage str Stade phénologique estimé

La méthode growing_degree_days() est dépréciée depuis la v1.2.0. Utilisez gdd() à la place.


drought(method, window)

Calcule l'indice de sécheresse sur les données historiques.

# SPI sur une fenêtre de 3 mois (défaut)
secheresse = weather.drought(method="spi", window=3)

print(f"SPI 3 mois  : {secheresse['spi_3month']:.2f}")
print(f"Sévérité    : {secheresse['drought_severity']}")

# Analyse combinée (SPI + Markov + Hurst)
analyse = weather.drought(method="combined", window=3)
print(f"Markov P(sec|sec) : {analyse['markov_p_dry']:.2f}")
print(f"Exposant de Hurst : {analyse['hurst_exponent']:.2f}")

Paramètres :

Nom Type Défaut Description
method str 'spi' Méthode de calcul (voir tableau ci-dessous)
window int 3 Fenêtre temporelle en mois (pour le SPI)

Méthodes disponibles :

Méthode Description
'spi' Standardized Precipitation Index (McKee et al., 1993)
'markov' Probabilité de persistance de sécheresse (chaîne de Markov)
'hurst' Exposant de Hurst - mémoire longue de la sécheresse
'combined' Combinaison des trois méthodes

La méthode drought_index() est dépréciée depuis la v1.2.0. Utilisez drought() à la place.


rain_prob(days, min_mm)

Prévoit la probabilité de pluie pour les prochains jours, en combinant les prévisions Open-Meteo (70 %) et les fréquences historiques de Markov (30 %).

proba = weather.rain_prob(days=3, min_mm=1.0)

print(f"Demain          : {proba['tomorrow'] * 100:.0f} %")
print(f"Dans 2 jours    : {proba['2_days'] * 100:.0f} %")
print(f"Message         : {proba['message']}")
print(f"Recommandation  : {proba['recommendation']}")

Paramètres :

Nom Type Défaut Description
days int 1 Nombre de jours à prévoir (1 à 7)
min_mm float 1.0 Seuil de pluie significative en mm

Retour : dict

Clé Type Description
tomorrow float Probabilité de pluie demain
N_days float Probabilité pour le jour N (N >= 2)
message str Phrase de synthèse avec le risque maximal
recommendation str Recommandation agronomique

La méthode rain_probability() est dépréciée depuis la v1.2.0. Utilisez rain_prob() à la place.


water_balance(crop, soil_type)

Simule le bilan hydrique quotidien du sol selon la méthode FAO-56. L'évapotranspiration de référence (ET0) est calculée par Hargreaves-Samani.

bilan = weather.water_balance(crop="maize", soil_type="ferrugineux")

print(bilan.tail(7)[[
    "precip", "et0", "pluie_eff", "evapotransp",
    "deficit_eau", "reserve_utile", "stress_hydrique_index"
]])

Paramètres :

Nom Type Défaut Description
crop str 'maize' Culture de référence
soil_type str 'ferrugineux' Type de sol béninois

Types de sols supportés : 'ferrugineux', 'ferrallitique', 'sableux', 'limoneux'.

Colonnes du DataFrame retourné :

Colonne Description
precip Précipitations observées (mm)
et0 Évapotranspiration de référence (mm)
pluie_eff Pluie efficace après déduction du ruissellement (mm)
evapotransp Évapotranspiration de la culture - ET0 x Kc (mm)
deficit_eau Déficit hydrique journalier (mm)
reserve_utile Eau disponible dans le sol (mm)
stress_hydrique_index Indice de stress hydrique (0 à 1)

et0_hargreaves(tmin, tmax, day_of_year)

Calcule l'évapotranspiration de référence (ET0) par Hargreaves-Samani pour un jour donné. Utile pour un calcul ponctuel sans passer par le bilan complet.

et0 = weather.et0_hargreaves(tmin=22.0, tmax=35.0, day_of_year=180)
print(f"ET0 : {et0:.2f} mm/jour")

Paramètres :

Nom Type Description
tmin float Température minimale (degC)
tmax float Température maximale (degC)
day_of_year int Jour de l'année (1 à 365)

Retour : float - ET0 en mm/jour.


Exemple complet

import kadi as kd

# Initialisation
weather = kd.Weather(lat=9.3, lon=2.3, name="Parakou")

# Données historiques
df = weather.historical(months=24)
print(df.head())

# Prévisions 7 jours
prevision = weather.forecast(days=7)

# Phénologie de la saison courante
debut = weather.onset()
fin = weather.cessation()
print(f"Saison : {debut['onset_1']} -> {fin['cessation_1']}")

# GDD pour le maïs semé le 15 mai
gdd = weather.gdd(crop="maize", start="2026-05-15")
print(f"Avancement : {gdd['pct_cycle']} % ({gdd['phenology_stage']})")

# Risque de sécheresse
s = weather.drought(method="combined")
print(f"SPI 3 mois : {s['spi_3month']:.2f} - {s['drought_severity']}")

# Probabilité de pluie
proba = weather.rain_prob(days=3)
print(proba["recommendation"])

# Bilan hydrique
bilan = weather.water_balance(crop="maize", soil_type="ferrugineux")
print(bilan[["precip", "reserve_utile", "stress_hydrique_index"]].tail(10))

Weather

Façade météorologique : point d'entrée principal pour l'utilisateur.

Orchestre les composants Location, WeatherLoader, Phenology, Hydrology et Risk pour exposer une API simple et unifiée.

Attributs

location (Location): Localisation associée à la façade météo. cache (str): Répertoire de cache (conservé pour la compatibilité). loader (WeatherLoader): Gestionnaire de données météorologiques. phenology (Phenology): Composant phénologique (chargé à la demande). hydrology (Hydrology): Composant hydrologique (chargé à la demande). risk (Risk): Composant des indicateurs de risque (chargé à la demande).

Source code in kadi/weather/session.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
class Weather:
    """
    Façade météorologique : point d'entrée principal pour l'utilisateur.

    Orchestre les composants Location, WeatherLoader, Phenology, Hydrology et
    Risk pour exposer une API simple et unifiée.

    Attributs:
        location (Location): Localisation associée à la façade météo.
        cache (str): Répertoire de cache (conservé pour la compatibilité).
        loader (WeatherLoader): Gestionnaire de données météorologiques.
        phenology (Phenology): Composant phénologique (chargé à la demande).
        hydrology (Hydrology): Composant hydrologique (chargé à la demande).
        risk (Risk): Composant des indicateurs de risque (chargé à la demande).
    """

    def __init__(
        self,
        lat: float = None,
        lon: float = None,
        name: str = None,
        cache: str = None,
        latitude: float = None,
        longitude: float = None,
        cache_dir: str = None,
    ):
        """
        Initialise une nouvelle façade météo pour une localisation.

        Args:
            lat (float): Latitude en degrés décimaux.
            lon (float): Longitude en degrés décimaux.
            name (str): Nom de la localité (optionnel).
            cache (str): Répertoire de cache (optionnel).
            latitude (float): Ancien nom du paramètre lat.
            longitude (float): Ancien nom du paramètre lon.
            cache_dir (str): Ancien nom du paramètre cache.
        """
        # Prise en charge des anciens noms de paramètres d'initialisation
        if latitude is not None:
            warnings.warn(
                "Le paramètre 'latitude' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'lat' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            lat = latitude

        if longitude is not None:
            warnings.warn(
                "Le paramètre 'longitude' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'lon' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            lon = longitude

        if cache_dir is not None:
            warnings.warn(
                "Le paramètre 'cache_dir' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'cache' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            cache = cache_dir

        if lat is None or lon is None:
            raise TypeError("Les coordonnées 'lat' et 'lon' sont obligatoires.")

        self.location = Location(lat, lon, name)
        # Conservé pour la compatibilité de signature
        self.cache = cache

        # Initialisation du gestionnaire de données
        self.loader = WeatherLoader(self.location, cache)

        # Composants métiers (initialisés à la demande)
        self.phenology: Optional[Phenology] = None
        self.hydrology: Optional[Hydrology] = None
        self.risk: Optional[Risk] = None

    def _load_data(
        self,
        forecast: bool = False,
        historical: bool = False,
    ) -> None:
        """
        S'assure que les données nécessaires sont chargées en mémoire.

        Args:
            forecast (bool): Si True, charge les prévisions si absentes.
            historical (bool): Si True, charge l'historique si absent.
        """
        if forecast and self.loader.forecast is None:
            self.loader.get_forecast()

        if historical and self.loader.historical is None:
            self.loader.get_historical()

    def _ensure_data(
        self,
        require_forecast: bool = False,
        require_historical: bool = False,
    ) -> None:
        """
        Ancien nom de ``_load_data``. Déprécié depuis v1.2.0.

        Args:
            require_forecast (bool): Ancien nom de forecast.
            require_historical (bool): Ancien nom de historical.
        """
        warnings.warn(
            "Weather._ensure_data est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather._load_data à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self._load_data(forecast=require_forecast, historical=require_historical)

    def _init_component(self, component: str) -> None:
        """
        S'assure que le composant demandé est initialisé.

        Args:
            component (str): Identifiant du composant parmi 'phenology',
                'hydrology' ou 'risk'.
        """
        if component == "phenology" and self.phenology is None:
            self._load_data(historical=True)
            hist = self.loader.historical
            self.phenology = Phenology(
                self.location,
                hist["precipitation"],
                hist[["temperature_min", "temperature_max"]],
            )

        elif component == "hydrology" and self.hydrology is None:
            self._load_data(historical=True)
            hist = self.loader.historical
            self.hydrology = Hydrology(
                self.location,
                hist["precipitation"],
                hist[["temperature_min", "temperature_max"]],
            )

        elif component == "risk" and self.risk is None:
            self._load_data(forecast=True, historical=True)
            self.risk = Risk(
                self.location,
                self.loader.historical["precipitation"],
                self.loader.forecast,
            )

    def _ensure_components(self, component: str) -> None:
        """
        Ancien nom de ``_init_component``. Déprécié depuis v1.2.0.

        Args:
            component (str): Identifiant du composant.
        """
        warnings.warn(
            "Weather._ensure_components est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather._init_component à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self._init_component(component)

    def forecast(self, days: int = None) -> dict:
        """
        Récupère la prévision météorologique court-terme.

        Args:
            days (int): Nombre de jours de prévision (défaut depuis CONFIG).

        Returns:
            dict: Dictionnaire des prévisions avec la localisation, les données
                brutes, la source et l'horodatage de mise à jour.
        """
        if days is None:
            days = CONFIG["weather"]["forecast_days_default"]

        # Plafonnement au maximum autorisé par la configuration
        if days > CONFIG["weather"]["max_forecast_days"]:
            days = CONFIG["weather"]["max_forecast_days"]

        df = self.loader.get_forecast(days=days)

        return {
            "location": {
                "name": self.location.name,
                "lat": self.location.lat,
                "lon": self.location.lon,
            },
            "data": df.reset_index().to_dict(orient="records"),
            "source": self.loader.source,
            "last_updated": pd.Timestamp.now().isoformat(),
        }

    def historical(
        self,
        metric: str = "all",
        months: int = 120,
        source: str = None,
        months_back: Optional[int] = None,
    ) -> pd.DataFrame:
        """
        Retourne les séries historiques météorologiques.

        Args:
            metric (str): Filtre de colonne : 'temperature', 'precipitation',
                'humidity' ou 'all' (défaut, toutes les colonnes).
            months (int): Nombre de mois d'historique à récupérer.
            source (str): Source des données de précipitation. Valeurs acceptées :
                'chirps', 'openmeteo', 'both'. Si None, utilise la valeur de CONFIG.
            months_back (int, optionnel): Ancien nom du paramètre ``months``.

        Returns:
            pd.DataFrame: DataFrame historique avec DatetimeIndex.
        """
        if months_back is not None:
            warnings.warn(
                "Le paramètre 'months_back' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'months' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            months = months_back

        df = self.loader.get_historical(months=months, source=source)

        # Filtre optionnel sur les colonnes
        if metric != "all":
            cols = [c for c in df.columns if metric in c]
            if cols:
                return df[cols]

        return df

    def gdd(
        self,
        crop: str,
        start: Union[str, pd.Timestamp],
        end: Union[str, pd.Timestamp] = None,
    ) -> dict:
        """
        Calcule l'accumulation des degrés-jours de croissance (GDD).

        Args:
            crop (str): Nom de la culture ('maize', 'rice', etc.).
            start (str ou pd.Timestamp): Date de semis (YYYY-MM-DD).
            end (str ou pd.Timestamp): Date de fin (optionnel).

        Returns:
            dict: Résultat du cumul GDD.
        """
        self._init_component("phenology")
        return self.phenology.gdd(crop, start, end)

    def growing_degree_days(
        self,
        crop: str,
        start_date: str,
        end_date: str = None,
    ) -> dict:
        """
        Ancienne méthode publique. Dépréciée depuis v1.2.0.

        Utilisez ``gdd(crop, start, end)`` à la place.

        Args:
            crop (str): Nom de la culture.
            start_date (str): Ancien nom de start.
            end_date (str): Ancien nom de end.

        Returns:
            dict: Résultat du cumul GDD.
        """
        warnings.warn(
            "Weather.growing_degree_days() est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez gdd() à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.gdd(crop, start_date, end_date)

    def onset(self) -> dict:
        """
        Détecte la date de démarrage de la saison agricole.

        Returns:
            dict: Résultat de l'onset.
        """
        self._init_component("phenology")
        return self.phenology.onset()

    def cessation(self) -> dict:
        """
        Détermine la date de fin des pluies utiles.

        Returns:
            dict: Résultat de la cessation.
        """
        self._init_component("phenology")
        return self.phenology.cessation()

    def drought(
        self,
        method: str = "spi",
        window: int = 3,
        window_months: Optional[int] = None,
    ) -> dict:
        """
        Calcule l'indice de sécheresse.

        Args:
            method (str): 'spi', 'markov', 'hurst', 'combined'. Par défaut 'spi'.
            window (int): Fenêtre temporelle en mois. Par défaut 3.
            window_months (int, optionnel): Ancien nom du paramètre window.

        Returns:
            dict: Indicateurs de sécheresse.
        """
        self._init_component("risk")
        return self.risk.drought(method=method, window=window, window_months=window_months)

    def drought_index(self, method: str = "spi", window_months: int = 3) -> dict:
        """
        Ancienne méthode publique. Dépréciée depuis v1.2.0.

        Utilisez ``drought(method, window)`` à la place.

        Args:
            method (str): Méthode de calcul.
            window_months (int): Ancien nom du paramètre window.

        Returns:
            dict: Indicateurs de sécheresse.
        """
        warnings.warn(
            "Weather.drought_index() est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez drought() à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.drought(method=method, window=window_months)

    def rain_prob(
        self,
        days: int = 1,
        min_mm: float = 1.0,
        days_ahead: Optional[int] = None,
        min_rainfall_mm: Optional[float] = None,
    ) -> dict:
        """
        Prévoit la probabilité de pluie.

        Args:
            days (int): Nombre de jours futurs.
            min_mm (float): Seuil minimum en mm.
            days_ahead (int, optionnel): Ancien nom du paramètre days.
            min_rainfall_mm (float, optionnel): Ancien nom du paramètre min_mm.

        Returns:
            dict: Probabilité et recommandations agronomiques.
        """
        if days_ahead is not None:
            warnings.warn(
                "Le paramètre 'days_ahead' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'days' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            days = days_ahead

        if min_rainfall_mm is not None:
            warnings.warn(
                "Le paramètre 'min_rainfall_mm' est obsolète et sera supprimé dans "
                "KadiPy v2.0. Utilisez 'min_mm' à la place.",
                category=DeprecationWarning,
                stacklevel=2,
            )
            min_mm = min_rainfall_mm

        self._init_component("risk")
        return self.risk.rain_prob(days=days, min_mm=min_mm)

    def rain_probability(self, days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict:
        """
        Ancienne méthode publique. Dépréciée depuis v1.2.0.

        Utilisez ``rain_prob(days, min_mm)`` à la place.

        Args:
            days_ahead (int): Ancien nom de days.
            min_rainfall_mm (float): Ancien nom de min_mm.

        Returns:
            dict: Probabilité et recommandations agronomiques.
        """
        warnings.warn(
            "Weather.rain_probability() est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez rain_prob() à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.rain_prob(days=days_ahead, min_mm=min_rainfall_mm)

    def water_balance(
        self,
        crop: str = "maize",
        soil_type: str = "ferrugineux",
    ) -> pd.DataFrame:
        """
        Simule le bilan hydrique quotidien (FAO-56).

        Args:
            crop (str): Type de culture.
            soil_type (str): Type de sol.

        Returns:
            pd.DataFrame: DataFrame avec le bilan hydrique.
        """
        self._init_component("hydrology")
        # Mise à jour des paramètres de culture et de sol avant le calcul
        self.hydrology.crop = crop
        self.hydrology.soil_type = soil_type
        self.hydrology.soil = self.hydrology.soil_params(soil_type)
        return self.hydrology.water_balance()

    def et0_hargreaves(
        self,
        tmin: float,
        tmax: float,
        day_of_year: int,
    ) -> float:
        """
        Calcule l'ETo par Hargreaves-Samani.

        Args:
            tmin (float): Température minimale (degC).
            tmax (float): Température maximale (degC).
            day_of_year (int): Jour de l'année (1 à 365).

        Returns:
            float: ETo en mm/jour.
        """
        self._init_component("hydrology")
        return self.hydrology.et0_hargreaves(tmin, tmax, day_of_year)

    def _setup(self) -> None:
        """
        Initialise toutes les classes composantes de la façade météo.
        """
        self._init_component("phenology")
        self._init_component("hydrology")
        self._init_component("risk")

    def _init_all_components(self) -> None:
        """
        Ancien nom de ``_setup``. Déprécié depuis v1.2.0.
        """
        warnings.warn(
            "Weather._init_all_components est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather._setup à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self._setup()

    # ----------------------------------------------------------------
    # Attributs et propriétés dépréciés (anciens noms)
    # ----------------------------------------------------------------

    @property
    def cache_dir(self) -> Optional[str]:
        """Ancien nom de ``cache``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.cache_dir est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.cache à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.cache

    @cache_dir.setter
    def cache_dir(self, value: Optional[str]) -> None:
        """Ancien setter de ``cache``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.cache_dir est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.cache à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self.cache = value

    @property
    def weather_data(self) -> WeatherLoader:
        """Ancien nom de ``loader``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.weather_data est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.loader à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.loader

    @weather_data.setter
    def weather_data(self, value: WeatherLoader) -> None:
        """Ancien setter de ``loader``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.weather_data est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.loader à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self.loader = value

    @property
    def risk_indicators(self) -> Optional[Risk]:
        """Ancien nom de ``risk``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.risk_indicators est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.risk à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        return self.risk

    @risk_indicators.setter
    def risk_indicators(self, value: Optional[Risk]) -> None:
        """Ancien setter de ``risk``. Déprécié depuis v1.2.0."""
        warnings.warn(
            "Weather.risk_indicators est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez Weather.risk à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        self.risk = value

cache_dir property writable

cache_dir: Optional[str]

Ancien nom de cache. Déprécié depuis v1.2.0.

weather_data property writable

weather_data: WeatherLoader

Ancien nom de loader. Déprécié depuis v1.2.0.

risk_indicators property writable

risk_indicators: Optional[Risk]

Ancien nom de risk. Déprécié depuis v1.2.0.

__init__

__init__(lat: float = None, lon: float = None, name: str = None, cache: str = None, latitude: float = None, longitude: float = None, cache_dir: str = None)

Initialise une nouvelle façade météo pour une localisation.

Parameters:

Name Type Description Default
lat float

Latitude en degrés décimaux.

None
lon float

Longitude en degrés décimaux.

None
name str

Nom de la localité (optionnel).

None
cache str

Répertoire de cache (optionnel).

None
latitude float

Ancien nom du paramètre lat.

None
longitude float

Ancien nom du paramètre lon.

None
cache_dir str

Ancien nom du paramètre cache.

None
Source code in kadi/weather/session.py
def __init__(
    self,
    lat: float = None,
    lon: float = None,
    name: str = None,
    cache: str = None,
    latitude: float = None,
    longitude: float = None,
    cache_dir: str = None,
):
    """
    Initialise une nouvelle façade météo pour une localisation.

    Args:
        lat (float): Latitude en degrés décimaux.
        lon (float): Longitude en degrés décimaux.
        name (str): Nom de la localité (optionnel).
        cache (str): Répertoire de cache (optionnel).
        latitude (float): Ancien nom du paramètre lat.
        longitude (float): Ancien nom du paramètre lon.
        cache_dir (str): Ancien nom du paramètre cache.
    """
    # Prise en charge des anciens noms de paramètres d'initialisation
    if latitude is not None:
        warnings.warn(
            "Le paramètre 'latitude' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'lat' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        lat = latitude

    if longitude is not None:
        warnings.warn(
            "Le paramètre 'longitude' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'lon' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        lon = longitude

    if cache_dir is not None:
        warnings.warn(
            "Le paramètre 'cache_dir' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'cache' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        cache = cache_dir

    if lat is None or lon is None:
        raise TypeError("Les coordonnées 'lat' et 'lon' sont obligatoires.")

    self.location = Location(lat, lon, name)
    # Conservé pour la compatibilité de signature
    self.cache = cache

    # Initialisation du gestionnaire de données
    self.loader = WeatherLoader(self.location, cache)

    # Composants métiers (initialisés à la demande)
    self.phenology: Optional[Phenology] = None
    self.hydrology: Optional[Hydrology] = None
    self.risk: Optional[Risk] = None

forecast

forecast(days: int = None) -> dict

Récupère la prévision météorologique court-terme.

Parameters:

Name Type Description Default
days int

Nombre de jours de prévision (défaut depuis CONFIG).

None

Returns:

Name Type Description
dict dict

Dictionnaire des prévisions avec la localisation, les données brutes, la source et l'horodatage de mise à jour.

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.

    Args:
        days (int): Nombre de jours de prévision (défaut depuis CONFIG).

    Returns:
        dict: Dictionnaire des prévisions avec la localisation, les données
            brutes, la source et l'horodatage de mise à jour.
    """
    if days is None:
        days = CONFIG["weather"]["forecast_days_default"]

    # Plafonnement au maximum autorisé par la configuration
    if days > CONFIG["weather"]["max_forecast_days"]:
        days = CONFIG["weather"]["max_forecast_days"]

    df = self.loader.get_forecast(days=days)

    return {
        "location": {
            "name": self.location.name,
            "lat": self.location.lat,
            "lon": self.location.lon,
        },
        "data": df.reset_index().to_dict(orient="records"),
        "source": self.loader.source,
        "last_updated": pd.Timestamp.now().isoformat(),
    }

historical

historical(metric: str = 'all', months: int = 120, source: str = None, months_back: Optional[int] = None) -> pd.DataFrame

Retourne les séries historiques météorologiques.

Parameters:

Name Type Description Default
metric str

Filtre de colonne : 'temperature', 'precipitation', 'humidity' ou 'all' (défaut, toutes les colonnes).

'all'
months int

Nombre de mois d'historique à récupérer.

120
source str

Source des données de précipitation. Valeurs acceptées : 'chirps', 'openmeteo', 'both'. Si None, utilise la valeur de CONFIG.

None
months_back (int, optionnel)

Ancien nom du paramètre months.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame historique avec DatetimeIndex.

Source code in kadi/weather/session.py
def historical(
    self,
    metric: str = "all",
    months: int = 120,
    source: str = None,
    months_back: Optional[int] = None,
) -> pd.DataFrame:
    """
    Retourne les séries historiques météorologiques.

    Args:
        metric (str): Filtre de colonne : 'temperature', 'precipitation',
            'humidity' ou 'all' (défaut, toutes les colonnes).
        months (int): Nombre de mois d'historique à récupérer.
        source (str): Source des données de précipitation. Valeurs acceptées :
            'chirps', 'openmeteo', 'both'. Si None, utilise la valeur de CONFIG.
        months_back (int, optionnel): Ancien nom du paramètre ``months``.

    Returns:
        pd.DataFrame: DataFrame historique avec DatetimeIndex.
    """
    if months_back is not None:
        warnings.warn(
            "Le paramètre 'months_back' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'months' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        months = months_back

    df = self.loader.get_historical(months=months, source=source)

    # Filtre optionnel sur les colonnes
    if metric != "all":
        cols = [c for c in df.columns if metric in c]
        if cols:
            return df[cols]

    return df

gdd

gdd(crop: str, start: Union[str, Timestamp], end: Union[str, Timestamp] = None) -> dict

Calcule l'accumulation des degrés-jours de croissance (GDD).

Parameters:

Name Type Description Default
crop str

Nom de la culture ('maize', 'rice', etc.).

required
start str ou pd.Timestamp

Date de semis (YYYY-MM-DD).

required
end str ou pd.Timestamp

Date de fin (optionnel).

None

Returns:

Name Type Description
dict dict

Résultat du cumul GDD.

Source code in kadi/weather/session.py
def gdd(
    self,
    crop: str,
    start: Union[str, pd.Timestamp],
    end: Union[str, pd.Timestamp] = None,
) -> dict:
    """
    Calcule l'accumulation des degrés-jours de croissance (GDD).

    Args:
        crop (str): Nom de la culture ('maize', 'rice', etc.).
        start (str ou pd.Timestamp): Date de semis (YYYY-MM-DD).
        end (str ou pd.Timestamp): Date de fin (optionnel).

    Returns:
        dict: Résultat du cumul GDD.
    """
    self._init_component("phenology")
    return self.phenology.gdd(crop, start, end)

growing_degree_days

growing_degree_days(crop: str, start_date: str, end_date: str = None) -> dict

Ancienne méthode publique. Dépréciée depuis v1.2.0.

Utilisez gdd(crop, start, end) à la place.

Parameters:

Name Type Description Default
crop str

Nom de la culture.

required
start_date str

Ancien nom de start.

required
end_date str

Ancien nom de end.

None

Returns:

Name Type Description
dict dict

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:
    """
    Ancienne méthode publique. Dépréciée depuis v1.2.0.

    Utilisez ``gdd(crop, start, end)`` à la place.

    Args:
        crop (str): Nom de la culture.
        start_date (str): Ancien nom de start.
        end_date (str): Ancien nom de end.

    Returns:
        dict: Résultat du cumul GDD.
    """
    warnings.warn(
        "Weather.growing_degree_days() est obsolète et sera supprimé dans "
        "KadiPy v2.0. Utilisez gdd() à la place.",
        category=DeprecationWarning,
        stacklevel=2,
    )
    return self.gdd(crop, start_date, end_date)

onset

onset() -> dict

Détecte la date de démarrage de la saison agricole.

Returns:

Name Type Description
dict dict

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.

    Returns:
        dict: Résultat de l'onset.
    """
    self._init_component("phenology")
    return self.phenology.onset()

cessation

cessation() -> dict

Détermine la date de fin des pluies utiles.

Returns:

Name Type Description
dict dict

Résultat de la cessation.

Source code in kadi/weather/session.py
def cessation(self) -> dict:
    """
    Détermine la date de fin des pluies utiles.

    Returns:
        dict: Résultat de la cessation.
    """
    self._init_component("phenology")
    return self.phenology.cessation()

drought

drought(method: str = 'spi', window: int = 3, window_months: Optional[int] = None) -> dict

Calcule l'indice de sécheresse.

Parameters:

Name Type Description Default
method str

'spi', 'markov', 'hurst', 'combined'. Par défaut 'spi'.

'spi'
window int

Fenêtre temporelle en mois. Par défaut 3.

3
window_months (int, optionnel)

Ancien nom du paramètre window.

None

Returns:

Name Type Description
dict dict

Indicateurs de sécheresse.

Source code in kadi/weather/session.py
def drought(
    self,
    method: str = "spi",
    window: int = 3,
    window_months: Optional[int] = None,
) -> dict:
    """
    Calcule l'indice de sécheresse.

    Args:
        method (str): 'spi', 'markov', 'hurst', 'combined'. Par défaut 'spi'.
        window (int): Fenêtre temporelle en mois. Par défaut 3.
        window_months (int, optionnel): Ancien nom du paramètre window.

    Returns:
        dict: Indicateurs de sécheresse.
    """
    self._init_component("risk")
    return self.risk.drought(method=method, window=window, window_months=window_months)

drought_index

drought_index(method: str = 'spi', window_months: int = 3) -> dict

Ancienne méthode publique. Dépréciée depuis v1.2.0.

Utilisez drought(method, window) à la place.

Parameters:

Name Type Description Default
method str

Méthode de calcul.

'spi'
window_months int

Ancien nom du paramètre window.

3

Returns:

Name Type Description
dict dict

Indicateurs de sécheresse.

Source code in kadi/weather/session.py
def drought_index(self, method: str = "spi", window_months: int = 3) -> dict:
    """
    Ancienne méthode publique. Dépréciée depuis v1.2.0.

    Utilisez ``drought(method, window)`` à la place.

    Args:
        method (str): Méthode de calcul.
        window_months (int): Ancien nom du paramètre window.

    Returns:
        dict: Indicateurs de sécheresse.
    """
    warnings.warn(
        "Weather.drought_index() est obsolète et sera supprimé dans "
        "KadiPy v2.0. Utilisez drought() à la place.",
        category=DeprecationWarning,
        stacklevel=2,
    )
    return self.drought(method=method, window=window_months)

rain_prob

rain_prob(days: int = 1, min_mm: float = 1.0, days_ahead: Optional[int] = None, min_rainfall_mm: Optional[float] = None) -> dict

Prévoit la probabilité de pluie.

Parameters:

Name Type Description Default
days int

Nombre de jours futurs.

1
min_mm float

Seuil minimum en mm.

1.0
days_ahead (int, optionnel)

Ancien nom du paramètre days.

None
min_rainfall_mm (float, optionnel)

Ancien nom du paramètre min_mm.

None

Returns:

Name Type Description
dict dict

Probabilité et recommandations agronomiques.

Source code in kadi/weather/session.py
def rain_prob(
    self,
    days: int = 1,
    min_mm: float = 1.0,
    days_ahead: Optional[int] = None,
    min_rainfall_mm: Optional[float] = None,
) -> dict:
    """
    Prévoit la probabilité de pluie.

    Args:
        days (int): Nombre de jours futurs.
        min_mm (float): Seuil minimum en mm.
        days_ahead (int, optionnel): Ancien nom du paramètre days.
        min_rainfall_mm (float, optionnel): Ancien nom du paramètre min_mm.

    Returns:
        dict: Probabilité et recommandations agronomiques.
    """
    if days_ahead is not None:
        warnings.warn(
            "Le paramètre 'days_ahead' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'days' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        days = days_ahead

    if min_rainfall_mm is not None:
        warnings.warn(
            "Le paramètre 'min_rainfall_mm' est obsolète et sera supprimé dans "
            "KadiPy v2.0. Utilisez 'min_mm' à la place.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        min_mm = min_rainfall_mm

    self._init_component("risk")
    return self.risk.rain_prob(days=days, min_mm=min_mm)

rain_probability

rain_probability(days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict

Ancienne méthode publique. Dépréciée depuis v1.2.0.

Utilisez rain_prob(days, min_mm) à la place.

Parameters:

Name Type Description Default
days_ahead int

Ancien nom de days.

1
min_rainfall_mm float

Ancien nom de min_mm.

1.0

Returns:

Name Type Description
dict dict

Probabilité et recommandations agronomiques.

Source code in kadi/weather/session.py
def rain_probability(self, days_ahead: int = 1, min_rainfall_mm: float = 1.0) -> dict:
    """
    Ancienne méthode publique. Dépréciée depuis v1.2.0.

    Utilisez ``rain_prob(days, min_mm)`` à la place.

    Args:
        days_ahead (int): Ancien nom de days.
        min_rainfall_mm (float): Ancien nom de min_mm.

    Returns:
        dict: Probabilité et recommandations agronomiques.
    """
    warnings.warn(
        "Weather.rain_probability() est obsolète et sera supprimé dans "
        "KadiPy v2.0. Utilisez rain_prob() à la place.",
        category=DeprecationWarning,
        stacklevel=2,
    )
    return self.rain_prob(days=days_ahead, min_mm=min_rainfall_mm)

water_balance

water_balance(crop: str = 'maize', soil_type: str = 'ferrugineux') -> pd.DataFrame

Simule le bilan hydrique quotidien (FAO-56).

Parameters:

Name Type Description Default
crop str

Type de culture.

'maize'
soil_type str

Type de sol.

'ferrugineux'

Returns:

Type Description
DataFrame

pd.DataFrame: 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).

    Args:
        crop (str): Type de culture.
        soil_type (str): Type de sol.

    Returns:
        pd.DataFrame: DataFrame avec le bilan hydrique.
    """
    self._init_component("hydrology")
    # Mise à jour des paramètres de culture et de sol avant le calcul
    self.hydrology.crop = crop
    self.hydrology.soil_type = soil_type
    self.hydrology.soil = self.hydrology.soil_params(soil_type)
    return self.hydrology.water_balance()

et0_hargreaves

et0_hargreaves(tmin: float, tmax: float, day_of_year: int) -> float

Calcule l'ETo par Hargreaves-Samani.

Parameters:

Name Type Description Default
tmin float

Température minimale (degC).

required
tmax float

Température maximale (degC).

required
day_of_year int

Jour de l'année (1 à 365).

required

Returns:

Name Type Description
float float

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.

    Args:
        tmin (float): Température minimale (degC).
        tmax (float): Température maximale (degC).
        day_of_year (int): Jour de l'année (1 à 365).

    Returns:
        float: ETo en mm/jour.
    """
    self._init_component("hydrology")
    return self.hydrology.et0_hargreaves(tmin, tmax, day_of_year)