google-earth-engine – How to Filter ImageCollection by Band Value in Google Earth Engine

google-earth-enginegoogle-earth-engine-javascript-api

I've filtered an imagecollcetion down to all images in an area between a specified range of dates annually (imagecollection A) and then filtered it further by narrowing it down to the most cloud-free images within that range.

//-------------------- Generate AOI
var pointBuffer = geometry.buffer({'distance': 6000});

//-------------------- Set Parameters
// Range of years
var startyear = 2022;
var endyear = 2023;

// Rage of months
var startMonth = 5;
var endMonth = 7;

// Minimum yellow threshold - for rapseed
var min_y_tresh = 0.035

//-------------------- Load Sentinel-2 TOA reflectance data.
var imagecollection_A = ee.ImageCollection('COPERNICUS/S2')
    .filterDate(startyear+'-01-01', endyear+1+'-01-01')
    .filterBounds(geometry)
    .filter(ee.Filter.calendarRange(startMonth, endMonth, 'month'));
print(imagecollection_A,"imagecollection_A Check")

//-------------------- Generate and map NDYI and NDYI Mask to image collection
// Function generates NDYI
var addNDYI = function(image) {
  var NDYI = image.normalizedDifference(['B3','B2']).rename('NDYI');
  return image.addBands(NDYI);
};

// Function masks NDYI to specified treshold
var addNDYI_Mask = function(image) {
  var masked = image.select('NDYI').updateMask(image.select('NDYI').gte(min_y_tresh)).rename('NDYI_Masked'); 
  return image.addBands(masked);
};

var imagecollection_B = imagecollection_A.map(addNDYI);
var imagecollection_C = imagecollection_B.map(addNDYI_Mask);
print(imagecollection_C,"Collection Check");

//-------------------- Get Cloud Free Image Per Year
var years = ee.List.sequence(startyear, endyear).getInfo();

var imagecollection_D = years.map(function(year){
  // Get least cloudy annual single scene.
  var annual = imagecollection_C.filterDate(year+'-01-01', (year+1)+'-01-01')
                  .sort('CLOUD_COVERAGE_ASSESSMENT');
  var annual_img = ee.Image(annual.first());
  return annual_img;
});
imagecollection_D = ee.ImageCollection(imagecollection_D);
print(imagecollection_D,"annual_least_ccCheck")

What I would like to do is filter imagecollection A based on the pixel values of the NDYI band that has been added.

I know I can filter band values using .reduce(ee.Reducer.max()), but this results in a single band when I try to apply it.

I'm trying to generate an imagecollection that has been filtered down to a specific date range annually, then filtered by an NDYI thresholded pixel value, and finally to the least cloudy image.

I can get everything else but I just can't figure out the NDYI thresholded filter. Any help would be greatly appreciated!

Below is a link to the complete code:
https://code.earthengine.google.com/bb51958baa978cd2fc82130a3975b8fa

Best Answer

Here's how you could get the (spatial) maximum NDYI value for each image in your collection. The caveat is that we are not getting the maximum value for each of the 10980x10980 px pixels at the native scale (10 meters), most likely the user memory would run out and the computation would fail. Instead, we can take advantage of the image pyramid and request the computation at a coarser scale. If this is something acceptable for your analysis, then give it a try. Here I used a 5' resolution -- you can try higher resolutions and see it it works for you:

//-------------------- Get maximum NDYI value for each image:
function getMaxNDYI(image){
// Adds a property to each image specifiying the MAX value of NDYI over the WHOLE image
// (within a specified grid -- see scale, crs, crsTransform)
    var maxValue = image.select("NDYI").reduceRegion({
      geometry:image.get("system:footprint"), // this is a very large geometry,
      // so we better use a coarse grid -- certainly not the native S2 scale (10 or 20m)
      // unless you use a specific smaller region(s) other than the image's footprint.
      crs:"EPSG:4326",
      crsTransform:[5/60,0,0,0,5/60,0], // 5 arcmin resolution grid
      bestEffort:true, 
      reducer:ee.Reducer.max()
    }).get("NDYI");
    return image.set({
      maxNDYI: maxValue
    })
}    
var imagecollection_D = imagecollection_C.map(getMaxNDYI).sort("maxNDYI", false)
print(imagecollection_D,"Collection D Check");
print(imagecollection_D.aggregate_array("maxNDYI"))

The result prints the image collection sorted by highest maxNDYI.

Finally, if you want to keep only the images with the highest maxNDYI for each year, we can do something like this:

//-------------------- For each year, get the maximum value of maxNDYI:
var years=ee.List.sequence(startyear,endyear)
function getYearMaxNDYIImage(year){
  year=ee.Number(year)
  var s = ee.Date.fromYMD(year,1,1)
  var e = ee.Date.fromYMD(year.add(1),1,1)
  return imagecollection_D
  .filterDate(s,e)
  .sort("maxNDYI",false) // ascending=False -- so the highest is first
  .first()
}
var imagecollection_E = ee.ImageCollection(
  years.map(getYearMaxNDYIImage) // Get the yearly MaxNDYI image for each year. 
  )
print(imagecollection_E,"Collection E Check");     //Only 2 images
print(imagecollection_E.aggregate_array("maxNDYI"))  // 2 values -- the highest for 2022 and 2023

The complete script is here:

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