Produit Sentinel-3 Synergy Vegetation.#
Produits utilisés : s3_syn_2_vg1
Mots-clés données utilisées ; Sentinel-3 Level-2 VG1 SYN
Aperçu#
Le produit Sentinel-3 SYN Level-2 VG1 est un produit de synthèse quotidien de 1 km similaire à VEGETATION, généré à partir de la chaîne de traitement Sentinel-3 Synergy. Il combine les observations de l’instrument OLCI (Ocean and Land Colour Instrument) et du radiomètre SLSTR (Sea and Land Surface Temperature Radiometer) à bord de Sentinel-3A et Sentinel-3B pour fournir des observations harmonisées de la surface terrestre pour le suivi de la végétation. Le produit fournit une réflectance de surface au sommet de la canopée (TOC) et le NDVI sur une grille latitude-longitude régulière avec une résolution spatiale approximative de 1 km à l’équateur.
Le produit SY_2_VG1 est conçu comme un produit de continuité SPOT/VEGETATION soutenant le suivi à long terme de la dynamique de la végétation sur de grandes zones. Il fournit des observations de synthèse quotidiennes utilisant l’approche de sélection de pixels la meilleure disponible, basée sur la méthode de composition NDVI maximum, pour améliorer la cohérence dans les applications de suivi de la végétation. Le produit inclut quatre canaux principaux de réflectance de surface : bleu (B0), rouge (B2), proche infrarouge (B3) et moyen infrarouge/SWIR (MIR). Le NDVI est également fourni et est dérivé des canaux proche infrarouge B3 et rouge B2.
Le produit fournit des mesures cohérentes de l’état de la végétation et de la réflectance de la surface terrestre à une résolution de 1 km, avec une fréquence de synthèse quotidienne, ce qui le rend précieux pour les applications environnementales et agricoles à grande échelle, notamment :
Suivi de la santé et de la verdure de la végétation
Alerte précoce à la sécheresse et évaluations de la sécurité alimentaire
Suivi de l’état des cultures et des parcours
Évaluation de la productivité des terres et de la dégradation des terres
Suivi de la phénologie et de la dynamique saisonnière de la végétation
Analyse des tendances de la végétation à long terme
Études sur le climat, les écosystèmes et les processus de surface terrestre
Suivi agricole à l’échelle régionale à continentale
En plus de la réflectance et du NDVI, SY_2_VG1 inclut des informations atmosphériques et géométriques de soutien telles que l’épaisseur optique des aérosols, la colonne totale d’ozone, la colonne totale de vapeur d’eau et la géométrie soleil-observation. Ces variables sont utiles pour interpréter les effets atmosphériques, filtrer les observations de mauvaise qualité et améliorer la fiabilité de l’analyse des séries temporelles de végétation.
Plus d’informations sur le produit peuvent être trouvées dans la documentation des produits Sentinel-3 Synergy et le Sentinel-3 SYN Land User Handbook.
Description#
Ce cahier couvrira les sujets suivants :
Inspection des produits et des mesures disponibles dans le datacube
Chargement des données Sentinel-3 SYN Level-2 VG1.
Tracer les résultats
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]:
%matplotlib inline
import datacube
import geopandas as gpd
import numpy as np
import xarray as xr
import pandas as pd
import matplotlib.pyplot as plt
from odc.geo.geom import Geometry
from deafrica_tools.plotting import display_map
from deafrica_tools.areaofinterest import define_area
Se connecter au datacube#
Connectez-vous au datacube pour que nous puissions accéder aux données de DE Africa.
[2]:
dc = datacube.Datacube(app="Sentinel-3 SYN SY_2_VG1")
Liste des produits#
Nous pouvons utiliser la fonctionnalité list_products du datacube pour inspecter les produits de DE Africa disponibles. Le tableau ci-dessous montre les noms de produits que nous utiliserons pour charger les données, une brève description des données et l’instrument satellite qui a acquis les données.
[3]:
dc.list_products().loc[dc.list_products()['description'].str.contains('Sentinel-3')]
[3]:
| name | description | license | default_crs | default_resolution | |
|---|---|---|---|---|---|
| name | |||||
| s3_ol_2_wfr_nrt | s3_ol_2_wfr_nrt | Sentinel-3 Level 2 Water Full Resolution (WFR)... | CC-BY-4.0 | EPSG:4326 | Resolution(x=0.003, y=-0.003) |
| s3_olci_l2_lfr | s3_olci_l2_lfr | Sentinel-3 OLCI L2 LAND | CC-BY-4.0 | EPSG:4326 | Resolution(x=0.003, y=-0.003) |
| s3_olci_l2_wfr | s3_olci_l2_wfr | Sentinel-3 OLCI L2 WATER | CC-BY-4.0 | EPSG:4326 | Resolution(x=0.003, y=-0.003) |
| s3_slstr_l2_lst | s3_slstr_l2_lst | Sentinel-3 LST | CC-BY-4.0 | EPSG:4326 | Resolution(x=0.003, y=-0.003) |
| s3_syn_2_vg1 | s3_syn_2_vg1 | Sentinel-3 Level-2 VG1 SYN product | CC-BY-4.0 | EPSG:4326 | Resolution(x=0.01, y=-0.01) |
Veuillez choisir le produit en spécifiant son nom, avec le produit s3_syn_2_vg1
[4]:
product = "s3_syn_2_vg1"
Liste des mesures#
Nous pouvons inspecter plus en détail les données disponibles pour le produit terrestre Sentinel-3 s3_olci_l2_lfr en utilisant 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 | ||||||||
| AG_VG1 | AG_VG1 | uint8 | 1 | 255.0 | [aerosol_optical_thickness] | NaN | 0.0 | 1.0000 |
| B0_VG1 | B0_VG1 | int16 | 1 | 32767.0 | NaN | NaN | 0.0 | 0.0001 |
| B2_VG1 | B2_VG1 | int16 | 1 | 32767.0 | NaN | NaN | 0.0 | 0.0001 |
| B3_VG1 | B3_VG1 | int16 | 1 | 32767.0 | NaN | NaN | 0.0 | 0.0001 |
| MIR_VG1 | MIR_VG1 | int16 | 1 | 32767.0 | [middle_infrared] | NaN | 0.0 | 0.0001 |
| NDVI_VG1 | NDVI_VG1 | int16 | 1 | 32767.0 | NaN | NaN | 0.0 | 0.0001 |
| OG_VG1 | OG_VG1 | uint8 | atm.cm | 255.0 | [total_column_ozone] | NaN | 0.0 | 1.0000 |
| SAA_VG1 | SAA_VG1 | int16 | degrees | 32767.0 | [solar_azimuth_angle] | NaN | 0.0 | 1.0000 |
| SZA_VG1 | SZA_VG1 | uint8 | degrees | 255.0 | [solar_zenith_angle] | {'mask': {'bits': [0, 1, 2, 3, 4, 8, 16, 32, 6... | 0.0 | 1.0000 |
| TOA_NDVI_VG1 | TOA_NDVI_VG1 | int16 | 1 | 32767.0 | NaN | NaN | 0.0 | 0.0001 |
| VAA_VG1 | VAA_VG1 | int16 | degrees | 32767.0 | [view_azimuth_angle] | NaN | 0.0 | 1.0000 |
| VZA_VG1 | VZA_VG1 | uint8 | degrees | 255.0 | [view_zenith_angle] | NaN | 0.0 | 1.0000 |
| WVG_VG1 | WVG_VG1 | uint8 | g/cm^2 | 255.0 | [total_column_water_vapour] | NaN | 0.0 | 1.0000 |
Le produit Sentinel-3 SYN SY_2_VG1 comporte treize mesures :
Mesure |
Description |
Application |
|---|---|---|
B0_VG1 |
Réflectance de surface dans la bande bleue à environ 450 nm. |
Support à la correction atmosphérique, analyse de la réflectance de surface, séparation végétation/arrière-plan. |
B2_VG1 |
Réflectance de surface dans la bande rouge à environ 645 nm. |
Utilisé avec le NIR pour évaluer la verdure de la végétation et l’activité photosynthétique. |
B3_VG1 |
Réflectance de surface dans la bande du proche infrarouge à environ 835 nm. |
Suivi de l’état de la végétation, sensibilité à la biomasse, calcul du NDVI et analyse de la productivité des terres. |
MIR_VG1 |
Réflectance de surface dans la bande moyen infrarouge / SWIR à environ 1665 nm. |
Stress hydrique de la végétation, suivi de la sécheresse, détection de la végétation brûlée/sèche, analyse de l’état de la surface terrestre. |
NDVI_VG1 |
Indice de végétation par différence normalisée calculé à partir de la réflectance de surface. |
Variable principale pour la productivité de la végétation, l’alerte précoce à la sécheresse, l’état des cultures, le suivi des parcours et l’évaluation de la dégradation des terres. |
TOA_NDVI_VG1 |
NDVI calculé à partir de la réflectance au sommet de l’atmosphère avant une correction complète de surface. |
Utile pour la comparaison avec le NDVI de surface, le contrôle qualité et la compréhension de l’influence atmosphérique. |
AG_VG1 |
Épaisseur optique des aérosols à 550 nm. |
Aide à évaluer la contamination atmosphérique et soutient le filtrage qualité des observations de végétation. |
OG_VG1 |
Colonne totale d’ozone. |
Support à la correction atmosphérique et interprétation de la qualité des valeurs de réflectance/NDVI. |
WVG_VG1 |
Colonne totale de vapeur d’eau. |
Support à la correction atmosphérique, analyse liée au climat et interprétation des conditions de stress de la végétation. |
SZA_VG1 |
Angle zénithal solaire. |
Aide à évaluer les conditions d’illumination et à filtrer les observations de mauvaise qualité. |
SAA_VG1 |
Angle azimutal solaire. |
Utilisé avec la géométrie d’observation pour comprendre les effets de réflectance directionnelle. |
VZA_VG1 |
Angle zénithal de vision ou d’observation. |
Aide à identifier les observations affectées par des angles de vision extrêmes. |
VAA_VG1 |
Angle azimutal de vision ou d’observation. |
Soutient l’interprétation de la réflectance bidirectionnelle et le filtrage qualité. |
Paramètres d’analyse#
La cellule suivante définit les paramètres qui définissent la zone d’intérêt sur laquelle effectuer l’analyse. #### Sélectionner l’emplacement Pour définir la zone d’intérêt, deux méthodes sont disponibles :
En spécifiant la latitude, la longitude et la zone tampon, ou en séparant les zones tampons de latitude et de longitude, cette méthode vous permet de définir une zone d’intérêt autour d’un point central. Vous pouvez saisir la latitude centrale, la longitude centrale et une valeur de zone tampon en degrés pour créer une zone carrée autour du point central. Par exemple, « lat = 10.338 », « lon = -1.055 » et « buffer = 0.1 » sélectionneront une zone avec un rayon de 0,1 degré carré autour du point avec les coordonnées « (10.338, -1.055) ».
Vous pouvez également fournir des valeurs de tampon distinctes pour la latitude et la longitude d’une zone rectangulaire. Par exemple, « lat = 10.338 », « lon = -1.055 » et « lat_buffer = 0.1 » et « lon_buffer = 0.08 » sélectionneront une zone rectangulaire s’étendant sur 0,1 degré au nord et au sud et 0,08 degré à l’est et à l’ouest à partir du point « (10.338, -1.055) ».
Pour des temps de chargement raisonnables, définissez la mémoire tampon sur « 0,1 » ou moins.
By uploading a polygon as a
GeoJSON or Esri Shapefile. If you choose this option, you will need to upload the geojson or ESRI shapefile into the Sandbox using Upload Files button
in the top left corner of the Jupyter Notebook interface. ESRI shapefiles must be uploaded with all the related files (.cpg, .dbf, .shp, .shx). Once uploaded, you can use the shapefile or geojson to define the area of interest. Remember to update the code to call the file you have uploaded.
Pour utiliser l’une de ces méthodes, vous pouvez décommenter la ligne de code concernée et commenter l’autre. Pour commenter une ligne, ajoutez le symbole "#" avant le code que vous souhaitez commenter. Par défaut, la première option qui définit l’emplacement à l’aide de la latitude, de la longitude et du tampon est utilisée.
Si vous exécutez le notebook pour la première fois, conservez les paramètres par défaut ci-dessous. Cela démontrera le fonctionnement de l’analyse et fournira des résultats significatifs. L’exemple couvre une ceinture centrale du bassin du Congo (forêt ombrophile de plaine sempervirente) en République démocratique du Congo (RDC), dans la partie nord-est du pays — au sein de la province de la Tshopo. Cette région se caractérise par une couverture nuageuse fréquente et dense, de sorte qu’elle est souvent difficile à surveiller efficacement avec des missions optiques ayant une fréquence de revisite plus faible, telles que les missions Sentinel et Landsat.
Pour exécuter le notebook pour une zone différente, assurez-vous que les données Sentinel-3 SYN SY_2_VG1 sont disponibles pour la zone choisie en utilisant le DEAfrica Explorer.
[6]:
# Method 1: Specify the latitude, longitude, and buffer)
aoi = define_area(lat=1.066, lon=26.455, buffer=0.9)
# Method 2: Use a polygon as a GeoJSON or Esri Shapefile.
# aoi = define_area(vector_path='aoi.shp')
#Create a geopolygon and geodataframe of the area of interest
geopolygon = Geometry(aoi["features"][0]["geometry"], crs="epsg:4326")
geopolygon_gdf = gpd.GeoDataFrame(geometry=[geopolygon], crs=geopolygon.crs)
# Get the latitude and longitude range of the geopolygon
lat_range = (geopolygon_gdf.total_bounds[1], geopolygon_gdf.total_bounds[3])
lon_range = (geopolygon_gdf.total_bounds[0], geopolygon_gdf.total_bounds[2])
[7]:
display_map(x=lon_range, y=lat_range)
[7]:
Charger le jeu de données Sentinel SYN SY_2_VG1 en utilisant dc.load()#
Maintenant que nous savons quels produits et mesures sont disponibles pour le produit, nous pouvons charger les données depuis le datacube en utilisant dc.load. Nous chargerons les données à partir des bandes satellites spectrales. En spécifiant output_crs='EPSG:4326' et resolution=(-0.01, 0.01), nous demandons au datacube de reprojeter nos données vers le système de coordonnées géographiques mondial (CRS), avec des pixels de 1000 x 1000 m. Enfin, group_by='solar_day' garantit que les images superposées prises à quelques secondes d’intervalle lors du passage du satellite sont combinées en une seule étape temporelle dans les données.
[8]:
query = {
'x': (lon_range),
'y': (lat_range),
'time':('2025-04-01', '2025-04-30'),
'output_crs': 'EPSG:4326',
'resolution': (-0.01, 0.01)}
ds_S3_syn_2_vg1 = dc.load(product=product,
group_by="solar_day",
**query)
ds_S3_syn_2_vg1
[8]:
<xarray.Dataset> Size: 21MB
Dimensions: (time: 30, latitude: 181, longitude: 181)
Coordinates:
* time (time) datetime64[ns] 240B 2025-04-01 ... 2025-04-30
* latitude (latitude) float64 1kB 1.965 1.955 1.945 ... 0.185 0.175 0.165
* longitude (longitude) float64 1kB 25.55 25.57 25.57 ... 27.34 27.36
spatial_ref int32 4B 4326
Data variables: (12/13)
AG_VG1 (time, latitude, longitude) uint8 983kB 0 0 0 0 0 ... 0 0 0 0
B0_VG1 (time, latitude, longitude) int16 2MB 429 437 388 ... 0 0 0
B2_VG1 (time, latitude, longitude) int16 2MB 783 783 723 ... 0 0 0
B3_VG1 (time, latitude, longitude) int16 2MB 4358 4391 4419 ... 0 0 0
MIR_VG1 (time, latitude, longitude) int16 2MB 2463 2441 2353 ... 0 0 0
NDVI_VG1 (time, latitude, longitude) int16 2MB 6960 6960 7200 ... 0 0 0
... ...
SAA_VG1 (time, latitude, longitude) int16 2MB 84 84 84 84 ... 0 0 0 0
SZA_VG1 (time, latitude, longitude) uint8 983kB 35 35 35 35 ... 0 0 0
TOA_NDVI_VG1 (time, latitude, longitude) int16 2MB 5520 5520 5680 ... 0 0 0
VAA_VG1 (time, latitude, longitude) int16 2MB 102 102 102 ... 0 0 0
VZA_VG1 (time, latitude, longitude) uint8 983kB 32 32 32 32 ... 0 0 0
WVG_VG1 (time, latitude, longitude) uint8 983kB 4 4 4 4 4 ... 0 0 0 0
Attributes:
crs: EPSG:4326
grid_mapping: spatial_ref⚠️ Remarque : Certaines mesures Sentinel-3 SYN (SY_2_VG1) sont stockées sous forme d’entiers avec un facteur d’échelle (par exemple, 0,0001) ou un décalage (par exemple, 1000) appliqué. Cette approche réduit les besoins de stockage tout en préservant la précision. Pour obtenir les valeurs physiques correctes, le facteur d’échelle et/ou le décalage doivent être inversés lors du traitement.
L’exemple ci-dessous démontre comment appliquer cette correction. Dans ce jeu de données, plusieurs mesures Sentinel-3 utilisent un facteur d’échelle de 10 000, comme indiqué dans la sortie de la section Liste des mesures ci-dessus. Vous pouvez récupérer les valeurs d’origine soit en divisant par 10 000, soit en multipliant par 0,0001, ou, plus généralement, en récupérant le facteur d’échelle à partir des attributs de mesure en utilisant
.attrs.get("scale_factor"), comme illustré dans l’exemple ci-dessous.
La cellule ci-dessous visualise le NDVI à partir de la réflectance de surface au moment de l’observation.
Sans appliquer le facteur d’échelle, faites très attention aux unités.
[9]:
(ds_S3_syn_2_vg1['NDVI_VG1'].isel(time=[0, 15, 20, -2])).plot(robust=True, col="time", col_wrap=4);
Après avoir appliqué le facteur d’échelle, faites très attention aux unités.
[10]:
# Retrieve scale factor using the .attrs.get('scale_factor') measurement attribute
scale_factor = ds_S3_syn_2_vg1['NDVI_VG1'].attrs.get('scale_factor')
[11]:
(ds_S3_syn_2_vg1['NDVI_VG1'].isel(time=[0, 15, 20, -2])*scale_factor).plot(robust=True, col="time", col_wrap=4);
Avant d’utiliser une bande de mesure, vérifiez toujours si un facteur d’échelle ou un décalage doit être appliqué avant d’utiliser les données pour une analyse plus approfondie. Après confirmation, le même processus décrit ci-dessus peut être utilisé pour visualiser toutes les autres bandes de mesure disponibles pour le produit.
Comprendre le NDVI BOA et TOA#
Le produit Sentinel-3 SYN SY_2_VG1 fournit à la fois le NDVI BOA/surface (NDVI_VG1) et le NDVI au sommet de l’atmosphère (TOA_NDVI_VG1). Le NDVI BOA représente les conditions de végétation au niveau de la surface après correction atmosphérique, tandis que le NDVI TOA est calculé avant une correction atmosphérique complète et inclut encore les effets atmosphériques tels que les aérosols, la vapeur d’eau et les nuages.
Comparer le NDVI BOA et le NDVI TOA aide les utilisateurs à comprendre comment la correction atmosphérique affecte les valeurs de NDVI. Dans ce notebook, NDVI_VG1 est utilisé comme la couche de végétation principale pour l’analyse, tandis que TOA_NDVI_VG1 est inclus comme une couche de soutien pour aider les utilisateurs à comprendre la différence entre le NDVI corrigé en surface et celui influencé par l’atmosphère.
La plupart des produits Sentinel-3 incluent un dataMask ou une couche de qualité qui aide les utilisateurs à supprimer les pixels affectés par des observations invalides, des nuages, des données manquantes ou d’autres problèmes de qualité. Cependant, comme un dataMask facilement disponible n’existe pas pour Sentinel-3 SYN SY_2_VG1, nous devons appliquer nous-mêmes une approche simple de masquage de qualité en supprimant les valeurs peu fiables, telles que les valeurs NDVI 0 et les pixels de réflectance invalides, avant de produire des cartes, des graphiques et des analyses de séries temporelles. Les étapes ci-dessous fournissent les conseils nécessaires pour appliquer le masquage de qualité. Les utilisateurs peuvent choisir la méthode qui convient le mieux à leur analyse et à leurs exigences en matière de données.
Appliquer le facteur d’échelle#
[12]:
ds_S3_syn_2_vg1_scaled = ds_S3_syn_2_vg1*scale_factor
The cell below extracts the surface/BOA NDVI layer (NDVI_VG1) and the Top-of-Atmosphere NDVI layer (TOA_NDVI_VG1) from the scaled Sentinel-3 SYN SY_2_VG1 dataset. It then calculates and prints the minimum and maximum values for each NDVI layer while ignoring missing values. This step helps confirm that the NDVI values are within the expected range of -1 to 1 and supports the quality-checking process before plotting or further analysis.
[13]:
boa_ndvi = ds_S3_syn_2_vg1_scaled["NDVI_VG1"]
toa_ndvi = ds_S3_syn_2_vg1_scaled["TOA_NDVI_VG1"]
print("BOA NDVI min:", float(boa_ndvi.min(skipna=True)))
print("BOA NDVI max:", float(boa_ndvi.max(skipna=True)))
print("TOA NDVI min:", float(toa_ndvi.min(skipna=True)))
print("TOA NDVI max:", float(toa_ndvi.max(skipna=True)))
BOA NDVI min: -0.08
BOA NDVI max: 0.92
TOA NDVI min: -0.08
TOA NDVI max: 0.912
This code identifies pixels where BOA NDVI_VG1 is equal to 0 and calculates how common these 0 values are for each observation date. It counts the total number of valid pixels, the number of zero-value NDVI pixels, and the percentage of valid pixels affected by 0 values. This summary helps assess whether 0 NDVI values are isolated or widespread, and supports the decision on whether they should be masked before plotting and time-series analysis.
[14]:
zero_mask = boa_ndvi == 0
zero_count = zero_mask.sum(dim=["latitude", "longitude"])
valid_count = boa_ndvi.notnull().sum(dim=["latitude", "longitude"])
zero_percent = (zero_count / valid_count) * 100
zero_summary = pd.DataFrame({
"time": boa_ndvi.time.values,
"valid_pixels": valid_count.values,
"zero_ndvi_pixels": zero_count.values,
"zero_ndvi_percent": zero_percent.values,
})
zero_summary
[14]:
| time | valid_pixels | zero_ndvi_pixels | zero_ndvi_percent | |
|---|---|---|---|---|
| 0 | 2025-04-01 | 32761 | 11609 | 35.435426 |
| 1 | 2025-04-02 | 32761 | 31803 | 97.075791 |
| 2 | 2025-04-03 | 32761 | 32761 | 100.000000 |
| 3 | 2025-04-04 | 32761 | 32729 | 99.902323 |
| 4 | 2025-04-05 | 32761 | 32761 | 100.000000 |
| 5 | 2025-04-06 | 32761 | 32761 | 100.000000 |
| 6 | 2025-04-07 | 32761 | 32761 | 100.000000 |
| 7 | 2025-04-08 | 32761 | 24863 | 75.892067 |
| 8 | 2025-04-09 | 32761 | 32761 | 100.000000 |
| 9 | 2025-04-10 | 32761 | 32761 | 100.000000 |
| 10 | 2025-04-11 | 32761 | 32761 | 100.000000 |
| 11 | 2025-04-12 | 32761 | 32761 | 100.000000 |
| 12 | 2025-04-13 | 32761 | 4462 | 13.619853 |
| 13 | 2025-04-14 | 32761 | 32761 | 100.000000 |
| 14 | 2025-04-15 | 32761 | 32761 | 100.000000 |
| 15 | 2025-04-16 | 32761 | 5325 | 16.254083 |
| 16 | 2025-04-17 | 32761 | 30805 | 94.029486 |
| 17 | 2025-04-18 | 32761 | 32761 | 100.000000 |
| 18 | 2025-04-19 | 32761 | 32761 | 100.000000 |
| 19 | 2025-04-20 | 32761 | 2429 | 7.414304 |
| 20 | 2025-04-21 | 32761 | 2298 | 7.014438 |
| 21 | 2025-04-22 | 32761 | 32761 | 100.000000 |
| 22 | 2025-04-23 | 32761 | 32761 | 100.000000 |
| 23 | 2025-04-24 | 32761 | 23801 | 72.650407 |
| 24 | 2025-04-25 | 32761 | 32714 | 99.856537 |
| 25 | 2025-04-26 | 32761 | 32761 | 100.000000 |
| 26 | 2025-04-27 | 32761 | 27359 | 83.510882 |
| 27 | 2025-04-28 | 32761 | 32761 | 100.000000 |
| 28 | 2025-04-29 | 32761 | 29783 | 90.909923 |
| 29 | 2025-04-30 | 32761 | 32761 | 100.000000 |
The next cells identifies observation dates where more than 10% of the valid BOA NDVI_VG1 pixels have values equal to 0. These dates are important because a high percentage of 0 NDVI values may indicate poor-quality observations, missing data, failed retrievals, or cloud-affected pixels. By identifying these dates, users can decide whether to mask the affected pixels, remove poor-quality dates, or investigate them further before producing NDVI plots and time-series analysis.
[15]:
high_zero_dates = zero_summary[zero_summary["zero_ndvi_percent"] > 10]
high_zero_dates
[15]:
| time | valid_pixels | zero_ndvi_pixels | zero_ndvi_percent | |
|---|---|---|---|---|
| 0 | 2025-04-01 | 32761 | 11609 | 35.435426 |
| 1 | 2025-04-02 | 32761 | 31803 | 97.075791 |
| 2 | 2025-04-03 | 32761 | 32761 | 100.000000 |
| 3 | 2025-04-04 | 32761 | 32729 | 99.902323 |
| 4 | 2025-04-05 | 32761 | 32761 | 100.000000 |
| 5 | 2025-04-06 | 32761 | 32761 | 100.000000 |
| 6 | 2025-04-07 | 32761 | 32761 | 100.000000 |
| 7 | 2025-04-08 | 32761 | 24863 | 75.892067 |
| 8 | 2025-04-09 | 32761 | 32761 | 100.000000 |
| 9 | 2025-04-10 | 32761 | 32761 | 100.000000 |
| 10 | 2025-04-11 | 32761 | 32761 | 100.000000 |
| 11 | 2025-04-12 | 32761 | 32761 | 100.000000 |
| 12 | 2025-04-13 | 32761 | 4462 | 13.619853 |
| 13 | 2025-04-14 | 32761 | 32761 | 100.000000 |
| 14 | 2025-04-15 | 32761 | 32761 | 100.000000 |
| 15 | 2025-04-16 | 32761 | 5325 | 16.254083 |
| 16 | 2025-04-17 | 32761 | 30805 | 94.029486 |
| 17 | 2025-04-18 | 32761 | 32761 | 100.000000 |
| 18 | 2025-04-19 | 32761 | 32761 | 100.000000 |
| 21 | 2025-04-22 | 32761 | 32761 | 100.000000 |
| 22 | 2025-04-23 | 32761 | 32761 | 100.000000 |
| 23 | 2025-04-24 | 32761 | 23801 | 72.650407 |
| 24 | 2025-04-25 | 32761 | 32714 | 99.856537 |
| 25 | 2025-04-26 | 32761 | 32761 | 100.000000 |
| 26 | 2025-04-27 | 32761 | 27359 | 83.510882 |
| 27 | 2025-04-28 | 32761 | 32761 | 100.000000 |
| 28 | 2025-04-29 | 32761 | 29783 | 90.909923 |
| 29 | 2025-04-30 | 32761 | 32761 | 100.000000 |
Quality Masking for Reliable NDVI Analysis#
Most Sentinel-3 products include a dataMask or quality layer that helps users remove pixels affected by invalid observations, cloud, missing data, or other quality issues. However, because a readily available dataMask is not exposed for Sentinel-3 SYN SY_2_VG1 in our current workflow, we apply a simple quality-masking approach ourselves by removing unreliable values, such as 0 NDVI values and invalid reflectance pixels, before producing maps, plots, and time-series analysis.
This code removes 0 values from the BOA NDVI_VG1 layer and compares the original NDVI image with the masked NDVI image. The purpose is to assess whether 0 values are influencing the visual interpretation of vegetation condition. In the masked image, pixels with NDVI equal to 0 are treated as no-data, allowing users to see the vegetation pattern without potentially unreliable low-value pixels affecting the plot.
[16]:
#keeps only pixels where boa_ndvi value is not equal to 0.
boa_ndvi_masked_zero = boa_ndvi.where(boa_ndvi != 0)
[17]:
#selects the first time step
time_index = 0
#plot the original against the masked data
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
#original
boa_ndvi.isel(time=time_index).plot(
ax=axes[0],
robust=True,
cmap="RdYlGn"
)
axes[0].set_title("Original BOA NDVI_VG1")
#masked
boa_ndvi_masked_zero.isel(time=time_index).plot(
ax=axes[1],
robust=True,
cmap="RdYlGn"
)
axes[1].set_title("BOA NDVI_VG1 with 0 values masked")
plt.tight_layout()
plt.show()
Compare BOA NDVI VG1 with TOA NDVI VG1#
This code compares TOA_NDVI_VG1 and BOA NDVI_VG1 for a selected observation date using a scatter plot. Each point represents one pixel, with TOA NDVI shown on the x-axis and BOA NDVI shown on the y-axis. We can see that the BOA values are generally higher than TOA values for the same acquisition as values are to the top-left of the 1:1 line. This is because atmospheric corrections remove the effect of aerosols, haze, or other interference and lead to a higher, “cleaner”, NDVI value.
[18]:
# Select the first time step for the comparison
time_index = 0
boa_flat = boa_ndvi.isel(time=time_index).values.flatten()
toa_flat = toa_ndvi.isel(time=time_index).values.flatten()
valid = np.isfinite(boa_flat) & np.isfinite(toa_flat)
plt.figure(figsize=(6, 6))
plt.scatter(toa_flat[valid], boa_flat[valid], s=2, alpha=0.3)
plt.axhline(0, linestyle="--")
plt.axvline(0, linestyle="--")
plt.axline((0, 0), slope=1, color="black", linestyle="--", label="1:1 Line")
plt.xlabel("TOA_NDVI_VG1")
plt.ylabel("BOA NDVI_VG1")
plt.title("TOA NDVI vs BOA NDVI")
plt.grid(True)
plt.show()
Informations Complémentaires#
Licence Le code de ce notebook 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 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 DE Africa <https://digitalearthafrica.slack.com/> ou sur le GIS Stack Exchange <https://gis.stackexchange.com/questions/ask?tags=open-data-cube> en utilisant la balise open-data-cube (vous pouvez consulter les questions posées précédemment `ici <https://gis.stackexchange.com/questions/tagged/open-data-
Si vous souhaitez signaler un problème avec ce notebook, vous pouvez en déposer un sur Github.
Version de Datacube compatible
[19]:
print(datacube.__version__)
1.9.13
Dernier test :
[20]:
from datetime import datetime
datetime.today().strftime('%Y-%m-%d')
[20]:
'2026-09-01'