简介:这份资源面向GIS从业者、地理信息专业师生及需要中国行政区划底图的数据分析人员,提供覆盖全国省级行政区的shapefile矢量数据,可用于区域划分分析、专题制图、城市规划与交通网络研究等场景。压缩包共20个文件,约3.27MB,以shp、shx、dbf三类核心文件为主:shp存储省份边界几何形状,shx建立空间索引,dbf保存省份名称等属性信息,另附txt说明与m脚本,便于在ArcGIS、MATLAB等环境中直接加载与可视化。数据覆盖内地31个省份及台湾省、香港、澳门特别行政区,边界完整无遗漏,适合作为全国地图绘制与空间统计的基础底图。目前已有1549人学习下载,读者可借此快速搭建省级行政区划实验环境,完成人口、经济或环境数据的空间关联与可视化呈现,减少自行搜集与整理边界数据的时间成本。
1. 拿到一份分省 SHP 之后,先别急着往 GIS 里拖
做全国尺度的空间分析,绕不开一份靠谱的行政区划底图。不管是做人口热力、路网密度、还是把业务数据按省聚合,第一步都是把「省界」这个空间骨架搭起来。我见过太多人卡在这一步:网上随手搜到的 shp 要么缺几个省,要么南海诸岛是空的,要么坐标系是地理坐标没投影,一算面积全是错的。这份「中国完整领土分省 SHP」解决的就是这个前置问题——它是一套按省级行政区分层的矢量面数据,完整覆盖领土范围,格式是经典的 Shapefile(.shp/.shx/.dbf/.prj 一整套)。适合谁?做数据可视化、做空间统计、做地图底图二次加工的人。新手能直接拖进 QGIS 出图,熟手更关心的是它的坐标系、字段结构和边界精度能不能扛住后续的叠加分析。下面我按「先验数据、再动手、最后避坑」的顺序,把这份资源拆开讲透。
2. 先验后动:SHP 的坐标系、字段与完整性怎么查
拿到任何一份 Shapefile,我的习惯是先做三件事:看坐标系、看属性表字段、看几何完整性。这三步不做,后面所有分析都是空中楼阁。很多人一上来就拖进软件画图,图是出来了,可一旦做面积量算或者和别的图层叠加,偏差就冒出来了,回头排查能耗掉一整天。
2.1 用 ogrinfo 快速体检,别等出图才发现问题
命令行下最省事的工具是 GDAL 自带的ogrinfo,不用打开笨重的桌面软件就能把元数据摸清楚。假设解压后目录里是china_province.shp,先跑一遍摘要信息:
# 查看图层概览:坐标系、要素数量、几何类型、字段列表 ogrinfo -so -al china_province.shp # 只看坐标系定义,确认是地理坐标还是投影坐标 ogrinfo -so china_province.shp china_province | grep -i "PROJCRS\|GEOGCRS\|AUTHORITY"-so表示 summary only,只输出摘要不打印每个要素;-al是对所有图层操作。输出里重点看三行:Geometry是不是 Polygon 或 MultiPolygon(分省数据通常是 MultiPolygon,因为一个省可能包含岛屿等多块);Feature Count是不是符合预期(省级行政区数量级在三十多个,具体以数据为准);Layer SRS里的坐标系。如果AUTHORITY显示EPSG:4326,说明是 WGS84 地理坐标,单位是度,直接算面积会得到「平方度」这种没有物理意义的数字,必须先投影。
提示:如果
ogrinfo报找不到命令,说明 GDAL 没装或没进 PATH。Windows 下可以用 OSGeo4W 装,macOS 用brew install gdal,Linux 用包管理器装gdal-bin。
2.2 属性表字段决定了你能怎么聚合
分省数据的价值一半在几何,一半在属性表。用下面这条命令把字段结构单独拎出来看:
# 输出字段名、类型、宽度,判断哪个字段能作为省级唯一标识 ogrinfo -so -al china_province.shp | sed -n '/Data axis/,/Geometry/p'常见做法是属性表里会有一个省级名称字段(比如NAME、PROV、省之类)和一个行政代码字段。行政代码比名称更可靠——名称可能有「省」「自治区」「特别行政区」后缀不一致的问题,做表连接时容易对不上。我一般会优先用代码字段做主键,名称字段只用来显示。如果这份数据的字段名是拼音或英文缩写,建议先导出一份字段对照表,避免后面写 SQL 时猜字段。
# 用 geopandas 读进来,直接看字段和坐标系,比命令行更直观 import geopandas as gpd gdf = gpd.read_file("china_province.shp") print("坐标系:", gdf.crs) # 确认 EPSG 编号 print("字段:", list(gdf.columns)) # 看有哪些属性列 print("要素数:", len(gdf)) # 省级单元数量 print("几何类型:", gdf.geom_type.unique()) print(gdf.head(3)) # 抽查前几行属性gdf.crs返回的是 pyproj 的 CRS 对象,能直接看到 EPSG 编号;geom_type.unique()能告诉你是不是混合了几何类型。如果发现有的要素是 Polygon、有的是 MultiPolygon,做统一处理时要么全部转成 MultiPolygon,要么用explode()拆开,否则某些空间操作会报类型不匹配。
2.3 完整性检查:领土范围不能只看大陆轮廓
「完整领土」这四个字是这份数据的核心卖点,但完整性得自己验,不能只信文件名。检查思路是:把数据画出来,看几个关键区域在不在——东南沿海的岛屿、最南端的岛礁、西部边境的完整边界。用 geopandas 快速出一张检查图:
import matplotlib.pyplot as plt # 画全国轮廓,肉眼确认关键区域是否缺失 fig, ax = plt.subplots(figsize=(12, 10)) gdf.plot(ax=ax, edgecolor="black", facecolor="none", linewidth=0.5) ax.set_title("Province Boundary Check") plt.savefig("check.png", dpi=150, bbox_inches="tight")出图后重点看两处:一是南部海域是否有独立的岛礁要素或包含在某个省级单元里;二是西部边界是否连续无断裂。如果发现某块区域是空白,说明数据在这一块有缺失,后续做全国统计时会漏算。另一个量化检查是看总面积——投影到等面积坐标系后算总面积,和已知的国土面积量级对比,偏差过大就说明边界有问题。
# 投影到等面积坐标系后再算面积,地理坐标下算面积没有意义 gdf_proj = gdf.to_crs("EPSG:6933") # 全球等面积圆柱投影 total_area = gdf_proj.geometry.area.sum() / 1e6 # 转成平方公里 print(f"总面积约 {total_area:.0f} 平方公里")EPSG:6933是等面积投影,适合做全国尺度的面积量算。注意这里算的是几何面积,不含海域,所以数值会比含海域的国土面积小,这是正常的,关键看量级对不对。
3. 从原始 SHP 到可用底图:投影、裁剪与字段标准化
验完数据只是第一步,真正拿来做分析前,还得把它加工成「能直接叠加业务数据」的形态。这一步的核心是三件事:统一坐标系、按研究区裁剪、把字段整理成自己能用的结构。跳过这步直接叠加,最常见的翻车就是坐标系不一致导致图层错位,或者字段名对不上导致连接失败。
3.1 坐标系选择:按用途定,别盲目用 4326
坐标系没有「最好」,只有「合不合适」。做全国尺度的面积统计、密度分析,用等面积投影;做 Web 地图展示,用 Web 墨卡托(EPSG:3857);做和 GPS 点位叠加,用 WGS84(EPSG:4326)。我一般会保留一份 4326 的原始版本作为母版,然后按分析需求派生出投影版本。
# 按用途派生不同投影版本,母版不动 gdf_wgs = gdf.to_crs("EPSG:4326") # 经纬度,用于和 GPS 点叠加 gdf_web = gdf.to_crs("EPSG:3857") # Web 墨卡托,用于在线地图底图 gdf_area = gdf.to_crs("EPSG:6933") # 等面积,用于面积/密度计算 # 分别导出,命名带投影标识,避免以后自己都分不清 gdf_wgs.to_file("province_wgs84.shp", encoding="utf-8") gdf_web.to_file("province_webmercator.shp", encoding="utf-8") gdf_area.to_file("province_equalarea.shp", encoding="utf-8")to_crs()的参数是目标坐标系,可以传 EPSG 编号字符串,也可以传 proj 字符串。导出时encoding="utf-8"很关键——Shapefile 的 .dbf 默认编码在不同系统上不一致,中文属性容易变乱码,显式指定 UTF-8 能规避大部分问题。如果目标软件不支持 UTF-8 的 dbf,可以改用 GBK,但要和读取端保持一致。
3.2 按研究区裁剪:用 clip 而不是手动删要素
如果你只研究某几个省或者某个流域,没必要保留全国数据。常见做法是用clip按边界裁剪,而不是手动选中删除——手动删容易漏掉飞地或者跨界的部分。假设你有一个研究区的边界文件study_area.shp:
import geopandas as gpd province = gpd.read_file("province_wgs84.shp") study = gpd.read_file("study_area.shp") # 先统一坐标系,再裁剪,顺序反了会报错或结果错位 if province.crs != study.crs: study = study.to_crs(province.crs) clipped = gpd.clip(province, study) clipped.to_file("province_clipped.shp", encoding="utf-8") print(f"裁剪后要素数: {len(clipped)}")gpd.clip()做的是几何相交裁剪,保留落在研究区内的部分。注意裁剪前必须统一 CRS,否则 clip 会直接抛异常或者给出错误结果。裁剪后要素数可能比原来少(完全在区外的被剔除),也可能几何被切割(跨界的被切开),这取决于研究区边界和省级边界的关系。
3.3 字段标准化:把属性表改成自己能维护的结构
原始数据的字段名往往不统一,有的用拼音,有的用英文缩写,有的还带特殊字符。做后续分析前,我习惯把字段重命名成一套固定规范,比如prov_name、prov_code、area_km2。这样不管换哪份底图,下游代码都不用改。
# 字段重命名 + 补充计算字段,形成标准化底图 gdf_std = gdf_area.copy() gdf_std = gdf_std.rename(columns={ "NAME": "prov_name", # 按实际字段名替换 "CODE": "prov_code", }) # 补充面积字段,单位平方公里 gdf_std["area_km2"] = gdf_std.geometry.area / 1e6 # 只保留需要的列,减小文件体积 gdf_std = gdf_std[["prov_name", "prov_code", "area_km2", "geometry"]] gdf_std.to_file("province_std.shp", encoding="utf-8")重命名时columns字典的键是原字段名,值是新字段名,必须和实际字段对得上,写错了不会报错但会多出一列 NaN。补充的area_km2是在等面积投影下算的,所以有物理意义。最后只保留必要列,能显著减小 .dbf 体积,加快后续读取速度。
4. 避坑与排查:分省 SHP 最容易翻车的五个地方
这份数据本身不复杂,但用起来翻车的点不少,而且很多是「看起来正常、结果错了」的隐性坑。下面五条是我和身边人踩过的,按「现象 → 原因 → 解决」写清楚。
4.1 面积算出来是「平方度」,数值小得离谱
现象:用gdf.geometry.area算面积,得到的是零点几到几十的数值,完全不是平方公里量级。原因:数据是 EPSG:4326 地理坐标,单位是度,面积自然是平方度。解决:先to_crs()到等面积投影(如 EPSG:6933 或 Albers 等积投影)再算面积,别在地理坐标下做任何面积或距离量算。
4.2 中文属性变乱码,省份名全是问号
现象:打开属性表,中文省名显示成???或乱码方块。原因:Shapefile 的 .dbf 编码和读取端不一致,常见是数据用 GBK 存、软件按 UTF-8 读。解决:读取时显式指定编码,gpd.read_file(..., encoding="gbk")或"utf-8"试一遍;导出时统一用 UTF-8,并在文档里注明编码,避免交接时出问题。
4.3 图层叠加错位,点位飘到海里
现象:把 GPS 点位和分省底图叠加,点全跑到边界外或者海里。原因:两个图层坐标系不一致,一个是 4326 一个是 3857,软件按数值直接叠了。解决:叠加前强制统一 CRS,point.to_crs(province.crs),并且养成「先查 CRS 再叠加」的习惯,别信软件自动对齐。
4.4 某个省「消失」了,要素数对不上
现象:明明是全国数据,画出来少了一个省,或者要素数比预期少。原因:可能是该省是 MultiPolygon 但被某步操作拆散后只保留了主块,也可能是裁剪时被误删。解决:检查len(gdf)和gdf.geom_type,用explode()看是不是被拆成多块;如果是裁剪导致,回到裁剪步骤确认研究区边界是否覆盖了该省。
4.5 边界锯齿严重,放大后像马赛克
现象:放大到城市级别,省界呈现明显折线锯齿,不够平滑。原因:这份数据是省级尺度,边界精度本来就按省级综合过,不适合做市级或更细的展示。解决:明确数据精度边界——省级分析够用,市级以上展示需要换更高精度的底图。别拿省级数据硬撑大比例尺出图,那是给自己找麻烦。
5. 进阶用法:把分省 SHP 接进自动化分析流水线
单次出图用桌面软件就够了,但如果你要反复做「业务数据按省聚合 + 出图」这件事,手动操作迟早会累死自己。我的习惯是把分省 SHP 作为底图母版,封装成一个可复用的空间连接函数,业务数据进来直接跑出结果。下面这套写法我用了很久,核心是「底图只读一次、投影只转一次、连接只做一次」。
import geopandas as gpd import pandas as pd def aggregate_by_province(business_csv, province_shp, value_col): """ 把带经纬度的业务数据按省聚合。 business_csv: 含 lon/lat 两列的业务数据 province_shp: 分省底图(任意坐标系,函数内统一) value_col: 要聚合的数值列名 """ # 底图投影到等面积,保证后续面积和空间关系正确 prov = gpd.read_file(province_shp).to_crs("EPSG:6933") # 业务数据转成点图层,原始经纬度按 WGS84 处理 df = pd.read_csv(business_csv) pts = gpd.GeoDataFrame( df, geometry=gpd.points_from_xy(df["lon"], df["lat"]), crs="EPSG:4326", ).to_crs(prov.crs) # 关键:转到和底图一致 # 空间连接:每个点落到哪个省 joined = gpd.sjoin(pts, prov, how="left", predicate="within") # 按省聚合,统计点数和数值总和 result = joined.groupby("prov_name").agg( point_count=(value_col, "size"), value_sum=(value_col, "sum"), ).reset_index() return result # 调用示例 out = aggregate_by_province("biz_points.csv", "province_std.shp", "amount") print(out.sort_values("value_sum", ascending=False).head(10))这段代码有几个关键点值得说。第一,底图在函数内统一转到EPSG:6933,业务点也跟着转,保证sjoin时坐标系一致,这是最容易出错的地方。第二,predicate="within"表示点必须落在省内才算匹配,边界上的点可能因为浮点误差落到省外,如果业务上不能丢点,可以改成"intersects"并加缓冲。第三,how="left"保留没匹配上的点,方便排查是数据问题还是边界问题。第四,聚合用groupby而不是循环,数据量大时性能差好几个量级。
再补一个验证环节。自动化流水线最怕「跑通了但结果是错的」,所以每次跑完我会做两个断言:一是聚合后的省数量不超过底图省数量,二是point_count的总和等于原始点数(用within时可能略少,差值就是落在边界外的点)。
# 结果自检:点数守恒 + 省数量合理 assert out["point_count"].sum() <= len(pts), "聚合后点数超过原始点数,逻辑有误" assert len(out) <= len(prov), "聚合后省数量超过底图,连接可能重复" # 打印未匹配的点数,判断是否需要调整 predicate unmatched = joined[joined["prov_name"].isna()] print(f"未匹配点数: {len(unmatched)}")assert在生产环境可以换成日志告警,但思路一样——让错误在流水线里尽早暴露,而不是等到出图才发现。未匹配的点单独拎出来看,如果集中在边界附近,说明是精度问题;如果随机分布,可能是经纬度写反了或者坐标系标错了。
从那以后我每次接新的分省底图,都强制走一遍「ogrinfo 验坐标系 → 投影后算面积 → 空间连接自检」这三步,再也没出现过点位飘海里或者面积差一个量级的事故。希望这份拆解能帮到你,拿到数据先验后用,别急着出图。
本文还有配套的精品资源,点击获取