Monthly Median NDVI Calculation – Differences between GEE and OpenEO

Hello,

I am encountering an issue while translating a Google Earth Engine (GEE) script to OpenEO.

In GEE, I developed a script that calculates the monthly median NDVI from Sentinel-2 L2A data, applying a cloud mask using the SCL band. I have attempted to translate this script for use with OpenEO, using the CDSE back-end.

However, I am observing significant differences in the resulting NDVI values and I am unable to determine the cause.

In particular, I have the following questions:

  • Are there any differences between the two datasets (GEE vs. CDSE)?
  • Are there differences in how missing or invalid values are handled when computing the median after applying the mask? (It seems not, since I have the same issue even without applying the mask.)

Thank you in advance for your help.

GEE script:

// 1. Import de la ROI
var roi = ee.FeatureCollection("projects/ee-leolgrcsigma/assets/roi");
Map.centerObject(roi, 11);
Map.addLayer(roi, {color: 'red', fillColor: '00000000'}, 'ROI');

// 2. Masque des nuages Sentinel-2
function maskS2clouds(image) {
  var scl = image.select('SCL');
  var mask = scl.neq(3).and(scl.neq(9)).and(scl.neq(8)).and(scl.neq(10));
  return image.updateMask(mask);
}

// 3. Calcul NDVI
function addNDVI(image) {
  var ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI');
  return image.addBands(ndvi);
}

// 4. Période : septembre 2022
var startDate = '2022-09-01';
var endDate = '2022-09-30';

// 5. Chargement de Sentinel-2 et traitement
var s2 = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(roi)
  .filterDate(startDate, endDate)
  .map(maskS2clouds)
  .map(addNDVI);

// 6. Calcul NDVI médian
var ndvi_median = s2.select('NDVI').median().clip(roi);

// 7. Affichage
Map.addLayer(ndvi_median, {min: -1, max: 1, palette: ['brown','white','green']}, 'NDVI_median_Sep_2022');

// 8. Export vers Google Drive
Export.image.toDrive({
  image: ndvi_median,
  description: 'NDVI_median_Sep_2022_roi',
  scale: 10,
  region: roi,
  crs: 'EPSG:4326',
  maxPixels: 1e13
});

OpenEO script :

import openeo
import rasterio
import matplotlib.pyplot as plt

connection = openeo.connect("openeo.dataspace.copernicus.eu").authenticate_oidc()

caen = {"west":  -0.39291668372321487, "south": 49.121819796877894, "east": -0.0411832949045951441, "north": 49.31929639176561}
t = ["2022-09-01", "2022-09-30"]

datacube = connection.load_collection(
    "SENTINEL2_L2A",
    spatial_extent = caen,
    temporal_extent = t,
    bands = ["B04", "B08", "SCL"],
    max_cloud_cover=100,
)

SCL = datacube.band("SCL")
mask = ((SCL == 3) | (SCL == 8) | (SCL == 9) | (SCL == 10))

datacube_masked = datacube.mask(mask) 

# Calcul du NDVI
ndvi = datacube_masked.process("ndvi", {
    "data": datacube_masked,
    "nir": "B08",
    "red": "B04"
})

# Médiane temporelle
ndvi_median = ndvi.reduce_dimension(
    dimension="t",
    reducer="median"
)

# Téléchargement
ndvi_median.download("median_ndvi.tif")

# Visualisation
with rasterio.open("median_ndvi.tif") as src:
    data = src.read(1)
    plt.imshow(data, cmap="YlGn", vmin=-1, vmax=1)
    plt.colorbar(label="NDVI")
    plt.title("NDVI Médian 2023")
    plt.show()

Hi,

This forum is mainly for “openEO Platform” (openeo.cloud).

You are using CDSE, which is related, but different. I would recommend to raise your question on the dedicated CDSE forum at https://forum.dataspace.copernicus.eu/c/openeo

side-note: you mention significant differences. Can you make that more concrete, e.g. in numbers or by sharing screenshots?