简介:这是一套覆盖全球国家/地区边界的GeoJSON矢量地图数据包,面向Web前端、GIS开发与数据可视化从业者,解决应用中缺少统一、标准地理边界数据的问题。资源包共3446个文件,核心为3442个json地图文件,辅以2个markdown说明文档、1个unlicense授权文件等,整体压缩后仅1.69MB,轻量易用。目前已有3687人学习下载,适合需要快速接入地图可视化的中高级前端或GIS开发者。数据源自Natural Earth,边界经过精心制图,可在D3.js、Leaflet、ECharts等可视化框架中直接加载;除国家轮廓外,还包含部分国家下属州/岛屿等更细粒度边界,可支撑从国家到区域的多层级地图展示,配合unlicense授权,开发者在项目中可灵活使用。
1. 做地图可视化时最容易被卡住的环节:边界数据去哪找
日常做地图可视化的时候,最容易被卡住的往往不是图表配置,而是最底层的数据:各国的边界 GeoJSON 从哪找?在线公共图源大多只提供整个地球的合并数据,拆不到国家级别;自己写脚本去抓,又容易遇到字段残缺、坐标异常的质量问题。这份“世界国家 geojson 大全”把各国地图数据按 JSON 格式拆成独立文件,拿来直接加载到地图库里就能画色块、做下钻、挂业务数据。它的定位是“够快、够用”:适合在地图上展示业务数据的工程师,也适合做地理数据分析、需要快速验证想法的人。我在给某跨平台系统做全球专题图底稿时,把它当作第一版数据底图,省掉了最繁琐的数据收集阶段。
2. 先看准数据再动手:GeoJSON 结构、坐标系与数据源选择
2.1 读透 GeoJSON 结构:坐标层级、环与 MultiPolygon
GeoJSON 是 JSON 的地理扩展。一个 FeatureCollection 里,features 数组的每个元素就是一个 Feature,每个 Feature 由 type、geometry、properties 组成。geometry.type 在地图边界数据里几乎只会出现 Polygon 和 MultiPolygon,properties 里存的是名称、国家代码等属性,具体字段名每个数据源不一样。
坐标嵌套是最容易看走眼的部分。Polygon 的形式是三层数组:最里面一层是坐标点,第二层是环,最外层是所有环的集合。也就是说,一个面可以由多个环组成,第一个环是外边界,后面的环是内部挖掉的部分,比如国界线内的湖泊。MultiPolygon 则再套一层,变成 Polygon 数组。解析时如果少取了一层,Polygon 的坐标会被误当成 MultiPolygon,渲染时就会出现部分面片丢失。
为什么国界数据经常是 MultiPolygon?因为一个主权国家可能包含离岛、海外领地,这些陆地并不相连,只能表示成多个 Polygon。只看主岛不看离岛,边界数量会少很多。拿到文件后先打印首层坐标结构,用 JSON.stringify 输出前两个点确认 type,再决定用几层循环去遍历,这个习惯能省掉后面大量调试时间。
这份资源里的数据默认使用 WGS84 坐标系,经纬度范围是经度 -180 到 180,纬度 -90 到 90。它保存的是地理坐标而不是投影坐标,不需要做投影转换,直接传给 Leaflet、MapLibre 等常用地图库都能识别。如果你在后端用 geopandas 做空间计算,读取时它也会自动识别成 EPSG:4326,一般不需要额外指定。
2.2 数据源横向对比与选型:别拿国家边界数据做省级下钻
不同维护方做出的国界数据差异很大。低精度的全球边界文件可以做到几十 KB,但大比例尺下能看到明显的直线段边界;高精度测绘图的数据细节丰富,文件体积也膨胀到几十 MB。我大致把市面上的国界数据分成三类,整理成一张选型表:
| 数据源类型 | 典型精度 | 面片数量与细节 | 属性字段 | 适合场景 |
|---|---|---|---|---|
| 开源社区维护的全球边界 | 较低,以 1:50m 精度为主 | 面片少,离岛细节弱 | 英文名、短代码 | 全球概览、3D 地球、专题色块 |
| 各国测绘部门开放数据 | 高,1:10m 或更高 | 面片多,含完整海岸线 | 官方编码、多语言名称 | 局部区域分析、大比例尺展示 |
| 商业地图服务商转换数据 | 中低,1:30m 左右 | 中等,已做适度简化 | 中文名、常用别名 | 后台报表、H5 展示 |
选型的核心依据是你的渲染比例尺。全球视角用低精度就够,用户交互点在大洲级别也不会露馅;但如果你要做某个国家的省级下钻,就必须切换成 admin level 1 的行政区划数据,国家边界文件在这个粒度下没有意义。这份资源按国家/地区维度拆分,定位就是“国家边界的够用版”,不需要自己再去拼接十几个相邻国家的 JSON。
提示:拿到任何 GeoJSON 后,先检查两个地方:文件里是否包含 properties 字段,以及 geometry 是否都是 Polygon/MultiPolygon。如果出现 LineString,说明这份数据可能是简化后的边界骨架,不适合做区域填充。
3. 三步跑通一个 GeoJSON 地图:从文本文件到屏幕渲染
3.1 数据读取与体检:先摸清文件家底再动手
拿到资源后第一件事不是直接渲染,而是先做一次数据体检。我会用 Python 快速遍历每个文件,统计面片数量、坐标范围、字段列表,输出一份“数据体检报告”。这一步能避免后期在页面加载出空白时才发现坐标范围不对。
import json import glob from os.path import basename results = [] for fp in glob.glob("geojson/*.geojson"): with open(fp, "r", encoding="utf-8") as f: data = json.load(f) features = data.get("features", []) sample_names = [ feat.get("properties", {}).get("name", "") for feat in features[:3] ] # 初始化经纬度边界 min_lon, min_lat, max_lon, max_lat = 180, 90, -180, -90 for feat in features: geom = feat.get("geometry", {}) if not geom or geom.get("type") not in ("Polygon", "MultiPolygon"): continue coords = geom.get("coordinates", []) if geom["type"] == "MultiPolygon": for poly in coords: for ring in poly: for lon, lat in ring: if lon < min_lon: min_lon = lon if lon > max_lon: max_lon = lon if lat < min_lat: min_lat = lat if lat > max_lat: max_lat = lat else: for ring in coords: for lon, lat in ring: if lon < min_lon: min_lon = lon if lon > max_lon: max_lon = lon if lat < min_lat: min_lat = lat if lat > max_lat: max_lat = lat results.append({ "file": basename(fp), "feature_count": len(features), "bbox": [min_lon, min_lat, max_lon, max_lat], "sample_names": sample_names, }) for r in results[:5]: print(r)这段脚本做的事情:遍历目录下所有 .geojson 文件,读取 Feature 数量,统计经纬度边界框,采样前三个要素的名称字段。输出结果里如果看到经度范围在 -180 到 180、纬度在 -90 到 90,说明坐标系统基本正常。如果出现经度在 0 到 360 或者纬度超过 90 的异常值,说明数据需要做坐标校准,不能直接进入渲染环节。
这里的 bbox 数组顺序是 [minLon, minLat, maxLon, maxLat],后续传给地图库的 fitBounds 方法可以直接使用。采样名称用来确认 properties 里有哪些可用字段,方便下一步做业务数据挂接。
3.2 渲染到 Leaflet 与 ECharts:两种主流程接入方式
数据体检没问题之后,就可以接入地图库了。前端两个最常用的选择是 Leaflet 和 ECharts,Leaflet 更贴近 GIS 的操作方式,支持缩放、点击、图层控制;ECharts 的优势是把地图当普通图表序列来配置,统计图联动更方便。下面是 Leaflet 的接入代码:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="/libs/leaflet/leaflet.css" /> <script src="/libs/leaflet/leaflet.js"></script> <style> #map { height: 600px; } </style> </head> <body> <div id="map"></div> <script> const map = L.map('map').setView([20, 0], 2); // 这里替换为你自己的底图服务地址 L.tileLayer('https://your-tile-server.example.com/{z}/{x}/{y}.png', { maxZoom: 12 }).addTo(map); fetch('/data/xxx.geojson') .then(res => res.json()) .then(data => { const layer = L.geoJSON(data, { style: () => ({ color: '#333', weight: 0.8, fillColor: '#e0e8f0', fillOpacity: 0.6 }), onEachFeature: (feature, layer) => { if (feature.properties && feature.properties.name) { layer.bindTooltip(feature.properties.name); } } }).addTo(map); map.fitBounds(layer.getBounds()); }); </script> </body> </html>这段代码里,L.geoJSON 负责把 GeoJSON 转成 Leaflet 的多边形图层,style 函数统一控制边界线和填充样式,onEachFeature 为每个要素绑定 tooltip,鼠标悬停时显示国家名称。map.fitBounds(layer.getBounds()) 让视图自动缩放到数据所在范围,省去手动调中心点和缩放级别。
ECharts 的接入更偏数据可视化,把 GeoJSON 注册成地图名后,用 series-map 配置色块和标签:
import * as echarts from 'echarts'; import geoJson from './geojson/xxx.geojson'; const chart = echarts.init(document.getElementById('chart')); echarts.registerMap('world', geoJson); chart.setOption({ series: [{ type: 'map', map: 'world', roam: true, label: { show: false }, itemStyle: { areaColor: '#f0f2f5', borderColor: '#5b626a' }, emphasis: { label: { show: true } } }] });这里 registerMap 的第一个参数是地图名,第二个参数是 GeoJSON 对象;series 里的 map 字段要与 registerMap 的地图名一致。如果文件里包含多个国家,series 可以按国家代码字段做数据映射,但前提是 GeoJSON 的 properties 里包含对应代码字段。
3.3 业务数据挂接:名称匹配不如代码匹配
数据挂接是地图应用里的刚需,比如把各国人口、销量、库存数据填充到色块上。具体做法是:外部数据表中的国家代码/国家名称,要与 GeoJSON 的 properties 字段精确匹配。我一般会写一个工具函数完成这个动作。
import json def attach_data(geojson_path, csv_data, match_field, value_field): with open(geojson_path, "r", encoding="utf-8") as f: geojson = json.load(f) # 把外部数据表变成 dict:code -> value lookup = {} for row in csv_data: lookup[row[match_field]] = row[value_field] # 遍历 feature 并写入 value 字段 for feature in geojson["features"]: props = feature.get("properties", {}) code = props.get(match_field) props["metric_value"] = lookup.get(code, None) return geojson这个函数输入 GeoJSON 路径和外部数据行,match_field 是两者共有的国家代码字段,比如 ISO 3166-1 的 alpha-2 代码,value_field 是要挂接的指标字段。匹配上的要素会新增 metric_value 属性,没匹配上的保持 None,方便在渲染时单独处理。
我在实际项目里遇到最多的问题是名称文本匹配失败。比如数据表里写的是中文简称,GeoJSON 里却存的是英文官方名,直接匹配基本都会落空。正确的做法是统一用国家代码做关联键,名称文本只用来展示,不参与关联逻辑。
4. 高频翻车点排查:坐标反转、边界缺失与性能卡顿
4.1 地图渲染到海中央:经纬度顺序反了
现象:地图渲染后,国家边界出现在完全错误的位置,图形错乱,明显不在大陆上。
原因:GeoJSON 规范规定坐标数组顺序是 [经度, 纬度],而很多人的直觉是 [纬度, 经度]。我手写坐标裁剪逻辑时把顺序写反过,导致加载到 Leaflet 后所有点都跑到了海里。
解决:检查原始数据里第一个点的坐标是否落在合理的陆地区域。如果经纬度倒置,可以在 Python 脚本里统一交换坐标位置,也可以在加载到地图库前用 mapshaper 跑一遍坐标归一化。我现在习惯写一个函数专门校验坐标范围,经度超过 180 或纬度超过 90 的,立刻停止并输出排查日志。
4.2 有些国家消失不见:边界文件缺失
现象:渲染全球地图时,某些区域没有显示,不报任何错误,数一下 Feature 数量才发现少了好几个文件。
原因:这份大全按国家/地区拆分文件,部分区域的数据可能依赖补充文件或使用了不同的边界代码;另一个常见原因是 MultiPolygon 被误当 Polygon 解析,多个面片只显示了一个主面。
解决:先用 3.1 节里的体检脚本输出 Feature 数量和文件名列表,按国家代码索引比对缺失项,找到对应文件确认是数据文件缺失还是加载时过滤条件写错。如果是 MultiPolygon 解析问题,就在加载逻辑里增加类型判断,单独处理多面片要素。
4.3 文件过大导致页面卡成幻灯片
现象:渲染带完整离岛、海岸线细节的全球数据时,拖动地图每操作一步要等几百毫秒,CPU 占用直接拉满。
原因:GeoJSON 是文本格式,坐标点没经过压缩,数据量动辄十几兆。浏览器解析大量坐标点很快,但图层绘制时如果同时渲染大量多边形,开销会指数级上升。
解决:一是做面片简化,用 mapshaper 把不必要的坐标点去掉,保留视觉轮廓不变;二是按缩放级别做分级,低 zoom 用低精度数据,高 zoom 才加载高精度边界;三是对 GeoJSON 做压缩传输,但需要在服务端开启 gzip。
# mapshaper 简化命令,参数含义见下方说明 mapshaper input.geojson -simplify 10% -o output.geojson format=geojson这里 -simplify 10% 的含义是保留原面片 10% 的坐标点。比例越小文件越小,但视觉上边界会变得粗糙。一般建议先用 25% 试水,看渲染效果和文件体积的平衡点在哪,再逐步下调。
4.4 与国内地图底图对不上:坐标系不统一
现象:用国内商业地图服务做底图时,叠加 GeoJSON 后边界偏移了几百米到一公里,放大后偏移更明显。
原因:国内商业地图使用 GCJ-02 坐标系,和 GeoJSON 默认的 WGS84 存在系统偏移,直接把 WGS84 经纬度丢给国内地图 SDK 渲染,必然出现边界错位。
解决:把数据通过坐标偏移算法转为 GCJ-02 后再喂给地图 SDK;如果业务对坐标系没有强制要求,直接用支持 WGS84 的开源底图更省事。需要国内底图的场景,就把转换做成预处理步骤,每次更新数据都跑一遍,而不是在页面上实时计算。
4.5 控制台报跨域错误:GeoJSON 静态文件加载失败
现象:本地调试时,fetch 本地 geojson 文件报 CORS 错误;或者部署到服务器后请求 404。
原因:浏览器的同源策略限制了脚本读取任意本地文件,直接用 file:// 协议打开页面时会拦截;部署后 404 则可能是服务器 MIME 类型配置不正确,或者路径大小写写错。
解决:本地开发启动一个静态文件服务器来承载页面和数据;部署时把 .geojson 的 MIME 类型配置为 application/geo+json;在 Web 框架里把 geojson 目录设置为静态资源目录,前端用相对路径引用。
5. 把数据变“可上生产”:拓扑修复与面片简化的两个技巧
数据体检通过、渲染也正常的 GeoJSON 只能算“能看”,离“能上生产”还差两步:拓扑修复和面片简化。拓扑修复解决的是边界重叠和缝隙,简化解决的是加载性能。
拓扑修复主要处理两个问题:相邻国家的共享边界出现小缝隙,或者同一区域出现重叠。缝隙在高倍缩放下几乎看不到,但如果你做的是区域内聚合分析,比如计算国家面积、合并相邻区域,就会因为边界不重合产生误差。我用 mapshaper 的 -clean 命令处理过几次,它能自动填补缝隙、合并重复的边界段。示例命令:
# 合并多个国家文件并修复拓扑 mapshaper geojson/a.geojson geojson/b.geojson -merge-layers -clean -o merged.geojson这里 -merge-layers 把两个文件合并成一个图层,-clean 修复缝隙并移除无效的负空间环,输出的是合并后的 GeoJSON。如果希望相邻边界完全共享顶点,可以把输出格式改成 TopoJSON:
mapshaper merged.geojson -filter-fields country_code,name -o merged.topojson format=topojson-filter-fields 只保留 country_code 和 name 两个属性,减少载荷;format 指定输出格式为 TopoJSON,在浏览器端的体积通常比 GeoJSON 小很多。不过要注意,TopoJSON 需要前端额外引入 topojson-client 来转换成 GeoJSON 才能被 Leaflet 直接识别,属于加一层依赖换取体积收益的取舍。
面片简化这块,我在做全球热力图时用过一轮,印象最深的是:简化比例选不好,视觉效果会变得很糟糕。35% 保留率在洲级尺度几乎看不出区别,但到市级尺度就会看到明显锯齿;10% 保留率在缩小时可以接受,放大到国界线就会露馅。后来我的策略是同一份数据导出三份:低精度、中精度、原始精度,前端按 zoom 级别切换加载。渲染性能从原来的 800ms 降到 150ms,压缩后的文件体积也减少了约 40%。
从那以后我每次拿到新的边界数据,都会强制走一遍同样的流程:坐标范围体检、字段清单确认、拓扑修复、分级简化、双格式输出。这样即使数据是从多个来源拼起来的,最后交给前端的也是干净稳定的产物。希望帮到你。
本文还有配套的精品资源,点击获取