2017县级行政区shp数据处理:坐标系识别、乱码修复与代码映射全解析
简介1100万基础地理数据库2017版提供全国县级行政区划边界矢量数据面向GIS开发、城乡规划、人口统计与环境分析等场景适合需要标准县级面图层做叠加计算或制图的研究者与工程师。资源共7个文件压缩包约46.33MB包含核心shp几何文件、dbf属性表、prj坐标系定义CGS_WGS_1984以及sbn/sbx/shx等空间索引文件配套结构完整可直接加载进ArcGIS、QGIS等平台使用。已有615人学习下载。借助该数据集可快速获取2017年县级行政边界免除手动矢量化与配准工作同时属性表内嵌县名、代码等信息便于与人口、经济等专题数据挂接开展区域差异、可达性、生态区划等分析。对需要统一行政单元口径的地理数据项目而言这是一套基础而实用的底图数据。1. 1100万基础地理数据库里的2017县级行政区shp先搞清这批数据的真实体量收到一套“1100万基础地理数据库_2017县级行政区shp文件”别急着把它当成1100万个要素的庞然大物。这个命名里的“1100万”通常指基础地理数据库中全部图层要素或坐标记录的综合规模真正承载“县级行政区”的 shp 面要素往往没有那么多。先确认数据构成与坐标系再决定用哪种方式读取后续处理才不会一开始就走偏。我会按一线做地理数据处理的顺序来讲先拆解文件组成与坐标识别再给两条可复现的读取与转换路线接着处理 2017 年行政代码与现状不一致的问题最后集中排一排乱码、缝隙、偏移这类高频坑并给一套十分钟的入库前验证流程。新手可以照着步骤走熟手则可以直接跳到第 5 章看边界情况。2. 读 shp 前该认的三件事文件三件套、坐标系与 1100 万的构成2.1 shp 从来不是一个文件缺了 shx 就开不了门的组件关系很多人第一次拿到 shp 会觉得奇怪明明叫“县级行政区.shp”压缩包里却躺着一堆后缀各不相同的文件。这不是数据发错了而是 shapefile 天生就是多文件构成的格式。一个完整可用的 shp 至少需要三个基础文件配套文件缺一个读取结果就会失灵或数据品质打折。文件后缀作用缺失后果.shp存储几何坐标面、线、点数据无法打开.shx几何索引用于快速定位记录GIS 软件打开时报“无法读取”或“索引损坏”.dbfdBASE 格式属性表每条要素一行记录图形还在但属性全无.prj坐标系描述WKT 文本软件按未知坐标系处理位置可能偏差数百米.cpg声明属性表字符编码常见为 UTF-8 或 GBK中文属性大概率乱码.sbn / .sbx空间索引部分软件自动生成缺失不影响打开影响查询效率我收到的这版 2017 县级数据发行时一般会附带 .cpg 文件但很多二次分发者只拷了 .shp、.shx、.dbf 三个文件就往外传导致接收方无法确认坐标系与编码。实际工作中我首先会检查.prj和.cpg是否存在缺少的立即向数据来源方要说明否则后续每一步都可能走歪。2.2 坐标系识别先看 prj 再定投影别等图偏了才后悔打开一个 shp 前先做一件事看坐标系。2017 年发布的县级行政区数据绝大多数已切换为国家 2000 大地坐标系CGCS2000但不同生产单位对数据作了不同的投影处理。同样是 CGCS2000地理坐标系与投影坐标系在面积量算和空间分析上表现完全不一样。常见做法是用文本编辑器直接打开 .prj 文件里面是一段 WKT 描述。如果看到GCS_China_Geographic_2000说明它是经度/纬度存储的地理坐标系如果看到CGCS2000_3_Degree_GK_CM_120E或类似描述则是高斯-克吕格投影坐标系。这两种情况下的处理方式完全不同。坐标系简称EPSG 代码存储单位适用场景CGCS2000 地理坐标系4490度全国范围分析、跨省拼接WGS84 地理坐标系4326度与 GPS 原始数据、标准在线底图叠加Web 墨卡托3857 / 常见投影代码米Web 地图切片展示CGCS2000 高斯投影4491-4505 等米省级及以下高精度量算判断方法不止读 .prj 一种。我一般会用 GDAL 工具快速读取元数据命令行下执行gdalsrsinfo county_2017.prj输出里会直接给出 EPSG 编号和投影参数。没有 GDAL 环境时直接看文件位置也行数据落在经度 73 到 135、纬度 18 到 54 这个范围附近大概率是地理坐标系如果坐标动辄几十万、几百万则是投影坐标系需要反查中央经线再做后续投影转换。2.3 1100 万这个数字该怎么读要素记录、顶点数量与文件体积“1100 万”最容易让人误判为县级行政区有 1100 万个。2017 年全国县级行政区划单位数量在数千个量级远达不到百万。对做基础地理数据库的人来说1100 万更可能是以下三者之一全库要素总数指数据库里所有图层境界、居民地、水系、交通、植被等的要素记录合计单看县级行政区图层要素数只占很小一部分几何顶点总数shp 里的每个面由成百上千个折点组成复杂行政区边界全部顶点累加后可达百万甚至千万规模解压后文件总记录有的生产单位把乡级以上的全部境界数据合并到一个图斑表里行数会非常接近这个量级。区分这三种含义直接决定你用什么方式处理。如果只是县级边界几千行的 GeoDataFrame 用个人电脑跑完全没问题如果要对全部 1100 万图斑做拓扑检查就必须考虑分块读取和空间索引。拿到数据后第一步就是打开属性表看记录数而不是纠结文件名里的数字。3. 用 OGR 与 GeoPandas 把 2017 县级 shp 读进来两条实操路线3.1 先用 GDAL/OGR 读元数据半小时能看穿一个 shp 的底色加载县级 shp 最容易犯的错是拿到文件直接拖进可视化界面等它画出图来才发现坐标系不对、属性乱码。我习惯先用 GDAL/OGR 把元数据读出来整个过程用不了一分钟却能把文件底细摸清。from osgeo import ogr # 打开 shp第二个参数 0 表示只读不修改源文件 shp_path county_2017.shp ds ogr.Open(shp_path, 0) if not ds: raise SystemExit(文件打不开检查 .shp/.shx/.dbf 三件套是否齐全) layer ds.GetLayer(0) # 图层名与要素数量 print(图层名:, layer.GetName()) print(要素数:, layer.GetFeatureCount()) # 空间参考信息能导出的就导出导不出说明 prj 缺失 spatial_ref layer.GetSpatialRef() if spatial_ref: print(spatial_ref.ExportToPrettyWkt()) else: print(未找到坐标系定义需要手动指定) # 读取第一个要素的几何类型确认是面还是多面 feature layer.GetNextFeature() geom feature.GetGeometryRef() print(几何类型:, geom.GetGeometryName())这段代码输出四类关键信息图层名、要素数量、坐标系定义、几何类型。要素数量让你确认数据实际规模坐标系定义决定后续是否需要重投影几何类型能帮你提前发现“县级行政区其实是线图层”这类发错文件的情况。参数方面ogr.Open()的第一个参数是路径第二个参数 0 表示只读如果改成 1 就允许写入但日常检查一律用只读避免误改原始数据。如果ExportToPrettyWkt()输出里包含GEOGCS[China Geodetic Coordinate System 2000那坐标系明确是 CGCS2000 地理坐标系对应 EPSG:4490。如果输出为空或报错回到第 2 章补读 .prj不要靠猜。3.2 用 GeoPandas 读入、转坐标与快速绘图确认元数据后进入数据分析环境。GeoPandas 是 Python 生态里处理 shp 最顺手的工具底层依赖 Shapely 与 Fiona读取 shp 时需要注意编码参数。import geopandas as gpd # 读取 shp # encoding 按 .cpg 声明选择常见为 gbk读出来乱码可逐个尝试 gdf gpd.read_file(county_2017.shp, encodinggbk) # 查看整体情况 print(gdf.shape) # 行数与列数 print(gdf.columns.tolist()) # 属性字段列表 print(gdf.crs) # 当前坐标系 # 统一转到 CGCS2000 地理坐标便于后续跨省拼接和叠加 gdf gdf.to_crs(EPSG:4490) # 快速计算每个县的面积单位平方公里 # 先投影到等积投影再算面积结果更接近真实值 gdf[area_km2] ( gdf.geometry.to_crs(EPSG:3857).area / 1_000_000 ) print(gdf[[NAME, area_km2]].head())read_file()的encoding参数是这一节最容易翻车的地方。国内县级数据属性表大多用 GBK 或 GB2312 编码如果默认按 UTF-8 读取省、市、县名称会变成乱码用utf-8读不出来的时候依次尝试gbk、gb18030。to_crs(EPSG:4490)只做坐标转换不改属性。面积计算这里我用了近似的等积投影方式面积结果仍有轻微误差但比直接在经纬度坐标系下计算稳妥得多量级没有问题。读取成功后先别急着分析先绘图检查一眼看面要素是否填满整个行政区域。用gdf.plot(figsize(12, 8))即可如果图形出现大面积空白或边界重叠说明数据存在几何问题下一章的避坑部分会专门讲。4. 属性表处理把 2017 年的行政区划代码对齐到当前4.1 先读懂县级 shp 的属性字段再动手2017 年版县级行政区的属性表字段不同生产批次差异较大但通常会包含以下几类核心信息字段名常见写法含义需要注意的问题NAME县级行政区名称可能带“县”“区”“市”后缀也可能不带需统一PAC / XZQHDM / CODE行政区划代码2017 年代码与当前代码可能存在差异PROVINCE / PROV所属省级行政区名称常有空格或简称差异CITY / CITY_NAME所属地级行政区名称直辖市辖区可能为空AREA / SHAPE_AREA面积字段平方度或平方米单位混乱尽量自己计算别直接引用处理 2017 年县级 shp最核心的动作是识别“行政区划代码列”。2017 年版数据里代码列未必叫 PAC也有可能叫 GB_CODE 或 XZDM你需要打印出字段名逐一确认。我的经验是先print(gdf.dtypes)看类型再print(gdf.head(20))看样例值。如果代码列是数字类型先转成字符串再处理避免前导零丢失。4.2 行政区划代码更新的三种情形与映射方法2017 年距今已有多年县级行政区划发生了不少调整。常见情形有三类撤县设区某县改为某市某区行政代码整体变化合并与拆分多个县合并成一个或一个县拆成多个名称变更但代码不变仅地名发生变化代码不动。如果直接拿 2017 年版代码关联现状数据会发现大量匹配不上。稳妥做法是保留原始代码列另建一列映射到最新代码。映射关系必须人工核对不要自动推断因为个别调整存在代码跳号。import pandas as pd # 假设 gdf 是已读取的 GeoDataFrame # 先保住原始代码列再新增映射列 gdf[CODE_RAW] gdf[XZQHDM].astype(str).str.strip() # 手工整理的代码映射表格式为 {2017版代码: 最新代码} # 实际操作中每个调整都需要对照最新的行政区划代码公告确认 code_map { 320281: 320281, # 假设这个县未调整保持原代码 321282: 321203, # 假设撤市设区映射到新区的代码 # ... 继续补齐其他变更记录 } # 未在映射表中的记录保留原代码并做标记 gdf[CODE_NEW] gdf[CODE_RAW].map(lambda x: code_map.get(x, x)) # 找出需要人工复核的记录 mask gdf[CODE_RAW] ! gdf[CODE_NEW] print(行政代码发生变化的数量:, mask.sum()) print(gdf.loc[mask, [NAME, CODE_RAW, CODE_NEW]].head(10))映射表是这套处理的关键。我的习惯是不在代码里写死变更逻辑而是维护一个独立的 CSV 映射文件方便其他同事核对修改。map方法对没有匹配的记录默认返回None所以我用code_map.get(x, x)让未匹配项保留原代码再单独筛选出来人工处理。这一步做完后续关联人口、经济等现状数据时代码对齐率会大幅提升。4.3 处理县界合并与拆分保留原代码再开新字段行政代码变更是属性层面的问题但合并与拆分还会带来几何层面的问题。比如某两个县合并成一个新区原始 shp 里是两个面要素现状统计需要的是一个面要素。此时需要按新代码做聚合几何上做 dissolve 合并。# 按新代码聚合合并几何和属性 merged gdf.dissolve(byCODE_NEW, aggfuncfirst) # 重置索引把 CODE_NEW 变回普通列 merged merged.reset_index() print(merged.shape) print(merged.head(10))dissolve(byCODE_NEW)会把相同新代码的相邻面要素合并成一个 MultiPolygonaggfuncfirst表示属性字段取第一行记录因为名称、代码在新代码级别应当一致。需要注意dissolve 之后的要素有可能出现“一个县变成两个不相连的多边形”这不一定是错误也可能是飞地或岛屿要结合具体地理位置判断不能直接当作数据异常处理。拆分情形则需要反过来原始 shp 中一个面要素要拆分两种用途通常用数据表关联的方式实现几何上拆分需要借助另一份参考边界。2017 年县级 shp 本身没有拆分信息我的做法是从最新的乡镇级边界数据中按属性截取再合并回县级空间。这种操作较复杂业务不涉及拆分时可以跳过。5. 避坑排查乱码、缝隙、坐标偏移与罕见几何异常的定位思路5.1 现象属性表打开全是乱码现象GeoPandas 读出来省份、县名全是“锟斤拷”“烫烫烫”一类字符或在 QGIS 里打开属性表文字不可读。原因属性表 .dbf 文件使用 GBK 编码存储读取时按 UTF-8 解码中文字符被错误映射。多数情况是 .cpg 文件缺失软件无法判断编码。解决首先看 .cpg 文件里面若写GBK或GB2312直接在read_file()里指定encodinggbk如果 .cpg 不存在就用文本编辑器打开 .dbf 头几个字节判断或者干脆依次尝试gbk、gb18030、utf-8。QGIS 里则在图层属性中修改“数据源编码”为 GBK。这里有个小规律乱码字符若表现为常见中文问号加英文字母组合多半是 GBK 被当 UTF-8 读若表现为可打印符号要考虑是不是编码声明与实际不一致。5.2 现象相邻县界之间出现缝隙或压盖现象放大查看时两个相邻县边界中间有条白缝或两块面相互重叠。原因shp 本质上是独立面要素的集合并不保证拓扑一致。生产方不同批次数据拼接时公共边界可能来自不同采集过程差几十米都常见。缝隙和压盖不是数据损坏而是数据生产精度差异。解决做县级汇总统计前必须用缓冲区消除微小缝隙。常用做法是geometry geometry.buffer(0)这一步能把自相交的碎屑清理掉再对相邻小缝隙做修复。如果是更复杂的拓扑问题比如需要生成无缝面层就要用unary_union做整体融合再切分但这样处理量大很多建议只对业务范围内的子集做。叠加分析之前务必先统一坐标系否则缝隙和压盖会因投影转换而放大。5.3 现象数据整体与在线底图发生几十米偏移现象把县级 shp 与在线底图叠加显示边界整体往一个方向偏移了几十米到一公里行政界线与底图影像不贴合。原因最常见的是坐标系不匹配。shp 是 CGCS2000 地理坐标在线底图是 WGS84 地理坐标两套坐标系在部分地区存在数十米差距还有的情况是 shp 本身是投影坐标但被当作地理坐标加载偏移会达到数公里量级。解决先读取 .prj 确认原始坐标系再统一到同一个 EPSG。CGCS2000 与 WGS84 之间的转换多数场景直接to_crs(EPSG:4326)即可误差在业务可接受范围如果对精度有高要求需要加七参数或网格改正。注意这里不要用平移方式硬凑治标不治本换一个底图又会偏。比较合适的做法是在读取后统一转成 EPSG:4490并把在线底图也切到 CGCS2000 切片很多图商底图支持 4490 版本。5.4 现象关键属性大量为空现象统计省份名称时发现几百行空值或者行政区划代码缺失导致关联数据失败。原因属性表在制作时漏填了字段或因代码位数限制被截断。最常见的是“地级市”字段在直辖市场景下为空以及代码字段数值类型前导零丢失。解决第一步先区分“不该为空但为空”和“本来就允许为空”。乡镇数据里地级市字段为空要警惕直辖市辖区则属正常。第二步做填充策略省级代码缺失的从更粗的省级边界数据里空间匹配补齐代码前导零丢失的用zfill(6)补足到 6 位。我在实际项目中做过一次统计空值分布往往集中在少数几个特殊区域优先处理这些区域能明显减少数据错误带来的连锁问题。5.5 现象一个县变成了两个甚至多个多边形现象选中某县名称属性表显示该要素几何类型是 MultiPolygon由多个不相连的部分构成更意外的是 dissolve 之后一个名字对应两块分开的区域。原因这是正常现象不代表数据错误。中国不少行政区存在飞地、岛屿、跨水域的属地导致一个县级行政区在地理上天然分裂成多块。shp 里它被存储为一个 MultiPolygon 要素展开后是多个 Polygon。解决分析时先确认是否要把飞地单独处理。县级尺度上做可视化不做拆分问题不大但做面积统计、叠加分析时MultiPolygon 的各个子块可能会与邻县边界产生交集。我通常会先用explore()方式把 MultiPolygon 展开成独立要素逐一检查最大块面积占整体比重确认没有“主块缺失”的异常。展开后若发现某县主体区域面积占比过小就要怀疑原始数据是否有几何错误再对照影像底图核实。另外很多空间计算库对 MultiPolygon 自相交很敏感先运行gdf.geometry.is_valid检查若有 False 记录执行gdf.geometry.buffer(0)修复。6. 用十分钟快检流程验证 2017 县级 shp 能不能用于业务6.1 一套文件、两次投影、三个查询拿到手先不急着写业务逻辑按下面的快检流程走一遍十分钟内能判断这套数据是否可用于生产。一套文件确认 .shp、.shx、.dbf、.prj、.cpg 全部存在。缺 .prj 就先补坐标系缺 .cpg 就先验证属性编码缺 .shx 找来源方重新导出。两次投影第一次打印gdf.crs确认原始坐标系第二次明确业务最终需要的坐标系执行to_crs()例如省级范围用 EPSG:4490市级高精度计算用高斯投影。两次投影之间用面积和边长做检验数值变化在合理范围内才算转换正确。三个查询# 1. 属性空值检查 print(空值统计:\n, gdf.isna().sum()) # 2. 几何有效性检查 invalid gdf[~gdf.geometry.is_valid] print(无效几何数量:, len(invalid)) # 3. 行政代码重复检查 dups gdf.duplicated(subsetCODE_NEW, keepFalse) print(重复代码记录数:, dups.sum())空值检查覆盖所有业务关键字段无效几何用 Shapely 的is_valid判断返回 False 的要素先buffer(0)修复再继续重复代码检查用于判断是否存在同一行政区被重复采集的情况。三项查询都通过数据具备入库条件不通过就把异常记录筛选出来逐条处理。6.2 抽样人工比对后再决定要不要信任这批数据自动化检查通过后还差最后一步抽样与可信底图人工比对。从不同省份区域里取三到五个县缩放查看行政边界走向是否贴合常见的地理标志比如河流走向、山脉山脊线。属性上随机抽几个县的名称和代码与公开的行政区划信息核对重点确认 2017 年与当前代码不一致的条目是否已经映射正确。我在某地市的业务项目里曾经跳过这一步直接出图结果因一版代码映射错误导致整个街道汇总数据偏了一截后来找原因才发现是省市两级代码混用。自此我养成了入库前只抽五个县做人工看一眼的习惯花费时间很短却能拦住大多数低级的源数据问题。这套 2017 年县级行政区 shp价值在于它反映了一个特定年份的行政区划快照用来做历史复盘、对比分析是合适且稳妥的。确定它能用之后建议把它作为基准数据在项目内统一共享后续再做人口、GDP 空间化时都基于同一套边界避免各人手里版本不一致。希望帮到你。本文还有配套的精品资源点击获取