简介:这份全国县级行政区划shp文件面向GIS从业者、地理信息专业学生及区域研究分析人员,提供中国全部县级行政区域的精确边界数据,可用于地图制作、人口统计、资源管理、城市规划与灾害响应等空间分析场景。资源包共14个文件,约29.94MB,以shp、shx、dbf三类核心文件为主,分别承载几何边界、空间索引与属性数据,另含prj投影文件、xml元数据及sbx、sbn空间索引文件,采用GCS_WGS_1984经纬度坐标系,可直接在ArcGIS、QGIS等主流软件中打开、查询与叠加分析。目前已有3477人学习下载,数据覆盖县、自治县、市辖区、旗、县级市等县级单位,属性表包含县名、代码等信息,便于筛选统计与投影转换、缓冲区分析等操作,是理解中国地理空间格局、开展区域研究与规划的基础数据资源。
1. 全国县级行政区划shp文件:从数据获取到空间分析的完整落地路径
做空间分析的人迟早会撞上一个需求:把业务数据挂到县级行政区划上。不管是做门店覆盖评估、物流分区规划,还是做人口热力图的底图,你都需要一份全国县级行政区划的 shp 文件。这份数据听起来简单——不就是一堆多边形加几个属性字段吗?但真正动手找过的人都知道,坑从数据获取那一刻就开始了:坐标系不统一、行政区划代码对不上、飞地和飞地式边界缺失、属性表里连县名都拼错。我前后折腾过三四套方案,从公开渠道下载过、从地图服务里导出过、也自己拼接清洗过,最后才摸出一套相对稳定的流程。这篇笔记就是把这套流程拆开讲清楚:数据从哪来、字段怎么理解、坐标系怎么统一、怎么和你的业务表做关联、以及那些只有踩过才知道的边界问题。适合已经会用 QGIS 或 ArcGIS 基本操作、但还没系统处理过全国尺度行政区划数据的同学。
2. 拿到 shp 文件之后先别急着画图:字段、坐标系与拓扑检查
2.1 全国县级行政区划 shp 文件里到底有什么
一份标准的全国县级行政区划 shp 文件,通常包含三类信息:几何图形(多边形边界)、属性字段(行政区划代码、名称、层级)、以及空间参考信息(坐标系定义)。属性表里最常见的字段包括adcode(行政区划代码)、name(名称)、level(层级,如 province/city/county)、parent(上级代码)、center(中心点坐标)等。不同来源的数据字段名可能略有差异,但核心逻辑一致:用adcode做唯一标识,用parent做层级关联。
我一般拿到文件后第一件事不是打开地图看,而是先看属性表。用 Python 的geopandas读进来,打印前几行和字段类型:
import geopandas as gpd # 读取 shp 文件,注意指定编码,避免中文乱码 gdf = gpd.read_file("county.shp", encoding="utf-8") # 查看字段名、数据类型、非空数量 print(gdf.dtypes) print(gdf.head(3)) # 检查 adcode 是否有重复 dup = gdf[gdf.duplicated(subset="adcode", keep=False)] print(f"重复的 adcode 数量: {len(dup)}") # 检查几何是否有效 invalid = gdf[~gdf.geometry.is_valid] print(f"无效几何数量: {len(invalid)}")这段代码做了三件事:确认字段名和类型、检查adcode唯一性、检查几何有效性。参数上,encoding="utf-8"是关键,很多 shp 文件属性表用 GBK 编码,读出来中文全是乱码,这时候换成encoding="gbk"再试。duplicated的subset参数指定按adcode判重,如果发现有重复,说明数据里混入了不同层级的记录,需要按level字段过滤。
2.2 坐标系不统一是第一个翻车点
全国县级行政区划 shp 文件常见的坐标系有三种:WGS84(EPSG:4326)、CGCS2000(EPSG:4490)、以及 Web 墨卡托(EPSG:3857)。WGS84 是经纬度坐标,单位是度;Web 墨卡托是投影坐标,单位是米。如果你拿到的文件是 WGS84,但业务数据是 Web 墨卡托,直接叠加就会错位到十万八千里。
统一坐标系的逻辑是:先确认源坐标系,再转换到目标坐标系。用geopandas的to_crs方法:
# 查看当前坐标系 print(gdf.crs) # 如果 crs 为空,需要手动指定源坐标系 if gdf.crs is None: gdf = gdf.set_crs("EPSG:4326") # 转换到 CGCS2000 地理坐标系 gdf_cgcs2000 = gdf.to_crs("EPSG:4490") # 如果要做面积计算,转成投影坐标系,比如 Albers 等面积投影 gdf_albers = gdf.to_crs("EPSG:6370") # CGCS2000 / Albers参数说明:set_crs只是给数据打上坐标系标签,不改变坐标值;to_crs才是真正的坐标转换。做面积统计时一定要用投影坐标系,经纬度坐标系下算出来的面积单位是“平方度”,没有物理意义。我一般用 Albers 等面积投影,适合全国尺度。
2.3 拓扑检查:飞地、缝隙和自相交
全国县级行政区划数据最让人头疼的是拓扑问题。常见的有三类:自相交(一个多边形自己和自己交叉)、缝隙(相邻县之间有空隙)、重叠(两个县边界有重叠区域)。这些问题在画图时可能看不出来,但做空间叠加分析时会导致结果错误。
用geopandas做基础拓扑检查:
# 检查自相交 self_intersect = gdf[~gdf.geometry.is_valid] if len(self_intersect) > 0: # 尝试修复:用 buffer(0) 是常见做法,但会轻微改变边界 gdf["geometry"] = gdf.geometry.buffer(0) # 检查重叠:两两求交,面积大于阈值的视为重叠 from shapely.ops import unary_union union = unary_union(gdf.geometry) if union.geom_type == "MultiPolygon": print("存在重叠或缝隙,需进一步排查")buffer(0)是修复自相交的常用技巧,原理是把多边形先向外缓冲再向内缓冲,消除自相交点。但它会轻微改变边界形状,如果对边界精度要求高,建议用make_valid方法(Shapely 1.8+ 支持)。重叠检查用unary_union把所有多边形合并,如果结果是 MultiPolygon,说明有分离的碎片,可能是缝隙或重叠导致的。
3. 把业务数据挂到行政区划上:关联、聚合与空间连接
3.1 用 adcode 做属性关联是最稳的方式
拿到县级行政区划 shp 文件后,最常见的需求是把业务表(比如各县的销售额、人口数)关联上去。关联键就是adcode。但这里有个坑:业务表里的行政区划代码可能是 6 位,而 shp 文件里可能是 12 位(带省市前缀)。需要先统一长度。
import pandas as pd # 读取业务表 biz = pd.read_csv("biz_data.csv", dtype={"adcode": str}) # 统一 adcode 长度:取前 6 位 biz["adcode_6"] = biz["adcode"].str[:6] gdf["adcode_6"] = gdf["adcode"].astype(str).str[:6] # 关联 merged = gdf.merge(biz, on="adcode_6", how="left") # 检查关联率 match_rate = merged["biz_value"].notna().sum() / len(merged) print(f"关联成功率: {match_rate:.2%}")参数说明:dtype={"adcode": str}是关键,否则 pandas 会把代码当成数字,丢掉前导零。str[:6]取前 6 位是因为县级代码固定 6 位,省市前缀不影响县级匹配。关联后用notna().sum()算匹配率,如果低于 95%,说明代码体系有问题,需要排查。
3.2 空间连接:当业务数据只有经纬度时
如果业务数据只有经纬度而没有行政区划代码,就需要用空间连接(Spatial Join)。逻辑是:把业务点转成 GeoDataFrame,然后判断每个点落在哪个县的多边形内。
from shapely.geometry import Point # 业务表有 lon, lat 字段 biz_gdf = gpd.GeoDataFrame( biz, geometry=[Point(xy) for xy in zip(biz["lon"], biz["lat"])], crs="EPSG:4326" ) # 统一坐标系 biz_gdf = biz_gdf.to_crs(gdf.crs) # 空间连接:点落在哪个县内 joined = gpd.sjoin(biz_gdf, gdf[["adcode", "name", "geometry"]], how="left", predicate="within") # 检查未匹配的点 unmatched = joined[joined["adcode"].isna()] print(f"未匹配点数: {len(unmatched)}")predicate="within"表示点完全落在多边形内部。如果点刚好在边界上,可能匹配不到,这时候可以用predicate="intersects",但会带来重复匹配的问题。未匹配的点通常是坐标偏移或落在水域、飞地等特殊区域,需要单独处理。
3.3 聚合到上级行政区:dissolve 的用法
有时候需要把县级数据聚合到市级或省级。用dissolve方法按parent字段合并多边形:
# 按 parent 字段聚合,同时求和业务字段 city_gdf = gdf.dissolve(by="parent", aggfunc={"biz_value": "sum"}) # 重置索引,保留 parent 作为列 city_gdf = city_gdf.reset_index()dissolve的原理是把相同parent的多边形合并成一个,aggfunc指定数值字段的聚合方式。注意:合并后的几何可能产生新的自相交,建议再做一次buffer(0)修复。
4. 避坑指南:全国县级行政区划 shp 文件最常见的 5 个问题
4.1 现象:地图上少了一块,某个县不见了
原因:数据源本身缺失,或者该县是飞地,几何被错误地合并到了相邻县。解决:先用gdf[gdf["name"].str.contains("某县")]确认是否存在,如果存在但画不出来,检查几何类型是否为 Polygon 或 MultiPolygon,以及is_valid是否为 True。
4.2 现象:adcode 关联后大量为空
原因:业务表的 adcode 是 6 位,shp 文件是 12 位,或者反过来。解决:统一截取前 6 位再做关联。另外注意有些数据用code而不是adcode作为字段名,需要先重命名。
4.3 现象:面积计算结果明显偏大或偏小
原因:在经纬度坐标系下直接算面积。解决:先to_crs转到投影坐标系,推荐 Albers 等面积投影(EPSG:6370 或自定义参数)。如果只是做相对比较,也可以用gdf.geometry.area在投影坐标系下计算。
4.4 现象:空间连接时一个点匹配到多个县
原因:点落在边界上,或者数据存在重叠区域。解决:用predicate="within"替代intersects,或者对匹配结果按距离排序取最近的一个。如果数据本身有重叠,需要先做拓扑修复。
4.5 现象:中文属性表乱码
原因:shp 文件的.dbf文件编码不是 UTF-8。解决:读取时指定encoding="gbk"或encoding="gb18030"。如果还是乱码,用QGIS打开后另存为 UTF-8 编码的 GeoJSON,再用geopandas读取。
5. 进阶技巧:用 GeoPandas 做县级行政区划的批量出图与自动化校验
5.1 批量出图:每个县一张图
做汇报时经常需要每个县单独出一张图。用matplotlib循环绘制:
import matplotlib.pyplot as plt # 按省分组,每个省出一张图 for province, group in gdf.groupby("parent"): fig, ax = plt.subplots(figsize=(10, 8)) group.plot(ax=ax, column="biz_value", legend=True, cmap="OrRd", edgecolor="gray", linewidth=0.5) ax.set_title(f"Province {province} County-Level Data") ax.axis("off") plt.savefig(f"output_{province}.png", dpi=150, bbox_inches="tight") plt.close()groupby("parent")按上级代码分组,column="biz_value"指定着色字段,cmap="OrRd"是颜色映射方案。bbox_inches="tight"去掉多余白边。注意:如果某个省的数据量太大,出图会很慢,可以先用simplify简化几何:
gdf["geometry"] = gdf.geometry.simplify(tolerance=0.01, preserve_topology=True)tolerance是简化容差,单位是度(经纬度坐标系下)。preserve_topology=True保证简化后不会产生自相交。
5.2 自动化校验:每次更新数据后跑一遍
数据更新后,手动检查容易漏。我一般写一个校验脚本,每次更新后自动跑:
def validate_county_shp(gdf): """校验县级行政区划 shp 文件的完整性""" errors = [] # 1. 检查 adcode 唯一性 if gdf["adcode"].duplicated().any(): errors.append("adcode 存在重复") # 2. 检查几何有效性 if not gdf.geometry.is_valid.all(): errors.append("存在无效几何") # 3. 检查坐标系是否定义 if gdf.crs is None: errors.append("坐标系未定义") # 4. 检查是否有空几何 if gdf.geometry.is_empty.any(): errors.append("存在空几何") # 5. 检查 adcode 长度是否一致 if gdf["adcode"].astype(str).str.len().nunique() > 1: errors.append("adcode 长度不一致") return errors # 使用 errors = validate_county_shp(gdf) if errors: print("校验失败:") for e in errors: print(f" - {e}") else: print("校验通过")这个校验函数覆盖了 5 个最常见的坑:重复代码、无效几何、坐标系缺失、空几何、代码长度不一致。每次拿到新数据先跑一遍,能省掉后面很多返工。
5.3 一个我踩过的坑:别用 buffer(0) 修复所有几何
早期我图省事,所有几何问题都用buffer(0)修复。后来发现,buffer(0)会把一些细长的多边形“压扁”,导致面积变化超过 5%。对于做面积统计的场景,这是不可接受的。正确的做法是:先用make_valid尝试修复,如果还不行,再手动检查具体是哪个县出了问题。make_valid是 Shapely 1.8 引入的方法,能更好地保留原始几何形状。
from shapely.validation import make_valid # 只对无效几何做修复 invalid_mask = ~gdf.geometry.is_valid gdf.loc[invalid_mask, "geometry"] = gdf.loc[invalid_mask, "geometry"].apply(make_valid)这个习惯帮我省了很多后悔药。每次处理行政区划数据,先跑校验,再针对性修复,最后再跑一遍校验确认。希望帮到你。
本文还有配套的精品资源,点击获取