客户端与服务器

适用于 PythonJavaScript 的 Earth Engine 客户端库可将复杂的地理空间分析转换为 Earth Engine 请求。您为客户端库编写的代码可能包含对客户端对象的引用和表示服务器端对象的变量。

请务必区分 Earth Engine 对象与代码中可能存在的其他 Python 或 JavaScript 对象或基元。您可以通过在脚本中操控客户端“代理”对象来操控服务器上的对象。您可以将代理对象识别为以 ee 开头的任何内容。这些 Earth Engine 代理对象不包含任何实际数据,而只是服务器上对象的句柄。首先,我们来考虑一个客户端字符串对象(这不是代理对象):

var clientString = 'I am a String';
print(typeof clientString);  // string

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
client_string = 'I am a String'
print(type(client_string))  # str

从输出结果中观察到,客户端(网络浏览器或记事本)已解读并运行此代码,并确定该变量类型为 string。现在假设您希望 Earth Engine 能够对此字符串执行某些操作。为此,您需要将字符串封装在一个合适的容器中,然后将其发送给 Google。该容器就是代理对象。示例如下:

var serverString = ee.String('I am not a String!');
print(typeof serverString);  // object
print('Is this an EE object?',
    serverString instanceof ee.ComputedObject);  // true

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
server_string = ee.String('I am not a String!')
print(type(server_string))  # ee.ee_string.String
print(
    'Is this an EE object?', isinstance(server_string, ee.ee_string.String)
)  # True

从输出结果可以看出,ee.Stringobject,而非 string。更具体地说,它是一个 ee.computedObject,这意味着它是服务器上某个内容的代理对象。您可以将 ee.Thing 视为将内容放入容器以发送到 Google 的方式。您的客户不知道容器中的内容,但您可以通过输出容器内容来了解其中的内容:

print(serverString);  // I am not a String

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
print(server_string.getInfo())  # I am not a String

如需查看容器本身的显示效果,请输出对象的字符串表示形式:

print(serverString.toString());  // ee.String("I am not a String!")

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
print(server_string)  # ee.String({"constantValue": "I am not a String!"})

如果您出于某种原因需要使用在客户端中运行的 Python 或 JavaScript 来操控容器中的任何内容,请使用 getInfo() 获取容器的内容并将其赋给变量:

var someString = serverString.getInfo();
var strings = someString + '  Am I?';
print(strings);  // I am not a String!  Am I?

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
some_string = server_string.getInfo()
strings = some_string + '  Am I?'
print(strings)  # I am not a String!  Am I?

循环

由于客户端不知道服务器端 ee.Thing 对象中的内容,因此客户端操作(例如条件语句和 for 循环)不适用于这些对象。因此,为避免对 getInfo() 进行同步调用,请尽可能使用服务器函数。例如,请考虑以下两种创建列表的方法:

不推荐 - 客户端 for 循环

var clientList = [];
for(var i = 0; i < 8; i++) {
  clientList.push(i + 1);
}
print(clientList);

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
client_list = []
for i in range(8):
  client_list.append(i + 1)
print(client_list)

推荐 - 服务器端映射

var serverList = ee.List.sequence(0, 7);
serverList = serverList.map(function(n) {
  return ee.Number(n).add(1);
});
print(serverList);

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
server_list = ee.List.sequence(0, 7)
server_list = server_list.map(lambda n: ee.Number(n).add(1))
print(server_list.getInfo())

服务器端映射示例有点傻,因为您可以只使用 ee.List.sequence(1, 8) 创建相同的列表,但它说明了一些重要概念。第一个概念是 map(),它只是将相同的函数应用于列表中的所有内容。由于此函数是在服务器上执行的,因此 getInfo()print() 等客户端函数无法在映射的函数中运行。因此,必须将 i + 1 代码替换为等效的服务器端代码:ee.Number(n).add(1)。请注意,n 是一个仅在服务器上存在的对象。由于该函数不知道其参数的类型,因此需要将其转换为 ee.Number

(如需了解在客户端上运行哪些函数,请参阅“客户端和服务器函数”部分。)

另外值得注意的是,有时客户端功能会很方便。例如,上一个 for 循环可用于构建列表并将其封装在服务器端对象中:

var toServerList = ee.List(clientList);

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
to_server_list = ee.List(client_list)

请注意,客户端处理是在您的笔记本电脑或浏览器(即宿主机的 CPU)中完成的,因此效率可能不如使用 Earth Engine 在服务器上执行工作。此外,为避免出现意外结果,最好不要在脚本中混用客户端和服务器功能。条件部分提供了可能产生意外后果的示例。

条件语句

服务器端对象不一定适用于客户端函数,反之亦然。例如,请考虑服务器端布尔值变量的情况:

var myList = ee.List([1, 2, 3]);
var serverBoolean = myList.contains(5);
print(serverBoolean);  // false

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
my_list = ee.List([1, 2, 3])
server_boolean = my_list.contains(5)
print(server_boolean.getInfo())  # False

如以下示例所示,该变量不会在客户端条件中运行,因为它是服务器端对象。如需正确检查服务器端布尔值,请使用服务器端函数:

不推荐 - 客户端条件

var clientConditional;
if (serverBoolean) {
  clientConditional = true;
} else {
  clientConditional = false;
}
print('Should be false:', clientConditional);  // True!

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
if server_boolean:
  client_conditional = True
else:
  client_conditional = False
print('Should be False:', client_conditional)  # True!

推荐 - 服务器端条件

var serverConditional = ee.Algorithms.If(serverBoolean, 'True!', 'False!');
print('Should be false:', serverConditional);  // False!

如需了解 Python API 以及如何使用 geemap 进行交互式开发,请参阅 Python 环境页面。

import ee
import geemap.core as geemap
server_conditional = ee.Algorithms.If(server_boolean, 'True!', 'False!')
print('Should be False:', server_conditional.getInfo())  # False!

客户端和服务器函数

前面几部分介绍了混用客户端和服务器对象和函数的几点原因,这些原因表明混用会导致效率低下或不合理。哪些对象和函数是客户端的,哪些是服务器端的?一般来说,任何初始化为 ee.Thing 的对象都是服务器对象,并且该对象 ee.Thing.method() 上的任何方法都是服务器函数。PythonJavaScript 参考文档中显示的对象和函数是客户端的。如前所述,您可以使用客户端功能创建对象,然后通过将客户端对象提供给 Earth Engine 构造函数(例如 ee.String())来封装该对象。