I'm trying to generate a smoothed MODIS(MOD11A2) time series by using the Savitzky-Golay filter.

There is this github repo https://github.com/Fernerkundung/EarthEngine_scripts/blob/master/savitzky_golay_smoothing.js, which run a simple implementation of Savitzky-Golay smoother but not fully developed as to process a whole image collection. The input data is a sample of numbers in a list format. My questions are (a) how can I extract each pixel time profile of my image collection in a list format so I can make it as input data (b) how can I iterate to the whole image extent? (c) Does anyone know another way to work with this filter in GEE?

var geometry = ee.Geometry.Rectangle([-65.2,-33.4,-65.8,-33.2]);

// Clip
var clip = function(image){
  return image.clip(geometry);

// Import MOD11A2
var temp = ee.ImageCollection('MODIS/006/MOD11A2')
    .filterDate('2001-01-01', '2018-12-31')

closed as too broad by PolyGeo May 1 at 21:57

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • As per the Tour there should be only one question asked per question. – PolyGeo May 1 at 22:00

The script you are referring to was originally posted by Guido Lemoine to the GEE Developers Board. He since updated the script to a new version: https://groups.google.com/d/msg/google-earth-engine-developers/A7wKQ8WMLIs/kNGt8gwQGQAJ

The new version works on an ImageCollection, so there is no need to iterate through the image pixels. In his example he uses MOD13Q1 NDVI time series, but you can adapt this to other Modis ImageCollections: https://code.earthengine.google.com/e9f4f2bb84f0bdd26ecfbea47a71885f

The relevant part of the new code:

// Add predictors for SG fitting, using date difference
// We prepare for order 3 fitting, but can be adapted to lower order fitting later on
var modis_res = modis.filterDate(start_date, end_date).filterBounds(aoi).map(function(img) {
  var dstamp = ee.Date(img.get('system:time_start'))
  var ddiff = dstamp.difference(ee.Date(start_date), 'hour')
  img = img.select(['NDVI', 'EVI']).divide(32768.0).set('date', dstamp)
  return img.addBands(ee.Image(1).toFloat().rename('constant')).
// Step 2: Set up Savitzky-Golay smoothing
var window_size = 9
var half_window = (window_size - 1)/2

// Define the axes of variation in the collection array.
var imageAxis = 0;
var bandAxis = 1;

// Set polynomial order
var order = 3
var coeffFlattener = [['constant', 'x', 'x2', 'x3']]
var indepSelectors = ['constant', 't', 't2', 't3']

// Change to order = 2 as follows:
//var order = 2
//var coeffFlattener = [['constant', 'x', 'x2']]
//var indepSelectors = ['constant', 't', 't2']

// Convert the collection to an array.
var array = modis_res.toArray();

// Solve 
function getLocalFit(i) {
  // Get a slice corresponding to the window_size of the SG smoother
  var subarray = array.arraySlice(imageAxis, ee.Number(i).int(), ee.Number(i).add(window_size).int())
  var predictors = subarray.arraySlice(bandAxis, 2, 2 + order + 1)
  var response = subarray.arraySlice(bandAxis, 0, 1); // NDVI
  var coeff = predictors.matrixSolve(response)

  coeff = coeff.arrayProject([0]).arrayFlatten(coeffFlattener)
  return coeff  

// For the remainder, use modis_res as a list of images
modis_res = modis_res.toList(modis_res.size())
var runLength = ee.List.sequence(0, modis_res.size().subtract(window_size))

// Run the SG solver over the series, and return the smoothed image version
var sg_series = runLength.map(function(i) {
  var ref = ee.Image(modis_res.get(ee.Number(i).add(half_window)))
  return getLocalFit(i).multiply(ref.select(indepSelectors)).reduce(ee.Reducer.sum()).copyProperties(ref)
  • That was really helpful! Thank you a lot. – Inacio Bueno Apr 30 at 20:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.