1

Using the Global Forecast System from NOAA, which provides relative humidity (RH) estimates several hours in to the future (up to 16 days to be exact). I create a binary of the RH, call it leaf wetness, where >=90% is 1, otherwise, 0.

I am trying to come up with a way to count which pixels are greater than or equal to 90% RH for different lengths of time. The count tracks continuous state of >=90%, so if a pixel drops below 90% (would be a 0 in a binary mask), the tracker changes back to 0 and starts at one next time >=90%. I made a small mock data set to kind of illustrate what I mean. The columns "1", "2", "3", are pixels in binary images of the image collection (and their matching pixels in the new images) and the images (layers) of the collection are for the hours of the forecast. The 1s & 0s between the hour and the "|||" is that pixel's value at that hour. The rolling counts are on the right side of the "|||". Those would each be represented in their own layers that keep counts based on continuous 1 in the binary collection. The starting collection has as many images as hours to be forecast and the value in that starting collection are binary. I want the new images, one for each hour as well, to be the counts of continuity.

Time  1 2 3 ||| 1 2 3 
00:00 1 0 1 ||| 1 0 1 = a new layer added to collection to represent the 1-hour counts  
01:00 1 1 1 ||| 2 1 2 = a new layer added to collection to represent the 2-hour counts
02:00 1 1 0 ||| 3 2 0 = a new layer added to collection to represent the 3-hour counts
03:00 1 1 1 ||| 4 3 1 = a new layer added to collection to represent the 4-hour counts
04:00 1 0 0 ||| 5 0 0 = a new layer added to collection to represent the 5-hour counts
05:00 1 1 1 ||| 6 1 1 = a new layer added to collection to represent the 6-hour counts

In the example data above, the "pixel" (column) labelled "1" is always one, so the count layers keep accumulating by one. But in the "pixel" 2, if I check at 3am, it has had 3 continuous hours of 1, however by 4am, due to the pixel being 0 at that time, the layer for counting continuous positive values (which represents wetness for my needs) would have a 0 and would start at 1 next time there is another 1 for that pixel in the binary, in this case, 5am.

I have a script that can give me a cumulative count for a specified length of time. I don't know how to make it return to 0 and restart when a 1 occurs in a future hour. I hope that can be the basis for what I need.

var cou_h = ['Henderson'];
var st_c = ['North Carolina'];
var cobo4 = ee.FeatureCollection("TIGER/2016/Counties").filter(ee.Filter.inList('NAME', cou_h)).geometry();
var st_nc = ee.FeatureCollection("TIGER/2018/States").filter(ee.Filter.inList('NAME', st_c));
var hend = cobo4.intersection(st_nc);

// If you want to specify a particular forecast from the series, 
// use 'forecast_hours' or 'forecast_time'. For example:
var colltrh = ee.ImageCollection("NOAA/GFS0P25")
                 .select(['temperature_2m_above_ground','relative_humidity_2m_above_ground'])       
                 .filter(ee.Filter.eq('creation_time',ee.Date(0).update(2022,6,1,0,0,0).millis()))
                 //.filter(ee.Filter.eq('forecast_hours', 1))
                 .map(function(im){return im.clip(hend)});

// RH threshold function
var pos_leaf_wetness = function(im){
  var plw = im.select('relative_humidity_2m_above_ground').gt(89).rename('leaf_wetness90');
  return im.addBands(plw);
}

// Add layers to collection
var colltrh = colltrh.map(pos_leaf_wetness);

// Just lookin'...
print(colltrh,'thr');
Map.addLayer(colltrh,null,'tr');


var hours_m = 120
var timeStep = 1

// A time series to keep rolling counts of the positive thresholded values?
var timeSeries = ee.ImageCollection(
  ee.List.sequence(0, hours_m, timeStep)
    .map(function (hourly) {
      var start = ee.Date(0).update(2022,6,1,0,0,0).advance(hourly, 'hours')
      var end = start.advance(timeStep, 'hours')
      var counting = colltrh.select('leaf_wetness90')
        .filterDate(start, end)
        .sum();
      return counting
        .set('system:time_start', start.millis())
        .set('empty', counting.bandNames().size().eq(0))
    })
  );

// Lookin around some more.
print(timeSeries,'timeseries');
Map.addLayer(timeSeries,null,'ts');

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

2
  • 1
    This post reminds of analyzing runs in arrays - you might be able to modify the procedure described in this blog post for your purpose: medium.com/google-earth/runs-with-arrays-400de937510a Nov 16, 2022 at 16:16
  • @JustinBraaten thanks for the link. Daniel's answer works. I've hit a hiccup in further processing as I'm trying to merge the result from his code with the starting image collection and there are duplicate images I'm trying to eliminate. I'm still trying to figure this out. May post another question soon whether or not I can figure this out, but I think I'm close to what I want now.
    – John Polo
    Nov 16, 2022 at 16:37

1 Answer 1

1

You can iterate() your collection, inspect the last value to do the accumulation.

var date = '2022-06-01'
var humidityThreshold = 90 
var hours = 30

var collection = ee.ImageCollection("NOAA/GFS0P25")
  .select('relative_humidity_2m_above_ground')
  .filter(ee.Filter.gte('forecast_time', ee.Date(date).millis()))
  .filter(ee.Filter.lte('forecast_time', ee.Date(date).advance(hours, 'hours').millis()))
  .filter(ee.Filter.eq('forecast_hours', 1))
  .sort('forecast_time')
  .map(function (image) {
    return image
      .gte(humidityThreshold)
      .copyProperties(image, image.propertyNames())
  })
  
var counts = ee.ImageCollection(collection.iterate(
  function (image, acc) {
    image = ee.Image(image)
    acc = ee.ImageCollection(acc)
    var last = ee.Image(acc.get('last'))
    var count = last.add(1)
      .where(image.not(), ee.Image(0))
      .rename('count')
      .byte()
    return acc.merge(ee.ImageCollection([count]))
      .set('last', count)
  },
  ee.ImageCollection([])
    .set('last', ee.Image(0))
))

https://code.earthengine.google.com/77c0591d0f1762bad030877bb7a905e1

5
  • Thanks for this. Do you know why it returns the accumulations in runs up to 6? I can't see any of the code that has "6" as an argument. I was hoping I could get a run up to 120. I know I didn't say that in the question, but I wasn't expecting a limit.
    – John Polo
    Nov 16, 2022 at 20:26
  • Since you're looking at a 30h window and there is data every 6h (see the print out of forecast_time), you get 30/6 = 5 data points. If you want 120, you need to set hours to 120 * 6 = 720. Nov 17, 2022 at 6:42
  • See the max layer: code.earthengine.google.com/ef062135756ae6c4932d4d212ba9936e Nov 17, 2022 at 6:47
  • I think it was a matter of using creation_time instead of forecast_time. The forecast_time is broken up into those 6h sections. creation_time is not. Sort of like this: code.earthengine.google.com/e217dc94071c39c87b1a21104186b60a
    – John Polo
    Nov 18, 2022 at 13:49
  • Excellent! Then I misunderstood those props. Nov 18, 2022 at 15:51

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.