1

I have an image collection that is a binary layer that indicates whether there is a burn or not in each pixel, for the specified time period by which I filtered the satellite images. This is calculated from the differences in the Normalized Burn ratio (dNBR), calculated for each image. A pixel has value 1 (which means it is burned) if the dNBR value is above the threshold value for a pixel, both when looking at the difference between NBR of day1 and day2, and looking at the difference between NBR of day1 and day3. Now I want to reduce the image collection to an image such that each pixel that burnt at any point of time (within the given time period) has a positive value and attach a timestamp as a band to the image, such that each burnt value pixel has a time of burn. The way I am implementing it is by adding a date band to the original filtered image collection, then finding the dNBR and then reducing the image collection by max() reducer. But the problem is the reducer is maximising even the time band and I am getting only one value as the time stamp. Is there a way to apply reduce to only one value and retain the value of the other band which the maximum value was part of? I have been breaking my head over it for quite some time. Please help

var vis = {"opacity":1,"bands":["B4","B3","B2"],"gamma":3.464},
sent = ee.ImageCollection("COPERNICUS/S2")

var land = sent2.filterBounds(geometry).filterDate("2019-01-01","2019-06-01")
.filterMetadata("MGRS_TILE","equals",'44RLT')
.filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE",5));
var landsort = land.sort("system:time_start");
print(landsort);

var addDate = function(image){
  var doy = image.date().getRelative('day', 'year');
  var doyBand = ee.Image.constant(doy).uint16().rename('doy')
  doyBand = doyBand.updateMask(image.select('B8').mask())

  return image.addBands(doyBand);
};

var a = ee.ImageCollection(landsort.toList(landsort.size()));
var a = a.map(addDate)
var a = a.toList(a.size())
var b = function(image){
 var nbr3 = ee.Image(image).normalizedDifference(["B8","B12"]);
 var nbr2 = ee.Image(a.get(a.indexOf(image).subtract(1))).normalizedDifference(["B8","B12"]);
 var nbr1 = ee.Image(a.get(a.indexOf(image).subtract(2))).normalizedDifference(["B8","B12"]);

 var e = nbr1.subtract(nbr2).gt(0.3);
 var f = nbr1.subtract(nbr3).gt(0.3);
 var g = e.multiply(f);
 var h = g.addBands(ee.Image(a.get(a.indexOf(image).subtract(2)))
.metadata('system:time_start'));
 return h;
};
var dnrcollect = ee.ImageCollection(a.map(b)).max();
Map.addLayer(dnrcollect)
0

Use: col.reduce(ee.Reducer.max(nBands)) instead of col.max().

The ee.Reducer.max() reducer allows you to specify additional bands to be included in the resulting image whose pixel values correspond to whatever image had the max value (of first band) among constituents.

From your example:

ee.ImageCollection(a.map(b)).reduce(ee.Reducer.max(NEED TO SPECIFY NUMBER OF EXTRA BANDS))

A toy example - use the inspector and expand all of the layer info to verify (see image below):

/**
 * @license
 * Copyright 2019 Google LLC.
 * SPDX-License-Identifier: Apache-2.0
 */

// Make some images with a data band and a date band.
var img1 = ee.Image.random(10).addBands(ee.Image.constant(10)).float().rename('data', 'date');
var img2 = ee.Image.random(20).addBands(ee.Image.constant(20)).float().rename('data', 'date');

// Make an image collection from the images.
var col = ee.ImageCollection([img1, img2]);

// Reduce the collection by the max of the first band and retain corresponding bands.
var maxImg = col.reduce(ee.Reducer.max(col.first().bandNames().size()))
  .rename(['maxData', 'maxDate']);

// Show both datasets on the Map so that inspector can be used to verify.
Map.addLayer(col, null, 'col');
Map.addLayer(maxImg, null, 'max');

enter image description here

Code Editor script: https://code.earthengine.google.com/b62c98246369e784ecd93852a7a2476e

1

You will need to do that transforming the image collection to an array. A good explanation about arrays can be found here. Your question is quite similar to this post, but adapted for your current function you will need the following:

// turn image collection into an array
var array = dnrcollect.toArray();
// sort array by the first band, keeping other bands
var axes = { image:0, band:1 }
var sort = array.arraySlice(axes.band, 0, 1);  // select bands from index 0 to 1 ("nd")
var sorted = array.arraySort(sort);
// take the first image only (MAX nd)
var length = sorted.arrayLength(axes.image)
// for the max value sorted
var valuesMax = sorted.arraySlice(axes.image, length.subtract(1), length);
// convert back to an image  
var max = valuesMax.arrayProject([axes.band]).arrayFlatten([['nd', 'time']]);
// get the min and max axis by selecting bands 0 and 1
var ndMax = max.select(0) // get nd
var timeReference = max.select(1) // get time stamp reference

Then you can add the images to the map using:

// add to the map
Map.addLayer(ndMax, {min: 0, max: 1}, 'ND max value' )
// get date numbers
var timeMin = new Date ("2019-01-01"); timeMin = timeMin.getTime(); 
var timeMax = new Date ("2019-06-01"); timeMax = timeMax.getTime(); 
Map.addLayer(timeReference, {min: timeMin, max: timeMax}, 'ND time for max')

link full code

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.