在这里同步一篇本人的原创文章。原文发布于2023年发布在知乎专栏,转移过来时略有修改。全文共计3万余字,盼望帮助到GEE小白快速进阶。
弁言
这篇文章重要解答GEE中.map()和.iterate()函数的用法。
首先解答一个疑问,为什么需要自己写循环?确实,GEE 为各种数据范例提供了无数常用的内置函数,对这些方法做排列组合足以应对大多数使用场景,算法效率也颇佳。在有内置函数的情况下,我永远推荐内置函数。
然而,对策赶不上需求,总偶然候需要部署个性化的算法。举个例子,数据聚合问题:现有一个小时级的气温数据集 hourlyTemp_imgcol,每小时整点提供一张近地表气温图像,要求借此生成一个每日平均均气温数据集 dailyTem_imgcol,每天提供一张日均近地表气温图像。那么,GEE 有没有这样的内置函数:- var dailyTemp_imgcol = hourlyTemp_imgcol.aggregate('daily');
复制代码 让我们一行代码出结果呢?没有。
以上的例子是细分学科下的使命,听起来照旧挺稀罕的。那我就再举一个编程初学者一定会接触的算法,数列递推:由一个初始值按照一定的递推公式,生成一个固定长度的列表。GEE有没有类似这样的递推函数:- var List = Number.RECURSIVE_SEQUENCE(function, list_length);
复制代码 来一步到位实现这个简单使命呢?很遗憾,也是没有的。
因此,尽管 GEE 内置函数库丰富,学会自己部署循环仍然很有须要。这是跨过初学者阶段、独立开展项目的必经之路。
在第一个例子中,我们需要按时间循环,每个日期计算一幅平均气温图像。在第二个例子中,我们需要按列表索引位置循环,每个索引位置上计算一个递推值。
第一个例子代表了一类问题,处理函数作用在每个元素上,让每个元素被映射到一个新的东西上,这样的话一定是多个输入和多个输出,而且有一一对应的关系。关于这种循环,我们使用.map()操作来处理,请阅读本文第(一)节。
第二个例子代表了另一类问题,处理函数的使命是产生累积的效果,背面步调的实行会依靠前面步调的产出。关于这一种循环,我们使用.iterate()操作来处理,请阅读本文第(二)节。
有些初学者会说,哪里需要那么贫苦,我以稳固应万变,老师教的for循环就是无脑易上手,再说GEE又不是不支持。我想说,这种风俗在技术上确实可行,但是如果是放在 GEE 上部署,会严峻拖慢计算效率。在展示如何写 GEE 式循环之前,我专门开一个第(〇)节,探究一下这个问题。非常建议初学者阅读,会帮助你更好地明白,GEE 如何处理用户提交的使命。
然而并不是说任何情况都不能使用for循环。 有些情况下,因为.map()和.iterate()的技术限制,循环必须在本地端进行。我会举一下这种应用场景的例子,放在第(三)节。
让我们开始吧。
(〇) 为什么GEE不推荐for、while循环?
GEE 常被描述为“地理云计算平台”。那么,之所以被称为云计算,就是因为操作和计算是在服务器上发生的,而不是在自己的电脑(本地)上完成的。向服务器告知计算使命时所用的“语言”,称为API。目前已有JavaScript API(即Code Editor)、Python API、Julia API。
-- 那么老哥,for和while是跟GEE服务器对话的语言吗?
-- 不是。
-- 岂非说GEE服务器不看for语句?
-- 它不看。
-- 唉不对啊,我显着在Code Editor上面写了for循环却还能运行的,你说服务器不看for,难不成还能是欣赏器给我跑的?
-- 恭喜你答对了,还真是欣赏器给跑的。
要解释这个问题,需要对GEE的工作机制有一个大概的了解。
作为小白,我们在Code Editor上写出几行代码并点击Run的一刻,可能很少想过这之后发生了什么。实际上,你的代码其实并不是直接抄一份送给服务器去读,而是要先在本地端欣赏器上去做剖析,重写成一套服务器听得懂的哀求,然后才把哀求奉上去。
本地翻译的过程,在于剖析每一个由用户声明的变量,把它们各自整理成一套能引导服务器算出结果的结构——你可以明白成是每一个云端对象在本地端的代理,是影子。它们会跟云端的、承载实际数据和计算的对象相对应。
在《Deferred Execution(延迟运行)》的页面上[1],谷歌解释了这一本地剖析的过程:
When you write a script in Earth Engine (either JavaScript or Python), that code does NOT run directly on Earth Engine servers at Google. Instead, the client library encodes the script into a set of JSON objects, sends the objects to Google and waits for a response. Each object represents a set of operations required to get a particular output, an image to display in the client, for example.
当您在Earth Engine上编写脚本时(无论JavaScript照旧Python),该代码不会直接在谷歌的Earth Engine服务器上运行。相反,客户端库将脚本编码为一组JSON对象,将对象发送给谷歌并等待响应。每个对象表示得到特定输出所需的一组操作,例如,要在客户端上显示的图像。
进一步解释一下:代码写好之后,只会在本地做剖析。本地端剖析之后,会生成一些JSON对象。这些JSON对象是做什么用的呢?是客户端写给服务器的字条:“云云云云算一遍,把结果发给我。”
由此可以看出,本地端剖析代码所能做的,只是剖析代码,帮服务器提前明白该怎么服务而已,不涉及任何实际数据。它编写的JSON文件,只是云端数据在本地端的影子。生成这个影子的目的,大概说这个影子里所容纳的信息,就是指挥Google服务器以某某运算组合依次操作谁人云端上的对象。
这样就解释了为什么for循环只在本地端有意义。我们在for循环里写的统统,是一种过程而不是一种对象,只会平添本地端的剖析负担。for循环对服务器上计算过程产生影响之前,是要本地客户端来徒手拆循环的。
说句题外话,这其实很像在编纂纪传体史书。汗青的原始记载就像我们写的代码一样,关注的是事情从头到尾怎么进行,是一种过程。而纪传体史书则关注重要人物个体如何发展,这更像GEE服务器的关注点,是一种对象。把流水账般的事项记载(过程),编制成人物个体的发展历程(对象及其操作序次),并对互相影响的人物各自立传、一起成书,这些就是史官(和GEE API)的工作。
明白了本地端和服务器端对象的区别和联系,让我们再来讨论以for为代表的本地端循环,为什么不被推荐。
举个例子,我们这里有一个重复1万次的for循环,内里的内容是让一个ee.Number(服务器端的数)每次加 i:
[code]// 语法没错但跑不通的例子,只是因为用了 for 循环var num = ee.Number(0);for (var i=1; i |