在 Google Earth Engine (GEE) 中下载村庄级别的遥感影像图,最推荐使用的是 Sentinel-2(哨兵2号) 卫星数据。它提供免费的 10 米高分辨率多光谱影像,非常适合观察村庄尺度的农田、宅基地和厂房细节。本文以提取某村庄2013年、2019年、2025年的土地利用数据为例,通过 Python 和 geemap,介绍如何从 GEE 平台中批量下载土地利用数据并保存至本地。
第一步:明确影像筛选逻辑
在 GEE 中下载影像,我们通常不直接下载某“一张”照片,因为单张照片往往会有云层遮挡。最科学的做法是:
1.框定时间范围(例 2025年夏季 6月-9月,此时植被茂盛,地物对比度高)。
2.筛选出该时间段内所有覆盖研究区的影像。
3.过滤掉云量过高的影像。
4.将剩下的优质影像合成为一张“无云”的高清底图(通常使用 median() 取中值算法去云)。
第二步:将 Shapefile (shp) 上传至 GEE
在 GEE 中,需要将研究区域的 shp 文件作为“资产(Asset)”上传。
1.准备文件:确保您的 shapefile 包含必要的文件。通常,一个完整的 shapefile 至少需要包含 .shp、.shx、.dbf 和 .prj 这四个同名文件。
2.打开 GEE:登录您的 Google Earth Engine Code Editor。
3.上传资产:
①在界面左侧的面板中,点击 Assets 选项卡。
②点击红色的 NEW 按钮,然后在下拉菜单中选择 Shape files。
③在弹出的 "Upload a new shapefile asset" 窗口中,点击 SELECT 按钮,或者将准备好的 .shp、.shx、.dbf 和 .prj 文件一起拖拽进去。
④在 Asset Name 框中,为您的资产命名(例如:Village)。注意:名称只能包含字母、数字和下划线,且不能与已有的资产重名。
⑤点击窗口右下角的 UPLOAD 开始上传。
4.等待处理:上传和处理需要一些时间。可以在右侧面板的 Tasks 选项卡中查看上传进度。当任务栏中的齿轮变成蓝色的对号(✔️)时,说明上传成功。
5.导入代码:回到左侧的 Assets 面板,找到刚才上传的 Village。点击它名字右侧的向右箭头(Import into script),该矢量边界就会被导入到您的代码编辑器顶部(Imports 区域),默认变量名为 table。
第三步:编写GEE代码提取影像
由于时间跨度大,且需要考虑高分辨率,我们将:
①2013年使用 Landsat 8 (30米分辨率),因为当时 Sentinel-2 尚未发射。
②2019年和2025年使用 Sentinel-2 (10米分辨率),以获得更清晰的村庄土地利用细节。
GEE 核心下载代码:
// ==========================================// 村庄土地利用遥感影像提取 (2013, 2019, 2025)// ==========================================// 1. 将导入的矢量边界重新命名为 roi (如果顶部 imports 中叫 table,这里就写 var roi = table;)// 请确保您已在顶部的 Imports 区域将上传的资产导入!var roi = table; // 假设您导入的变量名是 table// 将地图中心定位到研究区域村庄,并显示红色空心边界Map.centerObject(roi, 15);Map.addLayer(roi, {color: 'red', fillColor: '00000000'}, '村庄边界');// 2. 定义一个去云和提取中值影像的通用函数functiongetCloudFree(collection, dateStart, dateEnd, bands) { return collection.filterBounds(roi) .filterDate(dateStart, dateEnd) .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10)) // 筛选云量小于10%的影像 .median() // 取中值,进一步去云和阴影 .clip(roi); // 裁剪到村庄边界}// 3. 提取 2013 年影像 (Landsat 8, 30m)var img2013 = getCloudFree( ee.ImageCollection("LANDSAT/LC08/C02/T1_L2"), '2013-05-01', '2013-10-31', // 选择生长季,便于区分地物 ['SR_B4', 'SR_B3', 'SR_B2'] // 提取真彩色波段 (红, 绿, 蓝));// 4. 提取 2019 年影像 (Sentinel-2, 10m)var img2019 = getCloudFree( ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED"), '2019-05-01', '2019-10-31', ['B4', 'B3', 'B2'] // 提取真彩色波段);// 5. 提取 2025 年影像 (Sentinel-2, 10m)// 注意:2025年数据获取取决于当前时间是否已有影像覆盖var img2025 = getCloudFree( ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED"), '2025-05-01', '2025-10-31', ['B4', 'B3', 'B2']);// 6. 可视化参数设置var visS2 = {min: 0, max: 3000, bands: ['B4', 'B3', 'B2']};var visL8 = {min: 7000, max: 12000, bands: ['SR_B4', 'SR_B3', 'SR_B2']};// 将影像添加到地图上以供预览Map.addLayer(img2013, visL8, '村庄 2013 (Landsat 8)');Map.addLayer(img2019, visS2, '村庄 2019 (Sentinel-2)');Map.addLayer(img2025, visS2, '村庄 2025 (Sentinel-2)');// ==========================================// 7. 导出高分辨率影像至 Google Drive// ==========================================// 导出 2013 年 (Landsat 8)Export.image.toDrive({ image: img2013.select(['SR_B4', 'SR_B3', 'SR_B2']), description: 'Cunzhuang_2013_L8', folder: 'GEE_Exports', // Google Drive 中的文件夹名 scale: 30, // Landsat 8 分辨率 region: roi.geometry().bounds(), // 导出边界的外接矩形 maxPixels: 1e13});// 导出 2019 年 (Sentinel-2)Export.image.toDrive({ image: img2019.select(['B4', 'B3', 'B2']), description: 'Cunzhuang_2019_S2', folder: 'GEE_Exports', scale: 10, // Sentinel-2 分辨率 region: roi.geometry().bounds(), maxPixels: 1e13});// 导出 2025 年 (Sentinel-2)Export.image.toDrive({ image: img2025.select(['B4', 'B3', 'B2']), description: 'Cunzhuang_2025_S2', folder: 'GEE_Exports', scale: 10, region: roi.geometry().bounds(), maxPixels: 1e13});
第四步:运行代码并下载数据
由于时间跨度大,且需要考虑高分辨率,我们将:
1.运行(Run):点击代码编辑器上方的 Run 按钮。您会在下方的地图窗口看到这三年的徐场村遥感影像叠加显示。可以勾选/取消勾选图层来对比变化。
2.执行导出任务:去代码编辑器界面最右侧的 Tasks(任务) 标签页。能够看到三个黄色的任务:Cunzhuang_2013_L8、Cunzhuang_2019_S2 和 Cunzhuang_2025_S2。
3.点击每个任务旁边的 蓝色 Run 按钮。在弹出的确认框中,直接点击右下角的 Run。等待任务完成(齿轮变成对号)。
4.打开 Google Drive,在 GEE_Exports 文件夹里就能找到这三张 .tif 格式的遥感影像了。
下载完成后,可以将这些 TIFF 文件导入 ArcGIS 中,进行后续的土地利用分类目视解译或面积统计分析了。
注意!!!
小编在导出数据时,出现任务条变成红色,并且右侧出现了一个带感叹号的红色警告小三角 (⚠)的情况。
就是说导出的是一张空图像。问题出在两颗不同卫星对“云量”这个属性的命名规则上:Sentinel-2(哨兵) 记录云量的属性名叫:CLOUDY_PIXEL_PERCENTAGELandsat 8(陆地卫星) 记录云量的属性名叫:CLOUD_COVER在我们之前共用的 getCloudFree 过滤函数中,使用了哨兵的 CLOUDY_PIXEL_PERCENTAGE。当这段代码去筛选 Landsat 8 时,系统发现 Landsat 8 根本没有这个属性,于是把所有的 Landsat 8 影像全给过滤掉了,导致最终合成了一张“空图片”,导出自然就失败了。解决方法:为了保证学术数据的严谨性,我们需要把 Landsat 8 和 Sentinel-2 的去云逻辑分开写。将代码中 第2步到第5步 的提取影像部分,替换为下面这段修复后的代码:// ==========================================// 修复后的影像提取逻辑 (区分 Landsat 和 Sentinel)// ==========================================// 1. 提取 2013 年影像 (Landsat 8, 30m)// 使用 CLOUD_COVER 属性var img2013 = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2") .filterBounds(roi) .filterDate('2013-05-01', '2013-10-31') .filter(ee.Filter.lt('CLOUD_COVER', 30)) // Landsat的云量属性,适当放宽到30%,median算法会消除残云 .median() .clip(roi);// 2. 提取 2019 年影像 (Sentinel-2, 10m)// 使用 CLOUDY_PIXEL_PERCENTAGE 属性var img2019 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") .filterBounds(roi) .filterDate('2019-05-01', '2019-10-31') .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20)) // 哨兵的云量属性 .median() .clip(roi);// 3. 提取 2025 年影像 (Sentinel-2, 10m)var img2025 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") .filterBounds(roi) .filterDate('2025-05-01', '2025-10-31') .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20)) .median() .clip(roi);
操作步骤:
1.替换完这几段提取逻辑后,直接点击 Run 重新运行代码。
2.再次去右侧 Tasks 面板点击 Cunzhuang_2013_L8 旁边的蓝色 Run 重新导出。这次由于匹配了正确的云量属性,Landsat 8 就能顺利找到当年覆盖村庄研究区域的高质量影像了。
当然如果你的研究区域是省市县这种大范围的,可以全部调用Sentinel-2(哨兵)数据,直接在第一部分的代码中将Landsat 8(陆地卫星) 数据掉换成Sentinel-2(哨兵)数据就可以。
本期推文到此结束,我们下期再见~