1

I have an image collection with many images (>800) and I want to calculate monthly NDVI and NBR median indexes and export the resulting images in a loop. I had no problem grouping the images in the same month (in a same year) and calculating NDVI and NBR, but I'm having a really hard time using a loop to export the resulting images with names matching the time period of each one. The "description" and "fileNamePrefix" give some variant of: ee.String({ "type": "Invocation", "arguments": { "date": {.

As I could see, I guess I have to first transform the images in a list and only then pass it through a loop. But I couldn't figure how to populate an empty list with new images (the rationale behind being: for each group of images, I'd calculate the median and add the resulting image to a list).

Here is a "clean" script (the one I'm working in is a mess of chunks of code trying to do what I want...): https://code.earthengine.google.com/?scriptPath=users%2Fthiagorbm%2FCerrado%3AProjetoEEJBB.sentinelV2

I'd like the image names to contain the year and month used to obtain the median values.

I'm new to Earth Engine and don't work very often with it. I'm not even sure I'm taking the best "path" to do what I want.

var vegSHP = ee.Geometry.Polygon(
        [[[-47.93318633174054, -15.923525740817693],
          [-47.84117583369367, -15.948285390673487],
          [-47.79070738886945, -15.876308885443688],
          [-47.83565782221213, -15.84506420705287],
          [-47.90844224604025, -15.867191599980972]]]);

var addNDVI = function(image) {
  var ndvi = ee.Image(0).expression(
    '2.5 * ((NIR - RED) / (NIR + RED))*100', {
      'NIR': image.select('B8'),
      'RED': image.select('B4'),
      
    });//.toUint16();
  return image.addBands(ndvi.rename('ndvi'));
};

var image  = ee.ImageCollection('COPERNICUS/S2')
.filterBounds(vegSHP);

var bandasFiltro= image.select('B.*')
.map(addNDVI);
print(bandasFiltro, "bandasFiltro");

var dates = bandasFiltro
    .map(function(bandasFiltro) {
      return ee.Feature(null, {'date': bandasFiltro
      .date()
      .format('YYYY-MM')});
    })
    .distinct('date')
    .aggregate_array('date');

var bandasFiltroData= bandasFiltro
.filterDate(dates.get(1),dates.get(2)) // pegando o primeiro valor da data e o segundo
.median()
.clip(vegSHP)
.set("periodo", ee.String(dates.get(0)).cat("_").cat(dates.get(2)))

for (var i = 0; i < 4-1; i++){ 
  var inicio= dates.get(i);
  var fim= dates.get(i+1);

  var imgMediana= ee.Image(bandasFiltro
  .filterDate(inicio,fim)
  .median()
  .clip(vegSHP))
  .set("periodo", ee.String(inicio).cat("_").cat(fim));
  //print(imgMediana);

  var id= ee.String("sentinel_2_")
  .cat(inicio).replace("\\-", "")
  .cat("_")
  .cat(fim).replace("\\-", "");
  //print(id)

  Export.image.toDrive({
  image:imgMediana,
  description: id,
  folder: 'ProjetoJBB',
  fileNamePrefix: id,
  region: vegSHP,
  crs: 'EPSG:4326',
  scale: 10,
  maxPixels: 10000000000});

}

1
  • Hi Thiago, the repo doesn't exist anymore. You are mixing server-side and client-side functions. That won't work. Please take a look at this article. Loop is not recommended for GEE, use map instead. You need to rewrite your code again with server-side functions. Do it and if you have more issues come back here again
    – aldo_tapia
    Commented May 9, 2022 at 1:49

1 Answer 1

1

Just like @aldo_tapia commented, you're mixing up server- and client-side objects, read through that link. It's not always obvious which arguments needs to be server side and which should be client side. For instance, the description in toDrive() must be client side. Your server-side string does not work there.

I would approach this by iterating over each month, creating a collection of monthly composites. Then create a client-side list of composite dates, using evaluate(), do client side iteration over these dates, pick up the corresponding composite by filtering the collection, and export it. You almost certainly want to mask clouds too, so I included that step too.

var aoi = ee.Geometry.Polygon([[
  [-47.93318633174054, -15.923525740817693],
  [-47.84117583369367, -15.948285390673487],
  [-47.79070738886945, -15.876308885443688],
  [-47.83565782221213, -15.84506420705287],
  [-47.90844224604025, -15.867191599980972]
]])
Map.centerObject(aoi)

var startDate = '2020-05-01'
var endDate = '2021-02-01' // Exclusive
var cloudThreshold = 30

var filter = ee.Filter.and(
  ee.Filter.bounds(aoi),
  ee.Filter.date(startDate, endDate)
)

var collection = ee.ImageCollection(
    ee.Join.saveFirst('cloudProbability').apply({
        primary: ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED').filter(filter),
        secondary: ee.ImageCollection('COPERNICUS/S2_CLOUD_PROBABILITY').filter(filter),
        condition: ee.Filter.equals({leftField: 'system:index', rightField: 'system:index'})
    })
).map(function (image) {
  var cloudFree = ee.Image(image.get('cloudProbability')).lt(cloudThreshold)
  var renamed = image.select(
    ['B2', 'B3', 'B4', 'B8', 'B11', 'B12'],
    ['blue', 'green', 'red', 'nir', 'swir1', 'swir2']
  ) 
  return renamed
    .addBands(renamed.normalizedDifference(['nir', 'red']).multiply(10000).rename('ndvi'))
    .addBands(renamed.normalizedDifference(['nir', 'swir2']).multiply(10000).rename('nbr'))
    .updateMask(cloudFree)
    .int16()
})
var numberOfMonths = ee.Date(endDate)
  .difference(startDate, 'months')
  .subtract(1)
  .round()
var monthOffsets = ee.List.sequence(0, numberOfMonths)
var monthlyComposites = ee.ImageCollection(
  monthOffsets.map(function (monthOffset) {
    var date = ee.Date(startDate).advance(monthOffset, 'months')
    return collection
      .filterDate(date, date.advance(1, 'month'))
      .median()
      .set('date', date.format('YYYY-MM'))
  })
)
monthlyComposites
  .aggregate_array('date')
  .evaluate(function (dates) {
    dates.map(function (date) {
      var composite = monthlyComposites
        .filter(ee.Filter.eq('date', date))
        .first()
        .int16()
      var name = 'composite-' + date
      Map.addLayer(composite, {bands: 'ndvi', min: -10000, max: 10000, palette: '#112040, #1C67A0, #6DB6B3, #FFFCCC, #ABAC21, #177228, #172313'}, 'NDVI ' + date)
      Export.image.toDrive({
        image: composite,
        description: name,
        folder: 'ProjetoJBB',
        fileNamePrefix: name,
        region: aoi,
        scale: 10,
        maxPixels: 1e13
      })
    })
  })

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

3
  • Thanks, Daniel! It did exaclty what I want, including the filtering of cloudy images. I'm trying to understand your script, to be able to adapt and improve working with Earth Engine. The majority of it I understood quite well, but I'm not sure what you did from "monthlyComposites.aggregate_array('date')" to the "composite" object. Would you mind explaining it to me? Commented May 10, 2022 at 16:07
  • monthlyComposites.aggregate_array('date') get a server-side list withthe date property of every image in the collection. The YYYY-MM strings. evaluate() is called on that list. It turns it into a client-side object, which get passed to the callback function. Commented May 10, 2022 at 18:48
  • dates.map() is now a client-side operation. The callback is invoked with each date in that list. It will be a regular client-side string. filter(ee.Filter.eq('date', date)) filter down the composites to a single one, first() picks up that one composite. int16() ensures all bands have the same data type for the export. Then, that composite is finally exported. Commented May 10, 2022 at 18:52

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.