0

I am using code from Nina Kattler to calculate the mean of max daily NDVI in particular districts in Mali over the growing season in 2019. The original code gives the mean NDVI over the growing season, and I have been struggling to figure out how to change it to calculate the mean of the maximum NDVIs from each day that MODIS takes an image.

To be more explicit, for each district I would like mean_max_NDVI = [sum(max_NDVIs from all images)/(number of images taken)]. My initial approach was to try and add the max method to the imageCollection, but this returns just an image which is not what I believe I need.

var gadm_level = '4';
var area = ee.FeatureCollection("users/geerootfold/gadm36_MLI_4");
print('area', area);
Map.centerObject(area, 6);
Map.addLayer(area, {}, 'area');


// Set the date
var year = '2019'
var startdate = year+'-05-01';
var enddate = year+'-09-30';

print(startdate)

// MODIS NDVI scale is 0.0001, so we need to correct the scale
var NDVI = function(image) {
  return image.expression('float(b("NDVI")/10000)')
};

var modis = ee.ImageCollection('MODIS/006/MOD13Q1')
  .filterBounds(area)
  .filterDate(startdate,enddate);

var modisNDVI = modis.map(NDVI)
print('MODIS after .map', modisNDVI) 

// This is an ImageCollection, containing a list of images that are within the startdata/enddate
// We want to make this a single image: a mosaic

//var mosaic = modisNDVI.mosaic()
 // .clip(area);
//print('mosaic', mosaic);

var mosaic = modisNDVI.mosaic()
  .clip(area);
print('mosaic', mosaic);

// Another alternative is to get the mean of the images from startdate/enddate:
//var mean = modisNDVI.mean()
//  .clip(area);
//print('mean', mean);

// Set visualisation parameters
var colour = {
 min: 0.0,
 max: 1.0,
 palette: [
 'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
 '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
 '012E01', '011D01', '011301'
 ],
};

Map.addLayer(mosaic, colour, 'spatial mosaic');

// Add reducer output to the Features in the collection.
var reducers = ee.Reducer.mean().combine({
  reducer2: ee.Reducer.stdDev(),
  sharedInputs: true
});


var MeansOfFeatures = mosaic.reduceRegions({
  collection: area,
  reducer: reducers,
  scale: 250,
});


print(ee.Feature(MeansOfFeatures.first()))

// Remove the content of .geo column (the geometry)
// https://gis.stackexchange.com/a/328257
var featureCollection = MeansOfFeatures.map(function(feat){
  var nullfeat = ee.Feature(null)
  return nullfeat.copyProperties(feat)
})

var size=featureCollection.size()
var toList = featureCollection.toList(size)
print(toList, 'toList')

// length + list
var idList = ee.List.sequence(0, size.subtract(1))


var newList = idList.map(function(x){
  
  var index = idList.get(x)
  var feat = toList.get(x)
  
  return ee.Feature(feat).set('year', 2019).copyProperties(feat)
})

print(newList, 'newList')

featureCollection = ee.FeatureCollection(newList)

// Export the featureCollection (table) as a CSV file to your Google Drive account
Export.table.toDrive({
  collection: featureCollection,
  description: 'modis250_ndvi_TZA_adm'+gadm_level+'_'+year,
  folder: 'GEE NDVI Output',
  fileFormat: 'CSV'
});

1 Answer 1

0

There are many ways to do this, below is one approach. It calculates the value for each district, using a combination of reduceRegion() with a ee.Reducer.max() reducer and aggregate_mean().

// You haven't shared users/geerootfold/gadm36_MLI_4, so I'm using some areas from GAUL
var districts = ee.FeatureCollection('FAO/GAUL_SIMPLIFIED_500m/2015/level1')
  .filter(ee.Filter.eq('ADM0_NAME', 'Mali'))
var year = 2019
var meanOfMaxNdvi = districts.map(toMeanOfMaxNdvi)

print(meanOfMaxNdvi)

Export.table.toDrive({
  collection: meanOfMaxNdvi, 
  description: 'meanOfMaxNdvi',
  // Explicitly specify your columns to exclude .geo
  selectors: ['ADM1_NAME', 'YEAR', 'MEAN_OF_MAX_NDVI'] 
})
  
function toMeanOfMaxNdvi(district) {
  var geometry = district.geometry()
  var ndvi = ee.ImageCollection('MODIS/006/MOD13Q1')
    .filterDate(ee.Date.fromYMD(year, 1, 1).getRange('year'))
    .select('NDVI')
  var maxNdvi = ee.FeatureCollection(
    ndvi.map(toMaxNdvi)
  )
  var meanOfMaxNdvi = maxNdvi
    .aggregate_mean('NDVI')
  return district
    .set('MEAN_OF_MAX_NDVI', meanOfMaxNdvi)
    .set('YEAR', year)
    
  function toMaxNdvi(image) {
    return ee.Feature(geometry, image
      .divide(10000)
      .float()
      .reduceRegion({
        reducer: ee.Reducer.max(), 
        geometry: geometry, 
        scale: 111319, // No need to do this at a scale smaller than the input imagery
        maxPixels: 1e13
      })
    )
  }    
}

https://code.earthengine.google.com/f71fe8fc314b881bc46fe175129969b9

6
  • Thank you for this response! It has helped me a lot. I am running into the issue of not all of the communes being measured because I can't figure out how to specify that I want the map function to map over communes from the NAME_4 column of my shapefile. Do you know how I can fix this? I have included the shapefile and my code below. code.earthengine.google.com/?asset=users/geerootfold/… code.earthengine.google.com/25a2fc5eb18e610542dcac251556c1dd
    – Matt
    Commented Aug 31, 2022 at 17:24
  • Also, I was wondering why the following code does not produce the answer I want? I tested it out on one particular district and got an NDVI different by over .2 the NDVI I get when I run your code for the same district and time period. The code I am referencing is at the bottom and not commented out. code.earthengine.google.com/477ccd8c19d125ec28d74753ac004f96. Thanks!
    – Matt
    Commented Aug 31, 2022 at 20:06
  • "how to specify that I want the map function to map over communes from the NAME_4 column of my shapefile". I don't quite follow what you mean there. You want to extract a subset of the districts based on name? Commented Sep 1, 2022 at 5:49
  • "NDVI different by over .2". The two scripts are doing two different things. The one I provided calculates the max for every image, then calculate the mean across the images. Your script calculates the max for each pixel, then takes the mean across the pixels. That gives different results. Commented Sep 1, 2022 at 5:53
  • Whichever way you go, you should try to avoid using reduceRegion() with a ee.Reducer.toList() reducer. In this case, you can just use ee.Reducer.mean(). Commented Sep 1, 2022 at 5:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.