5

I'm trying to map the maximal amount of days without rain using the TRMM dataset in Google Earth Engine. I'm doing this by iterating over the collection and if no rain has fallen, one gets added to the cell (code below). When rain has fallen the value gets multiplied by 0 so the "counter" is reset. Then I would like to store each result of each iteration in a Image collection and then select the maximum value to get the longest consecutive dry period.

But that is the theory. When I put this in a script I get an error while adding the image of one iteration to a list.

Does anyone know why this is and how this can be solved?

Code:

var list = [];
function drylength(current, previous){
  var mask = current.remap([0], [1], 0,"precipitation").rename('precipitation');
  var sum = mask.add(previous).multiply(mask);

  list.push(sum);
  return sum;
}

var dataset = TRMM
.filterDate("2015-01-01","2015-02-01")
.sort('system:time_start:');

var totalPrecipitation = dataset.iterate(drylength, dataset.max()
.remap([0], [0], 0, "precipitation")
.rename('precipitation'));

print(list);
print(totalPrecipitation);
Map.addLayer(ee.Image(totalPrecipitation), imageVisParam);

Furthermore it appears only 3 items are stored in the list which makes me assume the iteration is more complex than a literal iteration where all images are calculated one by one?

Here is an image of the error:

Google Earth Engine errors

Errors written if image is not visible or for search engines:

Failed to decode JSON.
Error: Field 'value' of object '{"type":"ArgumentRef","value":null}' is missing or null.
Object: {"type":"ArgumentRef","value":null}.

and:

Unknown variable references: [_MAPPING_VAR_0_0, _MAPPING_VAR_0_1].

1 Answer 1

6

Furthermore it appears only 3 items are stored in the list which makes me assume the iteration is more complex than a literal iteration where all images are calculated one by one?

You've got some of the idea.

In Earth Engine, your JavaScript code is, by default, not at all involved in the substantial computation. Instead, it is executed to construct a data structure representing the desired computation (in JSON), which is then sent to the Earth Engine servers to be run.

Therefore, any client-side function in map or iterate is not called for every item in the collection, but once to get that representation. (Or in this case, three times because you have three separate server calls: two prints and a Map.addLayer.)

The documentation for map mentions this, but I see the documentation for iterate doesn't.

The mapped function is limited in the operations it can perform. Specifically, it can’t modify variables outside the function; it can’t print anything; it can’t use JavaScript ‘if’ or ‘for’ statements. However, you can use ee.Algorithms.If() to perform conditional operations in a mapped function.

So, you cannot expect the code you have written, which modifies a variable from inside the function supplied to iterate, to be useful. It does not collect the actual data, but only the placeholders that are meant to be used by iterate (hence names like _MAPPING_VAR_0_0).

Instead, what you must do is put the data you want to generate in the iterated value. In this case, this is easy to do — just return the accumulated list, and when you want the previous image take the last element of the list. So, I think this is what you wanted to do:

function drylength(current, previous){
  previous = ee.List(previous);  // cast to establish the client-side type
  var mask = current.remap([0], [1], 0,"precipitation").rename('precipitation');
  var sum = mask.add(previous.get(-1)).multiply(mask);

  return previous.add(sum);
}

var dataset = TRMM
.filterDate("2015-01-01","2015-02-01")
.sort('system:time_start:');

var list = ee.List(dataset.iterate(drylength, ee.List([
  dataset.max()
    .remap([0], [0], 0, "precipitation")
    .rename('precipitation')
])));
var totalPrecipitation = list.get(-1);

print(list);
print(totalPrecipitation);
Map.addLayer(ee.Image(totalPrecipitation), imageVisParam);

(Disclaimer: I have only modified the code so that it runs successfully, not checked whether the outputs are scientifically reasonable.)

5
  • That makes a lot of sense! Thanks for the help! I get how it works now. Only one more issue, when I add these images to a collection (to get the values) I get an error. I have been working on this for a few hours now... This is the error I get: max: Tile error: Expected a homogeneous image collection, but an image with an incompatible band was encountered. Mismatched type for band 'precipitation': Expected: Type<MaskOnly>. Actual: Type<Byte<0, 1>>. Image ID: 1 This band might require an explicit cast. Any idea what causes this and how it can be solved?
    – Sam
    Commented Mar 19, 2018 at 16:50
  • @Sam You should post a new separate question, so you can include sample code and everyone sees it rather than people looking at comments on other questions.
    – Kevin Reid
    Commented Mar 19, 2018 at 16:52
  • I thought It would fit better here as it handles with the same thing I am trying to achieve, but I will do that then
    – Sam
    Commented Mar 19, 2018 at 16:54
  • @Sam There's several reasons to do it this way: It keeps things better organized and findable; often someone who answers the first question can't help with the followup; and ideally, questions' answers turn out to cover a general problem, not just debugging one particular program (as this one did: modifying variables in iterate). You can always include a link to your previous question to provide context.
    – Kevin Reid
    Commented Mar 19, 2018 at 16:58
  • Yeah you're right, I was reluctant to do so as I don't wan't to spam the site with what could possibly be stupid questions ;) But I'm writing the new question now
    – Sam
    Commented Mar 19, 2018 at 17:01

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.