Produit Sentinel-3 Synergy Vegetation.#

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 :

  1. Inspection des produits et des mesures disponibles dans le datacube

  2. Chargement des données Sentinel-3 SYN Level-2 VG1.

  3. 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 :

  1. 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.

  2. 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 2bd708e83f234057b711ab3606b4d0da 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]:
Make this Notebook Trusted to load map: File -> Trust Notebook

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);
../../../_images/sandbox_notebooks_Datasets_Sentinel_3_SYN_VG1_26_0.png

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);
../../../_images/sandbox_notebooks_Datasets_Sentinel_3_SYN_VG1_29_0.png

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()
../../../_images/sandbox_notebooks_Datasets_Sentinel_3_SYN_VG1_43_0.png

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()
../../../_images/sandbox_notebooks_Datasets_Sentinel_3_SYN_VG1_46_0.png

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'