Google Earth Engine – How to Extract Landsat Time Series for Multiple Locations

google-earth-enginelandsattime series

I want to extract Landsat time series for multiple locations. I refer to the code in this tutorial.Because I chose a time period of 2019-01-01 to 2019-12-30, I should get multiple columns of ndvi data, but I only got two columns of ndvi data.What's wrong? the result like this:enter image description here

This is the code

var points = table.map(function(feature) {
  return ee.Feature(feature.geometry(), {'id': feature.id()})
})

// Cloud masking *** from Example:"Landsat8 TOA Reflectance QA Band"
var maskL8 = function(image) {
  var qa = image.select('BQA');
  /// Check that the cloud bit is off.
  // See https://www.usgs.gov/land-resources/nli/landsat/landsat-collection-1-level-1-quality-assessment-band
  var mask = qa.bitwiseAnd(1 << 4).eq(0);
  return image.updateMask(mask);
}

// Adding a NDVI band
function addNDVI(image) {
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi')
  return image.addBands([ndvi])
}

var startDate = '2019-01-01'
var endDate = '2019-12-31'
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')
              // ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
    .filterDate(startDate, endDate)
    // .map(maskL8sr)
    .map(maskL8)
    .map(addNDVI)
    .filter(ee.Filter.bounds(points))

// // Show the farm locations in green
Map.addLayer(points, {color: 'green'}, 'Farm Locations')

// handling masked pixels
var triplets = collection.map(function(image) {
  return image.select('ndvi').reduceRegions({
    collection: points, 
    reducer: ee.Reducer.first().setOutputs(['ndvi']), 
    scale: 10,
  })// reduceRegion doesn't return any output if the image doesn't intersect
    // with the point or if the image is masked out due to cloud
    // If there was no ndvi value found, we set the ndvi to a NoData value -9999
    .map(function(feature) {
    var ndvi = ee.List([feature.get('ndvi'), null])
      .reduce(ee.Reducer.firstNonNull())
    return feature.set({'ndvi': ndvi, 'imageID': image.id()})
    })
  }).flatten();
  
// Granules overlap
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('ndvi')];
        });
      return row.select([rowId]).set(ee.Dictionary(values.flatten()));
    });
};
var sentinelResults = format(triplets, 'id', 'imageID');
// There are multiple image granules for the same date processed from the same orbit
// Granules overlap with each other and since they are processed independently
// the pixel values can differ slightly. So the same pixel can have different NDVI 
// values for the same date from overlapping granules.
// So to simplify the output, we can merge observations for each day
// And take the max ndvi value from overlapping observations
var merge = function(table, rowId) {
  return table.map(function(feature) {
    var id = feature.get(rowId)
    var allKeys = feature.toDictionary().keys().remove(rowId)
    var substrKeys = ee.List(allKeys.map(function(val) { 
        return ee.String(val).slice(0,8)}
        ))
    var uniqueKeys = substrKeys.distinct()
    var pairs = uniqueKeys.map(function(key) {
      var matches = feature.toDictionary().select(allKeys.filter(ee.Filter.stringContains('item', key))).values()
      var val = matches.reduce(ee.Reducer.max())
      return [key, val]
    })
    return feature.select([rowId]).set(ee.Dictionary(pairs.flatten()))
  })
}
var sentinelMerged = merge(sentinelResults, 'id');

Export.table.toDrive({
    collection: sentinelMerged,
    description: 'landsat_Multiple_Locations_NDVI_time_series',
    folder: 'earthengine',
    fileNamePrefix: 'landsat_ndvi_time_series_multiple',
    fileFormat: 'CSV'
})

Please give me some advice.

Best Answer

I thought you want to extract from Landsat. If that, you should notice that the step of Granules Overlap is specific to Sentinel images as subscribed from tutorial.

following by modified codes

var table = ee.FeatureCollection("users/cdg15541077683/point");

var points = table.map(function(feature) {
  return ee.Feature(feature.geometry(), {'id': feature.id()})
})

// Cloud masking *** from Example:"Landsat8 TOA Reflectance QA Band"
var maskL8 = function(image) {
  var qa = image.select('BQA');
  /// Check that the cloud bit is off.
  // See https://www.usgs.gov/land-resources/nli/landsat/landsat-collection-1-level-1-quality-assessment-band
  var mask = qa.bitwiseAnd(1 << 4).eq(0);
  return image.updateMask(mask);
}

// Adding a NDVI band
function addNDVI(image) {
  var ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi')
  return image.addBands([ndvi])
}

var startDate = '2019-01-01'
var endDate = '2019-12-31'
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')
              // ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
    .filterDate(startDate, endDate)
    // .map(maskL8sr)
    .map(maskL8)
    .map(addNDVI)
    .filter(ee.Filter.bounds(points))
    
    
var vizParams = {
  bands: ['B5', 'B4', 'B3'],
  min: 0,
  max: 0.5,
  gamma: [0.95, 1.1, 1]
};

Map.addLayer(collection.median(), vizParams, 'collection')
// // Show the farm locations in green
Map.addLayer(points, {color: 'green'}, 'Farm Locations')


// handling masked pixels
var triplets = collection.map(function(image) {
  return image.select('ndvi').reduceRegions({
    collection: points, 
    reducer: ee.Reducer.first().setOutputs(['ndvi']), 
    scale: 30,
  })// reduceRegion doesn't return any output if the image doesn't intersect
    // with the point or if the image is masked out due to cloud
    // If there was no ndvi value found, we set the ndvi to a NoData value -9999
    .map(function(feature) {
    var ndvi = ee.List([feature.get('ndvi'), -9999])
      .reduce(ee.Reducer.firstNonNull())
    return feature.set({'ndvi': ndvi, 'imageID': image.id()})
    })
  }).flatten();
  
print(triplets);

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('ndvi')];
        });
      return row.select([rowId]).set(ee.Dictionary(values.flatten()));
    });
};
var landsatResults = format(triplets, 'id', 'imageID');



Export.table.toDrive({
    collection: landsatResults,
    description: 'landsat_Multiple_Locations_NDVI_time_series',
    folder: 'earthengine',
    fileNamePrefix: 'landsat_ndvi_time_series_multiple',
    fileFormat: 'CSV'
})
Related Question