简介面向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, encodinggbk) 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 projaea lat_125 lat_247 lat_00 lon_0105 x_00 y_00 ellpsGRS80 unitsm 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, encodinggbk)zfill(6)是补零的关键防止前导零丢失导致前两位判断错误。筛选后导出时指定encodinggbk否则中文名称在 ArcGIS 里可能乱码。这一步的逻辑是先用字符串操作把编码规整再做前缀匹配比直接数值比较稳妥。如果数据里还有市级字段也可以按市筛选方法一样。3.2 空间叠加把点数据聚合到县级单元最常见的需求是有一批带经纬度的点门店、采样点、事件点要统计每个县里有多少个。做法是把点转成 GeoDataFrame做空间连接。import pandas as pd # 假设 points.csv 有 lon、lat 两列 pts pd.read_csv(points.csv) pts_gdf gpd.GeoDataFrame( pts, geometrygpd.points_from_xy(pts[lon], pts[lat]), crsEPSG:4326 # WGS84 经纬度 ) # 统一到同一坐标系再做空间连接 pts_gdf pts_gdf.to_crs(gdf.crs) joined gpd.sjoin(pts_gdf, gdf[[code_col, name, geometry]], howinner, predicatewithin) # 按县统计点数 count_by_county joined.groupby(code_col).size().reset_index(namepoint_count) print(count_by_county.head())这里有两个容易翻车的点。一是坐标系必须一致点数据常见是 WGS84EPSG: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_topologyTrue) # 转回地理坐标再导出GeoJSON 规范要求 WGS84 gdf_simplified gdf_simplified.to_crs(EPSG:4326) gdf_simplified[[code_col, name, geometry]].to_file(county_2017.geojson, driverGeoJSON)simplify的容差单位跟当前坐标系一致投影坐标下是米地理坐标下是度。preserve_topologyTrue保证简化后相邻面不出现缝隙或重叠代价是简化程度受限。导出前转回 EPSG:4326 是因为 GeoJSON 规范如此不转的话部分前端库渲染会偏。文件体积如果还是大可以只导出需要的字段几何精度再放宽但要注意别把小的市辖区简化没了。4. 避坑与排查县级 shp 落地时最容易翻车的五件事4.1 中文属性乱码打开全是问号现象用 ArcGIS 或 QGIS 打开 dbf名称字段显示为乱码或问号。原因dbf 的编码声明缺失或与读取端不一致国内数据常见 GBK而部分工具默认按 UTF-8 读。解决用 GeoPandas 读取时显式指定encodinggbk导出时同样指定在 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, layercounty_2017, driverGPKG)中心点坐标在打点标注、距离计算时很有用提前算好省得每次重算。存 GeoPackage 的好处是单文件、支持中文、支持多图层后续可以往里加市级、省级图层形成一套完整底表。匹配外部数据时用编码做主键名称做辅助校验中心点做空间兜底三层保障基本不会错。验证这套底表是否可靠我习惯做两个抽查一是随机抽十个县用中心点反查是否落在自身面内二是把面积汇总到省跟公开的省级面积做量级对比偏差超过合理范围就说明几何或投影有问题。# 抽查中心点是否落在面内 sample base.sample(10, random_state42) inside sample.geometry.contains(sample.geometry.centroid) print(中心点落在面内比例:, inside.mean()) # 按省汇总面积 prov_area base.groupby(prov_code)[area_km2].sum().sort_values(ascendingFalse) print(prov_area.head())这两个检查花不了几分钟但能挡住大部分低级错误。从那以后我每次拿到新的区划数据都强制走一遍编码检查、坐标系确认、几何有效性、面积抽查这四步再进业务。希望帮到你。本文还有配套的精品资源点击获取