简介:面向GIS从业者、地理数据分析人员及规划相关专业学生的县级行政边界矢量数据集,以1:100万比例尺呈现2017年中国各县级行政区划范围,可用于人口统计、城市规划、环境保护与灾害管理等空间分析场景,帮助读者快速获得可直接加载的行政边界底图。压缩包共7个文件、约46.33MB,以shp主文件承载几何图形,dbf存储县名与代码等属性,prj定义CGS_WGS_1984坐标系,shx、sbn、sbx索引文件提升检索与读取效率,xml则记录元数据信息,结构完整、开箱即用。目前已有615人学习下载。读者可据此完成行政区划叠加分析、专题制图与空间统计,省去自行数字化边界的时间,并借助配套索引与坐标系文件保证数据在主流GIS软件中的准确性与兼容性。
1. 拿到一份 2017 县级行政区 shp,先别急着拖进软件
做 GIS 数据落地的人大概都有过这种经历:项目要出图、要做空间统计、要跑行政区划匹配,翻遍硬盘发现手头只有全国省级边界,县级那层要么缺、要么属性字段对不上。这时候一份完整的县级行政区 shp 就是救命的。这份 1100 万基础地理数据库的 2017 县级行政区 shp,核心价值在于它把全国县级单元的边界几何和行政区划属性打包成了标准 Shapefile,能直接进 QGIS、ArcGIS、PostGIS 或者用 GeoPandas 读。它适合三类人:做专题制图需要底图的、做空间分析需要行政单元聚合的、以及做数据清洗需要一套可信区划参照的。但 2017 这个年份意味着区划调整已经发生,直接拿来套当下数据会翻车,后面会细说。
2. 拆开 shp 看结构:字段、坐标系与几何质量
2.1 Shapefile 的五个文件各管什么
Shapefile 不是单个文件,而是一组同名文件的集合。拿到压缩包解压后,至少要有.shp、.shx、.dbf、.prj四个,缺一个都会出问题。.shp存几何坐标,.shx是几何索引,.dbf是属性表,.prj存坐标系定义。很多人在网上下的包只有.shp和.dbf,打开后坐标是乱的,就是因为丢了.prj。这份县级数据如果带.prj,先看里面写的是什么投影。1100 万比例尺的基础地理数据,国内常见的是地理坐标系 CGCS2000 或西安 80,也可能是 Albers 等积投影。用文本编辑器打开.prj看一眼,或者用下面这段代码直接读。
import geopandas as gpd # 读取 shp,注意指定编码,dbf 里中文属性常见 GBK gdf = gpd.read_file("county_2017.shp", encoding="gbk") print("要素数量:", len(gdf)) print("坐标系:", gdf.crs) print("字段列表:", list(gdf.columns)) print("几何类型:", gdf.geom_type.unique())这段代码做三件事:确认要素数量是否接近全国县级单元总数(2017 年前后大约 2800 多个县级单元,含市辖区、县、县级市、旗等),确认坐标系是否已定义,确认几何类型是不是 Polygon 或 MultiPolygon。如果gdf.crs返回None,说明.prj缺失或没被识别,后面所有面积计算和叠加分析都会错。字段列表里重点找行政区划代码字段,常见命名是adcode、code、xzqhdm之类,这是后续关联其他数据的主键。
2.2 坐标系判断与统一
地理坐标系下直接算面积得到的是平方度,没有实际意义。做面积统计前必须投影到等积坐标系。国内常用 Albers 投影,参数按 CGCS2000 椭球设置。如果原始数据是地理坐标,用下面这段转。
# 若原始为地理坐标,投影到 CGCS2000 Albers 等积投影 albers = "+proj=aea +lat_1=25 +lat_2=47 +lat_0=0 +lon_0=105 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs" gdf_proj = gdf.to_crs(albers) gdf_proj["area_km2"] = gdf_proj.geometry.area / 1e6 # 抽查几个县的面积是否落在合理区间 print(gdf_proj[["name", "area_km2"]].head(10))lat_1和lat_2是标准纬线,取 25 和 47 覆盖中国大部分区域,lon_0取 105 是中央经线。转完后面积单位是平方米,除以 1e6 得平方公里。抽查时注意:东部平原县面积可能几百到两千平方公里,西部地广人稀的县可能上万,如果出现个位数或几十万的异常值,多半是投影参数不对或几何有自相交。这一步是血泪经验,很多人跳过坐标系检查直接算面积,结果报表数字离谱还找不到原因。
2.3 属性表里哪些字段真正有用
县级行政区 shp 的属性表通常包含行政区划代码、名称、上级代码、行政级别等。2017 年的数据里,行政区划代码一般是 6 位,前两位省、中间两位市、后两位县。这个编码规则是关联统计年鉴、人口数据、经济数据的关键。拿到数据后先做一次编码完整性检查:长度是否都是 6 位、有没有空值、有没有重复。
# 检查行政区划代码质量 code_col = "adcode" # 按实际字段名替换 gdf["code_len"] = gdf[code_col].astype(str).str.len() print("编码长度分布:") print(gdf["code_len"].value_counts()) print("空值数量:", gdf[code_col].isna().sum()) print("重复编码数量:", gdf[code_col].duplicated().sum())如果长度分布里出现 5 位或 7 位,说明有前导零丢失或录入错误。Shapefile 的 dbf 对字段类型有约束,数值型字段存 6 位编码时前导零会丢,比如010101变成10101。解决办法是在读取时强制按字符串处理,或者读进来后补零。重复编码则可能是数据里混入了市级或省级要素,需要按行政级别字段过滤。这些检查看着琐碎,但不做的话后面关联数据会对不上,排查起来非常费劲。
3. 把 shp 用起来:从读取到空间分析的完整链路
3.1 用 GeoPandas 做基础筛选与导出
实际项目里很少直接用全国全量数据,通常先按省或按区域筛选。假设要提取某个省的县级单元做专题图,用属性筛选比空间裁剪快得多。
# 按上级代码筛选某省,假设省级代码前两位为 33 target_province = "33" gdf["prov_code"] = gdf[code_col].astype(str).str.zfill(6).str[:2] subset = gdf[gdf["prov_code"] == target_province].copy() print("筛选后要素数:", len(subset)) # 导出为新的 shp,注意编码 subset.to_file("province_33_county.shp", encoding="gbk")zfill(6)是补零的关键,防止前导零丢失导致前两位判断错误。筛选后导出时指定encoding="gbk",否则中文名称在 ArcGIS 里可能乱码。这一步的逻辑是:先用字符串操作把编码规整,再做前缀匹配,比直接数值比较稳妥。如果数据里还有市级字段,也可以按市筛选,方法一样。
3.2 空间叠加:把点数据聚合到县级单元
最常见的需求是有一批带经纬度的点(门店、采样点、事件点),要统计每个县里有多少个。做法是把点转成 GeoDataFrame,做空间连接。
import pandas as pd # 假设 points.csv 有 lon、lat 两列 pts = pd.read_csv("points.csv") pts_gdf = gpd.GeoDataFrame( pts, geometry=gpd.points_from_xy(pts["lon"], pts["lat"]), crs="EPSG:4326" # WGS84 经纬度 ) # 统一到同一坐标系再做空间连接 pts_gdf = pts_gdf.to_crs(gdf.crs) joined = gpd.sjoin(pts_gdf, gdf[[code_col, "name", "geometry"]], how="inner", predicate="within") # 按县统计点数 count_by_county = joined.groupby(code_col).size().reset_index(name="point_count") print(count_by_county.head())这里有两个容易翻车的点。一是坐标系必须一致,点数据常见是 WGS84(EPSG:4326),而县级 shp 可能是 CGCS2000 或投影坐标,不统一就sjoin会得到空结果或错位。二是predicate参数,within表示点落在面内,边界上的点可能被漏掉,需要的话可以改用intersects再处理重复。统计结果里没有点的县不会出现,如果要保留所有县,用左连接再填充零。
3.3 导出 GeoJSON 给前端或轻量工具用
Shapefile 在 Web 端不友好,转 GeoJSON 是常规操作。但全国县级数据转 GeoJSON 体积会很大,需要先简化几何。
# 简化几何,容差按投影单位,这里约 100 米 gdf_simplified = gdf_proj.copy() gdf_simplified["geometry"] = gdf_simplified.geometry.simplify(100, preserve_topology=True) # 转回地理坐标再导出,GeoJSON 规范要求 WGS84 gdf_simplified = gdf_simplified.to_crs("EPSG:4326") gdf_simplified[[code_col, "name", "geometry"]].to_file("county_2017.geojson", driver="GeoJSON")simplify的容差单位跟当前坐标系一致,投影坐标下是米,地理坐标下是度。preserve_topology=True保证简化后相邻面不出现缝隙或重叠,代价是简化程度受限。导出前转回 EPSG:4326 是因为 GeoJSON 规范如此,不转的话部分前端库渲染会偏。文件体积如果还是大,可以只导出需要的字段,几何精度再放宽,但要注意别把小的市辖区简化没了。
4. 避坑与排查:县级 shp 落地时最容易翻车的五件事
4.1 中文属性乱码,打开全是问号
现象:用 ArcGIS 或 QGIS 打开 dbf,名称字段显示为乱码或问号。原因:dbf 的编码声明缺失或与读取端不一致,国内数据常见 GBK,而部分工具默认按 UTF-8 读。解决:用 GeoPandas 读取时显式指定encoding="gbk",导出时同样指定;在 QGIS 里可以在图层属性中设置编码。如果已经乱码且无法恢复,只能找原始数据重导,所以拿到数据第一件事就是确认编码。
4.2 坐标系显示为 Unknown,面积算出来是天文数字
现象:gdf.crs为 None,或者算出的面积单位明显不对。原因:.prj文件缺失,或者投影定义写的是 Esri 私有格式而读取库不识别。解决:先判断数据本身是地理坐标还是投影坐标,看坐标数值范围——经纬度在 -180 到 180 之间,投影坐标通常是几十万到几百万。确认后手动赋 CRS,地理坐标用gdf.set_crs("EPSG:4326"),再按需投影。不要在不确认的情况下直接算面积。
4.3 行政区划代码前导零丢失,关联全错
现象:用编码关联统计表时大量匹配不上。原因:dbf 把编码存成数值型,010101变成10101。解决:读取时用dtype={code_col: str}强制字符串,读进来后zfill(6)补零。如果数据已经丢失前导零且无法从其他字段恢复,只能按名称加上级代码做辅助匹配,但会有重名风险,属于后悔药级别的补救。
4.4 2017 区划与当前数据对不上
现象:拿这份数据去匹配近几年的统计口径,发现某些县没了、某些县新设了、某些县改名了。原因:2017 之后经历了多轮区划调整,撤县设区、合并、更名都有。解决:如果项目允许,以数据年份为准做口径说明;如果必须对齐当前区划,需要找区划变更对照表做映射,或者用更新的边界数据。这一点没有捷径,只能提前确认数据年份是否满足业务要求。
4.5 几何自相交导致空间分析报错
现象:做sjoin或overlay时报拓扑错误,或者面积计算出负值。原因:部分面的边界存在自相交或重复节点,来源数据精度问题。解决:用gdf.geometry.is_valid检查,对无效几何做buffer(0)修复,或者用make_valid。修复后再做叠加分析。注意buffer(0)对某些复杂几何会改变形状,修复后要抽查。
# 检查并修复无效几何 invalid = gdf[~gdf.geometry.is_valid] print("无效几何数量:", len(invalid)) gdf["geometry"] = gdf.geometry.apply(lambda g: g.buffer(0) if not g.is_valid else g)5. 进阶用法:用县级 shp 做一套可复用的区划匹配底表
单次出图用完就丢太浪费,我一般会把这份县级 shp 加工成一张可复用的区划底表,后续所有项目直接调用。具体做法是:保留编码、名称、上级编码、面积、中心点坐标几个字段,几何做适度简化后存成 GeoPackage,比 Shapefile 更适合长期维护,字段名长度和编码都不受限制。
# 构建可复用底表 base = gdf_proj.copy() base["centroid_x"] = base.geometry.centroid.x base["centroid_y"] = base.geometry.centroid.y base["area_km2"] = base.geometry.area / 1e6 # 只保留必要字段,几何简化后存 GeoPackage keep = [code_col, "name", "prov_code", "area_km2", "centroid_x", "centroid_y", "geometry"] base[keep].to_file("county_base.gpkg", layer="county_2017", driver="GPKG")中心点坐标在打点标注、距离计算时很有用,提前算好省得每次重算。存 GeoPackage 的好处是单文件、支持中文、支持多图层,后续可以往里加市级、省级图层形成一套完整底表。匹配外部数据时,用编码做主键,名称做辅助校验,中心点做空间兜底,三层保障基本不会错。
验证这套底表是否可靠,我习惯做两个抽查:一是随机抽十个县,用中心点反查是否落在自身面内;二是把面积汇总到省,跟公开的省级面积做量级对比,偏差超过合理范围就说明几何或投影有问题。
# 抽查中心点是否落在面内 sample = base.sample(10, random_state=42) inside = sample.geometry.contains(sample.geometry.centroid) print("中心点落在面内比例:", inside.mean()) # 按省汇总面积 prov_area = base.groupby("prov_code")["area_km2"].sum().sort_values(ascending=False) print(prov_area.head())这两个检查花不了几分钟,但能挡住大部分低级错误。从那以后我每次拿到新的区划数据,都强制走一遍编码检查、坐标系确认、几何有效性、面积抽查这四步,再进业务。希望帮到你。
本文还有配套的精品资源,点击获取