1

I get the number of images a ImageCollection using the size() function, but when I use that number in a for loop, it does not work.

See the last chunk of code in this script:

// study area 
var roi = ee.FeatureCollection([
    ee.Feature(    // study area.
      ee.Geometry.Rectangle(20, -5, 75, 35), {label: 'study Area'})
  ]);

// images
var now = ee.Date(Date.now())
var NDVICollection=ee.ImageCollection('MODIS/006/MOD13Q1')
    .filterDate('2019-12-01',now)
    .select('NDVI');

// masker
var masker = function(image){ 
    var mask1 = image.select('NDVI').lte(10000);
    var mask2 = image.select('NDVI').gte(2000);
    return image.updateMask(mask1).updateMask(mask2);
    };

// Create clipping function
var clipper = function(image){
    return image.clip(roi);
  };

var ndvi = NDVICollection.map(clipper);

// PREPARE DATA FOR EXPORT
// Tyler Erickson provided the stacking function
// https://gis.stackexchange.com/a/254778/67264
var stackCollection = function(collection) {
    var first = ee.Image(collection.first()).select([]);
    // Write a function that appends a band to an image.
    var appendBands = function(image, previous) {
        var dateString = ee.Date(image.get('system:time_start')).format('yyyy-MM-dd');
        return ee.Image(previous).addBands(image.rename(dateString));
    };
    return ee.Image(collection.iterate(appendBands, first));
  };
var ndvi_img = stackCollection(ndvi);
print(ndvi_img);

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

Map.centerObject(roi,2);

var size =NDVICollection.size() ;
print(size);//4
  Map.addLayer(roi,{},'Study Area');
// it does not work!!!!!!!!!!!!!!!!!!!!!!
  for(var i = 0;i<size;i++){
    Map.addLayer(ndvi_img.select(i),visParams,'img_'+i);
  }

Kadir Şahbaz
  • 76,800
  • 56
  • 247
  • 389
宋英旭
  • 13
  • 2

2 Answers2

1

The issue is that you are mixing server variables (that live on the earth engine server) and local functions (that live on your browser). The for() loop runs on your browser. It doesn't know what the size variable contains.

By adding the .getInfo() you are forcing the value to be sent to the browser, and then your size variable becomes a local variable. This makes the script only bring data to your browser when you actually want it, otherwise running completely on the server. Which is why you can make image collections, and band-math operations very quickly, which would take your browser years to complete.

It is a concept that I struggled with, (and still do sometimes). It is why you can't do what would feel like a simple operation.

var serverNumberA = ee.Number(2); // 2
var serverNumberB = ee.Number(3); // 3

var badSum = serverNumberA + serverNumberB // ERROR
var goodSum = serverNumberA.add(serverNumberB); // CORRECT

For a better explanation see the client_server Docs

Sean Roulet
  • 2,200
  • 11
  • 25
0

Sean's description of the issue is right - you need to transfer server-side EE objects to the client to perform client-side operations, like for loops, with them.

However, .evaluate() is the better method for transferring EE objects to the client because it is asynchronous; your browser is less likely to freeze up.

Here is .evaluate() implemented in your script:

// Get the number of bands.
var nBands = ndvi_img.bandNames().size();

// Call evaluate on nBands to transfer it client-side for use in client for loop.
nBands.evaluate(function(nBands) {
  for(var i=0; i<nBands; i++) {
    Map.addLayer(ndvi_img.select(i), visParams,'img_'+i);
  } 
});

Full Code Editor script

Justin Braaten
  • 6,146
  • 1
  • 20
  • 42