GEE:分块处理以降低内存压力

作者:CSDN @ _养乐多_

从 456535 毫秒,到粉丝的 2931 毫秒,再到 30 毫秒。从旧版本到新版本,在使用相同研究区和分块行列数的前提下,切分矢量边界的算法提升了 15000 多倍速度。

在 Google Earth Engine(GEE)平台上处理大型数据集时,加载整个数据集可能导致内存不足的问题。分块处理可以将数据划分为小块,逐块处理,从而有效降低内存压力。这对于处理大规模数据集、避免系统崩溃和提高算法性能至关重要。

本文将介绍如何通过分块处理,优化 GEE 中对大规模数据集的操作,以提高效率、降低资源消耗。

示例代码链接:https://code.earthengine.google.com/7968b77d6428308da33adf186b9b8b36?noload=true



一、旧版本

1.1 旧版本

GEE:内存超限?将研究区划分成规则的小块运算

程序执行时间: 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函数的参数类型说明
矢量数据集合featureCollectionvecSplitByRowCol 函数返回的矢量数据集合
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个,还不支持多个边界的最值求解。有空会改进。暂时的解决方法,如下图所示,可以将以下红框内的碎小边界删除,只保留最大的。

评论 55
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_养乐多_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值