GEE代码总报错连环追问?城乡规划GIS数据处理实战教程(含:完整代码集)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

如果你正在做“GEE代码总报错连环追问?城乡规划GIS数据处理实战教程(含:完整代码集)”这类任务,最常见的痛点不是不会点按钮,而是复制了一段 Google Earth Engine 代码后,投影、类型、影像集合、区域边界、导出参数一个接一个报错。本文以城乡规划中常见的建设用地识别、NDVI分析、夜间灯光辅助判断和统计导出为例,整理一套可复用的 GEE 数据处理流程和完整代码。

引言:为什么城乡规划GIS数据处理很适合用GEE

城乡规划GIS数据处理经常需要同时处理遥感影像、行政边界、土地覆盖、道路水系、人口或夜间灯光等多源数据。传统桌面GIS软件可以完成这些任务,但在大范围、多时相、批量统计场景下,数据下载、裁剪、投影转换和镶嵌会消耗大量时间。

GEE,也就是 Google Earth Engine,适合处理这类任务,因为它把大量遥感数据和云端计算能力放在同一个平台中。你可以直接调用 Sentinel-2、Landsat、VIIRS 夜间灯光、ESA WorldCover 等公开数据,并用 JavaScript 代码完成筛选、云掩膜、指数计算、分类、统计和导出。

但 GEE代码报错也很常见,尤其是初学者容易遇到以下情况:

  • ImageImageCollection 混用。
  • 在服务端对象上使用客户端 JavaScript 方法。
  • 区域边界没有正确加载或字段名写错。
  • 导出时 scale、region、maxPixels 参数设置不合理。
  • 影像波段名不一致,导致 select 或 normalizedDifference 报错。
GEE代码报错与城乡规划GIS数据处理完整代码流程
GEE城乡规划GIS数据处理的典型流程:研究区、影像筛选、指数计算、建设用地识别、统计与导出。

背景:城乡规划中常见的GEE数据处理任务

在城乡规划、国土空间规划、城市更新和用地适宜性分析中,GEE常用于快速回答以下问题:

  • 某个城市或乡镇范围内,近几年建设用地扩张明显吗?
  • 哪些区域植被覆盖较低,可能存在硬化地表或开发建设活动?
  • 夜间灯光强度与城镇建成区范围是否一致?
  • 不同街道、乡镇、规划单元的平均NDVI、建设用地面积如何统计?
  • 如何把结果导出到 ArcGIS Pro、QGIS 或 Excel 中继续制图和汇报?

本文示例不追求替代专业分类模型,而是提供一套适合学习和实战初稿分析的 GEE完整代码。你可以把它用于课程作业、规划现状分析、遥感辅助判读和前期快速评估。

注意:GEE中的快速阈值法适合做初步识别和趋势分析。正式规划成果仍应结合高分辨率影像、地类调查、现状用地数据和实地核查。

原理:先搞清楚GEE代码为什么总报错

1. GEE对象分为服务端对象和客户端对象

GEE最大的学习门槛之一,是区分服务端对象和客户端对象。服务端对象包括 ee.Imageee.ImageCollectionee.FeatureCollectionee.Number 等,它们不会立刻在浏览器本地计算,而是在GEE服务器上延迟执行。

所以很多 GEE代码报错,其实是因为你把服务端对象当作普通 JavaScript 变量处理。例如,不建议直接对 ee.Number 使用普通的加减乘除,而应使用 addsubtractmultiply 等方法。

// 错误思路:把服务端 ee.Number 当作普通数字
// var area = ee.Number(100);
// print(area + 10);

// 正确写法
var area = ee.Number(100);
print(area.add(10));

2. ImageCollection不是Image

GEE初学者常见报错是:对影像集合直接调用只属于单景影像的方法。比如 Sentinel-2 筛选后得到的是 ImageCollection,需要通过 medianmeanmosaicfirst 转成 Image 后再计算指数。

var collection = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');

// collection 是 ImageCollection
// 需要合成后得到 image
var image = collection.median();

3. 城乡规划GIS数据处理要统一空间范围和分辨率

在城乡规划GIS数据处理中,不同数据源的分辨率和投影并不一致。Sentinel-2常用10米或20米波段,VIIRS夜间灯光分辨率更粗,行政区边界可能来自矢量数据。GEE可以自动处理很多重投影细节,但你在统计和导出时仍要明确 scaleregioncrs

步骤:GEE城乡规划GIS数据处理完整代码

下面是一套完整的GEE JavaScript代码,适合直接粘贴到 GEE Code Editor 中运行。你需要把研究区部分替换为自己的边界。代码包括:加载研究区、Sentinel-2云掩膜、NDVI和NDBI计算、建设用地初步提取、夜间灯光叠加、分区统计和结果导出。

步骤1:准备研究区边界

如果你已经把行政区、街道、乡镇或规划单元上传到 GEE Assets,可以使用自己的资产路径。下面代码给出两种写法:一种是使用自定义资产,另一种是临时绘制 geometry。

// ==========================
// 1. 研究区设置
// ==========================

// 方法A:使用你上传到GEE Assets的矢量边界
// 替换为自己的路径,例如:users/yourname/planning_area
// var roiFc = ee.FeatureCollection('users/yourname/planning_area');
// var roi = roiFc.geometry();

// 方法B:如果你在地图上手动画了一个 geometry,可直接使用
// 注意:geometry 需要在 GEE Code Editor 左侧 Imports 中存在
var roi = geometry;

// 地图定位
Map.centerObject(roi, 10);
Map.addLayer(roi, {color: 'red'}, '研究区边界');

如果这里报错 geometry is not defined,说明你没有在地图上画研究区,也没有导入名为 geometry 的对象。解决方法是:在 GEE Code Editor 左侧 Geometry Imports 中新建一个多边形,或改用自己的 Asset 路径。

步骤2:筛选Sentinel-2影像并做云掩膜

城乡规划中做地表覆盖分析,Sentinel-2是比较常用的免费数据源。这里使用 COPERNICUS/S2_SR_HARMONIZED,它是地表反射率产品,适合做NDVI、NDBI等指数。

// ==========================
// 2. Sentinel-2 云掩膜函数
// ==========================

function maskS2Clouds(image) {
  // SCL 是场景分类波段
  var scl = image.select('SCL');

  // 去除云、云影、雪等像元
  var mask = scl.neq(3)   // 云影
    .and(scl.neq(8))      // 中概率云
    .and(scl.neq(9))      // 高概率云
    .and(scl.neq(10))     // 薄卷云
    .and(scl.neq(11));    // 雪或冰

  return image.updateMask(mask)
    .divide(10000)
    .copyProperties(image, ['system:time_start']);
}

// ==========================
// 3. 筛选影像集合
// ==========================

var startDate = '2023-05-01';
var endDate = '2023-10-31';

var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
  .filterBounds(roi)
  .filterDate(startDate, endDate)
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
  .map(maskS2Clouds);

print('Sentinel-2影像数量', s2.size());

// 中值合成
var s2Median = s2.median().clip(roi);

// 真彩色显示
Map.addLayer(
  s2Median,
  {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3},
  'Sentinel-2真彩色'
);

如果 s2.size() 打印结果为0,后续代码大概率会连环报错。请优先检查日期、研究区范围、云量阈值和数据集名称。

步骤3:计算NDVI和NDBI

NDVI是归一化植被指数,常用于判断植被覆盖。NDBI是归一化建筑指数,常用于辅助识别建设用地或不透水面。两者不是规划用地分类的最终结果,但可作为城乡规划GIS数据处理中的快速判读指标。

// ==========================
// 4. 指数计算
// ==========================

// NDVI = (NIR - Red) / (NIR + Red)
// Sentinel-2: B8为近红外,B4为红光
var ndvi = s2Median.normalizedDifference(['B8', 'B4']).rename('NDVI');

// NDBI = (SWIR - NIR) / (SWIR + NIR)
// Sentinel-2: B11为短波红外,B8为近红外
var ndbi = s2Median.normalizedDifference(['B11', 'B8']).rename('NDBI');

Map.addLayer(
  ndvi,
  {min: -0.2, max: 0.8, palette: ['brown', 'yellow', 'green']},
  'NDVI植被指数'
);

Map.addLayer(
  ndbi,
  {min: -0.5, max: 0.5, palette: ['blue', 'white', 'red']},
  'NDBI建筑指数'
);

步骤4:用阈值初步提取建设用地

这里用一个简单规则:NDBI较高且NDVI较低的区域,更可能是不透水面或建设用地。这个方法简单直观,适合教学和快速分析,但不能完全区分裸地、工地、道路和建筑物。

// ==========================
// 5. 建设用地初步提取
// ==========================

// 阈值需要根据城市、季节、影像质量调整
var builtUp = ndbi.gt(0.05).and(ndvi.lt(0.35)).rename('BuiltUp');

// 掩膜显示,只显示疑似建设用地
var builtUpMask = builtUp.updateMask(builtUp);

Map.addLayer(
  builtUpMask,
  {palette: ['ff0000']},
  '疑似建设用地'
);

如果你发现农田裸土也被识别为建设用地,不一定是代码错了,而是阈值法本身的局限。可以结合夜间灯光、已有土地覆盖数据或人工样本进一步修正。

步骤5:叠加VIIRS夜间灯光辅助判断

夜间灯光可以辅助判断城镇活动强度。对于城乡规划中的城镇边界识别、中心城区扩张分析和建设强度判断,VIIRS数据有参考价值。但它分辨率较粗,不适合识别单个小区或单栋建筑。

// ==========================
// 6. VIIRS夜间灯光
// ==========================

var viirs = ee.ImageCollection('NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG')
  .filterBounds(roi)
  .filterDate('2023-01-01', '2023-12-31')
  .select('avg_rad')
  .mean()
  .clip(roi);

Map.addLayer(
  viirs,
  {min: 0, max: 30, palette: ['black', 'purple', 'yellow', 'white']},
  'VIIRS夜间灯光'
);

// 结合夜间灯光修正疑似建设用地
var builtUpWithLight = builtUp.and(viirs.gt(1)).rename('BuiltUp_Light');

Map.addLayer(
  builtUpWithLight.updateMask(builtUpWithLight),
  {palette: ['orange']},
  '夜间灯光修正后的疑似建设用地'
);

步骤6:计算建设用地面积

在城乡规划GIS数据处理中,面积统计是最常见的交付结果之一。GEE中可以用 ee.Image.pixelArea() 计算像元面积,再与分类结果相乘。

// ==========================
// 7. 面积统计
// ==========================

// 像元面积,单位为平方米
var areaImage = ee.Image.pixelArea()
  .divide(10000)
  .rename('area_ha'); // 转为公顷

// 建设用地面积影像
var builtAreaImage = areaImage.updateMask(builtUpWithLight);

// 对整个研究区求和
var builtArea = builtAreaImage.reduceRegion({
  reducer: ee.Reducer.sum(),
  geometry: roi,
  scale: 10,
  maxPixels: 1e13
});

print('疑似建设用地面积(公顷)', builtArea);

如果这里出现 Too many pixels,通常说明研究区太大或 scale 太小。可以提高 scale,例如设置为30、100,或只统计分区后的结果。

步骤7:按街道、乡镇或规划单元分区统计

如果你的研究区是一个 FeatureCollection,并且每个要素代表一个街道、乡镇或规划单元,可以使用 reduceRegions 批量统计。下面假设你有一个名为 roiFc 的分区边界。

// ==========================
// 8. 分区统计示例
// ==========================

// 如果你使用的是资产边界,请取消下面两行注释并替换路径
// var roiFc = ee.FeatureCollection('users/yourname/planning_units');
// var roi = roiFc.geometry();

// 如果暂时只有一个 geometry,可把它包装成 FeatureCollection
var roiFc = ee.FeatureCollection([
  ee.Feature(roi, {'name': 'study_area'})
]);

var zonalStats = builtAreaImage.reduceRegions({
  collection: roiFc,
  reducer: ee.Reducer.sum(),
  scale: 10
});

// 重命名统计字段,便于导出后阅读
zonalStats = zonalStats.map(function(feature) {
  var areaHa = feature.get('sum');
  return feature.set('built_area_ha', areaHa);
});

print('分区建设用地面积统计', zonalStats);

如果导出的表中没有你想要的分区名称,请检查矢量边界的字段名。例如你的字段可能叫 NAMEtown街道名XZQMC,需要在后续制表时使用正确字段。

步骤8:导出GeoTIFF和CSV结果

GEE结果通常需要导出到本地,再进入 QGIS、ArcGIS Pro 或 Excel 继续制图和汇报。栅格结果可导出为 GeoTIFF,分区统计表可导出为 CSV。

// ==========================
// 9. 导出栅格:疑似建设用地
// ==========================

Export.image.toDrive({
  image: builtUpWithLight.toByte(),
  description: 'GEE_BuiltUp_2023',
  folder: 'GEE_Export',
  fileNamePrefix: 'builtup_2023',
  region: roi,
  scale: 10,
  maxPixels: 1e13
});

// ==========================
// 10. 导出表格:分区统计
// ==========================

Export.table.toDrive({
  collection: zonalStats,
  description: 'GEE_BuiltUp_ZonalStats_2023',
  folder: 'GEE_Export',
  fileNamePrefix: 'builtup_zonal_stats_2023',
  fileFormat: 'CSV'
});

运行导出代码后,不会自动下载文件。你需要到 GEE Code Editor 右侧的 Tasks 面板,点击 Run,确认导出参数后等待任务完成。

常见坑:GEE代码报错连环追问的排查方法

1. geometry is not defined

原因:代码中使用了 geometry,但你没有在地图上绘制或导入这个变量。

  • 在地图上画一个多边形,并确保左侧 Imports 中变量名为 geometry。
  • 或者改用 ee.FeatureCollection 读取你上传的边界资产。

2. Image.select: Pattern did not match any bands

原因:你选择的波段名不存在。例如把 Landsat 的波段名写到 Sentinel-2 数据上,或数据集版本不同导致波段名不一致。

  • 先使用 print(image.bandNames()) 查看实际波段名。
  • Sentinel-2常见波段包括 B2、B3、B4、B8、B11、SCL。
  • Landsat Collection 2 的反射率波段常见命名为 SR_B2、SR_B3、SR_B4 等。
print('合成影像波段', s2Median.bandNames());

3. reduceRegion运行很慢或Too many pixels

原因:研究区过大、scale太小、统计影像波段太多,都会导致计算量过大。

  • 只选择需要统计的单个波段。
  • 适当增大 scale,例如从10改为30或100。
  • 设置合理的 maxPixels
  • 大范围任务优先使用 reduceRegions 分区统计。

4. 导出结果为空白

原因可能有多个:研究区没有覆盖影像、掩膜条件过严、阈值设置不合理、region传错对象。

  • 先把原始真彩色影像添加到地图,确认研究区内有数据。
  • 检查 s2.size() 是否大于0。
  • 降低云量限制或扩大日期范围。
  • 临时放宽阈值,例如把 ndbi.gt(0.05) 改为 ndbi.gt(0) 测试。

5. 分区统计字段全是null

原因:分类影像在分区内没有有效像元,或掩膜后统计区域被完全遮掉。

  • 检查 builtUpWithLight 图层是否在地图上显示。
  • 尝试不用夜间灯光修正,直接统计 builtUp。
  • 检查矢量边界是否与影像位置重叠。

方法比较:GEE、QGIS、ArcGIS Pro在城乡规划GIS数据处理中的分工

方法 适合任务 优势 限制
GEE 大范围遥感影像处理、多时相分析、快速统计导出 无需批量下载原始影像,适合云端计算和快速试验 需要写代码,复杂制图和精细编辑不如桌面GIS直观
QGIS 结果检查、专题制图、矢量编辑、空间叠加 免费开源,插件丰富,适合教学和日常GIS处理 大范围遥感批处理对本机性能要求较高
ArcGIS Pro 规划成果制图、地理数据库管理、空间分析模型构建 工程化能力强,适合规范化成果生产 软件授权和环境配置成本较高
Python GIS 自动化处理、批量转换、模型训练、结果复核 灵活,可与 GeoPandas、Rasterio、GDAL、Pandas 结合 环境依赖较多,初学者容易遇到安装问题

比较稳妥的工作流是:用GEE完成遥感数据筛选、指数计算和初步统计;导出GeoTIFF和CSV后,用QGIS或ArcGIS Pro做成果检查、边界叠加、图面整饰和最终制图。

检查清单:运行GEE完整代码前先看这10项

  • 研究区是否正确:Map.addLayer后能否看到红色边界。
  • 影像数量是否大于0:检查 print(‘Sentinel-2影像数量’, s2.size())
  • 日期是否合理:尽量选择云少、植被状态稳定的季节。
  • 波段名是否存在:bandNames() 查看,不要凭记忆写。
  • ImageCollection是否已合成:计算NDVI前应得到单个 ee.Image
  • 阈值是否适合本地:NDBI和NDVI阈值需要根据城市景观调整。
  • 夜间灯光是否过度筛选:乡村建设用地可能夜间灯光较弱。
  • 统计单位是否清楚:本文面积结果使用公顷。
  • 导出scale是否匹配:Sentinel-2常用10米,但大范围统计可适当放大。
  • 是否点击Tasks运行:Export代码只创建任务,不会自动开始导出。

FAQ:GEE代码报错与城乡规划GIS数据处理常见问题

Q1:这套GEE完整代码能直接用于正式建设用地认定吗?

不建议直接作为正式认定成果。本文代码适合城乡规划GIS数据处理中的快速识别、趋势分析和教学实训。正式建设用地认定应结合权威地类数据、高分辨率影像、规划用地数据和人工核查。

Q2:为什么我的GEE代码没有报错,但地图上没有结果?

这通常不是语法问题,而是数据筛选或掩膜问题。请检查研究区是否正确、影像集合是否为空、云掩膜是否过严、NDVI和NDBI阈值是否过高,以及夜间灯光条件是否把有效区域过滤掉。

Q3:Sentinel-2的B11是20米,B8是10米,计算NDBI会不会有问题?

GEE会在计算过程中处理重采样和投影问题,但统计和导出时你仍应明确scale。对于规划尺度的快速分析,设置 scale 为10或20都可以测试;如果更关注稳定统计,可以用20米或30米并与结果精度要求匹配。

Q4:如何把GEE导出的结果拿到QGIS或ArcGIS Pro中制图?

栅格结果用 Export.image.toDrive 导出为GeoTIFF,表格结果用 Export.table.toDrive 导出为CSV。下载后,在QGIS或ArcGIS Pro中加载GeoTIFF,再叠加行政区、道路、水系、规划边界等矢量数据进行制图。

Q5:为什么同一段GEE代码别人能运行,我这里总报错?

最常见原因是变量名、资产路径、研究区边界和数据集版本不同。请优先检查 geometry 是否存在、Asset路径是否有权限、波段名是否匹配、日期范围内是否有影像。不要只看报错最后一行,要回到第一个出错对象排查。

Q6:城乡规划中只用NDVI和NDBI够吗?

不够。NDVI和NDBI适合做快速判读,但容易混淆裸地、工地、道路、屋顶和干旱农田。更可靠的做法是结合土地覆盖产品、夜间灯光、POI、道路密度、建筑轮廓、规划用地数据和人工样本分类。

结论:先把流程跑通,再优化分类精度

GEE代码报错并不可怕,关键是按对象类型、数据范围、波段名称、掩膜条件和导出参数逐项排查。对于城乡规划GIS数据处理,建议先用本文这套完整代码跑通研究区、影像合成、NDVI、NDBI、建设用地提取、面积统计和导出流程。

当基础流程稳定后,再根据项目要求继续优化:调整阈值、加入更多特征、使用监督分类、叠加现状用地数据、在QGIS或ArcGIS Pro中做人工复核。这样处理,既能减少GEE代码报错带来的连环追问,也能让规划分析结果更可解释、更容易交付。