在Google Earth Engine(GEE)这个强大的地理空间分析平台中,Array数据类型扮演着至关重要的角色。作为处理多维数值数据的核心容器,它比常规的Image或Feature对象更适合处理矩阵运算和数学转换。我在实际项目中经常发现,许多初学者对List和Array的区别感到困惑——简单来说,List是通用容器,而Array是专门为数值计算优化的结构。
Array的核心特征包括:
重要提示:GEE中的Array对象始终存储在服务器端,所有操作都通过远程过程调用执行,这与客户端的JavaScript数组有本质区别。
最直接的创建方式是通过ee.Array()构造函数:
javascript复制// 从JavaScript数组创建1D Array
const arr1D = ee.Array([1, 2, 3, 4]);
// 创建2D矩阵
const arr2D = ee.Array([
[1, 2, 3],
[4, 5, 6]
]);
// 创建特定形状的零值数组
const zeros = ee.Array.zeros([3, 4]); // 3行4列
在实际应用中,我们经常需要生成特定模式的数组:
javascript复制// 创建单位矩阵
const identity = ee.Array.identity(3); // 3x3单位阵
// 线性序列生成
const sequence = ee.Array.sequence({
start: 0,
end: 100,
step: 5,
count: 20 // 可选参数
});
// 随机数组生成
const random = ee.Array.random({
min: -1,
max: 1,
shape: [5,5]
});
处理遥感数据时,经常需要在不同维度间转换:
javascript复制const ndviArray = ee.Array([[0.1, 0.3], [0.5, 0.7]]);
// 获取维度信息
print(ndviArray.length()); // 第一维长度
print(ndviArray.shape()); // 完整形状[2,2]
// 维度扩展
const expanded = ndviArray.arrayRepeat(3, 1); // 在第二维度重复3次
// 转置操作
const transposed = ndviArray.transpose();
GEE Array支持完整的数学运算链:
javascript复制const a = ee.Array([1,2,3]);
const b = ee.Array([4,5,6]);
// 逐元素运算
const add = a.add(b); // [5,7,9]
const sin = a.sin(); // 计算正弦值
// 矩阵乘法
const matrixA = ee.Array([[1,2],[3,4]]);
const matrixB = ee.Array([[5,6],[7,8]]);
const dotProduct = matrixA.matrixMultiply(matrixB);
// 约简运算
const sum = a.reduce('sum'); // 6
const max = b.reduce('max'); // 6
遥感分析中经常需要在Array和Image间转换:
javascript复制// Image转Array
const image = ee.Image('COPERNICUS/S2/20210101T100319_20210101T100321_T32TQM');
const array = image.select(['B4','B3','B2']).toArray();
// Array转Image
const newImage = ee.Image(array.arrayFlatten([
['red_band', 'green_band', 'blue_band']
]));
处理表格数据时,Array能极大提升效率:
javascript复制const points = ee.FeatureCollection.randomPoints({
region: geometry,
points: 100
});
// 提取属性值到Array
const values = points.aggregate_array('property').map(function(val){
return ee.Number(val);
});
const valueArray = ee.Array(values);
处理大型数组时的关键要点:
ee.Array.slice()分批处理javascript复制// 分块处理示例
const largeArray = ee.Array.random(1000, 1000);
const chunkSize = 100;
for(let i=0; i<10; i++){
const chunk = largeArray.slice({
axis: 0,
start: i*chunkSize,
end: (i+1)*chunkSize
});
// 处理每个分块...
}
维度不匹配错误:
array.shape()输出array.reshape()调整客户端-服务器混淆:
ee.Array()包装内性能瓶颈:
ee.Algorithms.If()减少计算量通过一个完整案例展示Array的强大功能:
javascript复制// 加载Sentinel-2数据集
var collection = ee.ImageCollection('COPERNICUS/S2')
.filterDate('2020-01-01', '2020-12-31')
.filterBounds(geometry);
// 计算NDVI并转为Array
var ndviArray = collection.map(function(image){
var ndvi = image.normalizedDifference(['B8','B4']);
return ndvi.toArray();
}).toArray();
// 计算时间序列统计量
var meanSeries = ndviArray.arrayReduce({
reducer: ee.Reducer.mean(),
axes: [1,2] // 空间维度
});
// 可视化结果
var chart = ui.Chart.array.values({
array: meanSeries,
axis: 0,
xLabels: ee.List.sequence(1, 12) // 月份
});
在这个案例中,我们利用Array高效处理了:
展示如何用Array实现图像处理算法:
javascript复制// 定义3x3 Sobel边缘检测核
var sobelX = ee.Array([
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
]);
// 应用卷积
var convolve = function(image, kernel){
var array = image.toArray();
var kernelArray = ee.Array(kernel);
return array.convolve(kernelArray);
};
// 实际应用
var s2Image = ee.Image('COPERNICUS/S2/20210101T100319_20210101T100321_T32TQM');
var edges = convolve(s2Image.select('B8'), sobelX);
这种方法的优势在于:
javascript复制// 最佳调试方法
var sampledValues = array.sample({
region: geometry,
scale: 30
}).first().get('array');
print('Sampled values:', sampledValues);
// 小数组直接获取
var smallArray = ee.Array([[1,2],[3,4]]);
print('Full array:', smallArray.getInfo());
javascript复制// 2D数组热力图
var visParams = {
min: 0,
max: 1,
palette: ['blue', 'green', 'red']
};
Map.addLayer(array.arrayGet([0]), visParams, 'First band');
// 3D数组切片可视化
var slice3D = array.arraySlice({
axis: 0,
start: 3,
end: 4
}).arrayProject([1,2]);
虽然GEE的Array功能强大,但有时需要结合其他工具:
javascript复制// 导出到Google Drive进行进一步分析
Export.table.toDrive({
collection: ee.FeatureCollection([
ee.Feature(null, {array: array.toString()})
]),
description: 'array_export',
fileFormat: 'CSV'
});
// 在GEE之外处理时,注意:
// 1. 字符串格式的数组需要解析
// 2. 大数组需要分块处理
// 3. 坐标系信息需要单独保存
通过实际测试展示两者的差异:
javascript复制var testSize = 1000;
// Array测试
var arrayTest = ee.Array.ones([testSize, testSize]);
var arrayOp = arrayTest.add(1).reduce('sum');
// List测试
var listTest = ee.List.sequence(1, testSize*testSize);
var listOp = listTest.map(function(x){
return ee.Number(x).add(1);
}).reduce(ee.Reducer.sum());
// 执行时间比较
print('Array time:', arrayOp);
print('List time:', listOp);
典型结果:
根据多年使用经验,我总结出以下关键原则:
一个典型的优化案例:
javascript复制// 不推荐写法
var slowOperation = list.map(function(x){
return ee.Number(x).pow(2);
});
// 推荐写法
var fastOperation = ee.Array(list).pow(2);
虽然本文已经涵盖Array的核心用法,但在实际项目中还可以进一步探索:
例如,构建一个光谱指数计算库:
javascript复制var spectralIndices = {
NDVI: function(arr){
return arr.arrayGet([3]).subtract(arr.arrayGet([2]))
.divide(arr.arrayGet([3]).add(arr.arrayGet([2])));
},
EVI: function(arr){
// 实现EVI计算公式...
}
};
// 使用方式
var indices = spectralIndices.NDVI(image.toArray());