# How to extract periods in collections in GEE?

I have the following code where I calculate the greenest pixel composite for the months 5-9 from Landsat 7.

``````var geometry =
/* color: #d63000 */
/* shown: false */
ee.Geometry.Polygon(
[[[20.81272957288212, 39.87257138492846],
[20.79075691663212, 39.8098319474331],
[20.935639118780557, 39.798226782067786],
[20.95555183850712, 39.860976816642435]]]);
var qa = image.select('BQA');
var cloud = qa.bitwiseAnd(1 << 4)
.and(qa.bitwiseAnd(1 << 6))
.or(qa.bitwiseAnd(1 << 8));
return image
.select(['B3', 'B4','B5'], ['Red', 'NIR','SWIR'])
.set('system:time_start', image.get('system:time_start'));
};

var dataset = ee.ImageCollection("LANDSAT/LE07/C01/T1_TOA")
.filterBounds(geometry)
.filter(ee.Filter.calendarRange(5,9,'month'))

var NDVI_l = function(image) {
var ndvi = image.normalizedDifference(['NIR', 'Red']).rename('NDVI');
};

var NDVI_col = dataset
.map(NDVI_l)
.select("NDVI");

var greenest = NDVI_col.qualityMosaic('NDVI');

print(greenest)

Map.addLayer(greenest.clip(geometry),{min:-1, max:1,  'palette': ['red','yellow', 'green']}, 'Greenest pixel composite');
``````

However, I want the following:

1. to calculate the period between the earliest greenup (from the greenest composite) per year and the latest browning (lowest NDVI pixel composite that I do no know how to calculate) per year (and for the period 1999-2018).
2. to create an image collection with these periods (so an image per year with the maximum values)

Questions:

1. how can I calculate a composite of the latest lowest pixel NDVI values per year?
2. how can I extract the period between the highest and lowest NDVIs per year?
3. how can I extract the NDVI images between the earliest greenup and latest browning events per year?

Perhaps I need something like the following for the annual intervals?

``````var years = ee.List.sequence(1999, 2018);

var byyear = ee.ImageCollection.fromImages(
years.map(function (y) {
return greenest.filter(ee.Filter.calendarRange(y, y, 'year'))
.max()
.set('year', y);
}))
``````

You can get the min/max values and day of year of each by using the numInputs option on the min and max reducers to bring along the corresponding day of year (from a date band). Example using Sentinel-2:

``````// Mask clouds, compute NDVI and add a DOY band.
function prepare(image) {
var qa = image.select('QA60')

// Bits 10 and 11 are clouds and cirrus, respectively.
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;

// Both flags should be set to zero, indicating clear conditions.

var doy = ee.Image.constant(image.date().getRelative('day', 'year')).int().rename('doy')

// Return the masked and scaled data, without the QA bands.
.copyProperties(image, ["system:time_start"])
}

// Find the max NDVI and the corresponding DOY and then the min NDVI/DOY after the max date.
function minMax(year) {
var date = ee.Date.fromYMD(year, 1, 1)
var collection = ee.ImageCollection('COPERNICUS/S2')
.filterBounds(geometry)
.map(prepare)

// Find the max NDVI and get the DOY that goes along with it.
var max = collection.reduce(ee.Reducer.max(2)).rename('maxNDVI', 'maxDOY')

// Find the min NDVI value AFTER the max DOY.
var min = collection.map(function(image) {
})
.reduce(ee.Reducer.min(2))
.rename('minNDVI', 'minDOY')

}

var results = ee.List.sequence(2016, 2020).map(minMax)
var resultsCollection = ee.ImageCollection.fromImages(results)
``````

You can do any math you want the min/max NDVI/DOY by mapping a function to do simple band math after that.

• thank you @Noel, it is very useful! I do play around with the code trying to adapt it with Landsat images and I get the error : Layer 1: Layer error: ImageCollection.reduce: Need 2 bands for Reducer.max, <ImageCollection> has 0. Is it because I use the NDVI collection for the reducer? Aug 24, 2021 at 7:20
• Without seeing the modifications you made, it's hard to guess what went wrong. The cloud masking for Landsat is different than for Sentinel, so probably something there. You can use the CloudMasking examples in the code editor examples to see how it's done for different landsat datasets. Aug 24, 2021 at 11:00
• apologies, I forgot to attach the link the code, here it is: code.earthengine.google.com/923bc7169f5330003268921194e82ed0 . I think that the masking is ok though. Aug 24, 2021 at 12:23
• You removed the line that added the doy band, and then later selected only the NDVI band , which also removed the doy band. code.earthengine.google.com/e937e6c06ef464fbde7c6e7e8bd99846 Aug 24, 2021 at 16:03

Here's a solution to obtain the desired output for the last two questions in the OP. There are two main tricks in the procedure: 1) create an image that is an inverse of the NDVI (to get the browniest pixel) and 2) create images based on the date of each image to calculate the date difference. I modified your NDVI_l function, as well as the function used to obtain the byyear var.

``````var NDVI_l = function(image) {
// Create image with ndvi
var ndvi = image.normalizedDifference(['NIR', 'Red']).rename('NDVI');
// Create image with date info
.divide(1000 * 60 * 60 * 24);// To obtain date in days
// Create image with the inverse of ndvi
var invNdvi = ee.Image.constant(1).divide(ndvi).rename('InvNDVI');
// Add all bands to the original image
};

// Create sequence of years to evaluate
var years = ee.List.sequence(1999, 2018);

var byyear = ee.ImageCollection.fromImages(
years.map(function (y) {
// Get images for the year of interest and map NDVI_l
var imCol =  dataset.filter(ee.Filter.calendarRange(y, y, 'year'))
.map(NDVI_l);
// Make quality mosaic based on NDVI value (priorizing high NDVI)
var greenest = imCol.qualityMosaic('NDVI');
// Make quality mosaic based on InvNDVI value (priorizing low NDVI)
var brownest = imCol.qualityMosaic('InvNDVI');
// Get date difference as absolute value and rename
var dateDiff = greenest.select('date').subtract(brownest.select('date'))
.abs()
.rename('dateDiff');

// Select only NDVI bands and rename
greenest = greenest.select('NDVI').rename('NDVImax');
brownest = brownest.select('NDVI').rename('NDVImin');

// Return NDVImax, NDVImin and dateDiff
• I'll try to answer each question. 1) the browning image takes as input all the NDVI images inside the collection (not only the greenest), that is why its calculated on the `dataset` collection. 2) the date difference image gives you the date difference (in days) between the lowest (browniest) and highest (greeniest) NDVI for each pixel. Aug 16, 2021 at 17:58
• Hmm, you could probably do that by masking the image collection (`dataset`) with the `date` bands. Something like: `var filtCol = dataset.map(function(image){ return image.updateMask(image.gte(greenest.select('date)))})` and then doing the `qualityMosaic`. However, for doing this you should find a way to identify which date is prior and posterior, in order to apply the updateMask accordingly as `.gte` and `lte`, respectively. Aug 16, 2021 at 20:40