Google Earth Engine – Fixing ‘List.add: Parameter Element Required’ Error While Calculating NBR

google-earth-enginespatio-temporal-data

I'm trying to calculate NBR through Google Earth Engine, for a series of fires associated with some polygons. As such, the polygons have the information of the associated event.

For each, I calculated the dates for the imagery collection (21 days before and 21 days after) and added them to the features.

However, when I try to calculate the NBR I get the error "List (Error) List.add: Parameter 'element' is required."

enter image description here

Can someone tell me where the error may be or possible solutions?

Script:
https://code.earthengine.google.com/a02106e1c22dba59a774a8fc8ad10516
Files:
https://code.earthengine.google.com/?asset=projects/ee-llopes/assets/fires_GEEex
https://code.earthengine.google.com/?asset=projects/ee-llopes/assets/Extent_ESR

// // *******************************************************************************************
// //                             POLYGONS TO BE USED

var POLYGONS = ee.FeatureCollection('projects/ee-llopes/assets/fires_GEEex');


// // *******************************************************************************************

// // // Set study area as map center.

Map.centerObject(POLYGONS);


// // *******************************************************************************************
//                              ------------- TIME RANGE -------------

// EPOCH to FORMAT YYYY-MM-DD

  // fire start 
function epoch2date_start(feature){
  var epoch = feature.get('FIRE_START');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  return feature.set('FIRE_START_DATE', date);
}

  // fire end
function epoch2date_end(feature){
  var epoch = feature.get('FIRE_END');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  return feature.set('FIRE_END_DATE', date);
}


// ------------- PREFIRE -------------

// PRE FIRE [21 days ]
function epoch2date_start21(feature){
  var epoch = feature.get('FIRE_START');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  var pre21fire_start = ee.Date(date).advance(-21,'day');
  return feature.set('pre21dateFIRE', pre21fire_start);
}

// PRE FIRE [1 day]
function epoch2date_start1(feature){
  var epoch = feature.get('FIRE_START');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  var pre1fire_start = ee.Date(date).advance(-1,'day');
  //return table.set('FIRE_START', date);
  return feature.set('pre1dateFIRE', pre1fire_start);
}


// ------------- POSTFIRE -------------



// Post FIRE [1 day]
function epoch2date_end1(feature){
  var epoch = feature.get('FIRE_END');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  var post1fire_start = ee.Date(date).advance(1,'day');
  return feature.set('post1dateFIRE', post1fire_start);
}

// post FIRE [21 days ]
function epoch2date_end21(feature){
  var epoch = feature.get('FIRE_END');
  var date = ee.Date(epoch).format('YYYY-MM-dd');
  var post21fire_start = ee.Date(date).advance(40,'day');
  return feature.set('post21dateFIRE', post21fire_start);
}

// Add new dates to features
var POLYGONS = POLYGONS.map(epoch2date_start)
                       .map(epoch2date_start21)
                       .map(epoch2date_start1)
                       .map(epoch2date_end)
                       .map(epoch2date_end1)
                       .map(epoch2date_end21);

print("POLYGONS_TIMERANGE", POLYGONS);


var fires_list = POLYGONS.toList(POLYGONS.size()); 
print("Fire list", fires_list);

// // *******************************************************************************************
//                              ------------- SATELLITE IMAGERY -------------
//                                               LANDSAT-8


//                                  ------------- MASKING -------------

// -- Create function to mask clouds on the QA_PIXEL band of Landsat 8 SR data.

function maskL8sr(image) {
  // Bits 3 and 4 are cloud shadow and cloud, respectively.
  var cloudsBitMask = (1 << 3);
  var cloudsShadowBitMask = (1 << 4);
  // Get the pixel QA band.
  var qa = image.select('QA_PIXEL');
  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudsShadowBitMask).eq(0)
                .and(qa.bitwiseAnd(cloudsBitMask).eq(0));
  return image.updateMask(mask);
}


// // //        ----------------------- IMAGERY COLLECTION -----------------------

var datestart = '2015-01-01';
var dateend = '2018-12-31';

// -- Obtain Landsat imaggery with filters & cloud masking
var ImCol =  ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
                  .map(maskL8sr)
                  .filterBounds(areaprojs)
                  .filterDate(datestart, dateend)
                  

// ------------- PREFIRE -------------

  
function calculatePREFIRE_NBR(feature) {
  var sdate = ee.Date(ee.Feature(feature).get('pre21dateFIRE'));
  var edate = ee.Date(ee.Feature(feature).get('pre1dateFIRE'));
  var roi = ee.Feature(feature).geometry();

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

// Apply platform-specific NBR = (NIR-SWIR2) / (NIR+SWIR2)
  var nbr_func = function(i) {
    var NBR = i.normalizedDifference(['SR_B5','SR_B4']).rename('partialNBR');
    return i.addBands(NBR);
  };

  var nbrs = filt_img.map(nbr_func)
    .select('partialNBR'); // Only band (partial) NBRis needed

  var getNBR = function(image) {

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

    return value_NBR;

  };

  var NBR_list = nbrs.toList(nbrs.size()).map(getNBR);

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

  return NBR_list
  //.set('system:time_start', ImCol.get('system:time_start'));

}

var preFIreNBRListMean = fires_list.map(calculatePREFIRE_NBR);

// ------------- POSTFIRE -------------


function calculatePOSTFIRE_NBR(feature) {
  var sdate = ee.Date(ee.Feature(feature).get('post1dateFIRE'));
  var edate = ee.Date(ee.Feature(feature).get('post21dateFIRE'));
  var roi = ee.Feature(feature).geometry();

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

// Apply platform-specific NBR = (NIR-SWIR2) / (NIR+SWIR2)
  var nbr_func = function(i) {
    var NBR = i.normalizedDifference(['SR_B5','SR_B4']).rename('partialNBR');
    return i.addBands(NBR);
  };

  var nbrs = filt_img.map(nbr_func)
    .select('partialNBR'); // Only band (partial) NBRis needed

  var getNBR = function(image) {

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

    return value_NBR;

  };  

  var NBR_list = nbrs.toList(nbrs.size()).map(getNBR);

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

  return NBR_list;

}

var postFIreNBRListMean = fires_list.map(calculatePOSTFIRE_NBR);



print("Pre-fire NBR List Mean", preFIreNBRListMean);
print("Post-fire NBR List Mean", postFIreNBRListMean);

Best Answer

Errors are produced because you have a lot of null values and the reducer cannot handle them. You can observe that if you comment following line in both functions:

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

Result is showed in following picture as follows.

enter image description here

This can be fixed with following lines in the complete code:

var preFIreNBRListMean = preFIreNBRListMean.map(function (ele) {
  
  var values = ee.List(ele).map(function (e) {
    
    return ee.Algorithms.If(e, e, -999);
    
  });
  
  var mean = ee.Algorithms.If(values.removeAll([-999]).reduce(ee.Reducer.mean()),
                              values.removeAll([-999]).reduce(ee.Reducer.mean()), 
                              -999);
  
  return [values, mean];
  
});

print(preFIreNBRListMean);

var mean = preFIreNBRListMean.map(function (ele) {
  
  return ee.List(ele).get(1);
  
});

var preFIreNBRListMean = preFIreNBRListMean.map(function (ele) {
  
  return ee.List(ele).get(0);
  
});

mean = mean.removeAll([-999]);

print(preFIreNBRListMean.add(mean.reduce(ee.Reducer.mean())));

After running complete code in GEE code editor, result looks as in following picture. Last value in bottom list (red rectangle) is the mean of all valid values. You can also observe that nulls were changed by -999 values. However, this is not a feature collection and you are also pretending to export that in your code so, it will be a future issue. As I don't know if this answer it will be useful (because there are a lot of nulls values and probably you need to fix it), I will consider list export after you comment if it is necessary.

enter image description here

Editing Note:

I think a lot of nulls could be because dates are not retrieving in right way for calculating NBR values or there are not images in POLYGONS area. You are going to understand that after you learn how to export a CSV file. It is easy to do that in this case. You only have to set the last value of each element (I guess it is calculated prefire mean for valid values) in original POLYGONS layer (I changed its name in this case to feats_list). Code snippet looks as follows.

var list = ee.List.sequence(0, POLYGONS.size().subtract(1));

var POLYGONS_list = POLYGONS.toList(POLYGONS.size());

var feats_list = list.map(function (ele) {
  
  return ee.Feature(ee.List(POLYGONS_list).get(ele)).set("mean_prefire", mean_prefire.get(ele));
  
});

print(feats_list);

// // *******************************************************************************************
//                                           EXPORT 

//Pre-fire NBR List Mean
Export.table.toDrive({
      collection: ee.FeatureCollection(feats_list),
      description: "preFIreNBRListMean",
      folder: "GEE_Folder",
      fileFormat: 'CSV',
      //selectors: ["OBJECTID","mean"]
});

After running the task for complete code, you can observe in obtained CSV file that there still is a null value but, all dates are as expected. Are there images in POLYGONS area? It can also be searched.

enter image description here

Related Question