Animations saisonnières sur les plans d’eau
Produits utilisés: `"Waterbodies" de DE Africa<https://docs.digitalearthafrica.org/en/latest/data_specs/Waterbodies_specs.html>`__; DE Africa WOfS; GeoMADs; CHIRPS
Mots clés: données utilisées; Waterbodies (pans d’eau), données utilisées; wofs_ls_summary_annual, données utilisées; CHIRPS, données utilisées; géomédiane sentinel-2, données utilisées; géomédiane landsat 5 et 7, données utilisées; géomédiane landsat 8 et 9
Aperçu
Le service de surveillance des plans d’eau continentaux de Digital Earth Africa identifie plus de 700 000 plans d’eau à partir de plus de trois décennies d’observations par satellite. Ce service cartographie les plans d’eau persistants et saisonniers et l’évolution de leur surface d’eau au fil du temps. Les plans d’eau cartographiés peuvent inclure, sans s’y limiter, des lacs, des étangs, des réservoirs artificiels, des zones humides et des segments de certains systèmes fluviaux. Pour plus d’informations sur le service de surveillance des plans d’eau, reportez-vous au « carnet de données » <../Datasets/Waterbodies.ipynb>`__.
Il est souvent judicieux de visualiser les changements dans les masses d’eau au fil du temps à l’aide d’animations. Les animations sont une forme d’analyse exploratoire qui peut aider à illustrer les modèles saisonniers répétitifs. Ce notebook montre comment générer des animations pour la composante de séries chronologiques des masses d’eau, ainsi que des données récapitulatives provenant des observations de l’eau depuis l’espace, des données sur les précipitations et des images en vraies couleurs.
Ce notebook démontre la création d’animations pour les plans d’eau au fil des saisons, afin que les modèles saisonniers puissent être identifiés et interprétés.
Avertissement : DE Africa Waterbodies Surface Area Change mesure la surface humide des plans d’eau telle qu’estimée à partir des satellites. Ce produit ne mesure pas la profondeur, le volume, la fonction du plan d’eau, ni la source de l’eau.
Description
Ce notebook démontre la génération de deux animations pour inspecter les changements dans l’étendue de l’eau, en s’appuyant sur le service Waterbodies de DE Africa<https://docs.digitalearthafrica.org/en/latest/data_specs/Waterbodies_specs.html>`__.
Les mesures prises sont les suivantes :
Chargement et préparation de séries chronologiques mensuelles, de données GeoMAD glissantes et de données pluviométriques.
Création d’une animation qui montre les changements mensuels et saisonniers de l’étendue d’eau pour un plan d’eau sélectionné.
Commencer
Pour exécuter cette analyse, exécutez toutes les cellules du bloc-notes, en commençant par la cellule « Charger les packages ».
Charger des paquets
Importez les packages Python utilisés pour l’analyse.
[1]:
import matplotlib.pyplot as plt
import datacube
import matplotlib.dates as mdates
import numpy as np
import matplotlib.animation as animation
from deafrica_tools.plotting import display_map
from IPython.display import HTML
from deafrica_tools.spatial import xr_rasterize
from deafrica_tools.waterbodies import (
get_geohashes,
get_waterbodies,
get_waterbody,
get_time_series,
display_time_series,
)
[2]:
dc = datacube.Datacube(app="Waterbody-anim")
Paramètres d’analyse
Ensuite, une animation montrant la progression mensuelle de l’étendue d’eau sera réalisée.
Le plan d’eau par défaut est le « réservoir de Mtera en Tanzanie <https://en.wikipedia.org/wiki/Mtera_Dam> ». Il s’agit d’un barrage hydroélectrique, l’étendue de l’eau est donc quelque peu influencée par la prise de décision humaine.
[3]:
# Set the central latitude and longitude
lat = -7.05
lon = 35.83
# Set the buffer to load around the central coordinates
buffer = 0.3
# Compute the bounding box coordinates
xlim = (lon - buffer, lon + buffer)
ylim = (lat + buffer, lat - buffer)
# Preview area on a map
display_map(xlim, ylim)
[3]:
Sélectionnez un plan d’eau
Nous utilisons la même procédure que ci-dessus pour charger les polygones disponibles dans une zone spécifiée et sélectionner le plan d’eau d’intérêt.
[4]:
# Create a bounding box from study area coordinates
bbox = (xlim[0], ylim[1], xlim[1], ylim[0])
# Select all water bodies located within the bounding box
polygons = get_waterbodies(bbox, crs="EPSG:4326")
# Explore the waterbody polygons located within the bounding box
polygons.explore()
[4]:
Inspecter les séries chronologiques
La série chronologique du réservoir de Mtera montre une certaine variation saisonnière de l’étendue de l’eau, souvent accompagnée de très grands événements de remplissage suivis d’une contraction progressive de l’étendue de l’eau.
Cette série chronologique est tirée directement de la base de données Waterbodies et est liée au géohash du plan d’eau ; dans ce cas, « ky9rzpq3c0 » comme on le voit en survolant le polygone dans l’explorateur ci-dessus. Vous trouverez plus d’informations sur le service Waterbodies et les séries chronologiques dans le bloc-notes « Datasets <../Datasets/Waterbodies.ipynb> » et dans la documentation « <https://docs.digitalearthafrica.org/en/latest/data_specs/Waterbodies_specs.html> ».
[5]:
selected_waterbody_geohash = "ky9rzpq3c0"
selected_waterbody = get_waterbody(selected_waterbody_geohash)
# Get time series for the selected water body
selected_waterbody_timeseries = get_time_series(waterbody=selected_waterbody)
display_time_series(selected_waterbody_timeseries)
Préparer des séries chronologiques pour le traçage
La série chronologique de l’étendue de l’eau doit être préparée pour être incluse dans l’animation, comme ci-dessous.
[6]:
tsw = (
selected_waterbody_timeseries.percent_wet.loc["2019-01-15":"2023-08-31"]
.resample("1M")
.mean()
.interpolate()
)
Charge le GeoMAD roulant (rolling GeoMAD)
Pour cette animation, nous nous intéressons à la variation saisonnière de l’étendue de l’eau et de l’état de la surface terrestre. Nous utilisons donc la géomédiane mobile trimestrielle à des pas de temps mensuels. Le GeoMAD mobile pour la période et l’étendue concernées est chargé ci-dessous, avec les bandes rouges, vertes et bleues chargées pour une visualisation en couleurs réelles.
[7]:
lat_range = (selected_waterbody.total_bounds[1], selected_waterbody.total_bounds[3])
lon_range = (selected_waterbody.total_bounds[0], selected_waterbody.total_bounds[2])
ds = dc.load(
product=["gm_s2_rolling"],
measurements=["red", "green", "blue"],
x=lon_range,
y=lat_range,
resolution=(-30, 30),
dask_chunks={"time": 1, "x": 3000, "y": 3000},
time=("2019-02-01", "2023-08-31"),
).compute()
Charger les précipitations mensuelles
Le tracé des précipitations mensuelles en fonction de l’étendue des eaux permet de mieux comprendre les tendances saisonnières de l’état des masses d’eau. La cellule ci-dessous charge les précipitations mensuelles à partir de CHIRPS et les prépare sous forme de données de séries chronologiques en supprimant les dimensions spatiales.
[8]:
ds_rf = dc.load(
product="rainfall_chirps_monthly",
measurements=["rainfall"],
x=lon_range,
y=lat_range,
time=("2019-01-01", "2023-08-31"),
)
ds_rf_month = ds_rf.mean(["longitude", "latitude"]).to_dataframe().drop(["spatial_ref"], axis=1)
Développer des séries chronologiques et des graphiques de précipitations
Pour cette animation, les précipitations et l’étendue des eaux de surface seront présentées avec une représentation en couleurs réelles du plan d’eau.
Pour présenter les précipitations et l’étendue de la surface, nous suivons les conventions de l’hydrogramme <https://www.futurelearn.com/info/courses/urban-stormwater-management-in-a-changing-climate/0/steps/349504>`__ qui est traditionnellement utilisé pour afficher les précipitations et le ruissellement. Une convention unique est que les précipitations sont présentées sur un axe Y secondaire inversé, c’est-à-dire tombant du haut.
[9]:
fig, ax1 = plt.subplots(figsize=(16, 6))
ax2 = ax1.twinx()
ax2.set_ylim([0,700])
ax2.invert_yaxis()
ax2.bar(ds_rf_month.index, ds_rf_month.rainfall, width=15, align="center", color="b", alpha=0.3)
ax2.set_ylabel("Rainfall (mm)")
ax1.plot(tsw, color="k", alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
ax1.set_ylim([40,110])
ax1.set_ylabel("Surface water extent (%)")
plt.title("Mtera Reservoir");

Vérifier la longueur de la série chronologique
Cette cellule imprime la longueur de la série temporelle pour :
Précipitations
Séries chronologiques sur l’étendue des eaux
Imagerie géomédienne
Pour garantir que la longueur est uniforme et que les images de l’animation correspondent.
[10]:
print(len(ds_rf_month.index))
print(len(tsw.index))
print(len(ds.time))
55
55
55
Générer une animation saisonnière
Enfin, nous exécutons l’animation. Le bloc de code ci-dessous prépare la mise en page de la figure puis définit les données de départ à présenter. La fonction de mise à jour affiche de nouvelles données pour chaque composant de l’animation à un intervalle commun.
La sortie de l’animation peut être enregistrée puis téléchargée en décommentant (en supprimant le #
) la commande ani.save('MteraReservoir.gif')
.
[11]:
# create a figure and axes
fig = plt.figure(figsize=(10, 5))
ax1 = plt.subplot(122)
ax2 = plt.subplot(121)
ax1.set_title("Monthly rainfall and water extent")
ax1.set_xlabel("Date")
ax1.set_ylabel("Total Precipitation (mm)")
bands = ["red", "green", "blue"]
cax = (
ds[bands].isel(time=0).to_array().transpose("y", "x", "variable").squeeze()
/ np.max(ds[bands].isel(time=0).to_array().transpose("y", "x", "variable").squeeze())
).plot.imshow(rgb="variable", animated=True, robust=True)
ax3 = ax1.twinx()
bars = ax3.bar(ds_rf_month.index, ds_rf_month.rainfall * [0], width=15, align="center", color="b")
ax3.bar(ds_rf_month.index, ds_rf_month.rainfall, width=15, align="center", color="b", alpha=0.2)
ax3.set_ylabel("Rainfall (mm)")
ax3.set_ylim(0, np.max(ds_rf_month.rainfall) + 20)
ax3.set_ylim([0,700])
ax3.invert_yaxis()
x = tsw.index
y = tsw.values
(line,) = ax1.plot(x, y, color="k")
ax1.plot(x, y, color="k", alpha=0.2)
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
ax1.tick_params(axis="x", labelrotation=45)
ax1.set_ylim([40,110])
ax1.set_ylabel("Surface water extent (%)")
def update(num, x, y, bars, line):
cax.set_array(
(
ds[bands]
.isel(time=num)
.to_array()
.transpose("y", "x", "variable")
.squeeze()
.clip(0, 2500)
/ np.max(
ds[bands]
.isel(time=num)
.to_array()
.transpose("y", "x", "variable")
.squeeze()
.clip(0, 2500)
)
)
)
ax2.set_title("Time = " + str(ds[bands].coords["time"].values[(num)])[:12])
bars[num].set_height(ds_rf_month.rainfall.iloc[num])
line.set_data(x[:num], y[:num])
return (line,)
plt.tight_layout()
ani = animation.FuncAnimation(
fig, update, len(x), fargs=[x, y, bars, line], interval=300, blit=True
)
# ani.save('MteraReservoir.gif')
plt.close()
HTML(ani.to_html5_video())
[11]: