1

I have an image composite which includes a day of year band. When I display the band using Map.addLayer and stretch by 100% in the Visualization Parameters I can see that the range of values is 151-281.

How can I get a list of all of the values within this range, i.e. a list of all the unique values of 'doy'? It should not be a long list as the composite image was derived from Sentinel 2 images restricted to a 5 month window. I just need to print the list. 'aoi' is a multi-polygon shapefile.

//Function to mask clouds using the Sentinel-2 QA band
function maskS2clouds(image) {
  var qa = image.select('QA60');

  // Bits 10 and 11 are clouds and cirrus, respectively.
  var cloudBitMask = 1 << 10;
  var cirrusBitMask = 1 << 11;

  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
      .and(qa.bitwiseAnd(cirrusBitMask).eq(0));

  return ee.Image(image.updateMask(mask)
  .divide(10000).copyProperties(qa)).set('system:time_start', qa.get('system:time_start'));
}

// Load Sentinel-2 TOA reflectance data.
  var S2 = ee.ImageCollection('COPERNICUS/S2')
                  .filterDate('2017-06-01', '2017-10-15')
                  .map(function(image){return image.clip(aoi)})//To clip all images to aoi
                  // Pre-filter to get less cloudy granules.
                  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
                  //Select required bands only
                  .select('B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B8A', 'QA60')
                  //Apply cloud mask
                  .map(maskS2clouds);

//Function to calculate redEdgeNDVI
var add_reNDVI = function(image){
  //Create band variables
  var redEdge = image.select('B5');
  var NIR = image.select('B8');
  var redEdgeNDVI = NIR.subtract(redEdge).divide(NIR.add(redEdge)).rename('reNDVI');
    return image.addBands(redEdgeNDVI);
};

//Map sentinel 2 collection to reNDVI
var S2_reNDVI = S2.map(add_reNDVI);

//Function to create DOY band
var addDate = function(image){
  var doy = image.date().getRelative('day', 'year');
  var doyBand = ee.Image.constant(doy).uint16().rename('doy');
  doyBand = doyBand.updateMask(image.select('reNDVI').mask());
    return image.addBands(doyBand);
};

//Map sentinel 2 collection to DOY function
var S2Date = S2_reNDVI.map(addDate);

// Make a "max redEdgeNDVI" pixel composite taking max reNDVI from all images 
var S2_max_reNDVI = S2Date.qualityMosaic('reNDVI');

//Add DOY layer
Map.addLayer(S2_max_reNDVI, {bands:['doy'], palette: ['ffeda0', 'feb24c', 'f03b20']}, 'DOY for Max reNDVI pixel composite');```
1

You are interested in actual pixel values, so you should use reduceRegions. You could use the reducer ee.Reducer.minMax() to get the min and max value. However, you want a list of all values present. For that you can use the reducer ee.Reducer.frequencyHistogram(). That returns a dictionary with the number of pixels per valid pixel input, which are the keys. So if you get those keys, you will have a list of valid pixels in your area of interest.

ReduceRegions returns a feature collection. So to get those keys, we need to map the output, make a list of all the keys and get only the distinct values:

// Get the list of values from the mosaic image
var freqHist = S2_max_reNDVI.select('doy')
        .reduceRegions({reducer: ee.Reducer.frequencyHistogram(), collection: table, scale: 20});
// rewrite the distionary
var values = ee.List(freqHist.map(function(feat){
  feat = ee.Feature(feat);
  var vals = ee.Dictionary(feat.get('histogram')).keys();
  return ee.Feature(null, {vals: vals});
}).aggregate_array('vals')).flatten().distinct();
print(values);

Note that I increased the scale a bit to overcome memory issues.

link code

  • This works when aoi is a single polygon. When I run it with a multi-polygon (11493 polygons but total area < single polygon) I get an error message. "Earth Engine memory capacity exceeded." I get the same message when I try to export using the same multi-polygon file. As the imageCollection is already clipped to the multi-polygon file at the start (.map(function(image){return image.clip(aoi)})), do the values change depending on the geometry file I use in the histogram or do they remain the same because the imageCollection was already clipped in the earlier process? – Adam G May 9 at 8:47
  • It should not take into account all masked values you created when clipping. Does it work when you change the argument 'geometry' in reduceRegion() to aoi.bounds(): code.earthengine.google.com/232fdd5d0ab717f293929bbb302a9bf0 ? – Kuik May 9 at 9:23
  • Unfortunately not. I get the error message "aoi.bounds is not a function". I also tried it in your script with my multi-polygon and got the same error message. Your script worked fine with your m-p. My m-p is a shapefile I uploaded as an asset and is described by GEE as a 'table'. The single polygon is also a shapefile and was uploaded and imported in the same way. – Adam G May 9 at 10:08
  • If you upload it as a 'table', it is a featureCollection, containing a multipolygon. You should first get the geometry from that featureCollection using ee.Feature(table.first()).geometry(), assuming that your featureCollection contains of only one feature. Otherwise, use table.union(). code.earthengine.google.com/aaca673edbcc7e4ef51e426ab74f91af . If it still runs out of computation, you could consider increasing the scale parameters or export the result as a table/featureCollection. – Kuik May 9 at 10:17
  • Your code works but when I add my m-p file and use ee.Feature(table.first()).geometry(); I get a list of 4 values and only 1 polygon is displayed. When I use var aoi = ee.Feature(table.union()).geometry(); I get “Image (Error) Element.geometry: Unable to use a collection in an algorithm that requires a feature or image. This may happen when trying to use a collection of collections where a collection of features is expected; use flatten, or map a function to convert inner collections to features. Use.... ”. I added my aoi: code.earthengine.google.com/c31af1ab4583618e2fabd540aeb4340c – Adam G May 9 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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