2

I have code that calculates NDVI, then calculate statistics inside a dictionary and then based on statistics calculte 3 layers that later on mosaiced. The problem is that for some reason I get this error:

range map: Layer error: Number.divide: Parameter 'left' is required.

I have read the debugging guide but I saw that this error might happen if I don't print the dictionary, but in my case I have had, so I don't know how to fix it.

This is the relevant part of my code:

//********try to create table with all the polygon stistics data***
//*******Results: print table similar, relates to all the polygon as one***
var tableWithStats = MyImage.reduceRegions({
  collection: geometry,
  reducer: ee.Reducer.mean().combine({
  reducer2: ee.Reducer.stdDev(),
  sharedInputs: true
  }),
  scale: 20
});

print(tableWithStats);

//***end of trial of print the statistics******


var std2 = ee.Number(tableWithStats.get("NDVI")).divide(2);
var mean1 = ee.Number(tableWithStats.get("NDVI"));

// the classes borders
var negBorder=mean1.subtract(std2);
var posBorder=mean1.add(std2);

//create the layers
var imageNDVI=MyImage.select('NDVI');
var gtPOS=MyImage.gt(posBorder).selfMask().rename('range');
var ltNEG=MyImage.lt(negBorder).selfMask().rename('range');
var betMEAN=MyImage.gt(negBorder).and(imageNDVI.lt(posBorder)).selfMask().rename('range');

var ndviClassCol = ee.ImageCollection.fromImages([ltNEG,betMEAN, gtPOS]);

// Mosaic the ImageCollection.
var ndviClassImg = ndviClassCol.mosaic();

// // Display the classified mosaic to the Map.
// Map.centerObject(geometry, 8);
Map.addLayer(ndviClassImg, {palette: ['blue', 'orange', 'green'], min: 0, max: 3},' map');

The code works if I run everything beside the addLayer part in the end.

how can I solve this?

EDIT: I'm adding here the full code:


/**
 * Function to mask clouds using the Sentinel-2 QA band
 * @param {ee.Image} image Sentinel-2 image
 * @return {ee.Image} cloud masked Sentinel-2 image
 */
function maskS2clouds(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 mask = qa.bitwiseAnd(cloudBitMask).eq(0)
      .and(qa.bitwiseAnd(cirrusBitMask).eq(0));

  return image.updateMask(mask).divide(10000)
  .copyProperties(image, ['system:time_start']);
}

// Map the function over one year of data and take the median.
// Load Sentinel-2 TOA reflectance data.
var dataset = ee.ImageCollection('COPERNICUS/S2')
                  .filterDate('2019-05-25','2020-01-06')
                  // Pre-filter to get less cloudy granules.
                  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
                  .select('B1','B2','B3','B4','B8','QA60')
                  .filterBounds(geometry)
                  .map(maskS2clouds);

var rgbVis = {
  min: 0.0,
  max: 0.3,
  bands: ['B4', 'B3', 'B2'],
};


var count=dataset.size();
print(count);

var clippedCol=dataset.map(function(im){ 
  return im.clip(geometry);
});


//test if clipping the image collection worked
Map.centerObject(geometry,9);
Map.addLayer(clippedCol.first(), rgbVis, 'RGB');

//function to calculate NDVI
var addNDVI = function(image) {
  var ndvi = image.normalizedDifference(['B8', 'B4'])
  .rename('NDVI')
  .copyProperties(image,['system:time_start']);
  return image.addBands(ndvi);

};

//NDVI to the clipped image collection
var withNDVI = clippedCol.map(addNDVI).select('NDVI');


var colorizedVis = {
  min: 0.0,
  max: 1.0,
  palette: [
    'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301'
  ],
};

//var ListImage=withNDVI.toList(withNDVI.size());


var listOfImages =(withNDVI.toList(withNDVI.size()));



var NumberOfImages=listOfImages.size();

print('number of images',NumberOfImages);




////////select the first image///////////

var MyImage=ee.Image(withNDVI.first());
 print(MyImage);
// // Map.addLayer(MyImage,colorizedVis,'ndvi');


//********try to create table with all the polygon stistics data***
//*******Results: print table similar, relates to all the polygon as one***
var tableWithStats = MyImage.reduceRegions({
  collection: geometry,
  reducer: ee.Reducer.mean().combine({
  reducer2: ee.Reducer.stdDev(),
  sharedInputs: true
  }),
  scale: 20
});

print(tableWithStats);



var std2 = ee.Number(tableWithStats.get("NDVI")).divide(2);
var mean1 = ee.Number(tableWithStats.get("NDVI"));



print(mean1);
print(std2);

// the classes borders
var negBorder=mean1.subtract(std2);
var posBorder=mean1.add(std2);

//create the layers
var imageNDVI=MyImage.select('NDVI');
var gtPOS=MyImage.gt(posBorder).selfMask().rename('range');
var ltNEG=MyImage.lt(negBorder).selfMask().rename('range');
var betMEAN=MyImage.gt(negBorder).and(imageNDVI.lt(posBorder)).selfMask().rename('range');

var ndviClassCol = ee.ImageCollection.fromImages([ltNEG,betMEAN, gtPOS]);

// Mosaic the ImageCollection.
var ndviClassImg = ndviClassCol.mosaic();

// // Display the classified mosaic to the Map.
// Map.centerObject(geometry, 8);
Map.addLayer(ndviClassImg, {palette: ['blue', 'orange', 'green'], min: 0, max: 1},'range map');
2
  • This image could occur if MyImage has no band named NDVI, but we can't be sure since you haven't included the definition of MyImage. Please include a code sample that is complete enough to run. (Also, about "The code works if I run everything beside the addLayer part in the end" — if you don't addLayer then you haven't actually run the computation, just defined it, so it's expected that errors won't show up there. When trying to remove things to simplify the problem, you need to always have a print or addLayer, but with a simpler input to it.)
    – Kevin Reid
    Commented Jan 12, 2020 at 18:38
  • @Kevin Reid I have added the full code. The image "MyImage" has band name NDVI.
    – ReutKeller
    Commented Jan 19, 2020 at 12:47

1 Answer 1

2
+50

I found two problems.

var tableWithStats = MyImage.reduceRegions({
  collection: geometry,
  reducer: ee.Reducer.mean().combine({
  reducer2: ee.Reducer.stdDev(),
  sharedInputs: true
  }),
  scale: 20
});
var std2 = ee.Number(tableWithStats.get("NDVI")).divide(2);
var mean1 = ee.Number(tableWithStats.get("NDVI"));
  1. You are using reduceRegions, which takes a FeatureCollection and produces a FeatureCollection with properties for each feature, but you are then looking up properties of the collection (which has the features in the collection), not the features in the collection.

  2. You are not accessing the properties with their actual names. The names can be seen in your print(tableWithStats); output.

To illustrate how to get the information out of reduceRegions: Using the following code will get you the values you wanted for the first feature/geometry in your collection. (You probably actually want some variety of .map(), not .first(), but that depends on what you're needing.)

var std2 = ee.Number(tableWithStats.first().get("stdDev")).divide(2);
var mean1 = ee.Number(tableWithStats.first().get("mean"));

On the other hand, if you actually wanted one value for your entire geometry, not a bunch of features, then you should use reduceRegion instead of reduceRegions:

var tableWithStats = MyImage.reduceRegion({
  geometry: geometry,
  reducer: ee.Reducer.mean().combine({
  reducer2: ee.Reducer.stdDev(),
  sharedInputs: true
  }),
  scale: 20
});

print(tableWithStats);

var std2 = ee.Number(tableWithStats.get("NDVI_stdDev")).divide(2);
var mean1 = ee.Number(tableWithStats.get("NDVI_mean"));

Note that in this case, the outputs of the reducer are given names prefixed with the band name.

2
  • thank you for this great answer. I have still a little question, when you explained about reduce region and regions, did you mean that if for example I have one layer with many polygons and I want to do this calculation for each polygon I should use reduce region, and if I have Imagecollection that I want to run this on all of it I should use Reduce regions?
    – ReutKeller
    Commented Jan 23, 2020 at 8:54
  • @Reut Perhaps think about how many answers you want: if you want one result covering a single region, use reduceRegion. If you have many regions that you want separate results for, use reduceRegions.
    – Kevin Reid
    Commented Jan 23, 2020 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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