简介一套覆盖全国所有省、市、区、县、乡、镇、街道的邮编与经纬度坐标数据共收录46090条行政区域信息层级细化到乡镇/街道。数据以SQL脚本形式打包压缩包内共有1个文件大小约942KB适合GIS开发、物流配送、地图服务与区域分析等场景使用将SQL导入主流关系型数据库后即可按省份、城市、区县等字段实现灵活查询与关联。目前已有794人学习下载。字段设计上包含行政区划名称、邮政编码、经度和纬度值能够支撑地址解析、配送路径优化、人口分布统计等常见应用也可与其他业务数据叠加进行更深层的区域洞察。对于需要准确位置信息、又希望免去从零采集整理工作量的开发者或分析人员这份结构化的SQL数据是一份可直接落地的基础数据集。1. 全国省市县乡镇街道邮编坐标数据离线打点与层级联动的救场资源做地图需求最怕的不是画图而是表里看着有数据真要用时缺县缺乡、坐标飘海。之前接一个配送范围可视化需求在线地理编码接口按天限额晚上批量跑就被限流最后靠一份全国省、市、区、县、乡、镇、街道邮编及经纬度坐标的离线表才把项目拉回来。这份资源按行政区划编码把六层行政区串联起来每行带邮编、经纬度、层级标记和上级路径解决地址下拉联动、范围圈选、坐标反查和可视化底表四类高频问题适合前后端开发和数据处理场景。2. 数据形态与字段设计code、name、level与坐标列一次讲清这类区划数据落表后核心字段其实非常朴素行政区划编码、名称、层级标记、经度、纬度、邮编。难点从来不在列多而在编码规则和坐标系这两个隐藏信息上。2.1 行政区划编码的隐含规则为什么前6位可以当唯一键12位行政区划编码自带层级关系截断补零就能得到上一级编码。编码长度覆盖层级示例形态2 位省级12示例4 位地级市1201示例6 位区县120102示例9 位乡镇街道120102000示例12 位村级/社区120102000000示例前6位在绝大多数业务里已经够用订单表挂区县、后台管理按区域筛选、统计报表聚合到区县直接截取code前6位就能关联。再往下做网格化管理或门店选址时才需要用到完整12位。名字会变编码不会。一个区改名、两个街道合并名称字段会跟着变但编码一旦分配就不轻易复用。所以后续所有关联操作我都建议用code做主键而不是name。名称重复的问题在数据里非常常见后面专门有避坑章节展开。2.2 经纬度坐标系GCJ-02与WGS-84的偏移问题拿到坐标先确认坐标系这是最容易踩的隐藏坑。GPS 设备直接读出的是 WGS-84而互联网地图底图多采用 GCJ-02 通用坐标系两者在同一地点会存在几十到几百米的偏移且不同位置偏移方向不一致。拿到文件第一件事看有没有coord_sys字段。没有就抽三个点丢到在线地图底图上对比点位如果叠在道路或行政边界上基本已经是互联网地图通用坐标点位整体偏离路网、方向又不统一说明很可能是 GPS 原始坐标需要做一次整体转换。我的习惯是转换交给成熟的开源工具库去做固定版本后全量跑一遍不自己推导偏移公式。自己写的公式涉及一堆参数边界区域误差会被放大返工成本远高于直接调库。2.3 首轮探查用Python把数据全貌读出来拿到表先跑一段探查脚本把结构、缺失、量纲一次看清。import pandas as pd df pd.read_csv( district_coord.csv, dtype{code: str}, # 编码列读成字符串防止前导0丢失 encodingutf-8-sig, # 兼容带BOM的CSVExcel导出的文件常踩这个坑 ) print(shape:, df.shape) print(df.dtypes) print(df.isna().sum()) # 缺失情况重点看 lng/lat/name print(df[level].value_counts()) # 六个层级是否齐全 print(df[[lng, lat]].describe()) # 看min/max坐标范围异常一眼可见dtype{code: str}是必须的。编码列如果按数字读入012345678901会被转成12345678901前导0一丢后面所有关联全部作废。describe()输出里如果lng出现 -180 或 180说明坐标列存在脏值不是正常数据。探查这一步的目的是确认这份数据有没有缺层。乡镇街道层级缺失很常见缺失比例太高时后面逆地理编码和级联组件都会出现断层需要提前决定是用上级坐标回填还是放弃该层。3. 层级补全与坐标清洗把原始表变成可直接交付的地址档案很多人拿到区划表就直接用结果做级联选择器时发现父级路径拼不出来做可视化时发现坐标有一堆离群点。原因是原始表往往只有当前层级的名称和编码缺少完整的血缘链路也没有做坐标范围预检。3.1 编码前缀截断法自动回填省市名称利用编码的层级规则逐级截断补零后回查名称就能把每一行的省份、城市、区县名称自动补出来。df[province_code] df[code].str[:2] 000000000000 df[province_name] df[province_code].map( df.set_index(code)[name] ) df[city_code] df[code].str[:4] 0000000000 df[city_name] df[city_code].map( df.set_index(code)[name] ) df[district_code] df[code].str[:6] 000000 df[district_name] df[district_code].map( df.set_index(code)[name] )这段代码的核心逻辑是str[:2]截取前两位省级编码后面补10个0还原成完整12位编码然后用map在编码到名称的映射表里反查。这样不需要任何外部接口纯靠编码自洽就能把四个层级全部补齐。运行后检查province_name.isna().sum()如果出现大量空值说明表本身缺省级节点或者有些行编码不规范。直辖市会表现为province_name和city_name相同这是正常的展示层按level判断去重即可。3.2 拼接完整地址与血缘链路有了省市名称再生成parent_code和完整address这份数据就能直接用于级联组件和地址解析。df[parent_code] None # 省级记录parent_code 置空 df.loc[df[code].str[2:] 000000000000, parent_code] None # 地市级截取前2位补零 df.loc[df[code].str[4:] 000000000000, parent_code] ( df[code].str[:2] 000000000000 ) # 区县级截取前4位补零 df.loc[df[code].str[6:] 000000, parent_code] ( df[code].str[:4] 0000000000 ) # 乡镇街道级截取前6位补零 df.loc[df[code].str[9:] 000, parent_code] ( df[code].str[:6] 000000 ) df[address] ( df[province_name].fillna() df[city_name].fillna() df[district_name].fillna() df[name].fillna() )血缘链路的价值在于前端级联选择器只需要把parent_code作为父子关系字段后端按这个字段排序建树不用再写递归解析编码。address列则是完整的四级地址文本给客服系统、订单导出、标签打印用非常方便。3.3 坐标范围预检查一眼揪出离群点清洗的最后一步是坐标范围检查。先用保守边界筛掉明显错误再按层级统计异常比例。def is_valid_coord(lng, lat): # 保守边界覆盖中国主体区域不含南海诸岛延伸海域 return 73.0 lng 135.0 and 18.0 lat 54.0 df[coord_ok] df.apply( lambda r: is_valid_coord(r[lng], r[lat]), axis1 ) # 按层级统计异常比例 print(df.groupby(level)[coord_ok].mean())这里参数是硬编码边界经度73到135纬度18到54。如果业务涉及南海范围需要单独扩展边界或引入国界矢量数据做空间判断。单点坐标超范围多半是经纬度写反某层级的异常比例特别高就要怀疑该层级的采集源出了问题。清洗完成后另存一份新文件不要覆盖原始表。保留原始数据等于留了一份后悔药后面排查问题还能对照。df.to_csv(district_clean.csv, indexFalse, encodingutf-8-sig)utf-8-sig编码导出是给 Excel 用户准备的不加 BOM 的话用 Excel 打开中文会乱码。4. 避坑笔记五个高频翻车场景的现象、原因与解法区划数据看着简单实际用起来坑密集。以下五条都是从真实项目里复盘出来的每一条都按现象、原因、解决展开。4.1 乡镇级坐标成片缺失或全是0现象做全国乡镇点位图时乡镇层级很多点不显示检查原始数据发现lng和lat是 0.0。原因乡镇层级坐标采集周期长部分点位当时没采到入库时用了 0.0 充当占位。解决不要把 0 坐标直接删掉那会造成层级断层级联选择器里乡镇街道选项还在但地图上找不到点。合理做法是用该乡镇所属区县的政府驻地坐标回填并在数据里加一列source_note标记为inferred区分真实坐标与推断坐标。4.2 导入GIS后点位飞到海外或叠加成一团现象用 GIS 软件加载 CSV 后一批点落在公海还有一批挤在地图角落。原因最常见的两种情况一是某段坐标的lng和lat列写反二是从度分秒格式转换成十进制度时公式出错度数变成了180.0之类的异常值。解决先跑范围检查把超界的点单独拎出来看。对疑似经纬度写反的数据检查交换两列后是否落回合理区域若合理则直接交换否则标记invalid_reason而不是硬删。4.3 同名异城导致按名称关联全部错乱现象拿业务表里的城市名去关联区划表结果大量数据对到别的城市报表数字完全对不上。原因不同地级市下存在同名街道县级市和市辖区也可能重名名称字段完全没有全局唯一性。解决一律用code关联不用name。展示层如果同时出现同名不同级必须在名称后面追加level后缀比如“某区县级市”和“某区市辖区”区分显示。4.4 坐标系未标注点位与路网整体偏移现象在地图上叠加点位和路网点位偏离道路几十米方向在不同区域还不一致。原因文件混用了两套坐标系一部分行来自 GPS 采集是 WGS-84另一部分来自地图平台是 GCJ-02又没有coord_sys字段区分。解决入库前固定坐标系全表统一后再使用。抽样几个点投到在线地图对比偏移方向能判断是哪套坐标确认后用成熟转换工具统一转换并锁定版本转换结果要抽点复验。4.5 区划调整后旧编码静默失效现象查询某个新设街道时返回空或者用旧编码 join 出来的区划名称已变。原因区划变更频繁街道合并、县改区、乡镇升街道都会让旧编码失效但数据表没有反映变更。解决给数据加data_version字段每次用官方发布的区划变更对照表做增量更新不要全量重建。旧编码留到alias字段里业务侧老数据通过 alias 映射到新编码避免历史订单关联断裂。5. 落地场景坐标反查、聚合可视化与级联选择器数据源数据清洗完就要解决实际问题。下面三个场景对应最常见的三类需求代码直接可用。5.1 逆地理编码百万点也能快速返回省市区街道在线地理编码接口有限流但 C 端下单量是实时的不能每次都请求外部接口。用 KDTree 在内存里做最近邻搜索百万级点位建树只要几秒。from scipy.spatial import cKDTree tree cKDTree(df[[lng, lat]].values) # 示例查询点非真实业务数据 query_point [120.5, 31.2] dist, idx tree.query(query_point, k3) # 0.2度约等于十几公里超出说明附近没有已知节点 if dist[0] 0.2: row df.iloc[idx[0]] print(row[name], row[address]) else: print(附近无匹配区划点)k3是故意多取几个候选避免同一个街道多个坐标点时返回结果抖动。阈值0.2度适合区县级数据如果数据是乡镇街道级可以缩到0.05度。注意经纬度上直接算欧氏距离在高纬度会有变形但街道粒度的最近邻反查精度足够不需要引入投影转换。5.2 按省聚合可视化气泡图自查数据覆盖度全图打点太糊通常按省聚合后画气泡图既能看业务分布也能顺带验证数据覆盖是否合理。import matplotlib.pyplot as plt prov_stats df.groupby(province_code).agg( cnt(code, count), lng(lng, mean), lat(lat, mean), ) # s为气泡面积这里用点位数量乘以系数控制大小 plt.scatter( prov_stats[lng], prov_stats[lat], sprov_stats[cnt] * 3, alpha0.6, ) plt.show()cnt代表该省下辖区划节点数量节点数极少且地理面积很大的省份就是数据覆盖异常的信号。气泡图在这里不只是展示也是一张「数据健康报告」。实际业务场景里把cnt替换成从业务表 join 进来的订单量或人口指标就是一张分级统计图。5.3 生成级联选择器JSON把区划树喂给前端前端做省市区县乡镇五级联动时最烦的是自己按编码拼树。用清洗好的parent_code字段一次遍历就能完成。import json def build_tree(rows): rows rows.sort_values(code) # 保证父编码先于子编码出现 root {code: , name: 全国, children: []} node_map {: root} for _, r in rows.iterrows(): node { code: r[code], name: r[name], level: r[level], children: [], } # parent_code 取不到时挂到根节点避免脏数据导致整棵树崩溃 parent node_map.get(r[parent_code], root) parent[children].append(node) node_map[r[code]] node return root tree build_tree(df) print(json.dumps(tree, ensure_asciiFalse))sort_values(code)很关键。编码按字符串排序时省级编码12会排在1201前面父节点必然先于子节点插入node_map。ensure_asciiFalse保证中文名称直接输出而不是 Unicode 转义序列。这个 JSON 可以直接给前端组件用也可以存成静态文件作为接口兜底数据。6. 进阶验证用一套脚本给自己的区划表做验收数据交付前要有一套能快速复跑的验收脚本把编码唯一性、坐标范围、抽样比对三个检查串起来。import pandas as pd def quick_check(path, sample_num100, seed7, onlineFalse): df pd.read_csv(path, dtype{code: str}) # 1. 编码唯一性检查 assert df[code].is_unique, 发现重复编码优先排查同一层级内的重复行 # 2. 坐标范围检查 ok_range df[lng].between(73, 135) df[lat].between(18, 54) bad_ratio 1 - ok_range.mean() print(坐标离群比例:, round(bad_ratio, 4)) # 3. 抽样比对可选 if online: samples df.sample(sample_num, random_stateseed) mism [] for _, row in samples.iterrows(): addr online_reverse(row[lng], row[lat]) if addr and row[name] not in addr: mism.append(row[code]) print(抽样不一致条数:, len(mism))random_state固定随机种子保证两次验证抽到同一批样本结果可复现。sample_num最少取 100 条太少覆盖不到边远区域。抽样比对里在线反查地址中不包含当前节点名称就判不一致边界地区允许 10% 左右的差异超过这个比例就要回到来源数据查层级和坐标问题。这套验证脚本不依赖固定环境拿到新版本数据先跑一遍通过后再进业务库。从那以后我每次拿到区划数据都强制走一遍编码唯一性、坐标范围、抽样比对三个步骤一刻钟的检查能省掉后面一整天的返工。希望帮到你。本文还有配套的精品资源点击获取