简介:面向地理信息系统开发者和地图可视化场景的世界国家地理边界数据包,整合了来自自然地球的全球国家与地区边界多边形,适用于网页地图、桌面地理信息系统、数据分析和可视化大屏等应用。资源以JSON格式文本为主体,共含三千四百余个JSON文件,另附少量说明文档和许可文件,整体仅1.69MB,体积紧凑、目录清晰,便于直接嵌入项目或二次加工。主要用于在应用程序和可视化工具中绘制各国地图,也适合需要精确边界数据进行地理信息处理的中高级开发者。目前已有3692人学习使用,说明其实用性得到了不少同行验证。拿到后可快速获得世界各国的边界坐标,无需自行从自然地球原始数据中提取转换,能显著缩短地图开发中地理数据准备的时间,也方便按国家代码或名称进行检索与调用。
1. geojson大全与地图json:先解决底图数据从哪来
做可视化大屏的时候,最怕的不是图表样式调不出来,而是底图数据找不到。之前接一个全球业务分布看板,客户要求每个国家的区块能点选、能下钻,我翻遍手头材料,发现每个仓库都存一版边界JSON,格式新旧不一,有的文件只有国名没有代码,跟业务库里的维度完全对不上。后来摸到这份“世界国家geojson大全”的老版本资源,才把事办利索——它把全球各国(含部分一级行政区划)的边界数据统一整理成标准GeoJSON,国家代码、通用名称、几何坐标都在properties字段里,省掉了逐个站点找数据、猜字段的功夫。这份geojson大全的核心就是地图json的批量整合,适合做数据地图、大屏可视化、GIS预处理的人,新手拿过去能直接加载,熟手能省下大量清洗整理的时间。
2. 读懂GeoJSON结构:坐标嵌套、坐标系与Feature组织方式
2.1 坐标嵌套:为什么一个多边形有三层括号
GeoJSON本身是一种JSON约束格式,核心是坐标数组,但不同几何类型的嵌套层数完全不同。很多人第一次打开文件看到一堆嵌套数组直接懵掉,其实规则没有想象中复杂。
| 几何类型 | 坐标最小嵌套形态 | 业务含义 |
|---|---|---|
| Point | [经度, 纬度] | 点要素 |
| LineString | [[经度, 纬度], [经度, 纬度]] | 线要素 |
| Polygon | [[[经度, 纬度], ...]] | 面要素 |
| MultiPolygon | [[[[经度, 纬度], ...]]] | 多面要素 |
以Polygon为例,最外层是geometry的coordinates属性;里面第一层数组叫“环”(ring),一个面可以有多个环,第一个环是外边界,后面的环是内部的洞;环里的每一层才是具体的顶点坐标。为什么需要三层?因为一个面必须至少有一个外环,而外环本身又需要一组顶点坐标来闭合,结构就固定成了三层。
很多人翻车就翻在这里:想手动删掉某个岛屿,删的时候多删了一层括号,导致Polygon退化成LineString,整个图层在渲染时就消失了,还不报错,排查半天才发现是JSON嵌套结构被改坏了。老版本数据里这种情况不少,尤其手工拼接过的文件。
为什么不直接用Shapefile?因为shp是二进制格式,字段结构散落在.dbf文件里,浏览器前端基本没办法直接读,必须经由后端转换一遍。GeoJSON是纯文本,任何JSON解析器都能打开,前端图表库和Python的json库可以直接处理,选它做交换格式是最省事的路径。这份资源把全球边界数据统一落地成GeoJSON,省掉的其实就是二进制GIS数据转文本数据的那一步。
2.2 Feature不再是普通JSON:对象与集合的组织方式
GeoJSON和普通JSON最大的区别是它有类型约定。一个Feature对象至少要包含type、properties、geometry三个键,geometry里放的又是一个独立的几何对象。而且完整的GeoJSON文件通常是一个FeatureCollection对象,真正的数据都在features数组里。
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "某区域", "ISO_A2": "XX" }, "geometry": { "type": "Polygon", "coordinates": [ [ [120.1, 30.2], [120.3, 30.4], [120.5, 30.6] ] ] } } ] }上面这段是GeoJSON的最小可用结构。properties承载业务信息,geometry承载边界信息,二者是解耦的。很多人不看properties,直接把整个文件丢给可视化库,结果地图画出来了数据挂不上,问题就出在没搞清properties里字段名是什么。
老版本资源还有一个常见差异:文件顶层可能是Feature而不是FeatureCollection。顶层是FeatureCollection的用于加载多个国家很方便,顶层是Feature的通常代表只有一个区域。批量处理时两者都要兼容,不能假设所有文件结构完全一致。处理多份数据时第一步先把每个文件顶层类型输出一遍,心里有数再写后续逻辑。
2.3 坐标系选择:WGS84是底线
GeoJSON规范要求坐标基于WGS84,经纬度用十进制度,顺序是先经度后纬度。这条是最大的隐蔽坑。很多老数据集是从其他GIS格式转出来的,转换时投影没还原,可能出现某厂商加密坐标系或平面投影坐标。这两种坐标混在GeoJSON里,加载到地图上整个图形会偏移几百米甚至几公里。
判断坐标系是否正确的办法很朴素:检查坐标数值范围。合法的经纬度,经度在-180到180之间,纬度在-90到90之间。如果看到经度数值是几百万,那基本可以断定是投影坐标没有还原。
还有一种情况是坐标看上去在合法范围,但整体位置漂移。你可以把某个已知经纬度的城市点叠加上去对比,如果城市点落在了国界线外几百米,说明这份边界数据经历过加密偏移处理,需要先做坐标纠偏再入库。某开发者曾拿到一份看起来完全正常的边界,所有检查都通过,结果所有区块堆在太平洋里,最后查出来是数据源用某厂商坐标算法导出过,坐标在真实国界基础上被统一偏移了。处理这个问题的唯一正确路径是先识别坐标基准,再做统一转换,没有任何玄学捷径。
3. Python实战:校验几何与按需求提取地图数据
3.1 先体检:用Python读一遍所有GeoJSON
拿到这份资源先别急着写业务逻辑,第一步是批量读取一遍,确认文件结构完好。下面这段脚本是通用体检流程,不依赖GIS库,只用到Python标准库。
import json from pathlib import Path root = Path("world_geojson") # 资源解压后的根目录 for f in root.rglob("*.geojson"): try: with open(f, "r", encoding="utf-8") as fp: data = json.load(fp) assert data["type"] in ("Feature", "FeatureCollection") count = len(data.get("features", [])) print(f, "OK, features:", count) except Exception as e: print(f, "ERROR:", e)先说逻辑:用pathlib的rglob递归扫描所有子目录下的geojson文件;json.load把文件解析成Python字典;随后断言type字段是不是两种合法的顶层类型。解析失败或顶层类型不对的文件会被标记成ERROR,需要单独处理。有些老资源混合存放,后缀既有.geojson也有.json,直接把rglob参数改一下就能兼容。
参数说明:encoding="utf-8"一定要写,尤其Windows下从某个仓库下载的资源,默认编码不一致会导致GBK乱码;rglob递归是必须的,因为这份资源通常按大洲或国家分为多级目录。这一步输出的文件清单和报错列表,就是后续工作的基础。
3.2 按国家代码提取子集
实际使用时经常只需要某一个国家或几个国家的边界,没必要每次加载全世界数据。提取子集的思路是遍历features,按properties里的代码字段匹配,然后单独导出。
import json with open("world.geojson", "r", encoding="utf-8") as fp: data = json.load(fp) target_code = "AB" # 替换成实际国家代码 for feat in data["features"]: props = feat.get("properties", {}) code = props.get("ISO_A2") or props.get("ADM0_A3") if code == target_code: single = { "type": "FeatureCollection", "features": [feat] } with open(f"{target_code}.geojson", "w", encoding="utf-8") as out: json.dump(single, out, ensure_ascii=False) break逻辑:遍历features,通过properties里ISO_A2优先取两字码,取不到再用ADM0_A3三字码兜底。为什么写两个字段?因为不同版本资源的字段命名不一致,有的叫ISO_A2,有的叫ADM0_A3,有的叫wb_a3。匹配成功后把该Feature重新包装成FeatureCollection导出,这样可以确保任何解析器都能正常识别。
导出时用ensure_ascii=False很关键,否则properties里的中文名称会被转成\uXXXX的转义序列,人没法读,排查问题时很难受。如果你业务数据里全是中文名,那就改成匹配properties.name字段,逻辑一致。
补充一点:只导出Feature而不包一层FeatureCollection,很多旧版解析器识别不了。这是一个很小的细节,但我见过不少人在这一步踩坑。
3.3 几何有效性检查与快速修复
提取完边界不等于它能正常渲染,几何自交、环方向不一致这类问题,在浏览器里会表现为区块填充异常,在GIS工具里则是打不开或报错。用shapely做几何有效性检查是成熟做法。
from shapely.geometry import shape from shapely.validation import explain_validity with open("AB.geojson", "r", encoding="utf-8") as fp: data = json.load(fp) for feat in data["features"]: geom = shape(feat["geometry"]) if not geom.is_valid: print("INVALID:", explain_validity(geom))逻辑:shapely的shape()函数从GeoJSON字典还原几何对象,is_valid做拓扑有效性检查,explain_validity返回可读的错误原因,最常见的提示是Self-intersection,也就是几何自交。自交多边形画到屏幕上会呈现蝴蝶结状的破面,填充效果完全失控。
修复时优先用shapely.ops里的orient统一环方向,然后对自交部分做buffer(0)处理。buffer(0)是GIS里经典的修复技巧,它会把自交点拆解开来重建有效多边形。如果部分区域修复后仍异常,单独把这个Feature提出来检查,多半是首尾顶点没闭合——第一个点和最后一个点经纬度不一致,补上闭合点即可。这份老资源是多年攒下来的,这一关跑完再入库能省下大量前端排查时间。
3.4 坐标基准统一:用pyproj检查与重投影
如果检测出坐标不在WGS84,常见做法是用pyproj做重投影。pyproj是目前最常用的Python坐标转换库,适合处理合法但坐标系不对的数据。
import json from pyproj import Transformer from shapely.geometry import shape, mapping transformer = Transformer.from_crs("EPSG:3857", "EPSG:4326", always_xy=True) with open("weird.geojson", "r", encoding="utf-8") as fp: data = json.load(fp) for feat in data["features"]: geom = shape(feat["geometry"]) new_geom = transform_geom(transformer, geom) feat["geometry"] = mapping(new_geom) def transform_geom(transformer, geom): from shapely.ops import transform return transform(transformer.transform, geom)逻辑:from_crs指定源坐标系与目标坐标系,这里把Web墨卡托投影转成WGS84经纬度;shapely的transform函数把几何里每个坐标点都套用转换器完成重投影。参数说明:EPSG:3857是Web墨卡托,EPSG:4326是WGS84经纬度,两者是Web地图最常见的坐标系组合。如果转换后坐标值仍有几百米的系统性偏移,那就是加密坐标偏移,标准投影库没有内置算法,需要用经验参数做纠偏,这个内容太深,普通可视化场景里直接换数据源更省事。
4. 转成可视化能用地图json:ECharts对接与属性映射
4.1 用registerMap加载GeoJSON
很多可视化项目的落地方式是把这个geojson喂给前端图表库。以ECharts为例,地图组件支持直接注册GeoJSON,用法非常成熟。新版项目用npm包引入,老版项目用script标签加载,两种方式接口一致。
import * as echarts from "echarts"; import worldJson from "./world.geojson"; echarts.registerMap("world", worldJson); const chart = echarts.init(document.getElementById("map")); chart.setOption({ geo: { map: "world", roam: true, itemStyle: { areaColor: "#e8e8e8" } } });逻辑:registerMap第一个参数是地图别名,后面所有引用都用这个别名;第二个参数就是GeoJSON对象,框架解析features并渲染边界。roam开启缩放和拖拽,大屏地图基本都开。itemStyle控制区块样式,areaColor是未映射数据的默认填充色。
参数说明:如果你用的是老版本框架,GeoJSON解析逻辑相对保守,对MultiPolygon的支持弱一些。这份资源里岛国、飞地、跨海领土全部依赖MultiPolygon,新版框架基本都能正常渲染,老版本如果出现区块缺失,优先考虑升级而不是改数据。
4.2 nameProperty与名称映射
地图能画出来只是第一步,数据能不能按区块绑上去,完全看名称匹配。框架默认从每个Feature的properties.name读取区域名,但这份老资源里的字段可能是name_cn、ISO_A2、ADM0_A3等等,业务侧的名称体系跟边界文件里的名称体系不一致时,地图上一片灰。
echarts.registerMap("world", worldJson, { nameProperty: "name_cn" }); chart.setOption({ series: [{ type: "map", map: "world", data: [ { name: "某区域", value: 120 } ], label: { show: true } }] });逻辑:registerMap第三参数传入统一映射配置,nameProperty告诉框架从properties里取哪个字段作为区域名。series.data里每一项的name与区域名匹配,value就挂到对应区块上。这就解释了为什么很多人在series里写对了数据却一盏灯都不亮——不是数据错了,是名称彼此对不上。
参数说明:nameProperty一旦全局指定,所有Feature都必须有该字段,缺失字段的区域在渲染时不会被识别。我的习惯是先用Python脚本把边界文件里的全量名称清单输出一份,跟业务维度表做交集,确认用哪个字段统一后,再把业务表名称批量改成一致格式。
4.3 TopoJSON不是GeoJSON:这个转换别偷懒
老资源里经常混着.topojson后缀的文件,也有人把topojson改后缀名当geojson用,结果是前端直接报错,图层一片空白。TopoJSON不是普通的JSON数据文件,它把相邻区块的共享边界压缩成弧线存储,结构跟GeoJSON完全不同,解析方式也不一样。
常见做法是先用命令行工具做预转换,再把转换结果交给前端。mapshaper是处理这类转换的常用工具。
mapshaper world.topojson -o format=geojson out.geojson逻辑:mapshaper读取拓扑压缩文件,format=geojson指定输出格式,out.geojson是转换产物。拓扑压缩过的数据解开后体积会明显变大,这是正常现象,不需要担心。
参数说明:mapshaper的-o参数还可以追加precision=0.001这类精度控制,把坐标保留三位小数,大约能压掉一多半体积,代价是边界细节微损,做全球地图完全足够。偷懒不转换的后果是前端黑屏并提示无效GeoJSON,排查过程非常浪费时间。转换后的文件建议做一次第3章的几何校验,因为拓扑解压偶尔会暴露出原数据的隐藏问题。
5. 避坑:坐标偏移、几何自交与老数据过期问题
5.1 地图整体漂移出界:坐标系被偷换了
现象:加载数据后,整个国家轮廓出现在海里,或者整体偏移到相邻国家的位置,点选时命中的区域完全不对。
原因:边界数据不是WGS84经纬度,而是投影坐标或加密坐标。投影坐标直接体现在数值范围异常,经度几百万那种;加密坐标则表现在数值合法但位置系统性偏移上。
解决:先确认坐标数值范围,经度范围应为-180到180,纬度应为-90到90;再用一个已知城市坐标做叠加验证;确认是投影坐标后用pyproj统一转回4326坐标系。加密坐标的纠偏需要额外算法,展示场景里直接换一份坐标干净的数据源更划算,不要跟偏移数据死磕。
5.2 某几个区域渲染不出来:几何自交或环方向不一致
现象:地图大部分区块正常,唯独个别区块没有填充色,或者填充后形状像打了结的蝴蝶结。
原因:手工编辑GeoJSON时顶点顺序错乱,或者外环内环方向不统一,导致几何自交。老资源是多年拼凑的,这种情况不算罕见。
解决:先用shapely的is_valid跑一遍全量检查,对无效几何用orient统一环方向,让外环逆时针、内环顺时针,符合标准要求。再不行就对特定几何做buffer(0)修复。如果单独提交的Feature首尾坐标不一致,补上闭合点。修复后重新导出并加载验证。
5.3 相邻边界出现缝隙或重叠:多源边界未做融合
现象:放大到省界级别时,两个相邻区块之间出现一条细缝,或者边界出现双重线叠加。
原因:不同区块的边界来自不同数据源,各自做了简化处理,共享边界的顶点序列不一致。视觉上就是缝隙或重叠。
解决:展示场景用mapshaper的-clean参数清理拓扑关系,让共享边界对齐;做GIS分析场景则必须使用同一数据源导出的面板数据,不能混用。清楚这一点能少走很多弯路——地图展示和GIS分析对边界的精度要求不一样,视觉无缝隙就是合格。
5.4 数据映射全部落空:名称与代码版本差异
现象:地图正常显示,区域区块都完整,但业务数据一项都挂不上去,所有区块保持灰色。
原因:边界文件的properties名称与业务数据维度不是同一套命名,或者ISO代码版本不一致。老资源里既有ISO_A2也有ISO_A3,还有不同年份的国家行政区划版本。
解决:先把边界文件里全量名称和代码输出成清单,再跟业务维度表做一次映射对齐。脚本如下。
import json data = json.load(open("world.geojson", encoding="utf-8")) for feat in data["features"]: p = feat["properties"] print(p.get("ISO_A2"), p.get("ISO_A3"), p.get("name"), p.get("name_cn"))逻辑:一行输出一个区域的两码、三码、英文名、中文名。参数说明:字段缺失会输出None,一眼就能看出哪些区块信息不全;这份清单同时可以作为业务数据表别名映射的依据。我拿到任何一份边界数据都会先跑这个脚本,几分钟就能避免整块时间花在排查询字段上。
6. 进阶验证:用简化阈值做多级缩放地图
6.1 精度阈值与数据体积的平衡
全世界边界数据动辄几十MB,直接塞进前端包,首屏加载会被拖垮。进阶做法是准备多级精度版本:全球视角加载低精度文件,下钻到国家再加载高精度文件。用shapely的simplify方法可以手动控制误差阈值。
from shapely.geometry import shape import json data = json.load(open("world.geojson", encoding="utf-8")) for feat in data["features"]: geom = shape(feat["geometry"]) simplified = geom.simplify(0.01, preserve_topology=True) feat["geometry"] = json.loads(simplified.to_geojson()) json.dump( data, open("world_simple.geojson", "w", encoding="utf-8"), ensure_ascii=False )逻辑:simplify内部使用的是道格拉斯-普克算法,第二个参数是容差值,单位与坐标一致(这里就是度),阈值越大边界越粗糙。preserve_topology=True的意义在于防止简化过程产生新的拓扑错误,这条很关键,不加的话简化后可能出现区块重叠。
参数说明:0.01度大约对应1公里级别,适合全球或全国尺度的轮廓展示;做单国下钻时建议用0.001甚至更小的值。多级缩放的思路就是按视角切换不同容差版本,全球版一百万字节,省级版一百字节,加载体积差好几个数量级。
验证简化效果的方法是抽查面积损失。简化前后几何的area比率在0.9以上一般可以接受;再直观一点,把两版数据同时渲染到地图,缩放到你接受的最低比例尺下看边界细节有没有崩。如果差太多就调小阈值重新导出。从那以后我每次拿到边界数据都会强制走一遍几何校验再入库,靠黑匣子玄学赌数据好不好用,纯属给自己挖坑。希望这篇拆解能帮你在用这份geojson大全时少走几步弯路。
本文还有配套的精品资源,点击获取