1

I am trying to calculate an ee.Image() object that contains the number of valid (= unmasked) pixels of a given Landsat ImageCollection. Originally I thought a simple ee.ImageColllection.count() would do the job, but as the function treats each image independently of the observation date, the areas where the WRS rows in the Landsat tiles overlap are counted twice. My intention on the other hand is to retrieve a date dependent information of valid pixels.

Given that I have tried the following:

var imgCol_L8_SR = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
          .filterBounds(ROI)
          .filter(ee.Filter.calendarRange(year_start,year_end,'year'))
          .filter(ee.Filter.calendarRange(month_start,month_end,'month'))
          .filter(ee.Filter.lt('CLOUD_COVER', cloud_cover))
          .map(maskL8SR);

// initial Image in list
var ini =ee.List([
  imgCol_L8_SR.select('B1').max()
    .remap([0], [0], 0, "B1")
    .rename('NOBS').int16()
]);

var fun_nobservations = function(date, previous) {
  // Cast
  date = ee.Date(date);
  previous = ee.List(previous);
  var filtered = imgCol_L8_SR.filterDate(date, date.advance(1,'day'));

  // count valid observations per pixel:
  var mask = ee.Image(filtered.count());

  // set valid obs. in overlap back to 1:
  mask = mask.where(mask.gt(1), 1);

  var sum = mask.add(previous.get(-1));
  return ee.Algorithms.If(filtered.size(), previous.add(sum), previous);
};

var start = ee.Date('1985-06-01');
var finish = ee.Date('2017-08-31');
var diff = finish.difference(start, 'day');
var range = ee.List.sequence(0, diff.subtract(1))
                   .map(function(day){return start.advance(day,'day')});

var nobs = ee.Image(ee.List(range.iterate(fun_nobservations, ini)).get(-1));

It counts the number of valid pixels dependent on the date and sums the single image layers iteratively in a list. The problem here is that given the study area size and the time-interval I run into a user memory exceeded error.

Any ideas on how this could be approached differently?

3
  • Iterating over the 11779 days between 1985 and 2017 is definitely not a good idea. What is your goal? Do you want to make e.g. yearly composites if you have sufficient images? And I do not understand what is wrong with the overlapping tiles being double counted?
    – Kuik
    Commented Jan 14, 2019 at 12:49
  • You are right, I could also iterate over the unique dates in my collection...how would I achieve that? Well, the problem is that I need an indicator of valid observations per date not in total.
    – kerneleon
    Commented Jan 14, 2019 at 13:38
  • You probably want to add a date band to the image collection: code.earthengine.google.com/4e202a29a38c6a6501171a32044829ae
    – Kuik
    Commented Jan 14, 2019 at 14:08

1 Answer 1

2

It's not clear what format you expect for the final answer: do you want an aggregate number of valid pixels for a region for each unique date (ie: a single time series) or do you want an image of the total number of valid observations at each pixel, ignoring overlap?

The first is just a mapped reduceRegion. The second you can get by adding a date band to each image (something like days since some epoch) and then reducing with countDistinct.

function maskL8sr(image) {
  // Bits 3 and 5 are cloud shadow and cloud, respectively.
  var cloudShadowBitMask = 1 << 3
  var cloudsBitMask = 1 << 5

  // Get the pixel QA band.
  var qa = image.select('pixel_qa')

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

  // Return the masked image, scaled to TOA reflectance, without the QA bands.
  return image.updateMask(mask).divide(10000)
      .select("B[0-9]*")
      .copyProperties(image, ["system:time_start"])
}

var EPOCH = ee.Date('1979-01-01')
var addDate = function(image) {
  var mask = image.mask().reduce(ee.Reducer.min())
  var days = image.date().difference(EPOCH, 'day')
  return ee.Image.constant(days).int()
      .clip(image.geometry())
      .updateMask(mask)
      .copyProperties(image, ["system:time_start"])
}

var year_start = 2015
var year_end = 2018
var month_start = 4
var month_end = 6

var imgCol = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
          .filterBounds(ROI)
          .filter(ee.Filter.calendarRange(year_start,year_end,'year'))
          .filter(ee.Filter.calendarRange(month_start,month_end,'month'))
          .map(maskL8sr)
          .map(addDate)

Map.addLayer(imgCol.reduce(ee.Reducer.countDistinct()), {min:0, max: 60})
1
  • The latter is what I wanted. A per-pixel information of valid observations neglecting WRS-row overlap. Thank you for your precise answer!
    – kerneleon
    Commented Jan 22, 2019 at 7:40

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.