
简介这份全国省、县界线shp格式矢量图面向地理信息、城乡规划、地图可视化及空间数据分析方向的从业者与学习者精确到县区域可用于行政区划底图制作、专题制图、区域统计与GIS空间叠加分析等场景省界与县界两级数据可满足从宏观到精细尺度的制图需求。资源以rar压缩包形式提供整体约14.56MB包内以shp矢量文件为主通常配套dbf属性表、shx索引与prj投影文件便于在ArcGIS、QGIS等软件中直接加载并读取行政区划字段。目前已有1613人学习下载说明其在同类底图资源中具备一定参考价值。读者可借此获得覆盖全国、细化到县级的行政边界数据省去逐级搜集与拼接的繁琐快速搭建统一坐标系的底图环境为后续空间分析、地图出图与区域研究提供可靠的数据基础。1. 拿到一份全国省、县界线 shp 之后先别急着画图很多做数据分析、选址评估、区域统计的朋友第一次拿到「全国省、县界线 shp 格式矢量图精确到县区域」这类数据时第一反应是丢进 QGIS 或 ArcGIS 里看一眼觉得能显示就完事了。真正上手做业务才会发现这份数据能不能用、怎么用取决于你对坐标系、属性表结构、边界拓扑这三件事的理解深度。它本质上是一套面状矢量数据省级一层、县级一层每个要素带行政区划编码和名称用来做空间聚合、区域裁剪、地图底图、按县汇总指标。适合做区域对比分析、门店覆盖评估、物流分区、报表地图的人。但如果你直接拿它去算面积、做叠加分析十有八九会翻车——因为坐标系没统一、属性字段对不上、边界有缝隙或重叠。这篇笔记就按我实际处理这类数据的顺序把选型理由、操作步骤、参数设置和踩坑记录讲清楚让你拿到 shp 之后能直接跑通一条可复现的流程。2. 坐标系与投影为什么你的面积算出来是错的2.1 地理坐标系和投影坐标系的区别以及选哪个shp 文件本身不携带坐标系信息它靠同目录下的 .prj 文件记录。很多来源的数据 .prj 要么缺失要么写的是 GCS_WGS_1984EPSG:4326也就是经纬度坐标。这种坐标系下直接算面积单位是「平方度」没有任何物理意义。我一般会先确认两件事数据当前是什么坐标系以及我要做什么分析。如果只是做展示、做点位落图EPSG:4326 够用。但如果要算面积、算距离、做缓冲区必须投影到等面积或等距投影。全国范围常用的是 Albers 等面积投影参数一般设中央经线 105°E双标准纬线 25°N 和 47°N。这个组合在国内区域统计里是常见做法能保证全国尺度下面积变形可控。具体操作上在 QGIS 里可以用「矢量 → 数据管理工具 → 重新投影图层」目标 CRS 选自定义 Albers。在命令行里用 GDAL 的 ogr2ogr 更可控ogr2ogr -f ESRI Shapefile \ -t_srs projaea lat_125 lat_247 lat_00 lon_0105 x_00 y_00 datumWGS84 unitsm no_defs \ county_albers.shp county_wgs84.shp这段命令把 county_wgs84.shp 从原坐标系重投影到 Albers 等面积投影输出 county_albers.shp。-t_srs后面是目标投影的 proj 字符串lat_1和lat_2是双标准纬线lon_0是中央经线unitsm保证输出单位是米。跑完之后再用字段计算器算面积单位就是平方米除以 1e6 得到平方公里。注意重投影前先备份原始文件。shp 格式在重投影后字段名可能被截断属性表有丢失风险。2.2 属性表字段怎么读编码才是主键省、县界线 shp 的属性表通常包含行政区划代码、名称、级别等字段。字段名可能是中文也可能是拼音缩写不同来源差异很大。我见过用NAME、CODE的也见过用省、市、县的。关键是要找到那个唯一的行政区划代码字段它才是做表连接的主键。常见做法是先打开属性表看字段列表找到类似adcode、code、xzqhdm这样的字段。行政区划代码是 6 位数字前两位是省中间两位是市后两位是县。用这个编码去和你的业务数据做 join比用名称匹配可靠得多——名称有重名、有简称、有空格编码不会。如果属性表里没有编码字段只有名称那就需要自己构造一个映射表。我一般会先用 Python 的 geopandas 读进来看看结构import geopandas as gpd gdf gpd.read_file(county_wgs84.shp) print(gdf.columns.tolist()) print(gdf.head(3)) print(gdf.crs)这段代码输出字段列表、前三条记录和坐标系信息。gdf.columns让你确认字段名gdf.head(3)看数据长什么样gdf.crs确认 .prj 是否被正确读取。如果gdf.crs返回 None说明 .prj 缺失或损坏需要手动指定。参数说明gpd.read_file默认读取同目录下所有关联文件.shp、.shx、.dbf、.prj。如果只想读部分字段可以加include_fields参数但一般没必要数据量不大。2.3 边界拓扑检查缝隙和重叠怎么发现省、县界线数据最隐蔽的问题是拓扑错误。县级面之间可能有微小缝隙也可能有重叠。这些错误在可视化时看不出来但做空间聚合时会出问题——比如按县汇总人口缝隙区域的人被漏掉重叠区域的人被算两次。检查方法是用 QGIS 的「拓扑检查器」或者用 Python 的 shapely 做自相交检查。我一般会跑一个简单的重叠检测from shapely.validation import explain_validity invalid gdf[~gdf.geometry.is_valid] for idx, row in invalid.iterrows(): print(idx, explain_validity(row.geometry))这段代码找出所有无效几何体并打印无效原因。常见原因包括自相交、环方向错误、重复节点。explain_validity会给出具体描述比如「Self-intersection」或「Ring Self-intersection」。如果发现无效几何体可以用buffer(0)做修复但这个操作会轻微改变边界形状。更稳妥的做法是用 QGIS 的「修复几何」工具或者用 GRASS 的 v.clean 做拓扑清理。修复后一定要重新检查确认没有新的缝隙产生。提示拓扑修复是不可逆操作务必在副本上做。修复后的数据要和原始数据做面积对比偏差超过 0.1% 就要警惕。3. 从 shp 到可用数据裁剪、聚合与导出的完整链路3.1 按业务范围裁剪用掩膜提取目标区域拿到全国数据后大多数业务只需要其中几个省或几个县。直接在全量数据上做分析会慢而且容易引入无关区域的干扰。裁剪是最常见的预处理步骤。在 QGIS 里可以用「矢量 → 地理处理工具 → 裁剪」输入图层选全国县界掩膜图层选你的业务范围比如某个省的面。在命令行里用 ogr2ogr 的-clipsrc参数ogr2ogr -f ESRI Shapefile \ -clipsrc province_mask.shp \ county_clipped.shp county_albers.shp-clipsrc指定裁剪用的掩膜图层输出只保留与掩膜相交的部分。注意这个操作是按几何相交裁剪不是按属性筛选。如果你只是想按属性选几个省用-where更高效ogr2ogr -f ESRI Shapefile \ -where adcode LIKE 44% OR adcode LIKE 45% \ county_selected.shp county_albers.shp-where后面跟 SQL 风格的过滤条件adcode LIKE 44%表示广东省的县。这种方式不改变几何形状只筛选记录速度更快。参数说明-where的字段名要和属性表里的实际字段名一致大小写敏感。如果字段名是中文需要加双引号。LIKE的通配符是%不是*。3.2 按县聚合指标空间连接和分组统计裁剪之后通常要把业务数据比如门店数、订单量、人口按县汇总。如果业务数据是点用空间连接如果是面用叠加分析。点数据按县聚合的典型流程是先做空间连接把每个点归属到所在的县然后按县编码分组统计。用 geopandas 可以这样写import geopandas as gpd import pandas as pd counties gpd.read_file(county_clipped.shp) points gpd.read_file(stores.shp) # 确保坐标系一致 points points.to_crs(counties.crs) # 空间连接每个点匹配到所在的县 joined gpd.sjoin(points, counties, howleft, predicatewithin) # 按县编码分组统计 result joined.groupby(adcode).agg( store_count(store_id, count), total_sales(sales, sum) ).reset_index() # 合并回县界数据 counties_with_stats counties.merge(result, onadcode, howleft) counties_with_stats.to_file(county_stats.shp, encodingutf-8)这段代码先统一坐标系然后做空间连接。predicatewithin表示点落在县面内部才算匹配。howleft保留所有点没匹配上的点县编码为空。分组统计用groupby加agg可以同时算计数和求和。最后用merge把统计结果挂回县界数据导出为新的 shp。参数说明sjoin的predicate参数可选within、contains、intersects。点落在边界上时within可能不匹配改用intersects更宽松。groupby的字段必须是连接后存在的字段如果adcode在连接后变成adcode_left或adcode_right需要先重命名。注意导出 shp 时中文字段名可能乱码建议在导出前把字段名改成英文或者导出为 GeoJSON 或 GeoPackage 格式。3.3 导出与格式转换shp 的局限和替代方案shp 是 legacy 格式字段名最长 10 个字符中文支持差单文件不能超过 2GB。如果数据量大或字段多建议导出为 GeoPackage.gpkg或 GeoJSON。用 ogr2ogr 转换格式ogr2ogr -f GPKG county_stats.gpkg county_stats.shp这条命令把 shp 转成 GeoPackage字段名不再受 10 字符限制中文也能正常保存。GeoPackage 是 SQLite 数据库支持空间索引查询速度比 shp 快。如果必须用 shp导出前把字段名改成英文缩写比如adcode、name、store_cnt、sales_sum。字段名超过 10 字符会被截断导致后续读取时找不到字段。参数说明-f GPKG指定输出格式为 GeoPackage。-lco可以指定图层创建选项比如-lco SPATIAL_INDEXYES创建空间索引。-nln指定输出图层名不指定则沿用原文件名。4. 避坑记录处理省、县界线 shp 时最容易翻车的五个点4.1 坐标系不统一导致面积偏差几十倍现象用 EPSG:4326 的县界数据算面积结果数值很小或者和其他来源的面积对不上。原因EPSG:4326 是地理坐标系单位是度不是米。直接算面积得到的是平方度和平方公里差好几个数量级。解决先重投影到 Albers 等面积投影再算面积。重投影后用gdf.geometry.area得到平方米除以 1e6 得到平方公里。对比官方公布的县面积偏差应在 1% 以内。4.2 属性表编码字段有前导零读进来变成数字现象行政区划代码110101读进来变成110101但010101变成10101前导零丢失。原因CSV 或 Excel 读取时把编码字段识别为数字类型自动去掉前导零。解决读取时指定dtype{adcode: str}或者在 Excel 里把该列设为文本格式。用 geopandas 读 shp 时dbf 里的字段类型如果是数值也会丢前导零。需要在读取后手动补零gdf[adcode] gdf[adcode].astype(str).str.zfill(6)。4.3 边界缝隙导致空间连接漏点现象空间连接后有些点没有匹配到任何县但看地图明明在县范围内。原因县与县之间有微小缝隙点恰好落在缝隙里。或者点的坐标系和县界坐标系不一致导致位置偏移。解决先检查坐标系是否一致用to_crs统一。如果坐标系一致仍有漏点用intersects代替within或者对县界做buffer(0.0001)轻微膨胀覆盖缝隙。膨胀后的面积变化可以忽略。4.4 中文属性导出乱码现象导出 shp 后中文县名变成乱码或问号。原因shp 的 dbf 文件默认编码是 Latin-1不支持中文。GDAL 导出时可以指定编码但部分软件读取时不识别。解决导出时加encodingutf-8但 shp 对 UTF-8 支持不完整。更稳妥的做法是导出为 GeoPackage 或 GeoJSON这两种格式原生支持 UTF-8。如果必须用 shp把中文字段名改成英文中文值用 UTF-8 编码读取时指定编码。4.5 县级面重叠导致指标重复计算现象按县汇总指标时总数比实际多或者某些县的数据异常高。原因县级面之间有重叠区域同一个点被匹配到多个县。解决先做拓扑检查找出重叠面。用 QGIS 的「拓扑检查器」或 Python 的gdf.geometry.overlaps检测。修复方法是用「融合」或「联合」操作消除重叠或者用「差异」操作把重叠部分分配给其中一个县。修复后重新做空间连接确认每个点只匹配一个县。5. 进阶技巧用空间索引和分块处理把全国数据跑进内存全国县级面大约 2800 多个要素数据量本身不大但如果和千万级的点数据做空间连接内存和速度就会成为瓶颈。我一般会用两个手段优化空间索引和分块处理。空间索引用 geopandas 的sindex它基于 R-tree能把空间连接的复杂度从 O(n*m) 降到 O(n log m)。用法很简单# 构建空间索引 counties_sindex counties.sindex # 对每个点先用索引快速筛选候选县再做精确判断 def find_county(point): candidates list(counties_sindex.intersection(point.bounds)) for idx in candidates: if counties.geometry.iloc[idx].contains(point): return counties.iloc[idx][adcode] return None points[adcode] points.geometry.apply(find_county)这段代码先构建县界的空间索引然后对每个点用intersection快速找到可能包含它的县再做contains精确判断。point.bounds返回点的外接矩形sindex.intersection返回与该矩形相交的县索引列表。这种方式比全量sjoin快很多尤其是点数据量大的时候。参数说明sindex.intersection接受一个边界框返回索引列表。contains是精确判断比intersects严格。如果点落在边界上contains可能返回 False改用intersects更安全。分块处理适合点数据超过内存的情况。用 pandas 的read_csv加chunksize参数分块读取每块做空间连接后追加到结果文件import pandas as pd chunk_iter pd.read_csv(points.csv, chunksize100000) for i, chunk in enumerate(chunk_iter): chunk_gdf gpd.GeoDataFrame( chunk, geometrygpd.points_from_xy(chunk.lon, chunk.lat), crsEPSG:4326 ).to_crs(counties.crs) joined gpd.sjoin(chunk_gdf, counties, howleft, predicatewithin) joined.to_file(fjoined_part_{i}.gpkg, driverGPKG)这段代码每次读 10 万行转成 GeoDataFrame统一坐标系做空间连接导出为 GeoPackage。最后把所有分块结果合并。chunksize根据内存大小调整一般 10 万到 50 万行比较稳妥。参数说明points_from_xy从经纬度列创建点几何crsEPSG:4326指定原始坐标系。to_crs转换到县界坐标系。driverGPKG指定输出格式比 shp 更适合分块追加。最后一个我踩过的坑分块处理时如果块与块之间的点有重复合并后会出现重复记录。我一般会在分块前对点数据去重或者用唯一 ID 做合并后的去重。这个习惯帮我省了很多后悔药。希望帮到你。本文还有配套的精品资源点击获取