☰
全国二普土壤类型数据实操指南:坐标系·编码·栅格化避坑全解
2026/10/11 14:41:21 网站建设 项目流程

简介:全国第二次土壤普查土壤类型数据集是一份面向土地资源管理、农业区划与生态保护工作者的GIS数据包,帮助使用者快速掌握全国及分省土壤类型分布,支撑土壤查询、制图与空间分析。压缩包共269个文件,整体约25.33MB,核心是32个面状矢量图层,配合dbf属性表、prj投影文件、doc和txt说明文档,其中dbf可查看各图斑的土壤属性,prj用于坐标转换,txt记录了土壤分类体系,另有gson等格式便于Web端轻量展示,可在ArcGIS、QGIS等平台直接打开并叠加气候、地形、植被等环境图层。目前已有3169人学习。资源既有全国土壤矢量底图,也细化为分省土壤矢量,便于开展省级尺度的土壤利用评估;同时配套土壤分类体系说明,可辅助理解中国土壤系统分类的层级、命名与转化关系。对于农业科研、土地利用规划、环境保护和气候变化适应等场景,这份数据都能提供底图支撑,适合科研人员、高校学生及基层规划人员用于土壤数据查询、专题图制作和初步研究。

1. 为什么这张全国土壤类型底图还在被反复下:年代久远并不等于不能用

做区域土壤属性制图、农业区划或者环境建模的时候,最难找的往往不是算法,而是一张能覆盖全域、分类口径统一的土壤类型底图。第三次全国土壤普查的数据还在分批发布,很多项目却等不到那天,这时候一个被反复下载了十几年的资源就派上了用场:全国第二次土壤普查土壤类型数据集。它的图斑来自上世纪七十年代末到八十年代初的实地调查,分类用的是发生学体系,但土类、亚类两级在区域尺度上非常稳定,拿来当底图、当训练样本、当流域归类依据都够用。适合GIS与遥感从业者、土壤学科研人员,也适合做环境影响评价时急需一张可靠土壤图的人。问题是数据集拿回来之后怎么读、怎么转、怎么不翻车,这篇就按实操路径拆一遍。

2. 数据集长什么样:shp/tif/dbf 三件套、坐标体系与属性字段清单

2.1 核心文件组成:shp、tif、dbf 各负责什么

公开渠道能下载到的二普土壤类型数据集,通常不是一个孤立的文件,而是一组打包文件。最常见的形态是:一个全国范围的矢量面图层(shapefile),一个栅格版本(tif),再加一张分类说明表(Excel 或 dbf),有的版本还附带土壤属性表(有机质、pH、全氮等,来自典型剖面)。先把文件认全,后面才不会在加载阶段就卡住。

shapefile 其实是多文件组合,拿到手至少要有 .shp、.shx、.dbf、.prj 四个文件,缺一个都可能加载异常。.shp 存几何形状,.shx 是索引,.dbf 是属性表,.prj 是坐标系定义文件。这里要特别说明:很多早期的资源包里 .prj 是缺失的,或者写的是老式 ESRI 格式,直接导致后续投影判断失误,这一点在避坑章节会重点讲。

文件类型常见格式内容说明
矢量面.shp/.shx/.dbf/.prj土壤图斑边界与分类属性主用文件
栅格版.tif/.img按土类编码的栅格图直接参与建模时用
分类说明.xlsx/.csv编码与土类名称对照建议先读这个
典型剖面属性.dbf/.txt各土种理化性质做回归或制图时参考

加载时的优先级有讲究。我一般先加载 shp 面图层,因为它的属性表里带土类、亚类、土属、土种多级信息;tif 版虽然使用方便,但如果制作方在栅格化时只保留了土类级别,后续想细分亚类就得回到矢量版重转。所以拿到资源包,先打开 shp 确认字段完整性,再决定是否信任 tif。

2.2 坐标系不能想当然:从投影参数反查数据出处

二普数据的年代决定了它的坐标系大概率不是你默认的 WGS84 或 CGCS2000。常见两种情况:一种是采用 Krasovsky_1940 椭球体上的 Albers 等积圆锥投影,另一种是 Gauss-Kruger 投影,可能基于北京54坐标系。如果你在软件里看到的坐标值像是几百上千万的大数,但地图却和在线底图对不上,基本就是投影没读对。

判断方法很直接。在 QGIS 里右键图层,选「图层属性 → 信息」,看坐标系一栏;如果显示 Unknown CRS,就要人工指定。我的经验是先从资源包的说明文件里找投影关键词,很多发布版本会带一句「Albers 等积圆锥投影,中央经线 105°E,双标准纬线 25°N 和 47°N」之类的参数说明,按这个参数在 QGIS 里创建自定义 CRS,图斑位置立刻就对了。

还有一种更隐蔽的情况:prj 文件里有投影名,但实际数据是另一种投影。这属于制作方在导出时改了投影却不更新 prj 文件,单看属性没用,得把图层叠到某省省的行政边界或河流矢量上做目视核对。做这一步别怕麻烦,二普图斑边界本来就和地形关系密切,如果发现山地土壤图斑整体悬在平原上空,那就是坐标系标定错了。

2.3 属性表读不出来的真正原因:编码在作怪

把 shp 拖进 ArcGIS 或 QGIS,图斑显示出来了,但属性表里的中文全是问号或者乱码,这不是数据损坏,是 dbf 文件的编码问题。dbf 格式本身不强制记录编码,早年采集的数据多是 GBK(或 GB2312)编码,而现代 GIS 软件默认按 UTF-8 读取,于是出现了「图对了字乱了」的诡异局面。

处理方式分软件说。QGIS 里可以在加载 shp 时手动指定编码:数据源管理器 → 编码选择 GBK,图层的属性表中文就正常了,这个方法不用改文件。ArcGIS 则麻烦一点,需要通过「表选项 → 导出」或在 Catalog 里设置代码页。如果用 Python 处理,直接指定 encoding 参数更省事。下面这段代码在 Jupyter 里跑一次,就能同时验证几何和属性:

import geopandas as gpd # 读取二普土壤类型 shp,编码按常见情况先试 GBK gdf = gpd.read_file(r"path/to/soil_2nd_survey.shp", encoding="gbk") # 如果上面报错或乱码,再试 gb18030,兼容性更好 # gdf = gpd.read_file(r"path/to/soil_2nd_survey.shp", encoding="gb18030") # 查看前五行属性 print(gdf.columns.tolist()) print(gdf.head())

这段代码的关键是 encoding 参数:GBK 是常见选择,但如果字段里有生僻字或繁体字,GB18030 是 GBK 的超集,能覆盖的字符更多。打印 columns 和 head 可以快速确认是不是读对了——如果字段名正常显示为「土类」「亚类」「土属」「土种」而不是乱码,编码这关就算过了。拿到了可读的属性表,下一步才能谈分类筛选和重分类。

3. 先看分类体系再动手:发生学分类的土类/亚类/土属/土种层级与符号定义

3.1 从属性表的字段推分类层级关系

二普调查用的分类体系是发生学分类,和现在三普推行的中国土壤系统分类是两个体系。前者强调成土条件和地理地带性,后者强调诊断层和诊断特性。这意味着拿到二普数据后,不要下意识用系统分类的思维去理解字段。常见的字段命名是 TLE 表示土类、TLA 表示亚类、SU 表示土属、TZH 表示土种,不同数据版本字段缩写可能有差异,但层级逻辑一致:土类是最粗的分类,亚类从土类里分出来,土属和土种依次更细。在属性表里看某一条记录,往往是「棕壤—棕壤性土—酸性岩棕壤—某某土种」这样一串。

这里有个实用技巧:先用土类字段做唯一值统计,确认数据覆盖的类别数量。二普资料的土类级别通常在 50 到 60 个左右,某平台发布的简化版可能只保留 12 个主要土类(红壤、黄壤、棕壤、褐土、暗棕壤、黑土、黑钙土、栗钙土、潮土、沼泽土、盐碱土、水稻土等),这是因为原始图斑中小类别太多,制图综合时做了合并。如果统计出来的类目数和说明文档对不上,就要警惕是不是读错了字段。

在 ArcGIS 或 QGIS 中做统计不需要写代码,直接用字段的「统计」功能就行。QGIS 的字段面板里右键字段名选「统计」,可以看到唯一值个数;ArcGIS 用「属性表 → 按属性选择 → 切换选择」也能确认。想快速定位大类的空间分布,建议开一个地图窗口,用土类字段做唯一值渲染,颜色一铺开,地带性规律立刻清楚——东部红壤黄壤、北方黑土黑钙土、西北栗钙土灰钙土,这个视觉检查比任何脚本都直观。

3.2 用符号化快速验证图斑完整性

符号化不只是为了好看,更是验证数据质量的手段。把渲染方式改成「唯一值」,分类字段选土类,软件会为每个土类分配颜色。这一步能暴露很多问题:如果地图上出现大量零散碎斑,说明图斑边界在数字化时被过度切割;如果某个土类只出现在图幅边缘的一条窄带上,可能是裁切出了问题,而不是真实分布。

我一般还会配一个半透明效果,叠加在线影像或地形阴影。二普土壤图斑和地形的关系非常密切:山地丘陵区对应淋溶土或雏形土,平原区对应水成土或人为土。如果看到某个图斑横跨了山脊和平原两种完全不同的地貌,基本可以判断是配准误差或边界拓扑错误,后面做分析时这类图斑要么修要么剔除。

3.3 按大类提取与重分类:SQL 查询与字段计算

实际项目里很少直接拿全部分类去做分析,通常只需要某一类或几类土壤。比如做水稻土分布调查,就只提取土类为水稻土的图斑。在 QGIS 里可以用「按表达式选择」:字段名 = '水稻土',选中后导出新图层。ArcGIS 里对应的是 Select by Attributes,SQL 写法如下:

SELECT * FROM soil_layer WHERE 土类 = '水稻土'

如果字段是英文 TLE,就写WHERE TLE = '水稻土'。注意中文值两边要加单引号,字符串比较是精确匹配,不能写TLE LIKE '%水稻%',除非确定字段里带了其他前缀。

重分类是另一个高频需求。比如要把 12 个土类归并成「地带性土壤 / 非地带性土壤 / 人为土」三类,不要在原始图层上直接改字段,建议新建一个字段,用条件赋值。QGIS 字段计算器里可以这么写:

CASE WHEN 土类 IN ('红壤','黄壤','棕壤','褐土','暗棕壤','黑土','黑钙土','栗钙土') THEN '地带性土壤' WHEN 土类 IN ('潮土','沼泽土','盐碱土') THEN '非地带性土壤' WHEN 土类 = '水稻土' THEN '人为土' ELSE '其他' END

这段表达式的逻辑很直接:用 IN 做多值匹配,比连续写 OR 条件清爽得多。重分类后务必再做一次统计,检查「其他」类的占比。如果其他类超过了 5%,说明原始类目里还有没列全的土类,回到统计结果里补全条件,避免静默丢数据。这类「字段计算 + 统计验证」的组合拳,是处理分类数据的基本功。

4. 落地应用的两条主线:直接制图与转栅格参与建模

4.1 重投影:坐标系不统一的处理流程

二普数据集是 Albers 等积圆锥投影还是高斯克吕格投影、基于北京54 还是西安80,直接决定了你能不能把它和其他数据叠在一起。大部分在线底图和遥感数据是 WGS84 或 CGCS2000 经纬度,而二普图斑是投影坐标,单位是米。所以落地第一步永远是统一坐标系。

用 GeoPandas 重投影很简单,调用to_crs时传入目标 EPSG 代码就行。如果你要配合 SRTM 高程数据做地形分析,目标坐标系最好和 DEM 一致,否则后面采样时要二次重采样,精度会有损失。示例代码:

import geopandas as gpd # 读取原始数据,编码沿用前面的 GBK gdf = gpd.read_file("soil_2nd_survey.shp", encoding="gbk") # 查看当前坐标系 print(gdf.crs) # 统一到 WGS84 经纬度,方便叠加在线底图 gdf_wgs = gdf.to_crs("EPSG:4326") # 或者统一到 CGCS2000 / 3 度带 Gauss-Kruger 投影 gdf_cgcs = gdf.to_crs("EPSG:4547") # 具体带号按区域选 # 写回新文件,注意编码 gdf_cgcs.to_file("soil_2nd_survey_cgcs.shp", encoding="utf-8")

这里的关键点有两个。第一,to_crs的输入可以是 EPSG 代码字符串,也可以是不存在 EPSG 的 Albers 参数,但最小化麻烦的办法是先把数据转到 EPSG:4326 确认位置无误,再转到项目需要的投影;第二,输出 shp 时把编码写成 UTF-8,但 dbf 的编码问题在写文件时依然存在,建议同时输出一个 GeoPackage(gpkg)格式,能彻底绕开 dbf 编码。GeoPackage 是 SqLite 容器,没有 dbf 那种编码历史包袱,QGIS 和 ArcGIS Pro 都原生支持。

4.2 矢量转栅格:指定字段、分辨率与像元对齐

栅格化的需求来自两个方向:一是要用 CNN 或随机森林做分类,输入必须是栅格;二是想把土壤类型作为环境变量跟地形、气候数据堆在一起,也需要统一的栅格网格。转栅格时最容易忽略的是像元对齐——直接用软件默认参数生成的栅格,分辨率、起始点和你的 DEM 差一点,后面采样就是一层隐患。

用 Rasterio 做栅格化可以完全控制这些参数。先读一个参考栅格(比如 DEM),拿它的 transform、width、height 当作模板,再调用features.rasterize把矢量画上去:

import geopandas as gpd import rasterio from rasterio.features import rasterize import numpy as np # 读取矢量,这里已经把土类编码成整数 gdf = gpd.read_file("soil_2nd_survey_cgcs.shp", encoding="utf-8") # 土类转到整数编码,方便栅格化 gdf["code"] = gdf["TLE"].astype("category").cat.codes + 1 # 以已有 DEM 为模板,保证像元严格对齐 with rasterio.open("dem_albers.tif") as ref: transform = ref.transform width = ref.width height = ref.height crs = ref.crs # 栅格化,背景值用 0 shapes = [(geom, code) for geom, code in zip(gdf.geometry, gdf["code"])] raster = rasterize(shapes, out_shape=(height, width), transform=transform, fill=0) # 写出 tif with rasterio.open( "soil_type_rasterized.tif", "w", driver="GTiff", height=height, width=width, count=1, dtype="int16", crs=crs, transform=transform, ) as dst: dst.write(raster, 1)

这段代码的要点是zip(gdf.geometry, gdf["code"]):逐图斑把几何对象和整数编码配对,rasterize 会按图斑覆盖关系填充像元。用 DEM 的 transform 作模板是为了对齐,这是最重要的习惯——不同来源的栅格即使 CRS 相同,如果起始点偏移零点几个像元,采样后就会混进邻类像元的值。写出的 tif 建议用dtype="int16",土类编码最大值通常不会超过几千,int16 够用且文件体积比 int32 小一半。

4.3 与地形栅格做叠加采样:把分类数据转成点位样本

转完栅格,下一步往往是从栅格里提取各类环境变量的值,构成训练样本表。Rasterio 读取多个栅格在某个点上的值有两种方式:一是用rasterio.sample按坐标采样,二是直接把整个栅格读成 numpy 数组再做索引。实际项目里,我更常用第二种,因为它能一次取出全部像元,配合经纬度网格生成全量样本,再用掩膜去掉无效值。

import rasterio import numpy as np import pandas as pd # 打开土壤类型栅格和高程栅格 with rasterio.open("soil_type_rasterized.tif") as soil_src: soil = soil_src.read(1) transform = soil_src.transform with rasterio.open("dem_albers.tif") as dem_src: dem = dem_src.read(1) # 用 numpy 构建坐标网格 height, width = soil.shape rows, cols = np.indices((height, width)) xs, ys = rasterio.transform.xy(transform, rows, cols) xs = np.array(xs).flatten() ys = np.array(ys).flatten() # 展平并组装 DataFrame df = pd.DataFrame({ "x": xs, "y": ys, "soil_code": soil.flatten(), "dem": dem.flatten(), }) # 剔除背景值和无效高程 df = df[(df["soil_code"] > 0) & (df["dem"] > -9999)] print(df.head()) print(df["soil_code"].value_counts())

rasterio.transform.xy按行列号反算地理坐标,返回的是一个二维数组,展平后和像元值一一对应。把土壤编码和高程放在同一行,就构成了最基础的训练样本表。这里有一个很容易踩的坑:Rasterio 读出来的 transform 是像元左上角的坐标,如果你在后续建模中要做空间交叉验证,样本点的 x、y 必须精确指向像元中心,需要再加半个像元分辨率,具体公式是x_center = x + transform.a / 2,在避坑章会再提一次。

5. 使用这场普查数据避坑手记:坐标偏移、乱码与分类对照翻车实录

5.1 现象:图斑整体偏移,落到相邻乡镇甚至跨县

第一次拿二普数据叠在线底图上,最常见的现象是整个土壤图层向某个方向平移了几百米到几公里,图斑边界和地形、水系对不上。

原因:坐标系标定错误,或者 prj 文件缺失。二普数据大量采用北京54 坐标系,而在线底图是 WGS84,两者椭球体不同,同一点的经纬度值存在系统差,在某些区域可达数百米。如果制作方在转换时选错了七参数或直接忽略了椭球转换,偏移就不可避免。

解决:先确认数据原始坐标系,再通过定义投影或重投影修正。如果在 QGIS 里看到 Unknown CRS,先在资源包说明里找投影参数,手动创建自定义 CRS 并「分配」给图层;分配后如果偏移不大,直接右键「导出 → 重投影」到 WGS84 即可。如果偏差很大,建议用控制点做配准而不是盲目硬转,因为大概率是原始数据本身就有位置误差。

5.2 现象:属性表中文全是问号,字段过滤无效

图层能加载,但打开属性表,土类、亚类字段里全是「?」或乱码,按中文查询一条都选不中。

原因:dbf 文件用的是 GBK 编码,软件默认按 UTF-8 解码,中文字符全部变成非法字节。还有一层隐藏原因:dbf 里的字段类型如果是数值型,而原始作业时把文本内容存成了数字代码,看起来正常但过滤时对不上名称。

解决:QGIS 中在加载数据源时设置编码为 GBK,或者用 GeoPandas 的encoding="gbk"读取。读进来之后,建议立刻把中文类名字段另存为一个整数编码字段,后续所有分析都基于整数编码,避免一碰到中文就出问题。如果 load 时选不到 GBK,就试 GB18030,兼容生僻字。

5.3 现象:与三普的调查样点对不上,同一位置土类完全不一致

用二普数据检查某地的土壤类型,发现和三普样点或野外踏勘结果完全对不上,一个说棕壤,一个说淋溶土。

原因:分类体系差异。二普用的是发生学分类,命名强调地带性和成土过程;三普用的是中国土壤系统分类,按诊断层和诊断特性命名。同一个剖面,在二普里叫棕壤,在系统分类里可能是简育湿润淋溶土。这不是数据错误,是两套语言体系,直接比对名称没有意义。

解决:不要按名字比对,要按「景观位置 + 剖面属性」做间接对照。我的习惯是拿到二普图斑后,先在 ArcGIS 里做一步「空间连接」到三普样点,每个样点上带上二普土类名称和 TLE 编码,再人工对几个样点的剖面描述确认对应关系,确定本区域的对照表后再批量转换。千万别全凭感觉建立「棕壤=淋溶土」这种一对一映射,很多土类在系统分类里会拆成多个。

5.4 现象:图斑之间有白缝或重叠,转栅格时出现空像元

栅格化之后,图像上出现几条细长的空值线,或者某些像元同时叠了多个图斑的值,类型发生跳变。

原因:原始数字化时图斑边界没有完全拓扑闭合,相邻图斑共享边不严格,导致缝隙和重叠;栅格化时这些区域没有落到任何图斑或同时落入多个图斑。

解决:转栅格前先做一步拓扑修复。用 GeoPandas 里对 geometry 批量执行buffer(0),可以消除大多数微小的缝隙和重叠;更严重的拓扑错误再用make_valid处理。处理后重新检查几何体数量,确保和原始图斑数一致,再走栅格化流程。缝隙很小的图斑用 buffer(0) 修好后对面积影响基本可忽略,但千万别用太大的 buffer 值,不然图斑边界会被磨圆,影响后续面积统计精度。

5.5 现象:小图斑密集得没法看,出图全是噪点

比例尺缩小到全省或全国时,图上全是密密麻麻的小图斑,颜色碎得像马赛克,图例也没法看。

原因:二普图斑是按调查精度数字化的,原始比例尺下很多图斑只有几十亩,缩编到小比例尺时这些微图斑变成了肉眼不可辨的碎斑,制图综合没做。

解决:做制图综合,按面积阈值筛选。先算出每个图斑的面积,然后把小于阈值的图斑合并到相邻最大图斑,或者直接在图面上不显示。阈值的选择看比例尺:省级出图一般取 1km² 以下剔除,市级取 0.1km² 以下剔除。合并操作在 QGIS 里可以用「消除」工具,也可以在 GeoPandas 里按面积过滤后再做一次 dissolve:

import geopandas as gpd gdf = gpd.read_file("soil_2nd_survey.shp", encoding="gbk") # 计算面积,如果原始坐标是经纬度就转投影再算 gdf = gdf.to_crs("EPSG:4547") gdf["area_km2"] = gdf.geometry.area / 1e6 # 过滤掉小于 1 平方公里的小图斑 gdf_filtered = gdf[gdf["area_km2"] >= 1.0].copy() # 按土类融合,消除边界 gdf_dissolved = gdf_filtered.dissolve(by="TLE", as_index=False) print(gdf_dissolved.shape)

这里dissolve(by="TLE")会把同一土类下相互接壤的图斑合并为一个多边形,减少碎斑数量。需要注意的是,按土类 dissolve 后,亚类层面的边界就没了,如果下游还要用亚类信息,就别在这一步做 dissolve,改成只做面积过滤,保留图斑边界但删掉小碎块。

6. 进阶:把这场土壤类型图做成一张可交付的专题图

6.1 图例的独立方案:主图用土类,附图用亚类

数据拿稳、坐标对了之后,真正考验功力的出图环节。我的做法是主图用土类做唯一值渲染,图例顺序按分类系统的排序来,不按字母表排,因为在图例里按字母排会打乱土类的地带性规律,读者一眼看不出地理分布逻辑。附图放亚类细节,用灰底衬托主图,只显示重点区域。

配色上不要默认软件随机分配的颜色,手动指定色带,原则是「红壤偏红、黄壤偏黄、棕壤偏棕、黑土偏黑」,这是给非土壤专业读者最直观的信号。面符号填充透明度设到 80% 左右,叠地形阴影底图,图斑的立体感立刻出来。

6.2 制图前强制走一遍的六项核对

出图前,我每一次都会强制自己走完六项核对,顺序不能乱:坐标系检查、编码读取检查、拓扑修复检查、面积总量校核、制图综合阈值检查、图例顺序与属性表对照检查。其中最容易翻车的是面积总量校核:转完投影后重新算一遍总面积,和说明文档里的全国数据对比,偏差超过 2% 就要回头查拓扑修复和制图综合的步骤;因为 dissolve 或 buffer 操作可能引入了面积误差。

面积校核这段可以直接用 GeoPandas 算:

import geopandas as gpd gdf = gpd.read_file("soil_2nd_survey_final.shp", encoding="utf-8") gdf = gdf.to_crs("EPSG:4547") total_area = gdf.geometry.area.sum() / 1e6 # 平方公里 print(f"总面积: {total_area:.1f} km²") # 和资源包说明里的总面积对比,偏差 >2% 就回头查

至于比例尺、指北针、公里网格这些整饰要素,按项目规范放好就行。真正决定这张图能不能交付的,是你对数据本身有没有把握。我有一回因为急着出图,跳过了拓扑修复,结果图上一道白缝正好穿过一个村里的农田,汇报时被当场指出来,场面非常被动。从那以后,每次出专题图之前,我都强制走一遍「重投影检查 → 编码检查 → 拓扑修复 → 面积校核 → 制图综合 → 图例对照」六步,再急也不省。二普数据是几十年前的调查成果,但底子扎实,处理得当照样能撑起今天的项目,希望这份实操拆解帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询