Google Earth Engine NDVI – How to Analyze Time Series for Multiple Polygons with Unique Date Ranges

google-earth-enginegoogle-earth-engine-javascript-apindvitime series

My question extends one asked recently and I am working with the code suggested there as answer:

This script takes a set of polygons and for each, averages NDVI over all images within the specific date range associated with it. Naturally the NDVI is reduced (averaged) to the polygon as well. It returns a feature for each polygon, with the averaged NDVI over the polygon and date range.

I have been trying to get all NDVI value within each date range, rather than averaging them. The result should be, for each polygon, a single mean-reduced (averaged for the polygon's pixels) NDVI value for each image within the range associated with the polygon.

I am stuck in the reduceRegion function. I have been trying to map it onto the NDVI images in order to add one averaged NDVI value to the feature for each image. Ideally the feature would have one NDVI value for each date. I think I can figure out myself then how to write the dates themselves to the file.

// var cohort = ee.FeatureCollection("users/miki98k/temp") // Unable to share the dataset unfortunately
var cohort = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([10, 0]).buffer(1000), {start_date: '2020-01-01', end_date_a: '2020-04-01'}),
  ee.Feature(ee.Geometry.Point([11, 0]).buffer(1000), {start_date: '2020-01-01', end_date_a: '2020-04-01'})
])
// in the real dataset, start_date and end_date_a vary between features

var l7 = ee.ImageCollection("LANDSAT/LE07/C02/T1_L2")
var results = cohort.map(calculateSeries)
print(results)


function calculateSeries(feature) {
  var sdate = ee.Date(feature.get('start_date'))
  var edate = ee.Date(feature.get('end_date_a'))
  var roi = feature.geometry()

  var filt_img = l7
    .filterDate(sdate, edate)
    .filterBounds(roi)
    .filterMetadata('CLOUD_COVER', 'less_than', 100)

  var ndvi_func = function(i) {
    var ndvi = i.normalizedDifference(['SR_B4', 'SR_B3']).rename('NDVI')
    return i.addBands(ndvi)
  }

  var ndvis = filt_img.map(ndvi_func)
    .select('NDVI') // Only NDVI band is needed

  // stuck here
  return ee.Feature(
    feature.geometry(), 
    ndvis.reduceRegion({ // Mean over the roi
      geometry: roi,
      reducer: ee.Reducer.mean(),
      scale: 30
    })
  )
}

Best Answer

Following version of your code can extract all individual NDVI values involved in mean calculation for each feature (last value is feature average). I used different date ranges for corroborating that code works as expected related to dates.

// var cohort = ee.FeatureCollection("users/miki98k/temp") // Unable to share the dataset unfortunately
var cohort = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([10, 0]).buffer(1000), {start_date: '2020-01-01', end_date_a: '2020-04-01'}),
  ee.Feature(ee.Geometry.Point([11, 0]).buffer(1000), {start_date: '2020-01-01', end_date_a: '2020-06-01'})
]);

Map.addLayer(cohort);
Map.centerObject(cohort);

// in the real dataset, start_date and end_date_a vary between features

var l7 = ee.ImageCollection("LANDSAT/LE07/C02/T1_L2")
  .filterMetadata('CLOUD_COVER', 'less_than', 100);
  
//print(l7.size());

var cohort_lst = cohort.toList(cohort.size());

print(cohort_lst);

function calculateSeries(feature) {

  var sdate = ee.Date(ee.Feature(feature).get('start_date'));
  var edate = ee.Date(ee.Feature(feature).get('end_date_a'));
  var roi = ee.Feature(feature).geometry();

  var filt_img = l7
    .filterDate(sdate, edate)
    .filterBounds(roi);

  var ndvi_func = function(i) {
    var ndvi = i.normalizedDifference(['SR_B4', 'SR_B3']).rename('NDVI');
    return i.addBands(ndvi);
  };

  var ndvis = filt_img.map(ndvi_func)
    .select('NDVI'); // Only NDVI band is needed

  var getNDVI = function(image) {

  var value_NDVI = ee.Image(image)
    .reduceRegion(ee.Reducer.mean(), ee.Feature(roi).geometry()) 
    .get('NDVI');

    return value_NDVI;

  };

  var NDVI_list = ndvis.toList(ndvis.size()).map(getNDVI);

  NDVI_list = NDVI_list.add(NDVI_list.reduce(ee.Reducer.mean()));

  return NDVI_list;

}

var ndviListMean = cohort_lst.map(calculateSeries);

print("ndviListMean", ndviListMean);

After running above code in GEE code editor, I got following result. Values into red rectangles are the average of above individual values (4 and 7 values; respectively). These list values can be easily exported to Google Drive by using a label for each feature (not included in this code).

enter image description here

Related Question