简介这是一个全球地理层级与经纬度数据资源包面向地图应用开发、地区选择组件构建及地理信息系统分析场景。包内含完整的全球各国除中国外及中国含特别行政区省市区县的层级结构数据并配有经纬度坐标可满足省市区联动下拉框、地图定位标注等常见功能的数据需求。资源共4个文件包含2个JSON数据文件和2个SQL脚本文件压缩包整体约385KB。JSON文件直接提供层级化地理数据便于前端应用读取SQL文件则包含建表结构与数据填充语句方便后端数据库存储和按SQL查询操作。目前已有376人学习下载。通过该压缩包开发者可获得全球与中国行政区划的上下级关联关系及经纬度数据直接用于地图展示、地区筛选、定位服务等多样场景免去自行爬取和整理地理数据的大量时间是地图开发与数据分析的实用数据集。1. 一份压缩包为什么能省掉三天的数据调研做海外站点或跨境系统时最让人头疼的往往不是业务逻辑而是「选省、选市、选区」这个看似基础的下拉联动。你随手在网上找一份省市区数据要么只有国内到区/县海外国家只有一级或者干脆没有要么有层级但没经纬度地图标点成了空壳。这份「全球各国省市区城市地区层级结构经纬度 json与SQL.zip」的核心价值就是一次性把国家、省级州/郡、市级、区县的层级关系、多语言名称和经纬度坐标全部打包好再用 JSON 和 SQL 两种格式同时交付。它解决的是一种「数据底盘」问题你不需要再去各个数据源挨个爬取、清洗、对齐字段解压后可以直接用来搭联动接口、初始化数据库表、或者在 GIS 场景里做坐标落点。适合后端开发、前端联动页面的实现者、做数据仓库集成的工程师以及任何需要离线行政区划数据兜底的业务方。2. 动手前先看懂结构这份数据到底长什么样2.1 先做结构体检打开压缩包的第一件事我拿到这类压缩包的第一个动作从来不是急着找 SQL 文件导入而是先解压看一下整体目录和文件命名。通常里面会分为 json 目录、sql 目录还可能附带一个 README 或字段说明文档。先把 README 读一遍确认这份数据的覆盖范围是「全球所有国家」还是「主流国家 主要城市」这直接决定你能不能拿它做生产数据。unzip 全球各国省市区城市地区层级结构经纬度_json与SQL.zip -d adcode_data cd adcode_data find . -type f | head -50这里unzip解压到指定目录find列出文件清单目的是快速看清文件组织方式。有些包会把 SQL 按大洲或按国家拆成多个文件有些则是一个全量文件JSON 也可能存在单文件全量、按国家拆分、按层级拆分三种组织方式。文件拆分粒度会影响你后续的导入方式和增量更新策略所以这一步不要略过。2.2 JSON 与 SQL 双格式的实质差异谁在什么场景下选谁JSON 和 SQL 这两种格式服务的是完全不同的接入场景。JSON 文件通常记录的是嵌套层级例如country - states - cities - districts一眼就能看出父子关系。前端下拉联动可以直接加载后端的地区服务接口也可以把 JSON 转换成内存字典树省去查库的网络开销。它最大的优势是「所见即所得」结构自描述不需要先建表再导入适合快速原型和中小规模系统。SQL 文件则适合走传统关系型数据库路线的场景。它一般包含建表语句和 INSERT 语句字段可能是id, parent_id, name, name_en, level, latitude, longitude这种扁平结构。你导入后可以直接用WHERE parent_id ?查询子级配合索引效率很好。但它有个前提必须先有一个「约定好的表结构」。如果包里的表结构和你现有系统的地区表字段不一致你可能要写一个中间转换层。两种格式我都建议保留。JSON 用来做离线校验、前端联动和测试SQL 用来做正式环境的表数据。不要贪图省事只留一种。2.3 用三层校验判断这份数据适不适合你的业务我拿到一份新数据习惯用三层校验来做快速评估。第一层是「覆盖度校验」——统计出每个层级有多少条记录对比你业务实际需要的国家和城市范围第二层是「层级完备性校验」——看是否每个国家的层级深度一致有没有某国只有省没有市、有市却没有区的情况第三层是「经纬度有效性校验」——检查坐标是否在合法范围内、有没有大量 0,0 坐标点。# 以 SQL 文件为例先看每个层级的记录数 grep -c INSERT INTO.*country *.sql grep -c INSERT INTO.*state *.sql grep -c INSERT INTO.*city *.sql grep -c INSERT INTO.*district *.sql这一组命令分别统计 country、state、city、district 的插入记录量。如果 city 或者 district 的数量明显偏少说明这份数据的覆盖面可能不够深或者只覆盖了少数国家。实际业务中如果你只需要英美加澳等几个国家的城市数据这个校验能帮你快速判断是否需要额外补充数据源。经纬度有效性也是重点。经度范围是-180 到 180纬度范围是-90 到 90只要出现超出范围的值数据源就一定有问题。0,0 坐标通常代表缺值前后端在地图落点时会全部堆在地图上的某个海面上表现就是「标注点全跑到了非洲西海岸」这块值得单独检查。3. 让数据真正跑起来JSON 联动与 SQL 入库的实操路径3.1 JSON 转成前端可用的联动数据两步清洗JSON 拿来直接给前端用之前我一般会做两件事第一是压缩冗余字段把name_en、name_local、short_name这些不管用不用都带着的字段按你的业务裁剪掉第二是统一code与parent_code的命名规范避免前端取字段时还要做映射。一个典型的目标结构如下{ code: US, name: 美国, level: 1, children: [ { code: US-CA, name: 加利福尼亚州, level: 2, location: { lat: 36.7783, lng: -119.4179 }, children: [] } ] }这段 JSON 中children表示下一级区划列表location.lat与location.lng分别存纬度和经度。之所以把location嵌套而不是平铺是为了让前端能直接绑定地图组件的数据字段同时也方便后端序列化时统一处理。前端做省级联动时读children数组渲染下一级即可不需要再从另一份独立的坐标表中关联查询。3.2 SQL 建表与导入一次性把库表跑起来SQL 文件通常有两种生成习惯一种是带CREATE TABLE的完整初始化脚本你直接执行即可另一种是只有INSERT语句需要你自己建表。如果是第二种我常用的建表结构如下CREATE TABLE region ( code VARCHAR(32) PRIMARY KEY, parent_code VARCHAR(32) DEFAULT NULL, name VARCHAR(128) NOT NULL, name_en VARCHAR(128) DEFAULT NULL, level TINYINT NOT NULL, latitude DECIMAL(10,6) DEFAULT NULL, longitude DECIMAL(10,6) DEFAULT NULL, sort_order INT DEFAULT 0, KEY idx_parent_code (parent_code), KEY idx_level (level) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这个设计里code作为主键parent_code关联父级。level用TINYINT1 代表国家、2 代表省/州、3 代表市、4 代表区县。latitude和longitude用DECIMAL(10,6)可以精确到米级足够地图标点使用。sort_order控制展示顺序。之所以把parent_code建索引是因为「查询某个省下的所有市」「查询某个市下的所有区」是最高频操作没有索引这个查询会全表扫描。导入时注意字符集。很多导出文件是从某类在线系统导出的编码可能是utf8mb4也可能是latin1如果不指定字符集直接导入中文名容易变成乱码。mysql -uyour_user -p your_db region.sql \ --default-character-setutf8mb4指定--default-character-setutf8mb4是为了防止客户端默认字符集与文件内容不一致导致乱码。你可以在导入后执行SELECT name FROM region LIMIT 10检查中文是否正常。如果出现乱码不要急着删库重来先看文件本身编码再用iconv转换后重新导入。3.3 经纬度字段的落库边界坐标到底怎么存经纬度在数据库里的存储方式有几种常见选择各有适用边界。DECIMAL(10,6)是最稳的方案范围和精度都可控DOUBLE或FLOAT是省事的方案适合快速接入但对精度要求不高的场景POINT类型配合空间索引适合做「附近的人」这类距离查询。我一般建议默认用DECIMAL(10,6)。原因很简单省市区级数据的坐标来源是行政中心或边界近似点精度本来就不是厘米级DECIMAL(10,6)约 0.1 米的精度完全够用而且它是定点存储不会出现FLOAT那种因浮点误差导致坐标值打印出来有一长串小数尾巴的尴尬。另外在查询排序和范围过滤时DECIMAL可以直接走 B 树索引行为直观可控。4. 省市区数据落地避坑指南5 条高频翻车现场4.1 海外省市区分三级业务只支持两级下拉框直接空白现象前端把数据渲染成「国家-省-市」三级联动但某些海外国家的数据只有「国家-省」两级市这一级没有记录导致用户选完省之后下拉框空白。 原因这份全球数据是按实际行政区划层级生成的有些国家地广人稀或行政区划本身就少不存在「市」这一层而前端代码写死了三级联动没有做空数据分支处理。 解决在后端返回层级树前做一层补全或标记。给每个节点增加has_children字段前端根据该字段决定是否渲染下一级如果业务强制要有第三级那就把「省」下的所有「区县」直接提升为市或者在展示层把空的市层级隐藏。常见做法是把前端联动组件改成递归渲染层级深度不写死。4.2 经纬度坐标整体偏移标点全部跑偏现象地图上标点位置与实际位置偏差很大例如某城市的中心点落到了城市边缘或坐标点跨越了国境线。 原因数据源用了不同的坐标系。常见的有 WGS-84、GCJ-02国测局坐标和 BD-09百度坐标。如果数据是海外来源大概率是 WGS-84如果是从国内在线服务抓取的可能是加密坐标。三种坐标系之间互转有固定偏移算法直接混用必然偏移。 解决先用几个已知城市核对坐标所属坐标系。判断方法可以取一个城市的坐标放到高德、百度、谷歌地图分别定位看哪家最准。确认后按坐标系转换公式统一。国内业务如果用高德地图需要把 WGS-84 转成 GCJ-02如果数据源本身是 GCJ-02 但你部署在海外用 Leaflet 加载 OpenStreetMap则需要反向转换。4.3 同一国家的行政区划变更上线后用户反馈地区名对不上现象用户反馈某个城市的名称或上级归属是旧的例如某地级市已经更名为其他名字或升格但系统还是显示旧名。 原因行政区划是动态数据撤县设区、地级市更名、新设新区每年都有发生。这份静态包只是某个时间点的快照时间一长必然过期。 解决设计数据更新机制。不要让业务代码直接依赖包里的静态表而是在地区表上加version字段和effective_date起效日期后续更新用新版本数据做增量比对只改有变化的记录。没有更新机制的话至少要维护一份手动修正清单把容易变的节点列入人工复核范围。4.4 主键用中文名数据一更新外键全部失效现象前期为了让开发方便直接用name作为主键或关联字段。后续用新版本数据做增量更新时发现某些城市改了名导致订单表、用户表中的旧记录关联不上新名称。 原因行政区划的名称是会变的把易变字段当主键是自找麻烦。名称相同但不同国家的城市也可能撞名例如英文名一样的城市在不同国家大量存在。 解决主键一律使用独立code字段。这份数据里通常已经带了国家码和区划码例如美加地区习惯用 FIPS 或 ISO 3166-2 编码接入时优先沿用。订单表、用户表只存code展示时再关联地区表取名称。这样即便名称变更历史数据仍然能正确归属到新的行政区划上。4.5 JSON 文件超过 10MB前端加载直接卡死现象把全量 JSON 通过script引入或fetch回来作为联动数据源页面初始化时下拉框要等好几秒才能点开移动端情况更严重。 原因全量省市区数据体量不小里面有大量字段和冗余层级一次全量加载对前端是很大的解析压力。 解决把 JSON 按国家拆分前端做按需加载。用户选择了国家才加载该国家的省级数据选择了省再异步加载该省下属的市区数据。实现上可以把大 JSON 文件预处理成country/{code}.json的结构后端接口做这种逐级下发。如果不想改前后端结构至少要做本地 localStorage 缓存第二次访问不再重复下载解析。5. 数据质量与更新策略静态包如何变成长期资产5.1 行政区划更新的节奏这件事不能一劳永逸行政区划的变化频率比你想象中高。常见的变更包括地区改名、县级市升格为地级市、新设功能区如新区、自贸区、合并拆分乡镇街道。这类变动虽然不频繁但每发生一次都会直接影响业务方的下单地址、物流路由、统计报表等模块。我的习惯是把地区数据当成「半衰期数据」看待。也就是说入库之后设置一个定期复核提醒例如每季度检查一次更新来源。手工更新时用新包和当前库里的数据做 diff生成增删改记录而不是把整张表TRUNCATE后再全量插入——否则历史订单关联的地区编码可能会因为重建主键而丢失。-- 对比新数据中已存在但名称变化的记录 SELECT r.code, r.name AS old_name, t.name AS new_name FROM region r JOIN region_new t ON r.code t.code WHERE r.name t.name;这条 SQL 意在找出来自新版本数据region_new里code没变但名称已经改变的城市。code不变是更新友好的前提这也是上一章强调主键必须独立、易变名称一律当普通属性的原因。变更确认后执行UPDATE语句更新名称即可业务表里的历史数据不需要任何改动。5.2 坐标数据缺失怎么办手动补点还是算法插值有些国家的数据里可能缺少部分城市的经纬度尤其是小城镇和偏远地区。遇到这种情况我不建议直接删除记录或者置空可以按优先级处理第一优先级是查询权威地理编码服务手动补点第二优先级是根据上级行政区的中心点和相邻城市坐标做近似估算第三优先级是保留坐标为空但做好标记前端不渲染地图标注只显示文字名称。补点时注意精度取舍。手动补的坐标用于地图展示够用但如果你的业务涉及距离计算、运费分区域定价那缺失坐标的地市应该走默认定价逻辑不要用估算坐标计算实际距离否则会产生运费纠纷。数据包里出现空坐标并不是不能用关键是把「可用」和「估算」区分开别混为一谈。5.3 多语言字段的维护别只存中文如果你的业务面向海外用户地区名称单独存中文是不够的。很多场景需要展示当地语言名称、英文名称以及中文别名。这份数据通常已经把英文名带上了你需要确认的是英文名是「官方标准名」例如 ISO 3166-1 里的官方拼写还是「通用英译名」二者在搜索和展示时的效果差别很大。我建议线上表中同时保留name_local当地语言、name_en英文、name_cn中文。具体展示哪种由请求头里的语言参数决定。只要 SQL 文件里没有这个字段你就在导入时多做一步映射避免后期为每一个地区补翻译字段而反复改表。这一步骤虽然不起眼但当你对接的海外电商用户开始按本地语言搜索城市名称时就会体会到它的价值。6. 让这份数据更好用用坐标反查行政区的离线实现最后分享一个把经纬度用起来的进阶技巧。很多业务场景里用户只会提交当前坐标需要你反推所在城市、区县。正常做法是调在线逆地理编码服务但服务有限额、有延迟而且离线兜底时必须能用本地数据撑住。这份省市区数据里带的每个地区的经纬度中心点可以做一层粗粒度的「离线反查」。思路是把每个区县的中心点坐标加载进内存用户提交坐标后计算它到所有中心点的球面距离取最近的那个区县作为候选结果。实现用 Haversine 公式算球面距离即可精度在市区级够用。import math def haversine(lat1, lng1, lat2, lng2): R 6371.0 phi1, phi2 math.radians(lat1), math.radians(lat2) dphi math.radians(lat2 - lat1) dlambda math.radians(lng2 - lng1) a math.sin(dphi / 2) ** 2 math.cos(phi1) * math.cos(phi2) * math.sin(dlambda / 2) ** 2 return 2 * R * math.asin(math.sqrt(a)) def find_nearest_region(lat, lng, centers): nearest, min_dist None, float(inf) for item in centers: dist haversine(lat, lng, item[lat], item[lng]) if dist min_dist: min_dist, nearest dist, item return nearest, min_dist这里centers是从 SQL 或 JSON 里读出来的区县中心点列表每条包含code、name、lat、lng。调用haversine计算两点的球面距离find_nearest_region遍历所有中心点返回距离最近的一条记录。注意离线反查只能定位到区县级别如果你要定位到街道这个数据包的粒度做不到需要更强的行政边界数据别指望一个省市区中心点包解决全链路。我踩过的坑是离线反查结果距离用户真实位置很远时没有做容错判断。某次模拟项目 X 里一个位于国境线附近的坐标反查到了邻国的城市原因是邻国中心点更近。后来我加了一个距离阈值判断超过阈值就返回「无法定位」而不是强行给出结果。这也算一个通用教训——离线兜底方案的价值在于能兜住常见情况不常见的情况必须留给在线服务去解决。另一个小技巧是加载时只保留level 4即区县级别的中心点不要在内存里加载全量省市区否则 10MB 量级的 JSON 转成 Python 对象后内存占用会被放大数倍。物尽其用的习惯很重要比如这份数据包的真正价值不在「下载了」而在你愿意为它建立多少周边能力——比如我在上面提到的离线反查、更新 diff、层级补全这些做扎实了这个静态包才真正变成你的基础设施。希望这些思路能帮到你少走弯路。本文还有配套的精品资源点击获取