1

I have written functions that processes all Landsat imagery and calculates NDVI. However, I have 59 GPS points, and I want a NDVI time series output per GPS point. After running my code, it seems that the resulting NDVI values are not per point, but a single value per image. I am guessing that some sort of bounding-box was automatically created and used for the calculation, instead of using the GPS points. Thus, I need to iterate my function over all 59 points, and save output into a table.

The GPS file is a ESRI points shape file.

What is the best way to do this?

Here is some of my code:

// import GPS locations from asset
GPS = GPS.geometry();

// Calculates the median NDVI and add value to image properties.
var meanNDVI = ndviLandsatCol.map(function(img) {
  var obs = img.reduceRegion({
    geometry: GPS,
    reducer: ee.Reducer.median(),
    scale: 30
  });
  return img.set('NDVI', obs.get('NDVI'));
});

The ndviLandsatCol variable is a pre-processed image collection with NDVI added as a band. I am still new to coding, and Google Earth Engine. Can someone advise on how to iterate this process over all my GPS points? How should I read in the GPS file, dictionary? And how can I save this into a .CSV without plotting the points and downloading accompanying file.

Any help would be appreciated.

kjtheron
  • 191
  • 1
  • 11

2 Answers2

1

If you want a value out of an image per point in a feature collection, you want reduceRegions, not reduceRegion. This will return a feature collection. Something like:

var allObs = ndviLandsatCol.map(function(img) {
  var obsAtTime = img.reduceRegions({
    collection: GPS,
    reducer: ee.Reducer.median(),
    scale: 30
  });
  return obsAtTime.map(function (feature) {
    return feature.copyProperties(img, ['system:time_start']);
  });
}).flatten();

Please note that you should remove the line GPS = GPS.geometry(); that you had, as that discards the structure of the feature collection and turns it into one geometry.

This will give you a flat table suitable for exporting to CSV, but you'll have to group the rows into time series yourself.

If you need to process the time series further within Earth Engine, then here's two different ways to group them:

  1. Use a ee.Join.saveAll. This gets you a FeatureCollection like GPS but with an extra property which contains the Features in the time series, and the festures each have properties corresponding to the bands from ndviLandsatCol.

    print(
      ee.Join.saveAll('series')  // 'series' is the name of the time series property.
        .apply(
          allObs.distinct('.geo').select([]),  // Determines properties of outer features (here, none but the geometry).
          allObs,
          ee.Filter.equals({leftField: '.geo', rightField: '.geo'})));
    
  2. Use a grouped reducer. This gets you a list of dictionaries of lists of numbers, each list of numbers being a time series of one explicitly-selected band from the original ndviLandsatCol. This is a bit messier, in my opinion, but might give you a simpler thing to work with.

    print(allObs.reduceColumns(
      ee.Reducer.toList().setOutputs(['NDVI'])
        .combine(ee.Reducer.toList().setOutputs(['time']))
        .group(0),
      [
        '.geo',               // select geometry for the group() operation
        'NDVI',               // first toList reducer
        'system:time_start',  // second toList reducer
      ]));
    
Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
1

Here is the final solution:

// Collect GPS, image, NDVI triplets.
var triplets = NDVILandsatCol.map(function(image) {
  return image.select('NDVI').reduceRegions({
    collection: GPS.select(['Site_ID']), 
    reducer: ee.Reducer.mean(), 
    scale: 30
  }).filter(ee.Filter.neq('mean', null))
    .map(function(f) { 
      return f.set('imageId', image.id());
    });
}).flatten();
print(triplets.first());

// Format a table of triplets into a 2D table of rowId x colId.
var format = function(table, rowId, colId) {
  var rows = table.distinct(rowId);
  var joined = ee.Join.saveAll('matches').apply({
    primary: rows, 
    secondary: table, 
    condition: ee.Filter.equals({
      leftField: rowId, 
      rightField: rowId
    })
  });

  return joined.map(function(row) {
      var values = ee.List(row.get('matches'))
        .map(function(feature) {
          feature = ee.Feature(feature);
          return [feature.get(colId), feature.get('mean')];
        });
      return row.select([rowId]).set(ee.Dictionary(values.flatten()));
    });
};

// Export table
var table1 = format(triplets, 'imageId', 'Site_ID');
var desc1 = 'Table_demo'; 
Export.table.toDrive({
  collection: table1, 
  description: desc1, 
  fileNamePrefix: desc1,
  fileFormat: 'CSV'
});

kjtheron
  • 191
  • 1
  • 11