1

I am developing a yearly NDVI trend analysis using Landsat data in Earth Engine over the last decades. My process is to add a NDVI band to every scene for each summer (green) month, then to reduce the collection by median to one image per month and finally to reduce all monthly images by max to generate one image for one year. My plan is to create a time series to see the positive/negative trends of NDVI over the years, but somewhere along the way the system:time_start property gets lost while reducing the images. I want to generate a chart but I always end up with: Error generating chart: No features contain non-null values of "system:time_start". I know you can copyProperties(image, ['system:time_start']) but I cannot find out where and how. To reduce the length of the code, I cut it to two months:

//cloud mask & UNIX time
var timeField = 'system:time_start';

function maskL8sr(image) {
  var cloudShadowBitMask = (1 << 3);
  var cloudBitMask = (1 << 5);
  var qa = image.select('pixel_qa');
  var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
              .and(qa.bitwiseAnd(cloudBitMask).eq(0));
  return image.updateMask(mask)
    .select('B[0-9]*')
    .copyProperties(image, ['system:time_start']);
}

var geometryexpl = ee.Geometry.Polygon([[155.23045236462508,69.80628971253647],
                                      [159.03172189587508,70.77679819617978],
                                      [155.23045236462508,70.77679819617978],
                                      [155.23045236462508,69.80628971253647]]);

var landsat = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR");

//compute NDVI of biweekly plots
var addVariables = function(image) {
  //compute time in fractional years since the epoch
  var date = ee.Date(image.get(timeField));
  var years = date.difference(ee.Date('1970-01-01'), 'year');
  //return image with added bands
  return image
    //add NDVI band
    .addBands(image.normalizedDifference(['B5', 'B4']).rename('NDVI'))
    //add time band
    .addBands(ee.Image(years).rename('t'))
    .float()
    //add a constant band
    .addBands(ee.Image.constant(1));
};

//filter landsat8 data by county boundary, date, apply cloud mask, add variables
var NDVImap_may_2014 = (landsat
  .filterBounds(geometryexpl)
  .filterDate('2014-05-01', '2014-05-31')
  .map(maskL8sr)
  .map(addVariables));
  
var NDVImap_june_2014 = (landsat
  .filterBounds(geometryexpl)
  .filterDate('2014-06-01', '2014-06-30')
  .map(maskL8sr)
  .map(addVariables));  
  
//reduce collection to one image using median of the rasters
var NDVI_may_2014 = NDVImap_may_2014.reduce(ee.Reducer.median());
var may_2014_NDVIbound = NDVI_may_2014.reduceRegions({
  collection: geometryexpl,
  reducer: ee.Reducer.median(),
  scale: 30
});

var NDVI_june_2014 = NDVImap_june_2014.reduce(ee.Reducer.median());
var june_2014_NDVIbound = NDVI_june_2014.reduceRegions({
  collection: geometryexpl,
  reducer: ee.Reducer.median(),
  scale: 30
});

var collection_2014_median = ee.ImageCollection.fromImages(
  [ee.Image(NDVI_may_2014), ee.Image(NDVI_june_2014)]);
  
var max_2014 = collection_2014_median.reduce(ee.Reducer.max());
var max_2014_bound = max_2014.reduceRegions({
  collection: geometryexpl,
  reducer: ee.Reducer.max(),
  scale: 30
});

var palettes = require('users/gena/packages:palettes');
print(palettes, 'palettes');
var palette_ndvi = palettes.colorbrewer.BuGn[5];

var ndviParams = {min: -1, max: 1, palette: palette_ndvi};

Map.addLayer(max_2014.select('NDVI_median_max').clip(geometryexpl), ndviParams, 'NDVI');

var chart = ui.Chart.image.series(
  collection_2014_median.select('NDVI_median'), 
  geometryexpl, 
  ee.Reducer.mean(), // default 
  400, // nominal scale Landsat imagery 
  'system:time_start')
    .setOptions({title: 'NDVI 1‐Year Time Series',
                 vAxis: {title: 'NDVI'}, });

print(chart);
2

This now works: https://code.earthengine.google.com/256b9a98c0f280e703fa5ad03a6dacf0

You need to copy properties in every function, secondly when you reduce to the median you lose system time data. Therefore you can manually by defining the date var may_date = ee.Date('2014-05-01') and then using .set({'system:time_start': may_date}) at the end of the reducer. Such that:

var may_date = ee.Date('2014-05-01')
  
//reduce collection to one image using median of the rasters

var NDVI_may_2014 = NDVImap_may_2014.reduce(ee.Reducer.median()).set({'system:time_start': may_date});

You do not appear to be using the NDVIbound_may_2014, however if you do, you will need to set the system start time as above.

From the look at your code you are doing it manually for each month, I would look into creating functions to do this. I would start here: https://developers.google.com/earth-engine/guides/ic_iterating Just a suggestion though

1
  • That did it, thanks a lot. Yes I plan to create a more elegant code by functions, I wanted to see if it all makes sense first Oct 23 '20 at 15:42

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.