2

New GEE user here. I am trying to calculate the mean value of the global human modification (GHM) layer in buffers around points with Google Earth Engine. I'm following this tutorial very closely - however, rather than getting a feature collection with the GHM value for each buffer, I get a feature collection with 0 elements. I suspect this may be due to differences between the GHM image collection and those used in the tutorial? Reproducible example below.

First, data:

// two random points
var pts = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([-90.3456, 43.5050]), {plot_id: 1}),
  ee.Feature(ee.Geometry.Point([-88.2457, 45.1320]), {plot_id: 2})]);

// global human modification layer - 1 band
var ghm = ee.ImageCollection('CSP/HM/GlobalHumanModification');

Next, two functions from the tutorial (first creates a buffer around points, the second calculates the stats for the buffer).

function bufferPoints(radius, bounds) {
  return function(pt) {
    pt = ee.Feature(pt);
    return bounds ? pt.buffer(radius).bounds() : pt.buffer(radius);
  };
}

function zonalStats(ic, fc, params) {
  // Initialize internal params dictionary.
  var _params = {
    reducer: ee.Reducer.mean(),
    scale: null,
    crs: null,
    bands: null,
    bandsRename: null,
    imgProps: null,
    imgPropsRename: null,
    datetimeName: 'datetime',
    datetimeFormat: 'YYYY-MM-dd HH:mm:ss'
  };

  // Replace initialized params with provided params.
  if (params) {
    for (var param in params) {
      _params[param] = params[param] || _params[param];
    }
  }

  // Set default parameters based on an image representative.
  var imgRep = ic.first();
  var nonSystemImgProps = ee.Feature(null)
    .copyProperties(imgRep).propertyNames();
  if (!_params.bands) _params.bands = imgRep.bandNames();
  if (!_params.bandsRename) _params.bandsRename = _params.bands;
  if (!_params.imgProps) _params.imgProps = nonSystemImgProps;
  if (!_params.imgPropsRename) _params.imgPropsRename = _params.imgProps;

  // Map the reduceRegions function over the image collection.
  var results = ic.map(function(img) {
    // Select bands (optionally rename), set a datetime & timestamp property.
    img = ee.Image(img.select(_params.bands, _params.bandsRename))
      .set(_params.datetimeName, img.date().format(_params.datetimeFormat))
      .set('timestamp', img.get('system:time_start'));

    // Define final image property dictionary to set in output features.
    var propsFrom = ee.List(_params.imgProps)
      .cat(ee.List([_params.datetimeName, 'timestamp']));
    var propsTo = ee.List(_params.imgPropsRename)
      .cat(ee.List([_params.datetimeName, 'timestamp']));
    var imgProps = img.toDictionary(propsFrom).rename(propsFrom, propsTo);

    // Subset points that intersect the given image.
    var fcSub = fc.filterBounds(img.geometry());

    // Reduce the image by regions.
    return img.reduceRegions({
      collection: fcSub,
      reducer: _params.reducer,
      scale: _params.scale,
      crs: _params.crs
    })
    // Add metadata to each feature.
    .map(function(f) {
      return f.set(imgProps);
    });
  }).flatten().filter(ee.Filter.notNull(_params.bandsRename));

  return results;
}

Next, some processing steps:

// buffer 5 km around points
var pts_buffer = pts.map(bufferPoints(5000, false));

// reduce to image, add time_start
var ghm2 = ghm.mean()
.set('system:time_start', ee.Date('2016-01-01').millis());

// put into image collection for zonal stats
var ghmCol = ee.ImageCollection([ghm2]);

Now, this is where my issue is. The zonalStats function returns an empty feature collection. What is happening here?

var params = {
  bands: [0],
  scale: 30,
  bandsRename: ['ghm']};

// something wrong here, getting a feature collection w/ 0 elements  
var pts_ghm = zonalStats(ghmCol, pts_buffer, params);
print(pts_ghm);

1 Answer 1

2

The code you're using does far more than you need for the stated problem, and the added complexity is what's causing your issue. Specifically, it's trying to be overly smart about the naming of results. That is, the reduceRegion is producing a result named 'mean', the name of the input band, but it's filter out any results that for which the property you specified 'ghm', is null (which is all of them).

Instead of using some complicated 60-line 'helper' function for what amounts to a single call, just call reduceRegions directly and be done.

var pts_buffer = pts.map(bufferPoints(5000, false));
var ghm2 = ghm.mean()
var pts_ghm = ghm2.reduceRegions({
  collection: pts_buffer,
  reducer: ee.Reducer.mean(),
  scale: 30
})

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.