作者:CSDN @ _养乐多_
从 456535 毫秒,到粉丝的 2931 毫秒,再到 30 毫秒。从旧版本到新版本,在使用相同研究区和分块行列数的前提下,切分矢量边界的算法提升了 15000 多倍速度。
在 Google Earth Engine(GEE)平台上处理大型数据集时,加载整个数据集可能导致内存不足的问题。分块处理可以将数据划分为小块,逐块处理,从而有效降低内存压力。这对于处理大规模数据集、避免系统崩溃和提高算法性能至关重要。
本文将介绍如何通过分块处理,优化 GEE 中对大规模数据集的操作,以提高效率、降低资源消耗。
示例代码链接:https://code.earthengine.google.com/7968b77d6428308da33adf186b9b8b36?noload=true
文章目录
一、旧版本
1.1 旧版本
程序执行时间: 456535 毫秒。
以前的时候记录了一个使用 for 循环将矢量数据划分成小区域以便解决内存超限问题,但是现在用户量越来越多,GEE能够给用户分配的内存越来越少,for 循环的计算速度又很慢,没有像 map 函数那样在服务器上并行计算快,也不能像 map 函数那样较少次数在客户端和服务器端进行数据传输,数据传输是比较耗时的过程。
结果展示,

1.2 粉丝改进代码
在旧版本博客的分享过程中,有粉丝朋友分享了自己在此基础上用 map 函数替换 for 函数的代码,写的很好,本文1.2节有代码链接和展示,但是裁剪后的矢量数据存在以下3个问题:(1)无法去除多余的矢量边界外的分块;(2)代码中使用了 getInfo 函数来读取坐标点,会降低计算速度;(3)丢失了顶部部分小块。
程序执行时间: 2931 毫秒。
结果展示,

二、新版本代码
为了解决以上问题,本人重新写了一份代码,使分块处理过程速度加快,结果不丢失任何一小块,且研究区矢量边界和分割后的小块边界适配。
2.1 代码链接
示例代码链接:https://code.earthengine.google.com/5b7e482f7557cc636c069c5e3db65b2e?noload=true
程序执行时间: 30 毫秒。写30毫秒是多次计算的均值,最快14毫秒。
2.2 核心函数
2.2.1 vecSplitByRowCol(table.geometry(), Row, Col)函数参数
执行分块程序的接口是vecSplitByRowCol函数,函数有三个参数,分别为矢量边界、行数、列数。返回值是一个featureCollection,该集合中包含所有分割后的小块的 feature 对象。
vecSplitByRowCol 函数的参数 | 类型 | 说明 |
---|---|---|
矢量边界数据 | 必须转为 geometry 对象,且格式 Polygon 而不是 MultiPolygon | 矢量边界数据,比如 table.geometry() |
行数 | 整数 | 想要分块的行数,比如5 |
列数 | 整数 | 想要分块的列数,比如5 |
返回值 | featureCollection | 返回一个矢量数据集合,包含了分割后每个小块的矢量数据 |
---|
2.2.2 getSmallVec(featureCollection, i, table.geometry())函数参数
根据 id 获取分块后的小矢量的接口是 getSmallVec 函数,函数有三个参数,分别是分块后的矢量集合,小块的 id 研究区矢量边界。
getSmallVec函数的参数 | 类型 | 说明 |
---|---|---|
矢量数据集合 | featureCollection | vecSplitByRowCol 函数返回的矢量数据集合 |
id | 整数 | 最后分块的 id,比如5,值域为 [0,分块的数量] |
研究区矢量边界 | 必须转为geometry对象 | 矢量边界数据,比如 table.geometry() |
2.3 示例代码
本人对核心代码进行了封装,访问该代码直接调用 API 即可。
代码示例如下所示。
Map.centerObject(table, 5);
// 记录程序开始时间
var startTime = new Date();
print('程序开始时间', startTime);
var eevp = require('users/949384116/lib:Tools/VectorProcessor')
var featureCollection = eevp.vecSplitByRowCol(table.geometry(), 5, 5)
.filterBounds(table.geometry())
.toList(100)
featureCollection.size().evaluate(function (nSize) {
// print('Feature Collection Size: ', nSize)
for (var i = 0; i < nSize - 1; i++) {
var smallVec = eevp.getSmallVec(featureCollection, i, table.geometry());
Map.addLayer(smallVec, {}, 'Vec ' + i);
// 这里可以使用分块后的矢量数据去处理自定义代码
}
});
// 记录结束时间
var endTime = new Date();
print('程序结束时间', endTime);
// 计算执行时间(以毫秒为单位)
var executionTimeMS = endTime - startTime;
var executionTimeStringMS = executionTimeMS.toString() + ' 毫秒';
print('程序执行时间: ' + executionTimeStringMS);
2.4 结果展示

三、问题答疑
【2024年3月13日】今天有粉丝问我为什么自己的研究区分割的结果只有一个点?
那是因为 vecSplitByRowCol 函数现在只支持 Polygon,不支持 MultiPolygon。
简单来说,就是分块对象是一个完整的研究区,如果你导入的研究区是由多个行政区的研究区组合而成,那就不行。所以只需要将你的多个小研究区合并成一个大的研究区就行。比如一个省原本有5个县的研究区,就需要将这5个县的研究区合并成一个大的完整的省的研究区进行再计算。
怎么合并矢量数据?
使用.union()
函数就行,代码如下,
var eevp = require('users/949384116/lib:Tools/VectorProcessor')
var featureCollection = eevp.vecSplitByRowCol(table.union().geometry(), 5, 5)
.filterBounds(table.geometry())
.toList(100)
【2024年6月6日】近几天有粉丝联系我说,分块代码中老是分出一个圆点。
针对这个问题,代码做了如下修改,
将代码15行
for (var i = 0; i < nSize; i++)
修改为
for (var i = 0; i < nSize - 1; i++)
【2024年11月19日】运行自己的研究区没有反应?
检查自己的矢量数据是不是有碎小闭合边界,如果有就删除,只保留面积最大的矢量边界。这是因为代码识别边界的最小值最大值时只能识别1个,还不支持多个边界的最值求解。有空会改进。暂时的解决方法,如下图所示,可以将以下红框内的碎小边界删除,只保留最大的。
