审图号行政区划数据实战:从解压到PostGIS落库的完整避坑指南

发布时间:2026/10/3 4:15:21
审图号行政区划数据实战:从解压到PostGIS落库的完整避坑指南
简介这份资源为2024年省、市、县三级行政区划矢量数据审图号GS20240650号由国家基础地理信息中心通过天地图平台发布更新至2024年1月坐标系为GCS_WGS_1984。数据面向GIS分析、地图制图、空间统计与区域研究等场景适合具备基础GIS操作能力的研究人员、规划从业者及学生使用可解决行政边界底图获取与统一口径的问题。压缩包共34个文件约12.7MB包含shp、shx、dbf、prj、cpg等Shapefile配套文件以及geojson格式另附九段线数据省、市、县三级边界齐全便于直接导入ArcGIS、QGIS等软件进行空间分析与可视化。目前已有1984人浏览学习数据来源权威、格式通用可作为区域统计、专题制图与空间建模的可靠底图省去自行整理边界与坐标转换的繁琐工作。1. 拿到一份带审图号的行政区划 zip先别急着解压你从数据共享渠道拿到一个名为「2024年省市县三级行政区划数据审图号GS20240650号.zip」的压缩包第一反应大概率是双击解压、拖进 QGIS 看一眼。先停一下。审图号这三个字不是装饰它意味着这份数据是经过官方地图审核的合规成果坐标系、行政边界、属性字段都有一套既定规范。你如果直接拿它去做 Web 地图、做空间统计、做区划变更对比第一步走错后面全是返工。这份数据解决的核心问题是给你一套 2024 年时点的省、市、县三级行政边界矢量附带标准行政区划代码能直接用于空间连接、分级统计和地图出图。适合谁做国土空间规划、做人口经济数据落图、做物流网点行政区归属、做数据大屏底图的后端和 GIS 工程师。不适合谁想要实时更新到最新撤县设市、想要乡镇级精度、想要带地形和影像底图的人。我见过太多人把这类数据当成普通 shapefile 处理结果坐标系对不上、字段乱码、县级边界缺失最后在群里问「为什么我的地图是歪的」。这篇就把从解压到落库、从坐标系到字段映射的完整路径讲清楚顺带把几个高频翻车点提前给你标出来。2. 解压后先看什么目录结构、坐标系与字段的快速体检2.1 一个 zip 里通常藏着哪几层文件带审图号的行政区划数据常见做法是按层级分目录或者用一套文件承载三级要素。你解压后大概率会看到类似这样的结构具体文件名以实际为准但逻辑相通2024年省市县三级行政区划数据/ ├── 省/ │ ├── 省.shp / .shx / .dbf / .prj / .cpg ├── 市/ │ ├── 市.shp / ... ├── 县/ │ ├── 县.shp / ... └── 说明文档.pdf 或 .txt也有一种做法是一个 GeoJSON 或一个 File Geodatabase里面用level字段区分省市县。两种都合理关键是你得先确认三件事坐标系是什么、字段编码是什么、三级要素是否共用同一套行政区划代码体系。我一般会先看.prj文件。如果里面写的是GCS_China_Geodetic_Coordinate_System_2000或者CGCS2000说明是地理坐标系单位是度。如果写的是CGCS2000_3_Degree_GK_Zone_XX或者带Albers、Lambert字样那就是投影坐标系单位是米。这两种在后续做面积计算和距离分析时差别巨大不能混用。2.2 用 GDAL 做一次不打开桌面的体检与其一个个拖进 QGIS不如先用命令行把底细摸清楚。GDAL 的ogrinfo是干这个的# 查看数据源概览确认图层数量和名称 ogrinfo -so 2024年省市县三级行政区划数据/县/县.shp # 查看坐标系和字段定义 ogrinfo -al -so 2024年省市县三级行政区划数据/县/县.shp # 如果是一个 GeoJSON 包含三级直接看字段 ogrinfo -al -so 2024年省市县三级行政区划数据/三级区划.geojson-so表示 summary only不输出每个要素的几何只给元信息。你会看到Layer name、Geometry、Feature Count、Extent、Layer SRS WKT以及Field列表。重点看Feature Count对不对省级一般 34 个左右市级 330 多个县级 2800 多个。数量明显偏少说明数据可能被裁剪过或者只含部分区域。字段方面常见的有adcode行政区划代码、name名称、level层级、parent父级代码、geometry。如果.dbf里中文显示乱码先别改数据改读取方式import geopandas as gpd # 指定编码读取中文 Windows 下 dbf 常见 gbk 或 gb18030 gdf gpd.read_file( 2024年省市县三级行政区划数据/县/县.shp, encodinggb18030 ) print(gdf.crs) print(gdf.columns.tolist()) print(gdf.head(3)[[adcode, name, level]])encoding参数只影响属性表读取不影响几何。如果gb18030还乱试utf-8或gbk。这一步做完你手里应该有一份干净的 GeoDataFrame坐标系明确、字段可读、要素数量对得上。2.3 审图号在数据里意味着什么审图号GS20240650号是地图审核的凭证它约束的是这份地图的边界画法、名称标注和层级关系。对你来说实际影响有三点第一边界不能随意简化到失真做 Web 瓦片时如果抽稀过度可能和审核版本不一致第二行政区名称和代码要按数据里的来不要自己用外部表去覆盖第三如果你要把这份数据作为底图对外发布保留审图号是合规要求。内部做分析、做统计落图一般不受额外限制但对外出图时记得把审图号带上。3. 把三级区划落进 PostGIS建表、导入与空间索引3.1 为什么建议进 PostGIS 而不是一直用 shapefileshapefile 单文件有 2GB 上限字段名不能超过 10 个字符中文编码容易出问题而且不支持真正的空间索引。你如果只是看一眼QGIS 够了但要做「每个县关联人口数据、按市汇总、再按省出图」这种链路PostGIS 是更稳的底座。它支持ST_Contains、ST_Intersects这类空间连接也支持 GiST 索引几万个点和几千个面做归属判断速度差一个数量级。常见做法是用shp2pgsql或 GDAL 的ogr2ogr把三级数据分别导入统一坐标系到EPSG:4326用于展示另存一份EPSG:4490或投影坐标系用于量算。3.2 建表与导入的完整命令先建库建表。这里以县级为例省市同理-- 创建 schema 便于管理 CREATE SCHEMA IF NOT EXISTS admin_2024; -- 建表字段按实际数据调整 CREATE TABLE admin_2024.county ( id SERIAL PRIMARY KEY, adcode VARCHAR(12), name VARCHAR(100), level VARCHAR(10), parent VARCHAR(12), geom GEOMETRY(MultiPolygon, 4326) ); -- 建空间索引 CREATE INDEX idx_county_geom ON admin_2024.county USING GIST (geom);导入用ogr2ogr它比shp2pgsql对中文和编码更友好ogr2ogr -f PostgreSQL \ PG:hostlocalhost port5432 dbnamegisdb userpostgres passwordyourpass \ 2024年省市县三级行政区划数据/县/县.shp \ -nln admin_2024.county \ -lco GEOMETRY_NAMEgeom \ -lco FIDid \ -nlt MULTIPOLYGON \ -t_srs EPSG:4326 \ -progress-nln指定目标表名-lco是图层创建选项-nlt强制几何类型为 MultiPolygon避免单面和多面混存导致失败-t_srs做坐标系转换。导入完成后用一条 SQL 验证SELECT COUNT(*), ST_SRID(geom) FROM admin_2024.county GROUP BY ST_SRID(geom);如果ST_SRID返回 4326说明坐标系写对了。如果返回 0说明导入时没识别到投影需要手动UPDATE或重新导入并显式指定。3.3 三级数据的关联查询怎么写省市县三级共用adcode体系关联逻辑是县级adcode前两位是省前四位是市。你可以用字符串截取做汇总-- 按省汇总县级数量 SELECT LEFT(adcode, 2) AS province_code, COUNT(*) AS county_count FROM admin_2024.county GROUP BY LEFT(adcode, 2) ORDER BY province_code; -- 把一张业务点表落到县 SELECT p.id, c.name AS county_name, c.adcode FROM biz_points p JOIN admin_2024.county c ON ST_Contains(c.geom, p.geom);ST_Contains要求点完全在面内边界上的点可能漏掉。更稳的是ST_Intersects但要注意一个点落在两个县边界上会返回两条。实际做归属时我一般先用ST_Contains对未匹配的点再用ST_DWithin找最近的面兜底。注意如果业务点表坐标系是EPSG:3857而区划表是4326直接ST_Contains会报错或结果错误。先用ST_Transform统一。4. 坐标系、面积与出图三个最容易翻车的环节4.1 地理坐标系算面积为什么不可信EPSG:4326的单位是度直接ST_Area得到的是平方度不是平方米。纬度不同一度对应的实际距离差很多。你要算县级面积必须转到投影坐标系。中国常用的是EPSG:4490CGCS2000 地理配 Albers 投影或者按 3 度带高斯克吕格。-- 转到 Albers 等积投影再算面积单位平方米 SELECT name, adcode, ST_Area(ST_Transform(geom, 4527)) / 1000000 AS area_km2 FROM admin_2024.county ORDER BY area_km2 DESC LIMIT 10;4527是 CGCS2000 / 3-degree Gauss-Kruger zone 39 附近的 EPSG 代码具体用哪个带取决于你的区域。更通用的做法是用 Albers 自定义投影参数按中国范围设from pyproj import CRS albers CRS.from_proj4( 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 / 1e6lat_1和lat_2是双标准纬线中国常用 25 和 47。这样算出来的面积和官方公布值偏差通常在千分之几以内做统计足够。4.2 出图时边界抽稀的度怎么把握做 Web 地图时原始县级边界可能几十 MB直接上瓦片会卡。抽稀用ST_SimplifyPreserveTopology它比ST_Simplify更不容易产生自相交-- 容差 0.001 度约 100 米按缩放级别调整 CREATE TABLE admin_2024.county_simplified AS SELECT id, adcode, name, ST_SimplifyPreserveTopology(geom, 0.001) AS geom FROM admin_2024.county;容差不是越小越好。0.001 度在县级尺度上肉眼几乎看不出差别但数据量能降一半以上。如果你做的是全国大屏0.01 度也够用。但记住审图号数据对外发布时简化后的边界不能改变行政归属关系抽稀后要检查有没有飞地丢失。4.3 属性字段的坑adcode 前导零和类型行政区划代码是字符串不是数字。北京的adcode是110000如果你在 Excel 或数据库里把它存成整数前导零会丢变成110000还好但像110101这种没问题真正危险的是某些代码本身以 0 开头的情况。更常见的问题是你在 pandas 里读进来adcode被推断成int64然后和另一张表按字符串关联时全部匹配不上。# 强制按字符串读取 gdf gpd.read_file(path, dtype{adcode: str, parent: str}) # 检查长度 print(gdf[adcode].str.len().value_counts())正常县级adcode是 6 位市级 6 位省级 6 位但后四位为 0。如果长度出现 5 位或 4 位说明前导零丢了需要zfill(6)补回来。5. 避坑与排查审图号区划数据最常见的 5 个翻车现场5.1 导入后中文全是问号或乱码现象ogr2ogr导入 PostGIS 后name字段显示为???或北京。原因shapefile 的.dbf编码和数据库客户端编码不一致常见是 dbf 用 GBK而数据库按 UTF-8 解释。解决导入时加-lco ENCODINGGBK或者先用 Python 读出来转成 UTF-8 再写库。如果已经导入可以用UPDATE配合convert_from修复但更省事的是重导。5.2 县级边界比市级还少或者出现大量飞地现象COUNT(*)县级只有 2000 出头或者某个市的县全部落在相邻市里。原因数据可能按某种分区裁剪过或者导入时-nlt设成了POLYGON导致 MultiPolygon 被拆散或丢弃。解决先确认原始数据的要素数再用-nlt MULTIPOLYGON重导。飞地问题通常是坐标系转换时参数不对检查-t_srs是否和目标一致。5.3 ST_Contains 匹配不上任何点现象业务点表几万条ST_Contains返回 0 条。原因两张表坐标系不同或者点的几何是POINT EMPTY或者区划表的几何有自相交导致ST_Contains失效。解决先SELECT ST_IsValid(geom) FROM county WHERE NOT ST_IsValid(geom)找出无效几何用ST_MakeValid修复。再确认ST_SRID一致。最后用ST_Intersects试一下如果ST_Intersects有结果而ST_Contains没有说明点在边界上。5.4 面积算出来和官方数据差十倍现象某县面积算出 0.5 平方公里实际应该是 500 多。原因忘了做ST_Transform直接在地理坐标系上ST_Area得到的是平方度。解决转到等积投影再算。另一个可能是单位搞错ST_Area返回平方米你除以了1000而不是1000000。5.5 审图号数据对外发布被要求整改现象把这份数据做成公开 Web 地图后收到合规提醒。原因审图号数据对外展示时边界画法、名称标注、层级显示都有规范不能随意改配色和简化到失真。解决对外发布前保留审图号标注边界简化容差控制在合理范围不要用这份数据去叠加未经审核的争议边界。内部使用一般不受影响但公开传播要谨慎。6. 用 DuckDB 做离线快速验证不装数据库也能查三级区划如果你不想为了看一眼数据就装 PostGISDuckDB 加 spatial 扩展是个轻量选择。它可以直接读 shapefile 和 GeoJSON支持空间函数跑在本地单文件里适合做数据体检和快速统计。import duckdb con duckdb.connect() con.execute(INSTALL spatial; LOAD spatial;) # 直接查 shapefile注意路径和编码 con.execute( CREATE TABLE county AS SELECT * FROM ST_Read(2024年省市县三级行政区划数据/县/县.shp) ) # 看字段和数量 print(con.execute(DESCRIBE county).fetchall()) print(con.execute(SELECT COUNT(*) FROM county).fetchone()) # 按省汇总adcode 前两位 print(con.execute( SELECT LEFT(adcode, 2) AS prov, COUNT(*) AS cnt FROM county GROUP BY prov ORDER BY prov ).fetchall()) # 算面积前先转到投影DuckDB 里用 ST_Transform con.execute( CREATE TABLE county_proj AS SELECT *, ST_Transform(geom, EPSG:4326, EPSG:4527) AS geom_proj FROM county ) print(con.execute( SELECT name, ST_Area(geom_proj)/1e6 AS area_km2 FROM county_proj ORDER BY area_km2 DESC LIMIT 5 ).fetchall())ST_Read自动识别格式ST_Transform做坐标系转换ST_Area在投影几何上算面积。DuckDB 的好处是零配置一个 Python 脚本就能跑完从读取到统计的全流程。我一般用它做第一轮验证数量对不对、字段全不全、面积量级合不合理。确认没问题再进 PostGIS 做正式落库。一个习惯每次拿到新的区划数据先跑一遍「省级 34、市级 330、县级 2800」的数量核对再抽三个已知面积的县做面积校验偏差超过 5% 就回头查坐标系。这个动作花不了五分钟但能帮你省掉后面几小时的排查。希望帮到你。本文还有配套的精品资源点击获取