Sentinel-1 Monthly Mosaic
Produits utilisés : s1_monthly_mosaic
Mots-clés données utilisées ; sentinel-1,:index:ensembles de données ; mosaïque mensuelle sentinel-1, index:SAR
Aperçu
Les capteurs radar à synthèse d’ouverture (SAR) présentent l’avantage de fonctionner à des longueurs d’onde non gênées par la couverture nuageuse et peuvent acquérir des données sur un site de jour comme de nuit. La mission « Sentinel-1 », qui fait partie de l’initiative conjointe Copernicus de la Commission européenne (CE) et de l’Agence spatiale européenne (ESA), assure une surveillance fiable et répétée de vastes zones grâce à son instrument SAR.
Les mosaïques mensuelles Sentinel-1 sont générées à partir de données de rétrodiffusion corrigées du terrain radiométrique (RTC), les variations dues aux variations géométriques d’observation étant atténuées. Dans la mosaïque mensuelle, les images RTC acquises au cours d’un mois calendaire sont combinées à l’aide d’un algorithme de composition multitemporelle. Cet algorithme calcule une moyenne pondérée des pixels valides, attribuant une pondération plus élevée aux pixels présentant une résolution locale plus élevée (par exemple, les pentes opposées au capteur). Cette approche de pondération en résolution locale minimise le bruit et améliore l’homogénéité spatiale des composites.
Vous trouverez plus d’informations sur la manière dont le produit est généré dans la « Documentation de l’écosystème de l’espace de données Copernicus <https://documentation.dataspace.copernicus.eu/Data/SentinelMissions/Sentinel1.html#sentinel-1-level-3-monthly-mosaics> ».
DE Africa donne accès aux mosaïques mensuelles de Sentinel-1 générées en mode d’acquisition interférométrique large (IW). Ces mosaïques sont organisées selon une grille UTM de 100 x 100 km et présentent une résolution spatiale de 20 mètres.
Description
Dans ce notebook, nous allons charger les mosaïques mensuelles Sentinel-1.
Les sujets abordés comprennent :
Inspection du produit mensuel de mosaïques Sentinel-1 et des mesures disponibles dans le datacube
Utilisation de la fonction « dc.load() » pour charger les mosaïques mensuelles Sentinel-1
Visualisation des mosaïques mensuelles
Comparaison des mosaïques mensuelles avec le NDVI mensuel et les scènes individuelles Sentinel-1 ***
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
[1]:
%matplotlib inline
import datacube
import xarray as xr
import matplotlib.pyplot as plt
from deafrica_tools.plotting import rgb
from deafrica_tools.plotting import display_map
from deafrica_tools.datahandling import mostcommon_crs
from deafrica_tools.bandindices import dualpol_indices
Se connecter au datacube
[2]:
dc = datacube.Datacube(app="Sentinel_1_mosaic")
Produits et mesures disponibles
Liste des produits
Nous pouvons utiliser la fonctionnalité « list_products » de Datacube pour inspecter les produits Sentinel-1 de DE Africa disponibles dans le Datacube. Le tableau ci-dessous indique les noms des produits que nous utiliserons pour charger les données, une brève description de celles-ci et l’instrument satellite qui les a acquises.
[3]:
dc.list_products().loc[dc.list_products()['name'].str.contains('s1')]
[3]:
| name | description | license | default_crs | default_resolution | |
|---|---|---|---|---|---|
| name | |||||
| s1_monthly_mosaic | s1_monthly_mosaic | Monthly Sentinel-1 mosaics of weighted terrain... | None | None | None |
| s1_rtc | s1_rtc | Sentinel 1 Gamma0 normalised radar backscatter | CC-BY-4.0 | EPSG:4326 | (-0.0002, 0.0002) |
[4]:
product = "s1_monthly_mosaic"
Liste des mesures
Nous pouvons examiner plus en détail les données disponibles pour chaque produit SAR à l’aide de la fonctionnalité « list_measurements » de Datacube. Le tableau ci-dessous répertorie chacune des mesures disponibles dans les données.
[5]:
measurements = dc.list_measurements()
measurements.loc[product]
[5]:
| name | dtype | units | nodata | aliases | flags_definition | add_offset | scale_factor | |
|---|---|---|---|---|---|---|---|---|
| measurement | ||||||||
| VV | VV | float32 | 1 | NaN | [vv] | NaN | NaN | NaN |
| VH | VH | float32 | 1 | NaN | [vh] | NaN | NaN | NaN |
Le produit Mosaïque mensuelle Sentinel-1 comporte deux mesures :
Rétrodiffusion en polarisation « VV », représentant la moyenne pondérée des valeurs de rétrodiffusion « VV » normalisées valides mesurées au cours du mois.
Rétrodiffusion en polarisation « VH », représentant la moyenne pondérée des valeurs de rétrodiffusion « VH » normalisées valides mesurées au cours du mois.
Les noms de bande à deux lettres indiquent la polarisation de la lumière transmise et reçue par le satellite. VV fait référence au satellite émettant de la lumière polarisée verticalement et recevant en retour de la lumière polarisée verticalement, tandis que VH fait référence au satellite émettant de la lumière polarisée verticalement et recevant en retour de la lumière polarisée horizontalement.
Charger les mosaïques mensuelles Sentinel-1 à l’aide de « dc.load() »
Maintenant que nous savons quels produits et mesures sont disponibles pour le produit, nous pouvons charger des données à partir du datacube en utilisant « dc.load ».
Dans l’exemple ci-dessous, nous allons charger la mosaïque Sentinel-1 pour une zone autour de la ville de Boma sur le fleuve Congo, d’avril 2023 à mars 2024.
Nous allons charger les données de deux bandes de polarisation : « VV » et « VH ». Les données étant fournies sous forme de grille UTM, nous utiliserons la projection la plus courante pour la zone à charger.
Remarque : pour une discussion plus générale sur la manière de charger des données à l’aide du datacube, reportez-vous au bloc-notes « Introduction au chargement des données <../Beginners_guide/03_Loading_data.ipynb> ».
[6]:
# Define the area of interest
latitude = -5.87
longitude = 13.06
buffer = 0.05
time = ('2023-04', '2024-03')
bands = ['vv','vh']
[7]:
#add spatio-temporal extent to the query
query = {
'x': (longitude-buffer, longitude+buffer),
'y': (latitude+buffer, latitude-buffer),
'time':time,
}
Visualiser la zone sélectionnée
[8]:
display_map(x=(longitude-buffer, longitude+buffer), y=(latitude+buffer, latitude-buffer))
[8]:
[9]:
output_crs = mostcommon_crs(dc=dc, product=product, query=query)
print(output_crs)
epsg:32733
[10]:
# loading the data
ds_monthly = dc.load(
product=product,
measurements=bands,
output_crs=output_crs,
resolution=(-20, 20),
**query
)
print(ds_monthly)
<xarray.Dataset> Size: 22MB
Dimensions: (time: 9, y: 556, x: 557)
Coordinates:
* time (time) datetime64[ns] 72B 2023-05-16T11:59:59.500000 ... 202...
* y (y) float64 4kB 9.356e+06 9.356e+06 ... 9.345e+06 9.345e+06
* x (x) float64 4kB 2.796e+05 2.797e+05 ... 2.908e+05 2.908e+05
spatial_ref int32 4B 32733
Data variables:
vv (time, y, x) float32 11MB 0.07513 0.06036 ... 0.1843 0.1746
vh (time, y, x) float32 11MB 0.02182 0.02375 ... 0.03499 0.03216
Attributes:
crs: epsg:32733
grid_mapping: spatial_ref
Visualiser la mosaïque Sentinel-1
Pour visualiser les données du capteur SAR Sentinel-1, nous calculons le rapport entre les bandes de polarisation verticale et horizontale.
[11]:
# adding a ratio band
ds_monthly['vh_over_vv'] = ds_monthly.vh/ds_monthly.vv
Les données sont mises à l’échelle en fonction des 2e et 98e percentiles des observations.
[12]:
data_min = ds_monthly.quantile(0.02)
data_max = ds_monthly.quantile(0.98)
[13]:
scaled = (ds_monthly - data_min)/(data_max - data_min)
[14]:
scaled
[14]:
<xarray.Dataset> Size: 67MB
Dimensions: (time: 9, y: 556, x: 557)
Coordinates:
* time (time) datetime64[ns] 72B 2023-05-16T11:59:59.500000 ... 202...
* y (y) float64 4kB 9.356e+06 9.356e+06 ... 9.345e+06 9.345e+06
* x (x) float64 4kB 2.796e+05 2.797e+05 ... 2.908e+05 2.908e+05
spatial_ref int32 4B 32733
quantile float64 8B 0.02
Data variables:
vv (time, y, x) float64 22MB 0.1371 0.1089 ... 0.3455 0.3271
vh (time, y, x) float64 22MB 0.2926 0.319 0.2788 ... 0.4726 0.4339
vh_over_vv (time, y, x) float64 22MB 0.5152 0.7303 ... 0.3057 0.2938L’attribution des bandes verticales et horizontales respectivement rouges et vertes, et du rapport à la bande bleue, est un moyen courant de visualiser les données SAR.
[15]:
rgb(scaled, bands=['vv','vh','vh_over_vv'], col='time', col_wrap=6)
Générer une visualisation en fausses couleurs
La cellule ci-dessous manipule les bandes de double polarisation pour produire une visualisation en fausses couleurs basée sur les classes de couverture terrestre attendues, compte tenu des valeurs de bande. Le code est adapté de Markuse, P., présenté dans Sentinel Hub <https://custom-scripts.sentinel-hub.com/custom-scripts/sentinel-1/sar_false_color_visualization-2/>`__. Le script inclut des valeurs de paramètres d’étirement pour divers objectifs de visualisation, notamment « terres plus rouges », « marées noires » et « eaux claires ». Dans ce cas, nous utilisons la spécification « land_green » car notre zone d’intérêt est caractérisée par une végétation dense et verte.
Tout d’abord, des fonctions sont définies pour produire la visualisation.
[16]:
def sat_enh(rgb_arr, saturation=1.0):
avg = sum(rgb_arr) / len(rgb_arr)
return [avg * (1 - saturation) + band * saturation for band in rgb_arr]
def classify_pixel_xr(VV, VH,
saturation=1.0,
brightness=1.0,
water_threshold=25,
manual_correction_water=[0.0, 0.0, 0.0],
manual_correction_land=[0.0, 0.0, 0.0]):
def safe_stretch(val, min_val, max_val):
return ((val - min_val) / (max_val - min_val)).clip(0.0, 1.0)
water_normal = [
safe_stretch(VV * 1, 0.0, 0.99),
safe_stretch(VV * 8, 0.0, 0.99),
0.5 + VV * 3 + safe_stretch(VH * 2000, 0.0, 0.99)
]
# adjust stretch values to make land greener
land_green = [
safe_stretch(VV * 3.0, 0.0, 0.99),
safe_stretch(VV * 1.4, 0.0, 0.99) + safe_stretch(VH * 8.75, 0.0, 0.99),
safe_stretch(VH * 1.75, 0.0, 0.99)
]
# Apply manual corrections and brightness
watervis = [band + corr for band, corr in zip(water_normal, manual_correction_water)]
landvis = [(band + corr) * brightness for band, corr in zip(land_green, manual_correction_land)]
# Saturation enhancement
watervis = sat_enh(watervis, saturation)
landvis = sat_enh(landvis, saturation)
# Water mask
water_mask = (VV / VH) > water_threshold
result = [xr.where(water_mask, w, l) for w, l in zip(watervis, landvis)]
# Stack to RGB array
rgb = xr.concat(result, dim='band')
rgb = rgb.assign_coords(band=['R', 'G', 'B'])
return rgb
La fonction est ensuite appliquée à notre ensemble de données mensuelles Sentinel-1 et le résultat est représenté graphiquement. Nous pouvons voir les zones urbaines, la végétation et l’eau en « fausses couleurs » améliorées.
[17]:
false_col = classify_pixel_xr(ds_monthly.vv, ds_monthly.vh).isel(time=0)
false_col.plot.imshow()
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [0.000113667695..298.3589].
[17]:
<matplotlib.image.AxesImage at 0x7f4ff403a4b0>
Avantages de l’utilisation des mosaïques mensuelles
L’espacement mensuel régulier permet une analyse facile des séries chronologiques, seules ou combinées avec d’autres ensembles de données.
Dans les régions nuageuses comme celle du sud-ouest du Nigéria, les mosaïques radar constituent un moyen fiable de suivre les changements tout au long de l’année, en particulier pendant la saison des pluies, lorsque les cultures poussent activement.
Ci-dessous, nous comparons les mosaïques radar aux mesures mensuelles du NDVI. Dans une grande partie de cette région, les données NDVI sont soit indisponibles certains mois (par exemple, juillet, septembre, novembre) en raison d’une couverture nuageuse persistante, soit affectées par des nuages résiduelles mal filtrées (par exemple, juin, août). Cela conduit à une représentation inexacte des tendances de croissance, comme le montre le graphique ci-dessous.
En revanche, les données radar ne sont pas affectées par la couverture nuageuse et suivent avec succès le développement des cultures tout au long de l’année, fournissant ainsi un ensemble de données plus cohérent et plus fiable pour surveiller les changements agricoles.
Dans les zones moins touchées par les nuages, le radar peut fournir des informations complémentaires aux données optiques, améliorant ainsi les capacités globales de surveillance.
[18]:
# Define the area of interest
latitude = 6.86
longitude = 3.33
buffer = 0.02
#add spatio-temporal extent to the query
query = {
'x': (longitude-buffer, longitude+buffer),
'y': (latitude+buffer, latitude-buffer),
'time':time,
}
La zone d’intérêt suivante se situe près de Lagos, au Nigéria. Il s’agit principalement de terres agricoles, traversées par une route principale d’est en ouest, à l’extrême nord de la région.
[19]:
display_map(x=(longitude-buffer, longitude+buffer), y=(latitude+buffer, latitude-buffer))
[19]:
[20]:
# loading the monthly NDVI data
ds_monthly = dc.load(
product=product,
measurements=bands,
output_crs=output_crs,
resolution=(-20, 20),
**query
)
print(ds_monthly)
<xarray.Dataset> Size: 4MB
Dimensions: (time: 10, y: 232, x: 232)
Coordinates:
* time (time) datetime64[ns] 80B 2023-05-16T11:59:59.500000 ... 202...
* y (y) float64 2kB 1.078e+07 1.078e+07 ... 1.077e+07 1.077e+07
* x (x) float64 2kB -8.005e+05 -8.004e+05 ... -7.959e+05 -7.958e+05
spatial_ref int32 4B 32733
Data variables:
vv (time, y, x) float32 2MB 0.159 0.1662 0.1793 ... 0.2324 0.2076
vh (time, y, x) float32 2MB 0.04548 0.04205 ... 0.04563 0.05365
Attributes:
crs: epsg:32733
grid_mapping: spatial_ref
[21]:
# load monthly mean NDVI from the NDVI anomaly product
ndvi_anom = dc.load(
product="ndvi_anomaly",
measurements=["ndvi_mean"],
**query,
)
[22]:
# visualise monthly NDVI
ndvi_anom.ndvi_mean.plot.imshow(col='time', col_wrap=6, cmap="RdYlGn");
Dans ce cas, nous calculerons l’indice de végétation radar (RVI) qui est l’un des indices de double polarisation disponibles dans le package d’outils DE Africa <https://docs.digitalearthafrica.org/en/latest/sandbox/notebooks/Tools/gen/deafrica_tools.bandindices.html>`__.
[23]:
# Calculate the chosen radar vegetation index and add it to the loaded data set
ds_monthly = dualpol_indices(ds_monthly, index='RVI')
[24]:
# Compare monthly NDVI trend and radar measured vegetation trend
fig, ax1 = plt.subplots(figsize=(11,4))
ndvi_anom.ndvi_mean.mean(['x','y']).isel(time=ndvi_anom.ndvi_mean.isnull().mean(['x','y'])<0.5).plot.line('b:^', ax=ax1, label='NDVI');
ax1.set_title('');
plt.legend(loc='lower left');
# Create a second y-axis
ax2 = ax1.twinx()
ds_monthly.RVI.mean(['x', 'y']).plot.line('r:^', ax=ax2, label='RVI');
ax2.set_title('');
plt.legend(loc='upper right');
La tendance du NDVI ci-dessus est peu fiable en raison de données manquantes et de la présence de nuages résiduels, notamment pendant la saison des pluies. On constate également que certains mois manquent dans la série chronologique du NDVI.
Comparaison avec des scènes individuelles de Sentinel-1
Les mosaïques mensuelles offrent un bruit réduit (comme indiqué ci-dessous) et une homogénéité spatiale améliorée par rapport aux scènes individuelles Sentinel-1.
[25]:
# reduce the time window to one month for this demonstration
query['time'] = ('2023-04')
# loading individual Sentinel-1 scenes
ds_S1 = dc.load(product='s1_rtc',
measurements=bands + ['mask', 'area'],
group_by="solar_day",
output_crs = ds_monthly.attrs['crs'], resolution = (-20,20),
**query)
print(ds_S1)
<xarray.Dataset> Size: 4MB
Dimensions: (time: 5, y: 232, x: 232)
Coordinates:
* time (time) datetime64[ns] 40B 2023-04-04T05:30:28.075105 ... 202...
* y (y) float64 2kB 1.078e+07 1.078e+07 ... 1.077e+07 1.077e+07
* x (x) float64 2kB -8.005e+05 -8.004e+05 ... -7.959e+05 -7.958e+05
spatial_ref int32 4B 32733
Data variables:
vv (time, y, x) float32 1MB 0.1334 0.1753 0.1383 ... 0.1928 0.1374
vh (time, y, x) float32 1MB 0.03488 0.03196 ... 0.05957 0.08005
mask (time, y, x) uint8 269kB 1 1 1 1 1 1 1 1 1 ... 1 1 1 1 1 1 1 1
area (time, y, x) float32 1MB 1.015 1.031 1.019 ... 1.016 1.041
Attributes:
crs: epsg:32733
grid_mapping: spatial_ref
[26]:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8,4))
# Plot individual Sentinel-1 observation
ds_S1.vh.isel(time=0).plot.imshow(robust=True, ax=ax1)
# monthly mosaic
ds_monthly.isel(time=0).vh.plot.imshow(robust=True, ax=ax2)
# title
ax1.set_title('S1 RTC scene')
ax2.set_title('S1 Monthly Mosaic')
[26]:
Text(0.5, 1.0, 'S1 Monthly Mosaic')
Avec un bruit réduit, les petites caractéristiques sont plus visibles dans la mosaïque mensuelle, notamment la route/autoroute principale dans la partie nord de la zone d’intérêt.
Autres considérations
La combinaison d’acquisitions provenant de différentes directions orbitales et angles de visée permet de réduire les effets d’ombre radar dans les mosaïques. Cependant, en terrain escarpé, certaines zones peuvent être constamment dans l’ombre radar, rendant impossible toute mesure fiable de la rétrodiffusion. Ces zones sont indiquées par NaN dans la mosaïque.
Lorsque la résolution temporelle élevée est cruciale, les scènes individuelles Sentinel-1 offrent une plus grande flexibilité pour l’analyse.
De plus, une approche de pondération de résolution locale peut être appliquée pour combiner des scènes Sentinel-1 de différentes périodes. Pour les paysages à évolution lente, une fenêtre temporelle supérieure à un mois peut être utilisée pour réduire davantage le bruit.
Génération d’une mosaïque pondérée
[27]:
# apply local resolution weighting to generate a mosaic
weight = 1 / ds_S1.area.where(ds_S1.mask==1)
ds_S1_weighted = (ds_S1[['vv', 'vh']].where(ds_S1.mask==1)*weight/weight.sum(dim='time')).sum(dim='time')
[28]:
ds_S1_weighted.vh.plot.imshow(robust=True);
La mosaïque générée ci-dessus diffère légèrement du produit mosaïque mensuel en raison des variations dans les méthodes de projection et de rééchantillonnage utilisées lors du traitement.
Informations Complémentaires
Licence : Le code de ce carnet est sous licence Apache, version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>. Les données de Digital Earth Africa sont sous licence Creative Commons par attribution 4.0 <https://creativecommons.org/licenses/by/4.0/>.
Contact : Si vous avez besoin d’aide, veuillez poster une question sur le canal Slack Open Data Cube <http://slack.opendatacube.org/>`__ ou sur le GIS Stack Exchange en utilisant la balise open-data-cube (vous pouvez consulter les questions posées précédemment ici). Si vous souhaitez signaler un problème avec ce bloc-notes, vous pouvez en déposer un sur Github.
Version de Datacube compatible :
[29]:
print(datacube.__version__)
1.8.20
Dernier test :
[30]:
from datetime import datetime
datetime.today().strftime('%Y-%m-%d')
[30]:
'2025-04-07'