ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

中国省级行政区划SHP数据实战:从坐标校验到批量出图全流程

中国省级行政区划SHP数据实战:从坐标校验到批量出图全流程

简介:这份中国省级SHP数据面向地理信息系统从业者、城乡规划研究人员、高校师生及数据分析爱好者,提供覆盖全国范围的行政区划矢量底图,可用于地图制作、空间分析、区域统计与可视化项目,帮助解决缺少标准行政边界数据的问题。压缩包共21个文件,约9.91MB,以shp、shx、dbf、prj为核心,分别承载几何图形、索引、属性表与坐标投影信息,并配有sbn、sbx空间索引及xml元数据,兼容ArcGIS、QGIS等主流平台。数据按省级、地市级、区县级分层组织,涵盖北京、天津、河北、山西、内蒙古、辽宁、吉林、黑龙江、上海、江苏、浙江、安徽、福建、江西、山东、河南、湖北、湖南、广东、广西、海南、重庆、四川、贵州、云南、西藏、陕西、甘肃、青海、宁夏、新疆、台湾、香港等全部省级行政区,便于按需裁剪与叠加分析。目前已有3432人学习下载,适合需要快速获取规范行政边界、开展空间制图与统计研究的用户参考使用。

1. 省级边界 SHP 拿到手之后:先搞清楚它到底能干什么

做 GIS 的人大概都有过这种经历:项目排期压到只剩三天,底图还没着落,甲方一句“先出个全国示意图”丢过来,你打开 ArcGIS 发现手头只有一份分辨率惨不忍睹的在线底图,导出即糊。这时候一份干净的中国省级行政区划 SHP 就是救命稻草。它本质上是一组矢量面数据,每个要素代表一个省级行政单元,属性表里通常挂着省名、行政代码、简称这类字段,几何类型是 Polygon 或 MultiPolygon,坐标系常见的是 WGS84 地理坐标或 CGCS2000。能解决的核心问题就三类:出图底图、空间统计的统计单元、以及和其他图层做叠加分析时的裁剪框。适合谁用?做专题制图的、跑空间自相关的、搞区域经济研究的、还有需要把业务数据按省聚合再落到地图上的开发同学。它不是万能的,比如你要做区县级分析,省级面太粗;要做路网分析,它没有线要素。但在“省级”这个粒度上,它是很多流程的起点。

2. 坐标系与字段结构:拿到 SHP 先别急着拖进软件

2.1 为什么坐标系判断错了,后面全白干

SHP 本身不强制存坐标系,但配套的 .prj 文件会记录投影信息。很多网上流传的省级边界包,.prj 要么缺失,要么写的是 GCS_WGS_1984,但实际坐标值却是投影后的米制坐标。这种“表里不一”的数据,你直接拖进 ArcGIS 或 QGIS,它会按 .prj 声明的坐标系去渲染,结果要么位置偏到太平洋,要么形状被拉伸得没法看。判断方法很简单:看坐标数值的量级。经纬度坐标的 X 值在 73 到 135 之间,Y 值在 3 到 54 之间;如果是投影坐标,数值通常是六位数甚至七位数。我一般会先用一个轻量脚本把范围打出来,心里有数再决定要不要重新定义投影。

import shapefile # 需要 pyshp 库,pip install pyshp sf = shapefile.Reader("china_province.shp") # 打印前五个要素的边界范围 for i, shp in enumerate(sf.shapes()[:5]): print(f"要素 {i} 的 bbox: {shp.bbox}") # bbox 返回 [xmin, ymin, xmax, ymax] # 如果 xmin 在 70~140 之间,基本可判定是经纬度坐标 # 如果 xmin 是 3xxxxx 或 1xxxxxx,说明是投影坐标,需要查 .prj 确认投影带

这段代码的逻辑是:不依赖任何桌面软件,直接用 pyshp 读几何的包围盒。参数上没什么可调的,关键是看输出。如果 bbox 的 X 范围落在 70 到 140 之间,那基本就是地理坐标系,单位是度;如果 X 是几万到几十万,那单位是米,得去找对应的投影带号。常见做法是,如果 .prj 缺失但数值是经纬度,就手动指定 EPSG:4326;如果数值是米制且大致对应中国区域,可能是 CGCS2000 的高斯投影,需要根据中央经线反推带号。

2.2 属性表里哪些字段真正有用

省级 SHP 的属性表字段五花八门,有的包给的是中文省名,有的给拼音,有的给行政代码,还有的塞了一堆面积、人口之类的冗余字段。真正做空间连接或分组统计时,最稳的关联键是行政代码,因为省名可能有“省”“自治区”“特别行政区”等后缀差异,拼音也有多音字问题。我一般会先看一眼字段列表和几条记录,确认代码字段的位数——省级代码通常是六位,前两位代表省,后四位是 0000 或者具体到地级市。如果只有省名没有代码,那就得自己做一张映射表,把“内蒙古自治区”和“内蒙古”这种别名统一掉,否则后面按省聚合时会出现同一个省分成两条记录的血泪教训。

# 查看字段名和前三条记录 fields = [f[0] for f in sf.fields[1:]] # 跳过 DeletionFlag print("字段列表:", fields) records = sf.records()[:3] for rec in records: print(dict(zip(fields, rec))) # 重点关注:是否有 adcode / code / 行政代码 这类字段 # 以及省名字段是中文还是拼音,有没有“省”字后缀

逻辑说明:sf.fields 的第一个元素是 DeletionFlag,必须跳过。records() 返回的是列表,每条记录是一个列表,和字段顺序一一对应。参数上,如果你发现代码字段是数值类型而不是字符串,要小心前导零丢失的问题——比如“110000”可能被读成 110000,虽然数值上一样,但做字符串匹配时会出问题。常见做法是统一转成字符串再补零到六位。

2.3 几何有效性检查:别等到做叠加分析才翻车

省级边界数据最常见的几何问题是自相交、悬挂节点和面重叠。这些问题在单纯出图时看不出来,但一旦做 Intersect 或 Union 就会报错或者产生碎屑面。我一般会在正式用之前跑一遍几何有效性检查,QGIS 里有“检查有效性”工具,代码里可以用 shapely 的 is_valid 批量判断。如果发现无效几何,优先用 buffer(0) 做修复,而不是直接删掉,因为省级面删一个就少一块。

from shapely.geometry import shape from shapely.validation import explain_validity for i, shp in enumerate(sf.shapes()): geom = shape(shp.__geo_interface__) if not geom.is_valid: print(f"要素 {i} 无效: {explain_validity(geom)}") # 常见输出:Self-intersection at or near point ... # 修复方式:geom = geom.buffer(0)

参数说明:buffer(0) 是一个经典技巧,它会把自相交的环重新构造成合法多边形,但对 MultiPolygon 可能会合并相邻部分,所以修复后要检查面积变化是否在可接受范围内。如果面积变化超过 1%,建议手动定位问题节点而不是无脑修复。

3. 从 SHP 到可用底图:投影、裁剪与格式转换的实操链路

3.1 按出图需求选投影,别一套 WGS84 走天下

很多人拿到 SHP 就直接用 WGS84 出图,结果中国地图被拉得又扁又长,因为经纬度坐标在平面显示时高纬度地区会被拉伸。正确的做法是根据出图范围选投影。全国图常用 Albers 等面积投影,参数是中央经线 105°E,双标准纬线 25°N 和 47°N。分省图可以用对应省份的中央经线做高斯-克吕格投影。在 QGIS 里重投影就是“导出 → 另存为”,选目标 CRS;在 ArcGIS 里用 Project 工具。代码里用 pyproj 做转换也很直接。

import pyproj from shapely.ops import transform from shapely.geometry import shape # 定义 Albers 投影,适合全国范围 albers = pyproj.CRS.from_proj4( "+proj=aea +lat_1=25 +lat_2=47 +lat_0=0 +lon_0=105 " "+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs" ) wgs84 = pyproj.CRS.from_epsg(4326) project = pyproj.Transformer.from_crs(wgs84, albers, always_xy=True).transform # 对单个几何做投影转换 geom_wgs = shape(sf.shapes()[0].__geo_interface__) geom_albers = transform(project, geom_wgs) print("投影后面积(平方米):", geom_albers.area)

逻辑说明:pyproj.Transformer 的 always_xy=True 保证输入输出都是 (x, y) 顺序,避免经纬度颠倒。参数上,lat_1 和 lat_2 是双标准纬线,选 25 和 47 是因为中国陆地大致在这个纬度范围内,等面积特性最好。lon_0=105 是全国图常用的中央经线。如果你只做某个省,比如四川,中央经线可以改成 105 或 102,标准纬线也相应调整。

3.2 用省级面做裁剪框:Clip 和 Intersect 的区别

当你有一份全国河流数据,只想保留某个省内的部分,就需要用省级面去裁剪。ArcGIS 里 Clip 和 Intersect 都能做,但行为不同:Clip 只保留输入要素落在裁剪框内的部分,属性表还是原来的;Intersect 会保留两者属性,但可能产生更多要素。我一般做底图裁剪用 Clip,做空间统计用 Intersect。QGIS 里对应“矢量 → 地理处理工具 → 裁剪”。代码里用 geopandas 的 clip 最省事。

import geopandas as gpd provinces = gpd.read_file("china_province.shp") rivers = gpd.read_file("rivers.shp") # 选出四川省 sichuan = provinces[provinces["name"].str.contains("四川")] # 用四川省边界裁剪河流 rivers_in_sichuan = gpd.clip(rivers, sichuan) rivers_in_sichuan.to_file("rivers_sichuan.shp", encoding="utf-8")

参数说明:gpd.clip 的第二个参数可以是 GeoDataFrame 或单个几何。如果 provinces 的坐标系和 rivers 不一致,clip 会报错,所以前面必须先统一 CRS。encoding="utf-8" 是为了保证中文属性在导出时不乱码,这个坑在 Windows 上尤其常见。

3.3 转成 GeoJSON 或 WKT:给前端和数据库用

SHP 是桌面软件的老朋友,但前端地图库和数据库更认 GeoJSON 或 WKT。转换时要注意坐标系和精度。GeoJSON 规范要求用 WGS84 经纬度,所以如果你之前投影到了 Albers,转 GeoJSON 之前得转回 4326。WKT 则没有坐标系强制要求,但入库时最好和数据库表的 SRID 对齐。

# SHP 转 GeoJSON provinces_wgs = provinces.to_crs(epsg=4326) provinces_wgs.to_file("china_province.geojson", driver="GeoJSON") # 导出为 WKT 字符串,用于入库 provinces_wgs["wkt"] = provinces_wgs.geometry.apply(lambda g: g.wkt) provinces_wgs[["name", "wkt"]].to_csv("province_wkt.csv", index=False)

逻辑说明:to_crs 做坐标系转换,driver="GeoJSON" 指定输出格式。WKT 导出时,geometry 的 wkt 属性直接给出字符串。参数上,如果几何特别复杂,WKT 字符串可能非常长,入库时要注意字段类型用 TEXT 或 CLOB,别用 VARCHAR 限长。常见做法是先用 simplify 做适当简化,比如 tolerance=0.01 度,能大幅减小体积且肉眼几乎看不出差别。

4. 避坑与排查:省级 SHP 用起来最容易翻车的五个地方

4.1 中文属性乱码:现象是省名显示成问号或方块

原因通常是 SHP 的 .dbf 文件编码和软件默认编码不一致。ArcGIS 对 UTF-8 支持较晚,很多老数据是 GBK 编码。解决方法是先在 QGIS 里看属性表是否正常,如果 QGIS 正常而 ArcGIS 乱码,就在 ArcGIS 里用“转换编码”工具或者直接改注册表。更稳的做法是用 geopandas 读进来时指定 encoding="gbk" 或 "utf-8",导出时统一用 utf-8。

4.2 面积计算出来是负数或零:现象是投影后面积字段异常

原因多半是几何环的方向不对,SHP 规范要求外环顺时针、内环逆时针,但有些数据反了。解决方法是读进来后用 shapely 的 orient 函数修正方向,或者直接用 abs(area) 取绝对值。如果面积是零,检查是不是把线要素误当成了面。

4.3 空间连接后一个省出现多条记录:现象是统计结果翻倍

原因通常是省级面有重叠,或者被连接的点数据落在边界上被两个省同时捕获。解决方法是先做几何有效性检查和重叠检测,然后用“空间连接”时选“一对一”而不是“一对多”,或者加一个距离阈值做最近匹配。

4.4 导出 GeoJSON 后前端加载慢:现象是浏览器卡顿

原因是省级边界的节点太密,一个省可能几十万个点。解决方法是做几何简化,用 Douglas-Peucker 算法,tolerance 根据显示级别定,全国图 0.01 度足够,分省图可以到 0.001 度。简化后体积能降一个数量级。

4.5 和 DEM 提取的 shp 叠加时对不上:现象是边界错位

原因通常是 DEM 提取的 shp 用的是投影坐标,而省级 SHP 是地理坐标,两者没统一。解决方法是先确认两者的 CRS,把地理坐标的那个投影到和另一个一致,或者都转成 4326 再做叠加。别小看这一步,我见过有人在这上面耗了一整天。

5. 进阶技巧:用省级 SHP 做渔网分割与批量出图

5.1 渔网分割:把省域切成规则网格做空间抽样

做空间分析时经常需要把研究区切成规则网格,比如 10km×10km 的渔网,然后统计每个网格里的点数量。用省级 SHP 做渔网,关键是先投影到米制坐标系,再用 create_grid 或者自己写循环生成。QGIS 里有“创建网格”工具,代码里可以用 shapely 的 box 配合 numpy 生成。

import numpy as np from shapely.geometry import box import geopandas as gpd # 假设 sichuan_albers 是投影后的四川省,单位米 minx, miny, maxx, maxy = sichuan_albers.total_bounds cell_size = 10000 # 10km # 生成网格中心点或网格面 cols = np.arange(minx, maxx, cell_size) rows = np.arange(miny, maxy, cell_size) cells = [box(x, y, x + cell_size, y + cell_size) for x in cols for y in rows] # 只保留与省域相交的网格 grid = gpd.GeoDataFrame(geometry=cells, crs=sichuan_albers.crs) grid = gpd.overlay(grid, sichuan_albers, how="intersection") grid.to_file("sichuan_grid.shp", encoding="utf-8")

逻辑说明:total_bounds 返回投影后的范围,单位是米。cell_size 根据研究尺度定,做省级抽样一般 5km 到 20km 都有人用。overlay 的 how="intersection" 保证只保留落在省域内的网格,边缘会被裁剪成不规则形状。参数上,如果网格数量太大,比如超过十万,建议改用点网格或者降低分辨率,否则后续空间连接会很慢。

5.2 批量出图:一个省一张图,自动加标题和指北针

做区域报告时经常需要每个省出一张图。手动在 ArcGIS 里重复三十多次是折磨,用 Python 的 matplotlib 配合 geopandas 可以批量搞定。关键是设置好中文字体,否则标题全是方块。

import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams["font.sans-serif"] = ["SimHei"] # 中文字体 matplotlib.rcParams["axes.unicode_minus"] = False provinces = gpd.read_file("china_province.shp") for idx, row in provinces.iterrows(): fig, ax = plt.subplots(figsize=(8, 6)) gpd.GeoSeries([row.geometry]).plot(ax=ax, color="#f0f0f0", edgecolor="black") ax.set_title(row["name"], fontsize=16) ax.axis("off") fig.savefig(f"output/{row['name']}.png", dpi=150, bbox_inches="tight") plt.close(fig)

参数说明:font.sans-serif 指定中文字体,Windows 上用 SimHei,Linux 上可以用 WenQuanYi 或 Noto Sans CJK。dpi=150 保证出图清晰度,bbox_inches="tight" 去掉多余白边。如果某个省有多个多边形(比如海岛),GeoSeries 会自动处理。这个脚本跑一遍,三十多张图几分钟就出来了,比手动操作省下的时间够你喝好几杯咖啡。

从那以后我每次拿到新的 SHP 数据,都强制走一遍“看 bbox → 查字段 → 验几何 → 统一 CRS”这四步,再也没出现过拖进软件才发现坐标不对的尴尬。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进