0

Following GEE code determines relative humidity (RH) in a point of land surface by using considerations exposed in this question and 'NASA/GLDAS/V20/NOAH/G025/T3H' products. Some individual values for pressure, temperature and specific humidity, also obtained from 'NASA/GLDAS/V20/NOAH/G025/T3H' products, were used for calculating relative humidity in a spreadsheet, with Clausius-Clapeyron formula, and results were identical to obtained with GEE (link to script).

var startDate = ee.Date('1981-01-01');
var endDate = ee.Date('1981-01-03');

var dataset = ee.ImageCollection('NASA/GLDAS/V20/NOAH/G025/T3H')
  .filterDate(startDate, endDate);

var imageScale = ee.Image(dataset
  .first())
  .projection().nominalScale();

print('Scale:', imageScale);

var count = dataset.size();

print("count original", count);

var days = endDate.difference(startDate, 'days');
var daysStep = 1;

var timeSeries = ee.ImageCollection(
  ee.List.sequence(0, days.subtract(1), daysStep)
    .map(function (offsetDays) {
      var start = startDate.advance(offsetDays, 'days');
      var end = start.advance(daysStep, 'days');
      var composite = dataset  // former lst
        .filterDate(start, end)
        .min(); // You need to decide how to combine the images
      return composite
        .reproject('EPSG:4326', null, 27829.87269831839)
        .set('system:time_start', start.millis())
        .set('empty', composite.bandNames().size().eq(0));
    })
  ).filterMetadata('empty', 'equals', 0);

print("count composite", timeSeries.size());

print("New Scale", timeSeries.first().projection().nominalScale());

var p = ee.Geometry.Point(-70.2166985,-38.5275513); //p18

var getDateHumidity = function(image) {

  // Reducing region and getting value
  var value_hum = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Qair_f_inst');

  var value_pres = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Psurf_f_inst');
    
  var value_temp = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Tair_f_inst');
  
  var f = ee.Number(0.00263)
    .multiply(ee.Number(value_hum))
    .multiply(ee.Number(value_pres));
  
  var g = ee.Number(17.67)
    .multiply(ee.Number(value_temp)
    .subtract(ee.Number(273.16))
    .divide(ee.Number(value_temp).subtract(ee.Number(29.65))))
    .exp();
  
  var h = f.divide(g);
    
  var time = ee.Image(image).get('system:time_start');
  
  // Return the time (in milliseconds since Jan 1, 1970) as a Date
  var humidity_list = ee.Date(time)
    .format()
    .slice(0,10)
    .split('-')
    .add(h);
  
  return humidity_list;
};

var humidity_list1 = dataset.toList(count).map(getDateHumidity);

var myFeatures1 = ee.FeatureCollection(humidity_list1.map(function(el){
  el = ee.List(el); // cast every element of the list
  var geom = p;
  return ee.Feature(geom, {
    'year':ee.Number(el.get(0)), 
    'month':ee.Number(el.get(1)),
    'day':ee.Number(el.get(2)),
    'value':ee.Number(el.get(3))
  });
}));


//print(myFeatures1);

var humidity_list2 = timeSeries.toList(count).map(getDateHumidity);

//print(humidity_list);

var myFeatures2 = ee.FeatureCollection(humidity_list2.map(function(el){
  el = ee.List(el); // cast every element of the list
  var geom = p;
  return ee.Feature(geom, {
    'year':ee.Number(el.get(0)), 
    'month':ee.Number(el.get(1)),
    'day':ee.Number(el.get(2)),
    'value':ee.Number(el.get(3))
  });
}));

//Map.addLayer(myFeatures); // see the result
//Map.centerObject(myFeatures, 8);

//print(myFeatures2);


// Export features, specifying corresponding names.
Export.table.toDrive({
  collection: myFeatures1,
  folder: "GEE_Folder", //my export folder
  description: 'daily_humidity',  //file name
  fileFormat: "CSV"});

// Export features, specifying corresponding names.
Export.table.toDrive({
  collection: myFeatures2,
  folder: "GEE_Folder", //my export folder
  description: 'daily_humidity',  //file name
  fileFormat: "CSV"});

Above script determines RH for an Image Collection with 8 images per day (each 3 hours) and RH for a composite of these eight daily images. Obtained values are exported to Google Drive and, in above example with used dates, were printed 16 values in first case and only 2 with composite function. Results are as follows:

16 values

1,1,1981,0.541049210101378
1,1,1981,0.612131423086637
1,1,1981,0.712494530538605
1,1,1981,0.732241931627969
1,1,1981,0.675578926045453
1,1,1981,0.582798200402885
1,1,1981,0.451271023830914
1,1,1981,0.400543900671863
2,1,1981,0.430912988431544
2,1,1981,0.513416664811639
2,1,1981,0.646455860743016
2,1,1981,0.727504186156708
2,1,1981,0.753175972018466
2,1,1981,0.681819169670857
2,1,1981,0.511359249899844
2,1,1981,0.428261514810568

2 values

1,1,1981,0.709153505818986
2,1,1981,0.537754061477617

As it can be observed in the script, I used a 'min' method in composite function for selecting RH values. However, above obtained values are very different from following two expected values:

1,1,1981,0.400543900671863
2,1,1981,0.428261514810568

I searched for the reason of this unexpected result and I found out that composite images are produced in GEE with the default projection; which is WGS84 with 1-degree scale. This is very different from original nominal scale of 'NASA/GLDAS/V20/NOAH/G025/T3H': 27829.87269831839 meters (0.25 arc degrees). So, I had to use following reproject method (.reproject('EPSG:4326', null, 27829.87269831839)) in composite function for finally run my script and to get that nominal scale.

On the other hand, only for curiosity, I changed 'min' method for 'max' and I also got following weird results:

1,1,1981,0.46162372954395
2,1,1981,0.640986494579716

So, is it necessary to use another (or other) complementary method with 'reproject', in a composite function, for obtaining expected result of true minimum values and preserve original resolution of 0.25 arc degrees?

Editing Note:

@Nicholas Clinton pointed out in his answer that the affirmation of "composite images are produced in GEE with the default projection" is not entirely right. It was simply found here. However, without following instruction in composite function:

.reproject('EPSG:4326', null, 27829.87269831839)

when I tried to print Feature Collection obtained with Image Collection in composite function, without any "reprojection", it was displayed the error message of following image where it pointed out: "The WGS84 projection is invalid for aggregations. Specify a scale or crs & crs_transform". Similar message was obtained when I tried to run the task. On the other hand, it can be also observed that scale of aggregated Image Collection is 111319.49079327357 m (equivalent to 1 arc degree); as mentioned in the previous link.

enter image description here

On the other hand, after reading link in Nicholas Clinton's answer, I adapted code for my ImageCollection:

var p = ee.Geometry.Point(-70.2166985,-38.5275513); //p18

var image = ee.ImageCollection('NASA/GLDAS/V20/NOAH/G025/T3H')
  .first()
  .select("Qair_f_inst");

print(image);

var printAtScale = function(scale) {
  print('Pixel value at ' + scale + ' meters scale',
    image.reduceRegion({
      reducer: ee.Reducer.first(),
      geometry: p,
      // The scale determines the pyramid level from which to pull the input
      scale: scale
  }).get("Qair_f_inst"));
};

printAtScale(100); // 8883
printAtScale(1000); // 9215
printAtScale(10000); // 8775
printAtScale(100000); // 8300
printAtScale(1000000); // 8300

and result evidences a scale issue:

enter image description here

The problem is increasing by errors propagating because relative humidity is the combination of other bands and constants in the Clausius-Clapeyron equation. So, it seems that I need one way for retrieving former scale in composite series.

1

This is not entirely right: "composite images are produced in GEE with the default projection." Although it might seem that way, the composite is not "produced" until you ask for it, by exporting for example. See this doc for more information. At that time, the inputs are requested in the output projection and scale, which you specify in the Export call. There is no need for reproject. It's better to think of those composites as being labeled with the default projection, because Earth Engine doesn't know what you really want yet.

0
0

Finally, I found a solution by using ee.List objects. First, I created a list with only relative humidity values iterating on original list with a function. Afterward, I sliced that list in groups of 8 elements where min values were determined. These values were stored in a new list and they were as expected.

var startDate = ee.Date('1981-01-01');
var endDate = ee.Date('1981-04-01');

var p = ee.Geometry.Point(-70.2166985,-38.5275513); //p18

var dataset = ee.ImageCollection('NASA/GLDAS/V20/NOAH/G025/T3H')
  .filterDate(startDate, endDate);

var list_dataset = dataset.toList(dataset.size());

//print(list_dataset);

var getRelativeHumidity = function(image) {

  // Reducing region and getting value
  var value_hum = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Qair_f_inst');

  var value_pres = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Psurf_f_inst');

  var value_temp = ee.Image(image)
    .reduceRegion(ee.Reducer.first(), p)
    .get('Tair_f_inst');

  var f = ee.Number(0.00263)
    .multiply(ee.Number(value_hum))
    .multiply(ee.Number(value_pres));

  var g = ee.Number(17.67)
    .multiply(ee.Number(value_temp)
    .subtract(ee.Number(273.16))
    .divide(ee.Number(value_temp).subtract(ee.Number(29.65))))
    .exp();

  var h = f.divide(g);

  var time = ee.Image(image).get('system:time_start');

  // Return the time (in milliseconds since Jan 1, 1970) as a Date
  var humidity_list = h; 

  return humidity_list;
};

var count = dataset.size();

var hum_list = dataset.toList(count).map(getRelativeHumidity);

//print("humidity list", hum_list);

var allDates = ee.List(dataset.aggregate_array('system:time_start'));

var allDatesSimple = allDates.map(function(date){
  return ee.Date(date).format().slice(0,10);
  }).distinct();

var len = hum_list.size();

var list = ee.List.sequence(0, len.subtract(1), 8);

var min_hum_list = list.map(function(ele){

  var start = ee.Number(ele).int(); 
  var end = ee.Number(ele).add(8).int(); 

  var new_list = ee.List([]);
  var element = ee.List(hum_list.slice(start, end)).reduce(ee.Reducer.min());

  new_list = new_list.add(element);

  return new_list;

}).flatten();

var paired = allDatesSimple.zip(min_hum_list);

//print (paired);

var myFeatures = ee.FeatureCollection(paired.map(function(el){
  el = ee.List(el); // cast every element of the list
  var geom = p;
  return ee.Feature(geom, {
    'date': ee.String(el.get(0)),
    'value':ee.Number(el.get(1))
  });
}));

//print(myFeatures);

// Export features, specifying corresponding names.
Export.table.toDrive(myFeatures,
"humedad", //my task
"GEE_Folder", //my export folder
"dayly_hum_min_p",  //file name
"CSV");

After running script, following values were printed for original list.

0: 0.5410492101013781
1: 0.6121314230866369
2: 0.7124945305386051
3: 0.7322419316279689
4: 0.6755789260454528
5: 0.5827982004028849
6: 0.451271023830914
7: 0.4005439006718627
8: 0.430912988431544
9: 0.5134166648116385
10: 0.6464558607430163
11: 0.7275041861567081
12: 0.7531759720184658
13: 0.6818191696708572
14: 0.511359249899844
15: 0.42826151481056796
16: 0.4330844926689814
17: 0.5064309937145617
18: 0.6474067363946048
19: 0.6849807488756209
20: 0.6194320104912739
21: 0.5297404218531643
22: 0.4120223030722458
23: 0.3556466153145915

Below values corresponding to min for groups of 8 elements. Twenty four values of this climatic element in original series were reduced to only three values as expected.

0: 0.4005439006718627
1: 0.42826151481056796
2: 0.3556466153145915

Now, time for producing these values in relatively long series it was reduced drastically.

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.