Skip to content

Weather Facade (kadi.weather.session)

Weather is the main entry point of the kadi.weather module. It orchestrates internal components (Location, WeatherLoader, Phenology, Hydrology, Risk) and exposes a simple, unified API for all weather and agronomic capabilities.


Initialization

import kadi as kd

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

Parameters:

Name Type Required Description
lat float yes Latitude in decimal degrees
lon float yes Longitude in decimal degrees
name str no Locality name
cache str no Local cache directory path

The parameters latitude, longitude, and cache_dir are recognized but deprecated since v1.2.0. Use lat, lon, and cache instead.

Public Attributes:

Attribute Type Description
location Location Associated location instance
loader WeatherLoader Weather data loader instance
phenology Phenology Phenological component (loaded on demand)
hydrology Hydrology Hydrological component (loaded on demand)
risk Risk Risk indicators component (loaded on demand)
cache str Cache directory path

Methods

forecast(days)

Retrieves short-term weather forecasts from Open-Meteo.

forecast_data = weather.forecast(days=5)

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

for day in forecast_data["data"][:3]:
    print(day)

Parameters:

Name Type Default Description
days int From CONFIG Number of forecast days (maximum 16)

Returns: dict

Key Type Description
location dict {'name', 'lat', 'lon'}
data list[dict] List of daily dictionaries with weather variables
source str Source utilized
last_updated str ISO timestamp of last update

historical(metric, months, source)

Returns historical weather time series from CHIRPS and Open-Meteo.

# All variables over 10 years (default)
df = weather.historical()

# Rainfall only over 6 months
df_rain = weather.historical(metric="precipitation", months=6)

# All variables with explicit source selection
df_full = weather.historical(months=120, source="both")

Parameters:

Name Type Default Description
metric str 'all' Filter: 'temperature', 'precipitation', 'humidity', 'all'
months int 120 Number of historical months
source str None Rainfall source: 'chirps', 'openmeteo', 'both'. If None, uses CONFIG.

The parameter months_back is deprecated since v1.2.0. Use months instead.

Returns: pd.DataFrame indexed by DatetimeIndex.


onset()

Detects rainy season onset date based on climate zone regime.

season_start = weather.onset()

print(f"Season 1 Onset : {season_start['onset_1']}")
print(f"Season 2 Onset : {season_start['onset_2']}")   # None in Nord zone (unimodal)
print(f"Algorithm      : {season_start['algorithm']}")
print(f"Confidence     : {season_start['confidence']}")

Returns: dict

Key Type Description
onset_date str Alias for onset_1 (backward compatibility)
onset_1 str First rainy season start date (YYYY-MM-DD)
onset_2 str Second rainy season start date, None in Nord zone
algorithm str Algorithm utilized
zone str Detected climate zone
confidence float Confidence index

The algorithm used depends on the location climate zone:

Zone Regime Algorithm
Nord (> 9.5° N) Unimodal Sivakumar
Sud and Centre Bimodal Walter-Anyadike

cessation()

Determines effective rainy season end date.

season_end = weather.cessation()

print(f"Season 1 End : {season_end['cessation_1']}")
print(f"Season 2 End : {season_end['cessation_2']}")   # None in Nord zone
print(f"Duration     : {season_end['duration_days']} days")

Returns: dict

Key Type Description
cessation_date str Alias for cessation_1 (backward compatibility)
cessation_1 str First rainy season end date (YYYY-MM-DD)
cessation_2 str Second rainy season end date, None in Nord zone
duration_days int Season duration in days (Nord zone)
total_rainfall float Annual rainfall total (mm)
zone str Climate zone

gdd(crop, start, end)

Calculates accumulated Growing Degree Days (GDD) since planting date. GDD measures thermal energy available for plant development.

result = weather.gdd(
    crop="maize",
    start="2026-05-15",
    end="2026-09-30",   # Optional: defaults to today if None
)

print(f"Accumulated GDD : {result['gdd_accumulated']:.1f} degC.day")
print(f"Current stage   : {result['phenology_stage']}")
print(f"Cycle progress  : {result['pct_cycle']}%")

Parameters:

Name Type Default Description
crop str required Crop type: 'maize', 'rice', 'manioc', 'sorghum', 'tomato'
start str or pd.Timestamp required Planting date (YYYY-MM-DD)
end str or pd.Timestamp None End date (defaults to today if None)

Returns: dict

Key Type Description
gdd_accumulated float Accumulated GDD over the period
crop str Crop name
gdd_total_cycle int Total GDD required for full growth cycle
pct_cycle int Percentage of growth cycle completed
phenology_stage str Estimated phenological stage

The method growing_degree_days() is deprecated since v1.2.0. Use gdd() instead.


drought(method, window)

Calculates drought index over historical data.

# SPI over a 3-month window (default)
drought_info = weather.drought(method="spi", window=3)

print(f"3-Month SPI     : {drought_info['spi_3month']:.2f}")
print(f"Severity        : {drought_info['drought_severity']}")

# Combined analysis (SPI + Markov + Hurst)
analysis = weather.drought(method="combined", window=3)
print(f"Markov P(dry|dry) : {analysis['markov_p_dry']:.2f}")
print(f"Hurst exponent    : {analysis['hurst_exponent']:.2f}")

Parameters:

Name Type Default Description
method str 'spi' Calculation method (see table below)
window int 3 Time window in months (for SPI)

Available Methods:

Method Description
'spi' Standardized Precipitation Index (McKee et al., 1993)
'markov' Drought persistence probability (Markov chain)
'hurst' Hurst exponent - long memory of drought
'combined' Combination of all three methods

The method drought_index() is deprecated since v1.2.0. Use drought() instead.


rain_prob(days, min_mm)

Forecasts rain probability for upcoming days, combining Open-Meteo forecasts (70%) and historical Markov frequencies (30%).

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

print(f"Tomorrow        : {prob['tomorrow'] * 100:.0f}%")
print(f"In 2 days       : {prob['2_days'] * 100:.0f}%")
print(f"Message         : {prob['message']}")
print(f"Recommendation  : {prob['recommendation']}")

Parameters:

Name Type Default Description
days int 1 Number of days to forecast (1 to 7)
min_mm float 1.0 Significant rainfall threshold in mm

Returns: dict

Key Type Description
tomorrow float Tomorrow rain probability
N_days float Rain probability for day N (N >= 2)
message str Summary statement with maximum risk level
recommendation str Agronomic recommendation

The method rain_probability() is deprecated since v1.2.0. Use rain_prob() instead.


water_balance(crop, soil_type)

Simulates daily soil water balance according to the FAO-56 methodology. Reference evapotranspiration (ET0) is calculated via Hargreaves-Samani.

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

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

Parameters:

Name Type Default Description
crop str 'maize' Reference crop
soil_type str 'ferrugineux' Beninese soil type

Supported Soil Types: 'ferrugineux', 'ferrallitique', 'sableux', 'limoneux'.

Returned DataFrame Columns:

Column Description
precip Observed precipitation (mm)
et0 Reference evapotranspiration (mm)
pluie_eff Effective rainfall after runoff deduction (mm)
evapotransp Crop evapotranspiration - ET0 x Kc (mm)
deficit_eau Daily water deficit (mm)
reserve_utile Available soil water reserve (mm)
stress_hydrique_index Water stress index (0 to 1)

et0_hargreaves(tmin, tmax, day_of_year)

Calculates reference evapotranspiration (ET0) via Hargreaves-Samani for a given day. Useful for point calculations without running a full water balance.

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

Parameters:

Name Type Description
tmin float Minimum temperature (degC)
tmax float Maximum temperature (degC)
day_of_year int Day of year (1 to 365)

Returns: float - ET0 in mm/day.


Complete Example

import kadi as kd

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

# Historical data
df = weather.historical(months=24)
print(df.head())

# 7-day forecast
forecast_data = weather.forecast(days=7)

# Current season phenology
start = weather.onset()
end = weather.cessation()
print(f"Season: {start['onset_1']} -> {end['cessation_1']}")

# GDD for maize planted May 15
gdd = weather.gdd(crop="maize", start="2026-05-15")
print(f"Progress: {gdd['pct_cycle']}% ({gdd['phenology_stage']})")

# Drought risk
drought_info = weather.drought(method="combined")
print(f"3-Month SPI: {drought_info['spi_3month']:.2f} - {drought_info['drought_severity']}")

# Rain probability
prob = weather.rain_prob(days=3)
print(prob["recommendation"])

# Water balance
wb = weather.water_balance(crop="maize", soil_type="ferrugineux")
print(wb[["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)