简介:江苏土壤类型空间分布标准shape文件,依据一比四百万中国土壤图编制,采用三位数字编码区分土类与亚类,面向地理信息、土壤调查及生态研究者,可用于省级尺度土壤类型制图与分析。压缩包共有16个文件,整体大小约1.03MB,以shp、dbf、prj、shx等矢量核心文件为主,辅以xlsx分类编码表、docx说明文档、jpg样式示例及省级行政区划边界,数据结构完整、即下即用。目前已有92人学习下载。资源中的土壤分类体系表可对照联合国粮农组织分类体系理解土类、亚类、土属、土种等制图单元;属性表SOIL_ID与编码表亚类一一对应,便于按编号检索和分类统计。同时提供可编辑地图文档与标准成图,用户可直接制图,也可与省级区划叠加套合,适合项目前期分析、课程设计或科研底图场景。
1. 江苏土壤类型空间分布 shapefile:先搞清楚你拿到的这张图是什么
做省域空间分析的人,最怕的不是没有数据,是拖进 GIS 的第一眼就翻车:江苏的图斑跑到海里、属性表全是问号、面积统计出来少了十倍。这份江苏土壤类型空间分布标准 shapefile(也就是常说的 shape 文件)解决的,正是这类「省域土壤底图不可用」的老大难。它把土纲、土类、亚类这些分类信息落到矢量图斑上,叠加行政区边界后能直接做面积统计、专题制图和耕地适宜性评价。适合自然资源、农业、生态和规划口的从业者,也适合刚开始碰省级土壤数据的 GIS 新人——前提是你愿意花半小时把坐标系、字段和编码这三件事先理清楚。
2. 读懂标准 shapefile 的结构:伴随文件、坐标参考系与土壤字段语义
「标准」这个词在 shapefile 场景下不是客套话,它至少包含四层意思:文件组织规范、坐标参考系明确、属性表字段有字典、几何拓扑干净。大多数拿到的省级土壤数据,四样里能占两样就不错。先把这个底摸清,后面所有处理才不会白做。
2.1 shapefile 不止一个 .shp:伴随文件各管什么事
shapefile 是 ESRI 定义的矢量存储格式,它天生不是一个单文件。一个能正常打开的标准 shapefile,至少要有 .shp、.shx、.dbf 三个文件;工程上还要带 .prj 和 .cpg,分别记录坐标参考系和字符编码。很多人收到压缩包只解压出一个 .shp,拖进 QGIS 直接报「无效数据源」,就是因为少了兄弟文件。
| 文件后缀 | 作用 | 丢失后果 |
|---|---|---|
| .shp | 存储点、线、面几何坐标 | 无法读取 |
| .shx | 几何空间索引,加速要素读取 | 打开变慢,部分软件拒绝加载 |
| .dbf | 属性表(dBASE 格式) | 图形能显示,属性表打不开 |
| .prj | WKT 格式的坐标参考系定义 | 软件只能盲猜坐标系,极易错位 |
| .cpg | 字符编码声明,如 UTF-8、GBK | 中文属性大概率乱码 |
ArcGIS 还会生成 .sbn/.sbx 作为空间索引,删掉不影响数据本身,只是重算一次而已。真正要记牢的是主文件名必须完全一致,比如jiangsu_soil.shp对应jiangsu_soil.dbf,改名要五个文件一起改,只改一个就会打不开。
我处理过一份所谓「标准」的全国土壤数据,压缩包里只有 .shp 和 .dbf,缺 .prj。没有 .prj,QGIS 默认按 WGS84 经纬度猜,而实际坐标是投影坐标,图斑直接飞到了哈萨克斯坦方向。所以拿到手第一件事不是打开,是清点文件完整性,缺 .prj 的一律先按未知坐标系隔离处理。
2.2 土壤属性表字段:从土纲到土属的层级结构与代码字典
土壤数据的属性表,考察的是有没有分类层级概念。中国土壤分类系统从上到下分土纲、亚纲、土类、亚类、土属、土种六级。江苏常见土类包括水稻土、潮土、滨海盐土、黄棕壤、红壤、砂姜黑土等,但属性表里通常不会直接给你「水稻土」三个字,而是给一个像110101这样的代码。
shapefile 的 .dbf 字段名最长只有 10 个字节,中文基本塞不下,所以你会看到TYPE_CODE、TYPE_NAME、AREA这类缩写或者拼音缩写。字段类型也值得警惕:有些数据源把面积字段存成文本,排序分组时会把1234.5和567.8当字符串处理,必须先转成 float。
多数不规范的土壤数据,属性表里只有代码没有中文名。这个时候必须找配套的图例字典——通常是发布的 PDF 图例、数据说明文档,或者论文附录里的代码对照表。找到后第一时间整理成 CSV 备查。没有字典的土壤数据就是黑匣子,你敢拿它做统计,后面解释不清结果时没人帮你扛。
用 GeoPandas 打开后建议先做三步体检:
import geopandas as gpd gdf = gpd.read_file("soil_raw.shp", encoding="gbk") print(gdf.columns.tolist()) # 看字段名 print(gdf.dtypes) # 看字段类型,重点确认面积字段 print(gdf.head(20)) # 抽查前 20 行,确认代码与名称对应关系代码说明:encoding="gbk"是尝试用中文编码读入,如果报错或乱码再换encoding="utf-8"。这里的价值不在代码本身,而在养成「先体检再使用」的习惯。字段名、类型、前 20 行数据能暴露九成问题。
2.3 江苏适用坐标系:先看清原始数据再谈转换
坐标参考系是土壤 shapefile 里最容易出问题的点。先做一个范围校验:江苏大致在 东经116°18′~121°57′,北纬30°45′~35°20′。如果某图层的坐标范围里出现几十万甚至几百万的数,那它一定是投影坐标系,不是经纬度。
省域尺度下,三种坐标系各有用途:
| 使用场景 | 推荐坐标系 | 理由 |
|---|---|---|
| 浏览、叠加查询 | CGCS2000 或 WGS84 经纬度 | 通用性最好,任何底图都能对齐 |
| 面积统计、距离量算 | CGCS2000 / 3-degree Gauss-Kruger CM 120E | 江苏主体落在 120°E 三度带内,变形小 |
| 全省专题制图 | CGCS2000 Albers 等积投影 | 双标准纬线等积,全省面积无系统性偏差 |
实操中我的习惯是:原始数据如果带 .prj,先看它写的是什么;如果缺 .prj,去数据发布页找说明。确认原始坐标系后用 QGIS 的「图层-导出-另存为」重新指定目标坐标系,一次性产出规范文件。要强调的是set_crs和to_crs的区别:前者只是给数据贴个坐标系标签,不改变坐标数值;后者才做真正的坐标变换。用错了,图斑位置不变但标注变了,后续叠加全乱。
3. 把原始土壤数据整理成江苏标准 shapefile:数据源选型与预处理
3.1 数据源怎么选:全国 1:100 万土壤图、省域专题图与栅格数据的取舍
做江苏省级土壤分析,常见数据源有三类。第一类是全国第二次土壤普查成果数字化后的 1:100 万中国土壤图,覆盖全国,分类体系是中国的七级分类,属性字段完整,缺点是比例尺小,边界精度一般,乡镇尺度基本不能用。第二类是部分省份公开或科研机构发布的省域土壤专题图,比例尺能到 1:20 万甚至 1:5 万,精度高不少,但格式、坐标系、字段结构五花八门,有的还是 CAD 导出的多边形。第三类是 HWSD(世界土壤数据库)这类全球栅格数据,优点是字段标准化,缺点是土壤分类用 FAO 体系,和国内习惯对不上,转矢量以后图斑细碎得没法看。
选择建议很直接:做省级宏观分析,优先用全国 1:100 万矢量数据裁剪;做县域或地块级评价,必须找省域高精度专题图;做跨境、全球对比研究才考虑 HWSD。不要混用两套不同分类体系的数据,哪怕都是代码字段,代码表不同,合并结果就是灾难。
数据源确定后,还要确认三件事:数据发布时采用的坐标系、属性表里分类字段的层级、以及数据是否有配套元数据文档。这些信息直接决定后续预处理脚本怎么改。
3.2 用 GeoPandas 按江苏边界裁剪:可直接改跑的脚本
拿到覆盖华东甚至全国的土壤矢量图后,第一步是按江苏省界裁剪。这里的关键不是剪,而是剪之前先统一坐标系、先粗筛再精剪,否则大范围数据做空间运算会慢到怀疑人生。
import geopandas as gpd # 读取原始土壤数据,编码按源数据实际情况选 gbk 或 utf-8 soil = gpd.read_file("china_soil_1m.shp", encoding="gbk") # 读取江苏省行政边界 jiangsu = gpd.read_file("jiangsu_admin.shp", encoding="utf-8") # 如果土壤数据缺坐标系,先按元数据说明手动指定,不然后面全错 if soil.crs is None: soil = soil.set_crs("EPSG:4490") # CGCS2000 经纬度,按实际情况改 # 边界统一到土壤数据的坐标系再裁剪 jiangsu = jiangsu.to_crs(soil.crs) bounds = jiangsu.total_bounds # 先用边界框粗筛,降低空间计算量 soil_box = soil.cx[bounds[0]:bounds[2], bounds[1]:bounds[3]] # 精确裁剪:gpd.clip 在老版本可能不可用,可换 gpd.overlay(soil_box, jiangsu, how="intersection") soil_js = gpd.clip(soil_box, jiangsu) print(f"裁剪前 {len(soil_box)} 个图斑,裁剪后 {len(soil_js)} 个图斑")参数说明:cx是 GeoPandas 的坐标索引器,按 x/y 范围直接过滤要素,这一步能把千万级要素筛到几十万级,后续 clip 才快。gpd.clip会按边界精确切割图斑,跨省图斑在省界处被切断,这是预期的结果。需要特别注意的是,源数据里如果已经有AREA字段,裁剪后这个面积值就失效了,必须用新几何重算,这一步很多人漏掉。
裁剪完成后立刻做投影转换,把数据转到适合江苏的投影坐标系:
# +proj=tmerc 是高斯-克吕格投影,lon_0=120 对应中央经线 120°E soil_js = soil_js.to_crs( "+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 " "+ellps=GRS80 +units=m +no_defs" ) # 重新计算面积,单位为平方千米 soil_js["area_km2"] = soil_js.geometry.area / 1e6这里用 PROJ 字符串而不是 EPSG 编号,是为了避免不同版本 GeoPandas 对 EPSG 支持不一致的问题。中央经线选 120°E,覆盖江苏绝大部分区域,投影变形在千分之一量级,省级面积统计完全够用。如果后续要和全国数据拼图,则统一改用 Albers 等积投影更稳妥。
3.3 统一字段与编码后输出:给后续使用铺路
裁剪和投影只是预处理的一半,另一半是字段整理。shapefile 的 DBF 格式对字段名长度要求很严格,超过 10 字节会被截断,截断后重名引发一堆玄学报错。所以在输出前,把关键字段重命名为短英文名,中文含义留到字典文件里说明:
# 假设源字段:DLBM(代码), DLMC(名称),这里做重命名和筛选 soil_js = soil_js.rename(columns={ "DLBM": "type_code", "DLMC": "type_name" }) # 只保留业务需要的字段,geometry 自动保留 soil_js = soil_js[["type_code", "type_name", "area_km2", "geometry"]] # 输出时显式指定 utf-8 编码,并生成 .cpg 文件可被 QGIS 识别 soil_js.to_file("jiangsu_soil_std.shp", encoding="utf-8")代码逻辑:先重命名再筛选,避免把一堆无关字段带进成品;to_file输出时会自动补齐 .shp/.shx/.dbf/.prj,encoding="utf-8"同时生成 .cpg 文件,这样 QGIS 打开不会乱码。
这里有个经验:如果你的下游是 ArcMap,建议把编码改成gbk输出。ArcMap 对 UTF-8 的 .dbf 支持不稳定,属性表容易乱码;反之 QGIS 对 GBK 也能良好识别。没有绝对正确的编码,只有匹配使用方的编码。输出完别急着用,用 QGIS 重新打开一次,看一下字段名是否完整、中文是否正常、图斑位置是否落在江苏,三样全过才算交付。
4. 把整理好的江苏土壤 shapefile 用起来:专题图、面积统计与叠加分析
4.1 在 QGIS 里制作江苏土壤类型专题图:符号化参数与配色建议
数据整理干净后,最直接的产出是一张能放进报告里的土壤类型分布图。QGIS 里操作路径不复杂:加载jiangsu_soil_std.shp和江苏行政边界,右键土壤图层进入「属性-符号化」,顶部下拉选「分类(Categorized)」,值字段选type_name,点击「分类」按钮生成所有土类。
| 参数项 | 建议设置 |
|---|---|
| 渲染方式 | 唯一值分类(Categorized) |
| 值字段 | type_name(土类中文名) |
| 配色方案 | ColorBrewer Set3,或按土类语义手动配色 |
| 边界线宽 | 0.2 mm,颜色 #666666 |
| 图例标题 | 土壤类型(土类) |
初学者容易犯的错是把土属甚至土种直接渲染,类别上百个,图例比地图还长。正确做法是先按土类渲染,几十个符号就够了;真要表达细类,用「基于规则」的渲染器,把几个相关土属合并成一组,图面才干净。如果想在属性表里新增一列大类型,用字段计算器写表达式:
CASE WHEN type_name LIKE '%水稻%' THEN '水稻土' WHEN type_name LIKE '%潮土%' OR type_name LIKE '%盐土%' THEN '潮土盐土' ELSE '其他' END这个表达式的重点是LIKE模糊匹配,能兜住命名口径不完全一致的数据。注意新增字段名要用英文短词,比如group_name,中文名留给显示别名。
4.2 按地级市汇总土壤类型面积:sjoin 与 groupby 的组合玩法
专题图只能看分布,领导更关心数字:江苏十三个地级市,每个市水稻土多少平方公里、盐土多少。用 GeoPandas 一行句读完:
import geopandas as gpd soil = gpd.read_file("jiangsu_soil_std.shp", encoding="utf-8") city = gpd.read_file("jiangsu_city.shp", encoding="utf-8") # 空间连接:把每个图斑挂到所在城市 soil_city = gpd.sjoin(soil, city, how="inner", predicate="intersects") # 面积字段已在投影坐标系下算好,这里直接分组汇总 table = soil_city.groupby(["city_name", "type_name"], dropna=False)["area_km2"].sum() table = table.reset_index() table.to_csv("soil_area_by_city.csv", index=False, encoding="utf-8-sig")参数说明:predicate="intersects"表示只要有相交就把图斑连到该城市;how="inner"过滤掉省界外零星要素;area_km2是第 3 章投影后重算的面积,不是源数据老字段。输出用utf-8-sig是为了让 Excel 打开 CSV 不乱码。
这个脚本的结果可以作为初稿,但不能直接交。把一个城市所有土壤类型面积加起来,和该市陆域面积对比,误差超过 2% 就要回头查:要么是沿海滩涂图斑缺失,要么是坐标系没转对。我一般还会输出一个县级汇总作为中间校验,因为县界精度通常比市界细,能暴露更多边界问题。
4.3 与土地利用数据叠加:先统一坐标系和精度,再做空间连接
土壤数据最常见的下游应用,是和土地利用现状数据叠加,分析耕地土壤类型构成。这里最大的坑是比例尺不匹配:土壤图一般是 1:100 万,国土调查数据是 1:1 万甚至更高精度,边界线对不齐是必然的,不是数据坏了。
正确的做法是接受精度差异,不强行修边界。把两组数据统一到同一投影坐标系后,直接做面面叠加,统计的是「土壤图斑与土地利用图斑的重叠面积」:
landuse = gpd.read_file("landuse_js.shp", encoding="utf-8") landuse = landuse.to_crs(soil.crs) # 叠加:每个交集多边形继承双方的属性 merged = gpd.overlay(soil, landuse, how="intersection") merged["area_km2"] = merged.geometry.area / 1e6 merged["type_name"].value_counts()这里输出的是分割后的碎多边形,每个多边形同时带土壤类型和地类属性,再做 groupby 就能得到交叉矩阵。要注意gpd.overlay会把两边的同名字段自动加_1后缀,比如两个数据都有name字段,结果里会出现name和name_1,分析前先columns看清再选字段。
5. 江苏土壤 shapefile 排查避坑:坐标系错位、编码乱码与碎图斑的翻车记录
省域土壤数据这一路走下来,翻车率最高的就集中在五个场景。每一条我都按「现象 → 原因 → 解决」写清楚,照着排查,大部分问题十分钟内能定位。
5.1 打开图斑就「飞到国外」:坐标参考系错位
现象:把土壤面图层和在线底图叠在一起,江苏的图斑出现在哈萨克斯坦方向,或者显示在非洲西海岸,坐标范围推出来有几百万的数值。
原因:八成是图层缺 .prj 文件,软件默认按 WGS84 经纬度解析;但原始数据本身是投影坐标,数值没有被转换,直接当作经纬度显示,位置自然荒谬。另一小半是数据原始坐标系是北京54或西安80,软件按 CGCS2000 读取,形成几百米平移错位。
解决:先去发布页或元数据里确认原始坐标系的准确名称,然后在 QGIS 里右键图层选择「设定 CRS」手动指定正确的坐标系。注意这里不是「另存为」重新投影,而是先用set_crs明确标签,再用「导出-另存为」转换成目标坐标系。在 GeoPandas 里同理:gdf = gdf.set_crs("EPSG:4490")之后,再gdf.to_crs(projected_crs)变换,顺序不能反。
5.2 属性表中文全乱码:数据源编码没对上
现象:打开属性表,土壤名称显示为「????????」或者「锟斤拷锟斤拷」,数字字段正常。
原因:源 .dbf 文件是 GBK 编码,但读取软件按 UTF-8 解析;要么 .cpg 文件缺失,软件只能猜编码。ArcGIS 和 QGIS 对这种情况的处理方式不同,ArcMap 更依赖系统区域设置,所以同一份数据在两个软件里表现还不一样。
解决:在 QGIS 的「数据源管理器」里把编码从 UTF-8 改为 GBK,重新加载即可。Python 侧更直接:
gdf = gpd.read_file("jiangsu_soil_std.shp", encoding="gbk") # 读出来后立即另存为标准 utf-8 版本 gdf.to_file("jiangsu_soil_utf8.shp", encoding="utf-8")这段的核心是「先按正确编码读入,再统一输出为 UTF-8」。但如果下游是 ArcMap,建议输出为encoding="gbk"更稳。判断标准只有一个:谁打开不乱码就按谁的来。
5.3 面积统计少了一位:用经纬度坐标直接算了面积
现象:全江苏土壤面积汇总出来只有几千平方公里,而江苏省陆域面积约 10.72 万平方公里,差着一个数量级。
原因:数据还停留在经纬度坐标系时,geometry.area返回的单位是平方度,不是平方千米。平方度随纬度变化不是一个固定值,直接除以系数得到的数字没有任何物理意义。还有人会顺手选了 Web 墨卡托投影(EPSG:3857)来算面积,高纬度区域面积被放大得离谱,江苏这种中纬度地区能虚胖 20% 以上。
解决:面积计算必须在等积或近等积投影下完成。江苏用 CGCS2000 三度带(中央经线 120°E)即可,代码见第 3.2 节。计算之前养成一个条件反射:gdf.crs.to_epsg()看一下,如果返回 4326 说明是经纬度,直接停下来先投影再算。算完后和全省陆域面积做对比验证,误差在 1% 以内才算通过。
5.4 汇总面积重复:跨地市图斑被每个地市各算一次
现象:按地级市汇总土壤面积,把十三个市的结果加起来,比全省土壤总面积多了几千平方公里。
原因:sjoin是一对多空间连接,一个横跨南京和镇江边界的图斑,会同时匹配到两个市,生成两行记录,面积被重复计入。如果你用的是按边界切分过的数据这没问题;但很多人拿的是原始未切分图斑直接 sjoin,重复不可避免。
解决:需要的是先切分再汇总。用gpd.overlay(soil, city, how="intersection")把跨市图斑沿市界线切成独立多边形,每个市只保留自己范围内那块,然后再 groupby 汇总:
pieces = gpd.overlay(soil, city, how="intersection") pieces["area_km2"] = pieces.geometry.area / 1e6 table = pieces.groupby(["city_name", "type_name"])["area_km2"].sum().reset_index()注意 overlay 结果会保留双方属性字段,出现city_name与土壤字段的_1后缀不奇怪,先打印columns确认再使用。如果只是做分布展示,不做精确统计,用 sjoin 也能接受,但报告里必须注明统计口径是「按图斑归属市」,否则别人重复你的结果时会对不上数字。
5.5 边界碎图斑成片出现:数据精度不匹配与无效几何
现象:裁剪或叠加后,江苏沿海和市界附近出现大量面积只有几十平方米的细长碎多边形,一眼看出是噪声。严重的甚至在图斑内部出现重叠空洞。
原因:土壤图比例尺只有 1:100 万,行政边界精度远高于它,clip 时边界处图斑被切成大量窄条;源数据里还可能存在无效几何——自相交多边形、重复要素、空几何,这些在叠加时会被放大。
解决:先修几何再过滤碎图斑。
# 修复无效几何:buffer(0) 是常见修法 if not soil.geometry.is_valid.all(): soil.geometry = soil.geometry.buffer(0) # 删除碎图斑:面积小于 1 平方公里的直接去掉,阈值按制图比例尺定 soil_clean = soil[soil.geometry.area / 1e6 >= 1.0]代码逻辑:is_valid是几何拓扑合法性检查,返回布尔序列;buffer(0)能让自相交和窄缝自动修正,这是 GIS 界的经典做法。面积阈值选 1 平方公里,对省级制图合适;如果你做的是县域精细分析,阈值应降到 0.01 平方公里甚至更低,否则会误删真实小图斑。这一步做完看着清爽,但统计口径也随之变了,报告里写清楚「已剔除小于阈值碎图斑」即可。
6. 进阶:用 Python 批量校验省级土壤 shapefile 的完整性与一致性
手工检查一份数据可以,交付十份不同来源的数据时就需要自动化。我会在数据整理完成后跑一遍批量校验脚本,把坐标范围、字段完整度、几何有效性一次性查完:
import geopandas as gpd from pathlib import Path base = Path("F:/soil_product") for shp in base.glob("*.shp"): try: gdf = gpd.read_file(shp, encoding="utf-8") except Exception: print(f"{shp.name}: 读取失败,可能是编码问题,改用 gbk 试试") continue issues = [] if gdf.crs is None: issues.append("缺坐标参考系") # 统一转经纬度验证范围,避免投影坐标数值干扰判断 if gdf.crs is not None: wgs = gdf.to_crs("EPSG:4490") xmin, ymin, xmax, ymax = wgs.total_bounds if not (116 < xmin < 116.5 and 30 < ymin < 31 and xmax < 122.5 and ymax < 35.5): issues.append(f"坐标范围超出江苏预期:{xmin:.2f},{ymin:.2f},{xmax:.2f},{ymax:.2f}") if not gdf.geometry.is_valid.all(): issues.append("存在无效几何") for col in ["type_code", "type_name", "area_km2"]: if col not in gdf.columns: issues.append(f"缺字段 {col}") if issues: print(f"{shp.name}: 不通过 -> " + "; ".join(issues)) else: print(f"{shp.name}: 通过")这段脚本的作用不是检查数据质量,是检查交付状态。坐标范围、字段、几何有效性三关过了,至少说明数据可以安全进入分析流程,不会在半路炸掉。to_crs("EPSG:4490")转回经纬度判断范围,比直接看投影坐标数字更直观,也不受各地投影参数差异影响。
我现在的习惯是:任何一版省域土壤数据,不管来自哪个渠道,先放进这个脚本里跑一遍再决定要不要用。坐标、编码、拓扑这三件事做扎实,shapefile 这步棋基本不会走错;反之,省下十分钟检查,后面花两个小时查错位的图斑和算不对的面积。希望这套处理路径能帮你在江苏土壤数据上少走一段弯路,把时间花在真正的地理分析上。
本文还有配套的精品资源,点击获取