4、GEE核心对象与数据类型:EE对象、Image、ImageCollection、Feature、FeatureCollection、Geometry、Dictionary、List等,以及类型转换与检查
Google Earth Engine(GEE)的编程范式与传统的JavaScript或Python开发有本质区别。在GEE中,几乎所有操作都围绕着一组核心的服务器端对象展开。理解这些对象(EE对象)及其数据类型,是掌握GEE的“第一性原理”。本章将系统梳理GEE的核心对象体系,重点剖析Image、ImageCollection、Feature、FeatureCollection、Geometry、Dictionary、List等关键类型的特性、适用场景、类型转换与检查方法,帮助您建立正确的“云端计算”思维模型。
4.1 核心原理:一切皆“EE对象”的延迟计算模型
GEE的架构核心在于客户端-服务器端分离。您在Code Editor中编写的JavaScript代码,并非在本地浏览器中执行计算,而是被序列化为JSON请求,发送至Google的云端服务器执行。因此,所有通过ee.*构造函数创建的对象,都是服务器端代理对象(即EE对象),它们并不包含真实数据,而是包含“如何获取数据”的指令。
这种设计带来了两个关键特性:延迟执行与并行计算。延迟执行意味着代码中的var image = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20201001')并不会立即下载影像,而是构建了一个引用。只有当您调用Map.addLayer()、Export.image.toDrive()或print()等“终端操作”时,GEE才会触发实际计算。并行计算则允许GEE将大规模计算任务分解到数千台服务器上同时处理,这是其处理PB级遥感数据的基础。
理解这一原理至关重要:您不能像操作本地JavaScript数组一样直接对EE对象进行循环或条件判断。例如,if (image.bandNames().size() > 3)这种写法是错误的,因为image.bandNames()返回的是ee.List对象,其大小在客户端是未知的。正确的做法是使用ee.Algorithms.If()或image.expression()等服务器端逻辑。
4.2 核心对象详解:栅格与矢量的双翼
4.2.1 栅格对象:Image与ImageCollection
ee.Image是GEE中最基本的栅格数据单元,由波段(Bands)、投影(Projection)、属性(Properties)和地理变换(Geotransform)组成。一个Image可以包含多个波段,例如Sentinel-2影像通常包含13个波段。创建Image的方式包括:ee.Image('image_id')从ID加载、ee.Image.constant()创建常量影像、ee.Image.random()生成随机影像,或通过image.addBands()、image.select()等操作派生新影像。
ee.ImageCollection是Image的集合,代表时间序列或同一区域的多景影像。它是时间序列分析、影像合成和变化检测的基础。常用操作包括:filterDate()按日期过滤、filterBounds()按空间范围过滤、sort()按属性排序、first()获取第一景、median()计算中值合成等。理解ImageCollection的“集合”语义至关重要——它类似于数据库中的表,支持映射(map())、过滤(filter())和归约(reduce())操作。
4.2.2 矢量对象:Geometry、Feature与FeatureCollection
ee.Geometry定义了地理空间形状,包括点(Point)、线(LineString)、面(Polygon)及多部分几何体(MultiPoint等)。它只包含几何信息,不包含属性数据。创建方式如ee.Geometry.Point([lon, lat]),注意GEE中坐标顺序为经度在前,纬度在后(即[longitude, latitude])。
ee.Feature是Geometry与属性字典(Dictionary)的组合,即“几何+属性”。例如,一个表示城市边界的Feature,其Geometry是Polygon,属性可能包含城市名称、人口等。创建方式为ee.Feature(geometry, properties)。
ee.FeatureCollection是Feature的集合,是矢量数据操作的核心。它支持类似ImageCollection的操作,如filter()、map()、reduce()等。此外,还支持空间连接(join())、缓冲区(buffer())和空间统计(reduceRegions())等高级操作。
4.2.3 辅助数据类型:Dictionary、List与基础类型
ee.Dictionary是键值对集合,类似于JavaScript中的对象,但所有键和值都必须是EE对象。常用于存储影像属性、Feature属性或中间计算结果。ee.List是EE对象的数组列表,支持get()、add()、slice()等操作。此外,还有ee.Number、ee.String、ee.Date等基础类型,它们将JavaScript原生类型包装为服务器端对象,以支持延迟计算。
| 对象类型 | 核心用途 | 典型创建方式 | 常用方法示例 |
|---|---|---|---|
| ee.Image | 单景栅格影像 | ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20201001') | select(), addBands(), clip(), normalize() |
| ee.ImageCollection | 影像集合/时间序列 | ee.ImageCollection('COPERNICUS/S2_SR') | filterDate(), filterBounds(), median(), map() |
| ee.Geometry | 几何形状定义 | ee.Geometry.Point([lon, lat]) | buffer(), centroid(), intersection() |
| ee.Feature | 矢量要素(几何+属性) | ee.Feature(geom, {name: 'A'}) | set(), get(), geometry() |
| ee.FeatureCollection | 矢量要素集合 | ee.FeatureCollection([f1, f2]) | filter(), map(), reduceRegions() |
| ee.Dictionary | 键值对存储 | ee.Dictionary({key: value}) | keys(), values(), get() |
| ee.List | 有序列表存储 | ee.List([1, 2, 3]) | get(), add(), slice(), length() |
4.3 类型转换与检查:避免“类型陷阱”的必备技能
GEE开发中最常见的错误之一就是类型不匹配。由于客户端与服务器端的隔离,您无法直接使用JavaScript的typeof或instanceof来检查EE对象的类型。因此,掌握类型转换与检查方法至关重要。
4.3.1 类型检查方法
GEE提供了多种服务器端类型检查机制:
- 使用
instanceof操作符:在客户端代码中,可以使用image instanceof ee.Image来判断一个变量是否为ee.Image类型。但注意,这只能检查客户端包装类型,无法检查服务器端实际数据类型。 - 使用
ee.Algorithms.ObjectType():该函数返回对象的服务器端类型名称,如'Image'、'FeatureCollection'等。例如:print(ee.Algorithms.ObjectType(image))将输出'Image'。 - 使用
.type()或.class()方法:部分对象支持type()方法,返回其类型信息。例如image.type()返回一个ee.Number编码的类型标识。 - 使用
ee.Algorithms.IsEqual():用于比较两个对象是否相等,也可用于类型判断的辅助。
4.3.2 类型转换操作
类型转换是GEE编程中的高频操作,以下为常见转换场景:
// 1. Image 转 ImageCollection(单景转集合)
var singleImage = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20201001');
var imageCollection = ee.ImageCollection([singleImage]);
// 2. ImageCollection 转 List(获取影像列表)
var collection = ee.ImageCollection('COPERNICUS/S2_SR').limit(10);
var imageList = collection.toList(10);
var firstImage = ee.Image(imageList.get(0));
// 3. FeatureCollection 转 List
var fc = ee.FeatureCollection('FAO/GAUL/2015/level0');
var featureList = fc.toList(100);
// 4. List 转 FeatureCollection
var myList = ee.List([ee.Feature(ee.Geometry.Point([0, 0]), {id: 1})]);
var newFc = ee.FeatureCollection(myList);
// 5. Dictionary 转 List(获取键或值)
var dict = ee.Dictionary({a: 1, b: 2});
var keys = dict.keys(); // ee.List
var values = dict.values(); // ee.List
// 6. 数值类型转换
var num = ee.Number(10.5);
var intNum = num.toInt(); // 转为整数
var floatNum = num.toFloat(); // 转为浮点数
// 7. 字符串与数值互转
var str = ee.String('123');
var numFromStr = ee.Number.parse(str);
var strFromNum = num.format('%.2f');
4.4 实战场景:构建一个完整的土地利用分类流程
为了将上述概念融会贯通,我们设计一个典型的实战场景:基于Sentinel-2影像的某区域土地利用分类。该场景将综合运用ImageCollection、Image、Geometry、FeatureCollection、Dictionary和List等核心对象。
场景目标:提取某城市周边区域的建设用地、耕地、林地和水体四类土地覆盖类型。
关键输入数据:Sentinel-2 SR影像集合(2023年6月-9月无云影像)、研究区边界(FeatureCollection)、样本点(FeatureCollection)。
操作流程:
- 数据准备与过滤:加载Sentinel-2影像集合,按日期和云量过滤,并裁剪至研究区范围。
- 影像合成:使用
median()方法合成无云影像,减少云和阴影影响。 - 样本数据准备:将样本点FeatureCollection转换为训练数据,提取影像波段值作为特征。
- 分类器训练:使用
ee.Classifier.smileCart()训练决策树分类器。 - 分类与结果输出:对合成影像进行分类,并导出结果。
// 步骤1:加载与过滤
var s2 = ee.ImageCollection('COPERNICUS/S2_SR')
.filterBounds(roi)
.filterDate('2023-06-01', '2023-09-30')
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10));
// 步骤2:中值合成
var composite = s2.median().clip(roi);
// 步骤3:提取样本特征
var training = samplePoints.map(function(feature) {
return feature.select(['B2', 'B3', 'B4', 'B8', 'B11'], ['blue', 'green', 'red', 'nir', 'swir1'])
.copyProperties(feature, ['landcover']);
});
// 步骤4:训练分类器
var classifier = ee.Classifier.smileCart().train({
features: training,
classProperty: 'landcover',
inputProperties: ['blue', 'green', 'red', 'nir', 'swir1']
});
// 步骤5:分类与导出
var classified = composite.classify(classifier);
Map.addLayer(classified, {min: 0, max: 3, palette: ['red', 'yellow', 'green', 'blue']}, 'Landcover');
结果对比与复盘:通过对比分类结果与高分辨率影像,我们发现建设用地与裸地的混淆度较高。原因在于两者在可见光波段的光谱特征相似。改进方案包括:增加SAR数据(如Sentinel-1)作为辅助特征,或使用更复杂的分类器(如随机森林smileRandomForest)。此外,样本点的数量和质量直接影响分类精度,建议每类至少50个样本,并确保样本点均匀分布。
4.5 常见误区与总结复盘
误区一:混淆客户端与服务器端类型。例如,直接对ee.List使用JavaScript的length属性,或对ee.Date使用getFullYear()方法。正确做法是使用size()和get('year')。
误区二:忽略坐标顺序。GEE中所有几何坐标均为[经度, 纬度],与常见的[纬度, 经度]顺序相反。使用ee.Geometry.Point([lat, lon])会导致点落在错误位置。
误区三:在客户端循环中调用服务器端函数。例如for (var i = 0; i < list.size(); i++)是错误的,因为list.size()在客户端是未知的。应使用list.size().getInfo()(不推荐,会阻塞)或改用map()、iterate()等服务器端迭代方法。
经验总结:
- 始终使用
ee.*构造函数创建服务器端对象,避免直接使用JavaScript原生类型。 - 在编写复杂逻辑前,先用
print()和Map.addLayer()验证中间结果的数据类型和内容。 - 利用
ee.Algorithms.ObjectType()进行类型断言,尤其在调试阶段。 - 对于类型转换,优先使用GEE内置方法(如
toList()、toInt()),而非手动构造。 - 当需要将服务器端值用于客户端逻辑时,使用
.getInfo()或回调函数,但注意这会牺牲性能。