中国矢量地图SHP处理全攻略:从读取到GeoJSON导出
发布时间:2026/10/16 9:35:00 锦皓数字建站

简介中国矢量地图SHP格式是一套覆盖国界、省界、市界、县界四级行政边界的标准Shapefile数据包面向GIS开发人员、数据分析师与城乡规划从业者可用于地图制图、地理统计和专题可视化。压缩包共28个文件核心为4个.shp几何文件配套.dbf属性文件、.prj投影文件、.shx索引文件及.sbx/.sbn空间索引结构规范完整总大小仅1.78MB轻量易用。数据可直接在ArcGIS、QGIS等主流GIS软件中加载支持叠加人口、经济、交通等专题层进行深入分析。已有8046人学习/下载是处理中国行政区划数据的高性价比工具。借助这套省、市、县级边界数据用户能够快速构建精准的电子地图为科研与决策提供可靠依据。1. 中国矢量地图SHP格式不是拿来就能用的先看清它是什么很多做数据分析和可视化的人第一次接触中国矢量地图SHP格式是因为手头有一份城市列表或业务网点坐标想画一张全国分布图。可下载解压之后面对的往往是一个没有预览图的压缩包、五个后缀名不同的文件以及日后无数的“图是歪的”“属性是乱码”“合并就报错”。问题不在于这份数据本身而在于大多数使用者把它当成了普通表格数据忽略了矢量地图的底层约定。本文按真实项目里最常走的路径把读取、坐标系判断、属性合并、几何修复、避坑到最后出图的完整链路走一遍。适合想用中国 SHP 数据做专题图或空间分析的开发者,新手可循着步骤落地熟手可以直接跳到第 3 章看坐标系的识别逻辑。2. 读取中国 SHP 的最小路径工具选型与文件三口组2.1 为什么首选 geopandas一条命令读进去处理中国 SHP 数据我一般不去打开图形界面一个个点图层而是直接用 Python 的地理数据处理栈。原因有三个一是数据往往要跟业务表做关联脚本里处理比手工快二是后续做坐标系转换、属性合并都需要可复现的命令三是读取、写出、简化、溶解这一套操作在脚本里可以一气呵成不用来回导中间文件。import geopandas as gpd gdf gpd.read_file(china_province.shp) print(gdf.head())这段命令在多数环境里就能把矢量数据读成一张 DataFrame。逻辑上read_file会自动识别 SHP 的三个核心配套文件并把几何列放到geometry字段里。参数方面读取时最常用的是encoding如果属性表中文显示成乱码改成encodinggbk重读基本能解决。还有一个容易被忽略的点read_file在缺少.prj文件时不会立刻报错而是把坐标系标成None很多后来画图位置偏移的问题其实在这一步就已经埋下了。2.2 SHP 不只是 .shp 一个文件三件套与附属文件的作用拿到一份中国矢量地图的压缩包解压后看到的一堆后缀并不是冗余。SHP 格式是典型的“多文件格式”几何、属性、坐标系分文件存放。后缀作用缺失后果.shp几何坐标本体无法读取.shx几何索引部分库读取变慢极端情况直接报错.dbf属性表字段名和值都在这里名称、行政编码全部丢失.prj坐标系描述文件读进来坐标系为 None位置可能整体偏移.cpg字符编码声明中文属性大概率乱码传输或拷贝时只拿走 .shp 是最常见的低级失误。我习惯在拿到压缩包后先看一遍目录确认这五个文件都在尤其 .prj 和 .cpg 是否存在。很多第三方分享的数据包会把 .prj 丢掉导致后续所有人都在猜坐标系这是血泪经验。2.3 读取后的体检字段、几何类型和坐标系打印读进来不等于能直接用。我每拿到一份中国 SHP第一时间会打印三样东西字段列表、几何类型和坐标系。print(gdf.columns.tolist()) # 查看属性字段 print(gdf.geometry.type.value_counts()) # 看是 Polygon 还是 MultiPolygon print(gdf.crs) # 坐标系None 的话要警惕 print(len(gdf)) # 要素数量全国县级数据通常接近三千几何类型这一步很关键。如果打印结果里Polygon和MultiPolygon混在一起说明数据里有岛屿或飞地后面做合并时不要感到意外。crs输出如果为None后续任何涉及距离计算或投影的步骤都不要开始先把坐标系落实。要素数量也有参考价值一份省级数据却有几万个要素多半是做过数据加工边界碎成小面不是干净的省级图层。3. 坐标系是第一道分水岭WGS-84、GCJ-02、BD-09 的判断与换算3.1 为什么国内拿到的 SHP 坐标“不老实”三种坐标系的关系所有中国矢量地图的坑里坐标系是最容易让新手翻车的一个。世界范围内普遍使用 WGS-84 经纬度坐标系GPS 设备采集的原始坐标就是这一套。但国内多数地图产品和业务数据在发布时会对坐标做偏移处理常见的有 GCJ-02以及在此基础上再偏移一次的 BD-09。举一个直观的对比同一地标在 WGS-84 底图和自己从平台导出的数据里位置会相差几百米单独看没问题一叠到标准底图上错位就非常明显。所以拿到任何一份中国 SHP第一反应不应该问“这个画出来对不对”而是问“这份数据的坐标是相对于哪套框架采集的”。如果它是从地图平台导出或由国内数据供应商加工过的大概率不是干净的 WGS-84如果它自称 WGS-84 却带着 .prj 声明为 EPSG:4326也改变不了坐标系判断的难题因为偏移发生在导出之前.prj 是后补上去的。3.2 判断当前数据坐标系的三个线索坐标系没有绝对可靠的自动识别方法但结合下面三条线索能排除大多数问题。第一个线索是 .prj 文件。它能告诉我们数据声明上的坐标系但不等于实际坐标一定准确。第二个线索是整体边界范围。全国范围的 WGS-84 经纬度大致在经度 73 到 135、纬度 18 到 54 这个区间如果打印出来的范围明显超出或落在奇怪位置就要怀疑投影或偏移问题。第三个线索最直观把数据和一个已知准确的 WGS-84 标准底图叠在一起找一段海岸线或省界看是否错位。错位方向相对固定、距离在数百米的基本可以判定是 GCJ-02。print(gdf.total_bounds) # 输出示例[73.6, 18.2, 134.7, 53.5]这段代码看的是全图最小经度、最小纬度、最大经度、最大纬度。如果输出范围符合中国国界的基本常识不代表没问题只是说几何上没有严重投影混乱。真正做判断一定要叠加参考图层单看数值会自欺欺人。3.3 坐标系转换的实操pyproj 做正规投影与反偏移处理的边界处理坐标转换我一般分两种场景。第一种是正常的投影转换例如把经纬度 WGS-84 转成 Web 墨卡托投影便于在 Web 端做距离缓冲和简化。第二种是把已被偏移的 GCJ-02 数据转回 WGS-84这种情况能转但不要在精度上期望过高。from pyproj import Transformer trans Transformer.from_crs(EPSG:4326, EPSG:3857, always_xyTrue) x, y trans.transform(116.391, 39.905) print(x, y)这里always_xyTrue很重要它强制输入顺序为经度、纬度避免在写代码时把 lat/lng 弄反。输出结果是投影坐标单位是米。对于真正的“反偏移”网上有很多近似算法做法大体是先按正向算法算出偏移量再通过迭代逐步逼近原始坐标。批量转换能达到米级精度指望它做到厘米级就是玄学。实际项目中如果数据最终要配合国内地图平台使用留着 GCJ-02 反而是更省事的做法强转成 WGS-84 再叠底图属于自找麻烦。4. 把碎层变成可用图层adcode 合并、空间关联与几何修复4.1 看懂属性表adcode 与 name 字段的分级逻辑中国矢量地图和普通地图最不一样的地方是属性表里几乎都带着行政区划代码。这个字段的命名不统一有的叫adcode有的叫PAC有的叫code但逻辑一致。6 位数字里前两位代表省级行政区前四位代表地级单位完整六位代表县级单位。print(gdf[[name, adcode]].sample(5))拿这个字段做分级处理非常方便。向省级合并只需要取前两位向市级合并取前四位。前提是这个字段格式统一遇到数字被读成浮点数时先用astype(str)转回字符串。如果数据里没有该字段就按名称维护一张手工对照表空间上逐行匹配成本高很多。我一般拿到数据先查这两列有没有空值空值太多就放弃 adcode 路线改用后面提到的空间关联方案。4.2 用 dissolve 把县级面合并成省级面代码与参数数据源如果是县级边界要画省级图或全国图直接用县级面会导致边缘线过密因此需要合并。gdf[prov_code] gdf[adcode].astype(str).str[:2] prov_gdf gdf.dissolve(byprov_code, aggfunc{name: first}).reset_index()dissolve的逻辑是按分组字段把多个面的公共边界移除生成新的更大的面。aggfunc{name: first}指保留该省级行政区里第一个县的名字实际上省级名称最好由prov_code另接标准名称表否则可能拿到一个县城名字而不是省名。结果是每个省级行政区一个要素但其中带岛屿或飞地的省份会是 MultiPolygon这是正常的。溶解后务必执行prov_gdf.geometry.is_valid检查很多原始数据在合并后才会暴露拓扑问题。4.3 几何修复自相交多边形和重叠面的处理县级合并到省级最常遇到的报错是拓扑异常常见的有面自相交、环方向不一致和相邻面重叠。报错信息通常是类似TopologyException或GEOSException的提示处理办法只有一个思路修复几何后再合并。from shapely.validation import make_valid gdf[geometry] gdf[geometry].apply(make_valid)更保守的做法是用缓冲区修复gdf[geometry] gdf[geometry].buffer(0)buffer(0)原理上相当于把每个面边界向外再向内收缩消除零宽度的自相交代价是边界顶点会变多。它比make_valid更容易理解但数据量也会变大修复完建议接着做简化。特别提醒修复、合并、简化这个顺序不能反一旦先简化再修复原来还能补救的错误会被放大。5. 避坑处理中国 SHP 地图最常见的 4 个翻车现场5.1 翻车一属性表里省市名称全是乱码现象read_file读完后name列显示成鍖椾含这类完全不可读的字符串或者出现一堆???。原因属性表 .dbf 用的是 GBK 或 GB2312 编码而默认读取按 UTF-8 解码中文自然全部失效。很多旧数据和部分从业务系统导出的数据都没有 .cpg 编码声明文件所以读取器只能靠猜猜错了就乱码。解决读取时显式指定编码即可。gdf gpd.read_file(china_province.shp, encodinggbk)如果后续需要交付给前端或其他团队建议统一转成 UTF-8 写出避免对方二次踩坑。我这里会用gdf.to_file(output.shp, encodingutf-8)来生成新文件并确认同目录出现.cpg文件这样别人读起来才不会乱码。5.2 翻车二叠加参考底图时边界整体偏移数百米现象自己的商铺点或其他点数据叠到中国 SHP 底图上所有点位都沿某个方向偏移少则一两百米多则近千米。单独画 SHP 不觉得有问题和在线底图一对比就露馅。原因数据实际是 GCJ-02 坐标系但 .prj 声明为 WGS-84读取器也按 WGS-84 处理。这种“假 WGS-84”在国内数据里极常见可能是加工方自己就没注意也可能是有意用近似参数写进 .prj后续使用者只能凭案例积累去识别。解决先找地标参考点验证是否偏移确认后再决定转不转。如果项目要求底图是 WGS-84就用已知的偏移算法批量转回如果只是画专题图且基于同一个平台的地图服务可以保留原坐标系但全项目只能统一用一套绝不能有的图层转有的图层不转。很多看起来是数据 Bug 的问题最后定位到坐标系混用属于典型的自我挖坑。5.3 翻车三一次 dissolve 就报 GEOSException现象执行dissolve(byprov_code)时弹出TopologyException: side location conflict或GEOSException: IllegalArgumentException直接中断。原因原始面数据里有自相交环或重叠面溶解算法在拓扑层面无法判定边界归属。常见于多个数据源拼接后的结果比如某些县的面跨越了相邻县的边界或者同一个县存在重复绘制。解决先统一修复再重试合并。gdf[geometry] gdf[geometry].apply(make_valid) gdf gdf[gdf.geometry.is_valid]make_valid会尽可能把无效几何修正为合法结果但部分错误严重的要素修复后可能变成空几何或面积为零的退化面所以要接一层is_valid过滤。过滤完的要素数量如果比原始少记得对比被筛掉的是哪些确认不是关键区域。5.4 翻车四几十 MB 的 SHP 一画图就卡死现象plot()操作等待很久或者导出到 Web 端后浏览器直接崩溃滚动地图像在做逐帧动画。原因SHP 本身顶点数量巨大尤其是全国县级边界。单纯几十 MB 文件不一定致命但叠加文字标注、点数据后渲染压力会成倍增长。更隐蔽的原因是很多县级边界保留了极高精度的碎线视觉上根本看不出区别却白白消耗性能。解决在保证边界整体形状的前提下用简化算法减少顶点。gdf_simple gdf.copy() gdf_simple[geometry] gdf.geometry.simplify( tolerance0.01, preserve_topologyTrue )tolerance是按坐标系单位设定的。经纬度坐标系里 0.01 度大约对应一公里级别适合全国缩略图如果只要省级边界可以放宽到 0.05。preserve_topologyTrue能保证面不会因为简化而互相穿透这是必开选项省掉后续修几何的麻烦。6. 收尾交付画图样式、数据瘦身与 GeoJSON 导出6.1 用 matplotlib 出图的几个视觉习惯数据整理到省级图层后画图本身反而最简单。我习惯用浅色填充配灰色边界因为专题图的主角是数据点底图颜色过深会喧宾夺主。import matplotlib.pyplot as plt fig, ax plt.subplots(figsize(8, 8)) prov_gdf.plot(axax, color#f2f2f2, edgecolor#999999, linewidth0.5) ax.axis(off) plt.show()这个阶段最值得花时间的不是配色而是确认图例和比例尺与数据单位一致。如果整体处于经纬度坐标系比例尺会随纬度变化不能直接用固定刻度进一步做印刷级出图时应该先投影到合适的分省投影坐标系。6.2 输出给 Web 前端GeoJSON 导出与字段裁剪交付给前端做地图展示SHP 不是合适格式通常我会转成 GeoJSON。生成文件前先裁减字段只保留要展示的name和adcode几十个字段全量导出去只会拖慢浏览器。prov_gdf[[name, adcode, geometry]].to_file( china_province_web.geojson, driverGeoJSON )driverGeoJSON是用户必须手写的参数写错时默认可能输出成其他格式。简化过程采用的是投影坐标系这样容差才能以米为单位控制但输出时转回经纬度坐标系否则前端底图对齐会出问题。6.3 我习惯保留工具链从原始文件到交付件一次成型从首次拿到中国 SHP 原始数据这几次操作里我慢慢固定了一条自己的处理链拿到数据先打印列、几何类型、坐标系把编码统一转成 UTF-8修复无效几何按 adcode 溶解投影坐标系下简化再回到 WGS-84 输出。这套顺序被我在几个模拟项目里反复用过每一步都不会省。最深刻的教训是有一回为了赶进度省略了几何修复直接合并结果地图没报错但边界出现了一条横跨数县的细缝最终交付后被对方一眼看出问题只能重新处理。一个数据处理链路里顺序本身就是参数的一部分。认真对待前面每一步后面出图才会是顺其自然的事希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。