I am currently applying a manual calibration on Landsat 8 images (manual because I want to simulate "noise" that could be introduced into the image--fed in as ee.Lists), and then compositing those altered images with ee.Algorithms.Landsat.simpleCloudScore. The idea is to slightly perturb each band's calibration gain, stored in the metadata of Landsat 8 images.

I am using several modules to do this (all linked below), and mapping the following function across the ee.ImageCollection:

  1. a function that takes in the perturbation array (ee.List), returning a wrapped function that returns an ee.Image: perturb.toa_Radiance(pert_row)

  2. the wrapped function in turn takes in an image, performs the manual calibration using the perturbation array (entered at the higher level in step 1)), followed by a cloud score and composite, then returns the composited image: perturb.toa_radiance(raw)

The relevant code snippets are below, with the links leading to the actual modules:

Function 1

exports.composite = function(collection,//img collection
                      asFloat,//do you want output to be reflectance units
                      percentile,//The percentile value to use when compositing each band.
                      cloudScoreRange,//pixels exceeding this will be rejected
                      pertrow) {//pert_row would be the list by which you are perturbing the

  var pert_row = ee.List(pertrow);
  // Select a sufficient set of images, and comptute TOA and cloudScore.
  var prepared =
      ee.Algorithms.Landsat.pathRowLimit(collection, maxDepth, 4 * maxDepth)
                   //.map(ee.Algorithms.Landsat.TOA)//This converts to Reflectance, change with your own function
                   .map(ee.Algorithms.Landsat.simpleCloudScore);//this should still work
  // Determine the per-pixel cloud score threshold.
  var cloudThreshold = prepared.reduce(ee.Reducer.min())

  // Mask out pixels above the cloud score threshold, and update the mask of
  // the remaining pixels to be no higher than the cloud score mask.
  function updateMask(image) {
    var cloud = image.select('cloud');
    var cloudMask = cloud.mask().min(cloud.lte(cloudThreshold));
    // Drop the cloud band and QA bands.
    image = image.select('B[0-9].*');
    return image.mask(image.mask().min(cloudMask));
  var masked = prepared.map(updateMask);
  print("just printing masked info", prepared);

  // Take the (mask-weighted) median (or other percentile)
  // of the good pixels.
  var result = masked.reduce(ee.Reducer.percentile([percentile]));

  // Force the mask up to 1 if it's non-zero, to hide L7 SLC artifacts
  result = result.mask(result.mask().gt(0));

  // Clean up the band names by removing the suffix that reduce() added.
  var badNames = result.bandNames();
  var goodNames = badNames.map(
          function(x) { return ee.String(x).replace('_[^_]*$', ''); });
  result = result.select(badNames, goodNames);

  if (!asFloat) {
    // Scale reflective bands by 255, and offset thermal bands by -100.
    // These lists are only correct for Landsat 8; different lists are
    // used for the other instruments.
    var scale = [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 1 ];
    var offset = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, -100, -100 ];
    result = result.multiply(scale).add(offset).round().uint8();
  return result;

Function 2

exports.toa_Radiance = function(pertrow){
  var pert_row = ee.List(pertrow);
  //usually it's a mistake to put such a humongous function inside another function
  //but we need the perturbation array to be a more global variable
  var toa_radiance = function(img){
    var image = ee.Image(img);
    //is wildcard supported (apparently, *)
    //this would probably be similar to the ndvi calculations
    //returns TOA value specific to band
    //var sun_ang = ee.Number(image.get('SUN_ELEVATION'));
    var d = ee.Number(image.get('EARTH_SUN_DISTANCE'));
    //var ESUN_list = [1876.61, 1970.03, 1848.9, 1571.3, 967.66, 245.73, 82.03, null,  361.72]; //CITE: Bunting 6S
    //var ESUN = ESUN_list[band_num.subtract(1)];-- only necessary if we are not using mult and add?
    //var sine_val = ee.Number(sun_ang.multiply(Math.PI).divide(180).sin());//radians
    //var constant = d.multiply(d).multiply(d).multiply(Math.PI).divide(sine_val);
    //now for the bands
    pert_row = ee.List(pertrow);
    print("input image", image);
    var extract_rad_gains = function(raw){//exports a list
      var b1_radmult = raw.get('RADIANCE_MULT_BAND_1');
      var b2_radmult = raw.get('RADIANCE_MULT_BAND_2');
      var b3_radmult = raw.get('RADIANCE_MULT_BAND_3');
      var b4_radmult = raw.get('RADIANCE_MULT_BAND_4');
      var b5_radmult = raw.get('RADIANCE_MULT_BAND_5');
      var b6_radmult = raw.get('RADIANCE_MULT_BAND_6');
      var b7_radmult = raw.get('RADIANCE_MULT_BAND_7');
      //var b8_radmult = raw.get('RADIANCE_MULT_BAND_8');//this be panchromatic
      var b9_radmult = raw.get('RADIANCE_MULT_BAND_9');
      print(b9_radmult, 'gain of b9');
      //List of MULT, excluding PAN
      var radmult_list =ee.List([b1_radmult, b2_radmult, b3_radmult, b4_radmult, b5_radmult, b6_radmult, b7_radmult, -9999, b9_radmult]);
      print(radmult_list, 'this is inside the extract rad gains func');
      return radmult_list;
    var extract_rad_biases = function(raw){
      //ADD (bias) for RADIANCE
      var b1_radadd = raw.get('RADIANCE_ADD_BAND_1');
      var b2_radadd = raw.get('RADIANCE_ADD_BAND_2');
      var b3_radadd = raw.get('RADIANCE_ADD_BAND_3');
      var b4_radadd = raw.get('RADIANCE_ADD_BAND_4');
      var b5_radadd = raw.get('RADIANCE_ADD_BAND_5');
      var b6_radadd = raw.get('RADIANCE_ADD_BAND_6');
      var b7_radadd = raw.get('RADIANCE_ADD_BAND_7');
      //var b8_radadd = raw.get('RADIANCE_ADD_BAND_8');//this be panchromatic
      var b9_radadd = raw.get('RADIANCE_ADD_BAND_9');

      //List of MULT, excluding PAN
      var radadd_list =ee.List([b1_radadd, b2_radadd, b3_radadd, b4_radadd, b5_radadd, b6_radadd, b7_radadd, -9999, b9_radadd]);

      return radadd_list;

    //cast as list
    var extracted_rad_gains = ee.List(extract_rad_gains(image));
    var extracted_rad_biases = ee.List(extract_rad_biases(image));
    print('REF MULT', extracted_rad_gains);

    var mult_list = perturb(extracted_rad_gains, pert_row);//should give perturbed info
    var add_list = perturb(extracted_rad_biases, pert_row);//should give perturbed info
    var band1 = image.select("B1");
    var band2 = image.select("B2");
    var band3 = image.select("B3");
    var band4 = image.select("B4");
    var band5 = image.select("B5");
    var band6 = image.select("B6");
    var band7 = image.select("B7");
    var band9 = image.select("B9");
    var band_list = ee.List([band1, band2, band3, band4, band5, band6, band7, band9]);
    //var toa_1 = (band1 * (mult_list.get(0))).add(add_list.get(0)).multiply(constant);
    var rad_1 = band1.multiply(ee.Number(mult_list.get(0))).add(ee.Number(add_list.get(0)));
    var rad_2 = band2.multiply(ee.Number(mult_list.get(1))).add(ee.Number(add_list.get(1)));
    var rad_3 = band3.multiply(ee.Number(mult_list.get(2))).add(ee.Number(add_list.get(2)));
    var rad_4 = band4.multiply(ee.Number(mult_list.get(3))).add(ee.Number(add_list.get(3)));      
    var rad_5 = band5.multiply(ee.Number(mult_list.get(4))).add(ee.Number(add_list.get(4)));
    var rad_6 = band6.multiply(ee.Number(mult_list.get(5))).add(ee.Number(add_list.get(5)));
    var rad_7 = band7.multiply(ee.Number(mult_list.get(6))).add(ee.Number(add_list.get(6)));
    var rad_9 = band9.multiply(ee.Number(mult_list.get(8))).add(ee.Number(add_list.get(8)));
    //print(constant, "constant");
    //print("this is rad_1", rad_1);

    var rad = ee.Image.cat([rad_1, rad_2, rad_3, rad_4, rad_5, rad_6, rad_7, rad_9]);
    return image.addBands(rad, null, true).toFloat()};

  return toa_radiance;//so this can apply to the image next

When I run the above mapped functions in the main script (https://code.earthengine.google.com/1515e322e19dbad45de2b26cb9605d6d), it throws a " [_MAPPING_VAR_0_0].", suggesting that this is a mixup between client-side and server-side. I know that there is something off about the wrapper function toa.radiance(image), but casting everything as ee objects did not seem to resolve issue. Is it inherently not possible to map a wrapped function? Does it have to do with the fact that some of these functions are brought in via the export method?


perturb: https://code.earthengine.google.com/1a3d804ad7f7f936cbe3fa54d67780bf

compositing: https://code.earthengine.google.com/4943b939b3d5533e26465dafcf163a3b

classification: https://code.earthengine.google.com/a81c7931bf5217fc47fa1823c7a4ba88

Main script (importing the above modules) : https://code.earthengine.google.com/1515e322e19dbad45de2b26cb9605d6d


suggesting that this is a mixup between client-side and server-side

Indeed. perturb.toa_Radiance contains several calls to print(). Since print is a function that itself sends a request to the EE servers, it cannot be used inside a map() — the mapped-over function parameter doesn't exist yet, which produces the error you see.

  • Indeed, getting rid of all the print() statements did the trick--perhaps some black boxes are not meant to be looked inside... Thank you again, Kevin – sbab May 1 at 18:48

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.