简介:本资源为面向GIS研究者、遥感分析人员及城乡规划从业者的高精度广东土地覆盖数据集,解决区域生态评估、城市扩张监测、农业用地识别等空间分析需求。数据包含10米分辨率栅格与省级行政边界矢量两类核心地理信息:2个.tif主文件(含WGS与UTM双坐标系版本)支持地类精细识别,1个.shp及其配套.shx、.dbf、.prj等共16个文件,完整构成可直接加载至ArcGIS或QGIS的标准化数据包,总大小163.29MB。已有274人学习下载,适用于环境建模、国土空间规划、灾害风险评估等实战场景。用户可直接调用tif进行重分类与叠加分析,结合shp开展行政区统计、缓冲区划分与空间查询;所有辅助文件(.tfw地理配准、.ovr金字塔、.xml元数据等)均已齐备,无需额外处理即可投入科研与项目应用。
1. 广东10米Landcover数据:不是“高清底图”,而是可参与空间建模的栅格分类体
很多人第一次打开GuangDong_Landcover_10m.tif,以为只是张带颜色的“广东卫星图”——点开属性才发现,它根本不是RGB影像,而是一张整型编码的分类栅格(Integer-coded land cover classification raster)。每个像素值对应一个预定义的地类代码(如1=常绿阔叶林、2=水稻田、3=城市建成区),而非反射率或灰度值。这意味着它不能直接做NDVI计算,但能立刻用于面积统计、景观格局指数(如PD、LPI、SHDI)、与人口/经济数据的空间叠加回归——这才是Landcover数据真正的生产力所在。
这份数据由ESRI在2020年发布,但关键不在“谁发的”,而在其双坐标系并行结构:GuangDong_Landcover_10m.tif(WGS84地理坐标系)和GuangDong_Landcover_10m_WGS.tif(WGS84 + GeoTIFF元数据增强版)共存,同时附带.prj、.tfw、.shp等完整GIS元数据链。这种设计不是冗余,而是为不同工作流预留接口:QGIS用户可直读WGS版做投影转换;ArcGIS Pro用户用WGS版配合.xml元数据自动识别分类字典;而.shp边界文件则专为区域掩膜(mask)和行政统计准备。对从事生态评估、国土空间规划或遥感验证的从业者来说,这组数据的价值不在于“看”,而在于“算”——它让一次裁剪、一次重分类、一次Zonal Statistics就能输出可上报的统计报表。
2. 解析Landcover编码体系:从tif像素值到语义标签的映射逻辑
2.1 栅格数据本质:整型分类图而非连续光谱影像
Landcover数据的核心是离散分类(discrete classification),其.tif文件存储的是16位无符号整型(UInt16),每个像素值代表一个地类ID。这与Landsat或Sentinel-2的反射率影像(Float32)有本质区别:前者是“类别标签”,后者是“物理测量值”。因此,任何试图用gdal_translate -scale拉伸对比度的操作都会破坏分类逻辑——你看到的“彩色图”只是GIS软件根据.vat.dbf或.xml中的颜色表做的可视化渲染,底层数值不可被当作连续变量处理。
提示:不要用
rasterio.plot.show()直接显示原始tif,它会把整型值当浮点渲染,导致色阶错乱。正确做法是先读取分类值,再映射颜色。
2.2 识别分类字典:三类元数据源交叉验证
该数据包提供三种分类字典来源,需全部核验以避免误读:
2.2.1.vat.dbf属性表(最权威)
GuangDong_Landcover_10m.tif.vat.dbf是ESRI标准的栅格属性表(Value Attribute Table),可用DBF阅读器或Pythondbfread库解析:
from dbfread import DBF import pandas as pd # 读取VAT表(注意路径) vat_path = "GuangDong_Landcover_10m.tif.vat.dbf" vat_table = DBF(vat_path, encoding='gbk') df_vat = pd.DataFrame(iter(vat_table)) print(df_vat[['VALUE', 'COUNT', 'Class_Name']].head(10))输出示例:
| VALUE | COUNT | Class_Name |
|---|---|---|
| 1 | 1245890 | Evergreen Broadleaf Forest |
| 2 | 8765432 | Paddy Field |
| 3 | 3456789 | Urban Built-up Area |
| ... | ... | ... |
VALUE列即像素值,Class_Name为中文地类名(GB2312编码),COUNT为该类像素总数。此表是分类体系的唯一事实源(Single Source of Truth),所有后续重分类必须以此为准。
2.2.2.xml元数据文件(含坐标与精度说明)
GuangDong_Landcover_10m_WGS.tif.xml包含关键元数据:
<SpatialDomain>中声明GCS_WGS_1984坐标系;<BandSpecificMetadata>下Category字段明确标注"Land Cover Classification";<QuantitativeAttribute>中Resolution为10.0米,且注明"Nominal ground sampling distance"—— 这是采样间隔,非绝对精度,实际分类误差需参考原始生产报告(本数据包未附)。
2.2.3.prj投影定义(WGS84地理坐标系)
广东省.prj内容为:
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]确认其为WGS84地理坐标系(经纬度),非UTM投影。所谓“UTM版本”实为用户自行重投影产物,原始包中并无UTM命名文件——这是常见误解点。
2.3 常见误读陷阱与验证方法
| 误操作 | 后果 | 验证方式 |
|---|---|---|
直接用gdalinfo查看STATISTICS_MINIMUM/STATISTICS_MAXIMUM | 得到错误的“值域范围”(如0-255),因GDAL默认按Byte类型读取 | gdalinfo -stats GuangDong_Landcover_10m.tif | grep "Type"确认Type=UInt16 |
| 用QGIS“图层属性→符号系统→单值渲染”手动设颜色 | 颜色与Class_Name不匹配,因未绑定VAT | 右键图层→“属性→符号系统→渲染类型:分类→值字段:VALUE→类:从VAT加载” |
对tif执行gdalwarp -t_srs EPSG:32649(UTM Zone 49N)后未重采样 | 像素变形,10m分辨率失效 | 重投影后用gdalinfo检查Pixel Size是否仍为(10.0,10.0)(地理坐标系下单位为度,需转为米) |
3. 实战:用Python完成Landcover数据标准化处理流水线
3.1 环境准备与依赖安装
本流程基于rasterio、geopandas、numpy构建,避免ArcGIS/ArcPy依赖,确保跨平台复现:
# 创建独立环境(推荐) conda create -n landcover-py python=3.9 conda activate landcover-py pip install rasterio geopandas numpy pandas scikit-image matplotlib # 安装dbfread用于读取.vat.dbf pip install dbfread注意:
dbfread默认UTF-8解码,但本数据.vat.dbf为GBK编码,需显式指定,否则中文乱码。
3.2 步骤一:读取栅格并提取有效分类值
import rasterio import numpy as np from rasterio.mask import mask from shapely.geometry import box # 1. 读取tif获取基础信息 with rasterio.open("GuangDong_Landcover_10m.tif") as src: profile = src.profile crs = src.crs transform = src.transform # 读取全图(内存敏感时改用windowed read) data = src.read(1) # Band 1 only nodata = src.nodata print(f"CRS: {crs}") print(f"Shape: {data.shape}") # 如 (12345, 23456) print(f"Data type: {data.dtype}") # uint16 print(f"Unique values: {np.unique(data)}") # 检查实际出现的VALUE逻辑说明:src.read(1)读取第一波段(Landcover为单波段),np.unique()返回所有出现的像素值。若结果含nodata值(如0或255),需在后续统计中排除。
3.3 步骤二:加载VAT表并构建分类映射字典
from dbfread import DBF import pandas as pd def load_landcover_dict(vat_path): """从.vat.dbf构建{VALUE: Class_Name}映射""" try: # GBK编码读取,兼容中文字段 table = DBF(vat_path, encoding='gbk') df = pd.DataFrame(iter(table)) # 清理空格,确保KEY为int mapping = {} for _, row in df.iterrows(): val = int(row['VALUE']) name = str(row['Class_Name']).strip() mapping[val] = name return mapping except Exception as e: print(f"VAT读取失败: {e}") # 备用方案:硬编码(仅作演示,实际必须用VAT) return {1: "Evergreen Broadleaf Forest", 2: "Paddy Field", 3: "Urban Built-up Area"} landcover_dict = load_landcover_dict("GuangDong_Landcover_10m.tif.vat.dbf") print("分类映射:", list(landcover_dict.items())[:5])参数说明:encoding='gbk'是关键,否则Class_Name字段为乱码;int(row['VALUE'])强制转为整型,避免字符串KEY导致匹配失败。
3.4 步骤三:用广东省边界.shp裁剪栅格(掩膜)
import geopandas as gpd # 读取矢量边界 gdf = gpd.read_file("广东省.shp") # 确保CRS一致(.shp通常为WGS84) if gdf.crs != crs: gdf = gdf.to_crs(crs) # 裁剪栅格 with rasterio.open("GuangDong_Landcover_10m.tif") as src: # 注意:mask函数要求geometry为list of dict,且需包含'geometry' shapes = [geom for geom in gdf.geometry] out_image, out_transform = mask(src, shapes, crop=True, nodata=nodata) out_meta = src.meta.copy() out_meta.update({ "height": out_image.shape[1], "width": out_image.shape[2], "transform": out_transform, "crs": crs }) # 保存裁剪后tif with rasterio.open("GuangDong_Landcover_Clip.tif", "w", **out_meta) as dest: dest.write(out_image)逻辑说明:mask()函数执行空间掩膜,crop=True自动裁剪至边界最小外接矩形;out_transform为新仿射变换矩阵,确保地理定位准确;out_meta更新尺寸与变换参数,避免写入错误元数据。
3.5 步骤四:按地类统计面积(平方公里)
# 1. 计算单个像素实地面积(WGS84下随纬度变化,此处简化用赤道近似) # WGS84下1度≈111.3km,故10米像素在赤道处面积约 (10/111300)^2 * 1e6 km² ≈ 0.00000081 km² # 更精确做法:用rasterio.warp.calculate_default_transform获取局部尺度 from rasterio.warp import calculate_default_transform _, _, _, _, _, pixel_area_km2 = calculate_default_transform( crs, crs, data.shape[1], data.shape[0], *rasterio.coords.BoundingBox(*rasterio.transform.array_bounds(data.shape[0], data.shape[1], transform)) ) # 实际中,我们用transform直接计算:pixel_width * pixel_height(单位:米) pixel_width_m = abs(transform.a) # transform.a为x方向像素大小(米) pixel_height_m = abs(transform.e) # transform.e为y方向像素大小(米) pixel_area_m2 = pixel_width_m * pixel_height_m pixel_area_km2 = pixel_area_m2 / 1e6 # 2. 统计各分类像素数 unique_vals, counts = np.unique(out_image[0], return_counts=True) # 排除nodata valid_mask = unique_vals != nodata unique_vals = unique_vals[valid_mask] counts = counts[valid_mask] # 3. 输出面积表 area_km2 = counts * pixel_area_km2 result_df = pd.DataFrame({ 'VALUE': unique_vals, 'Class_Name': [landcover_dict.get(v, f"Unknown_{v}") for v in unique_vals], 'Pixel_Count': counts, 'Area_km2': area_km2 }).sort_values('Area_km2', ascending=False) print(result_df.to_string(index=False, float_format="%.3f"))输出示例:
VALUE Class_Name Pixel_Count Area_km2 2 Paddy Field 8765432 87.654 1 Evergreen Broadleaf Forest 1245890 12.459 3 Urban Built-up Area 3456789 34.5684. 进阶技巧:构建可复用的Landcover分析模板与常见坑位规避
4.1 创建标准化分析模板(Jupyter Notebook结构)
为避免每次重复写代码,建议构建如下Notebook结构:
| 单元格 | 内容 | 说明 |
|---|---|---|
00_setup | import+load_landcover_dict() | 封装依赖与字典加载,支持一键切换数据源 |
01_load_and_clip | rasterio.open+mask+ 保存裁剪图 | 输入shp_path和tif_path,输出clip.tif |
02_reclassify | np.where()或skimage.measure.label | 示例:合并“旱地”“水浇地”为“耕地”,代码可配置化 |
03_zonal_stats | rasterstats.zonal_stats | 输入clip.tif和districts.shp,输出各区县地类面积表 |
04_visualize | matplotlib+rasterio.plot | 生成分类图+图例,导出PDF/PNG |
提示:将
02_reclassify模块化为函数,接受reclass_rules = {2:1, 4:1, 5:2}(原VALUE→新VALUE),比硬编码更易维护。
4.2 关键参数表:Landcover处理中不可忽略的5个数值
| 参数 | 位置 | 典型值 | 修改影响 | 验证命令 |
|---|---|---|---|---|
nodata | rasterio.open().nodata | 0或255 | 影响统计是否计入背景值 | gdalinfo -stats file.tif |
transform.a | rasterio.open().transform.a | 0.00008983(WGS84下10m≈0.00008983°) | 决定地理定位精度 | gdalinfo file.tif | grep "Pixel Size" |
dtype | rasterio.open().dtypes[0] | 'uint16' | 若误读为'uint8',VALUE>255类将溢出 | gdalinfo file.tif | grep "Type" |
crs | rasterio.open().crs | EPSG:4326 | 投影不匹配导致裁剪错位 | gdalinfo file.tif | grep "Coordinate System" |
count | rasterio.open().count | 1 | Landcover必为单波段,多波段则异常 | gdalinfo file.tif | grep "Band Count" |
4.3 三个高频报错及速查方案
错误1:ValueError: Input shapes do not overlap raster.
原因:.shp边界与.tif范围无交集(常见于CRS不一致或.shp为空几何)。
速查:
gdf.total_bounds # 查看.shp范围 rasterio.transform.array_bounds(data.shape[0], data.shape[1], transform) # 查看.tif范围若gdf.total_bounds为(0,0,0,0),说明.shp未正确加载几何。
错误2:rasterio.errors.RasterioIOError: Read or write failed.
原因:.tif文件损坏或权限不足(尤其Windows下路径含中文)。
速查:
# Linux/Mac file GuangDong_Landcover_10m.tif # 应返回 "GeoTIFF" # Windows PowerShell Get-Item GuangDong_Landcover_10m.tif \| Select-Object Length # 文件大小应>100MB错误3:KeyError: 255(在landcover_dict[v]时)
原因:VAT表中无VALUE=255记录,但栅格中存在该值(常为NoData或未分类像元)。
速查:
np.unique(data) # 查看实际VALUE list(landcover_dict.keys()) # 查看VAT中定义的VALUE # 解决:扩展字典或过滤 landcover_dict.setdefault(255, "NoData")4.4 一个实用技巧:用GDAL快速验证分类完整性
无需Python,一条GDAL命令即可检查分类值是否全部落入VAT定义范围:
# 提取所有像素值并去重 gdal_translate -of GTiff -ot UInt16 GuangDong_Landcover_10m.tif temp.tif # 用gdalinfo输出统计(需GDAL 3.4+) gdalinfo -stats temp.tif | grep "STATISTICS" -A 5 # 更直接:用gdal_calc.py生成值域报告 gdal_calc.py -A GuangDong_Landcover_10m.tif --calc="numpy.unique(A)" --outfile=/vsimem/unique.tif实际工作中,我习惯在数据入库前运行:
# 输出所有VALUE及其频次(文本格式,便于grep) gdalinfo -stats GuangDong_Landcover_10m.tif 2>&1 | grep -E "(Min|Max|STATISTICS)" | head -20若Min与Max之间存在大量未在VAT中定义的值,说明数据生产存在质量问题,需退回上游确认。
最后,记住一个铁律:Landcover数据的生命力不在“分辨率数字”,而在分类体系的严谨性与元数据的完备性。10米只是空间粒度,真正决定分析深度的,是你能否准确解读VALUE=2究竟是“双季稻”还是“单季稻”,以及Class_Name字段是否与《土地利用现状分类》国标严格对齐。这份广东数据包之所以值得深挖,正是因为它用.vat.dbf和.xml把分类语义钉死在了字节层面——而你的任务,就是把这串字节,变成可支撑决策的平方公里数。
本文还有配套的精品资源,点击获取