3. 矿区研究区划定与矢量数据管理
在矿区时序变化检测中,研究区边界不是一张“背景图”,而是后续所有统计、裁剪、采样与面积量算的空间基准。边界一旦错位,后面再精细的分类算法也会把变化量算到错误的位置上。本节围绕三件事展开:把矿区边界与开采面矢量导入 Earth Engine、用 FeatureCollection 组织并操作这些矢量、以及按矿区范围做裁剪与空间过滤,最后给出多矿区批量管理的组织方式。
一、矢量数据的导入与坐标基准
矿区边界常见的来源有三类:测绘或矿权管理部门提供的 SHP/KML、在 GIS 软件中手工勾绘的开采面、以及从高分辨率影像目视解译得到的范围。Earth Engine 支持直接上传 Shapefile(需打包为 zip,且同一压缩包内只放一组同名 .shp/.shx/.dbf/.prj)和 KML,上传后在 Assets 中生成一个 table 类型资产。需要特别注意的是坐标系:GEE 内部以 WGS84 经纬度存储几何,上传时若原始数据是 CGCS2000 高斯投影或地方独立坐标系,平台会做投影转换,但转换精度取决于 .prj 是否完整。因此导入后应先用面积做一次量纲校验——若某矿区在测绘报告中面积为 3.2 km²,而 geometry.area() 返回的数量级明显偏离,就说明投影或单位出了问题。
// 导入已上传的矿区边界资产
var mineBoundary = ee.FeatureCollection('projects/demo/assets/mine_boundary_2020');
// 面积校验(单位:平方米),与测绘面积对比
var areaCheck = mineBoundary.geometry().area(1);
print('边界面积(m²):', areaCheck);
开采面矢量通常按年份或按采区组织,导入时建议保留原始属性字段,例如 mine_id、mine_name、year、stage(剥离/采掘/复垦)。这些字段是后续批量管理和按时间筛选的依据,丢失后只能靠几何位置反推,代价很高。
二、FeatureCollection 的核心操作
FeatureCollection 是 GEE 中矢量数据的容器,一个矿区边界是一个 Feature,多个矿区合起来就是一个 FeatureCollection。日常最常用的操作可以归为四类:
- 筛选:
filterBounds()按空间范围筛选,filterMetadata()按属性筛选,例如只取year == 2022的开采面。 - 合并与融合:
merge()把多个 FeatureCollection 拼在一起;union()把几何合并为一个整体,适合把同一矿区多年的开采面合成总扰动范围。 - 几何运算:
geometry().buffer(30)生成缓冲带,difference()求差集,可用于扣除已复垦区域。 - 遍历与聚合:
map()对每个 Feature 施加同一函数,aggregate_sum()等做属性汇总。
一个容易踩的坑是:filterBounds() 默认只保留与范围相交的要素,但不会裁剪几何本身。若某开采面跨越矿区边界,筛选后它仍以完整几何存在,直接统计面积会偏大。此时应配合 intersection() 做真正的裁剪。
三、按矿区范围裁剪与空间过滤
裁剪分两个层次。第一层是影像裁剪:用 image.clip(mineBoundary) 把影像限制在矿区边界内,减少无效计算;第二层是矢量裁剪:用 feature.intersection(boundary) 把开采面裁到矿区范围内。两者配合,才能保证“统计的像元”和“统计的矢量”落在同一个空间口径上。
// 取某矿区边界
var oneMine = mineBoundary.filter(ee.Filter.eq('mine_id', 'M01'));
// 取该矿区 2022 年开采面,并裁剪到矿区边界内
var mining2022 = ee.FeatureCollection('projects/demo/assets/mining_faces')
.filter(ee.Filter.eq('mine_id', 'M01'))
.filter(ee.Filter.eq('year', 2022))
.map(function(f){
return f.setGeometry(f.geometry().intersection(oneMine.geometry(), 1));
});
// 影像裁剪
var img = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_123032_20220715')
.clip(oneMine);
print('裁剪后开采面积(m²):', mining2022.geometry().area(1));
空间过滤还有一个实用场景:当研究区跨越多景影像时,用 filterBounds() 从影像集合中挑出覆盖矿区的景,再逐景裁剪,比先裁后筛效率高得多。判断标准是影像的 system:footprint 与矿区边界相交,而不是影像中心点落在矿区内。
四、多矿区批量管理的组织方式
当矿区数量从 1 个增加到几十个时,逐个写变量名的方式会迅速失控。推荐的组织思路是“一张主表 + 统一字段 + 循环处理”。主表是一个 FeatureCollection,每个 Feature 代表一个矿区,字段至少包含 mine_id、mine_name、area_km2、start_year。所有开采面、复垦面矢量都带 mine_id 字段,通过这个键与主表关联。
批量处理时,用 mineBoundary.map() 对每个矿区执行同一套流程:筛选该矿区的开采面、裁剪影像、计算指标、把结果写回 Feature 属性。这样输出的仍是一个 FeatureCollection,可以直接导出为 CSV 或表格资产,每个矿区一行,便于横向对比。若矿区数量很大、单次计算超时,可改用 Export.table.toDrive() 分批导出,或在客户端用 ee.List 分块循环。
上图把“主表—开采面—批量处理—结果表”的关系画在一起,核心是 mine_id 这条关联键贯穿始终。实际执行时,先确认主表与开采面的 mine_id 取值完全一致(可用 aggregate_distinct 列出两边的编码做比对),再跑批量流程,能避免大量“筛不到要素”的静默错误。
五、结果如何判断是否可用
矢量管理做完后,至少做三项检查:其一,每个矿区的裁剪后开采面积不应大于其边界面积,若出现大于,说明 intersection 未生效或坐标系不一致;其二,多年开采面的并集面积应随时间单调不减(除非有复垦扣除),若某年突然缩小,多半是筛选条件写错;其三,导出结果表的行数应等于主表矿区数,缺行意味着某个矿区的筛选键不匹配。这三项检查不需要额外数据,用 GEE 自带的聚合函数即可完成,建议固化为每次批量运行后的固定动作。
print(collection.first()) 检查属性是否完整,确认 mine_id 等关键字段存在后再进入后续流程。