简介一套世界地图及主要国家边界数据的 GeoJSON 文件合集面向 Web 前端开发者、数据可视化爱好者及地理信息处理人员解决在各项目中快速获取多国地图矢量数据的需要。压缩包共 25 个文件其中 23 个为 JSON 格式的 GeoJSON 地图数据涵盖美国、中国、俄罗斯、日本、德国、法国、英国、印度、巴西等主要国家并附有世界地图数据另含 2 个 MHT 格式的 D3.js 学习参考页面便于结合可视化库理解地图绘制方法。包体整体约 11.31MB数据较完整用户可按需通过 mapshaper.org 在线工具简化文件体量。使用这些标准化的地理数据可以快速实现地图区域的着色、数值统计、交互式热力等常见可视化效果省去从零收集和清洗边界信息的麻烦。目前已有 6263 人学习下载适合需要快速上手地理数据可视化、制作世界或国家等级地图的开发者直接取用。1. 世界地图 GeoJson为什么先处理数据再谈地图渲染做访问来源分布、销售大屏、区域下钻这类地图可视化时最让人头疼的不是写 ECharts 配置而是拿到一份「看起来没问题」的 GeoJson。你以为下载下来直接注册就能用结果要么白屏要么卡到浏览器崩溃要么某个国家死活选不中——这些现象大半不是渲染代码的锅而是 GeoJson 数据本身的结构、坐标层级和字段没被正确理解。这份资源里包含世界地图和多个主要国家边界的 JSONGeoJson数据能直接用于地图注册、区域选中和分级着色。它的价值在于省去到处找边界数据的清洗时间但真正把它用好还得先弄清楚 GeoJson 是怎么组织的以及它为什么动辄几十 MB。这篇文章就按我实际拆过的流程来讲先看结构再讲用法最后把我踩过的坑一次说明白。2. GeoJson 的骨架FeatureCollection、坐标层级与数据膨胀2.1 为什么一份 GeoJson 会大到加载白屏GeoJson 本质上是一个 JSON 对象没有二进制压缩也没有索引结构。它里面每一个国家的边界都是一串按顺序排列的经纬度坐标点。世界地图要表达多少个国家少则几十个多则两百多个每个国家的外边界少则几千个坐标点多则几十万个坐标点。把这些全部用明文写在一个 JSON 文件里体积自然会膨胀到几十 MB。我拿到过一份看起来挺完整的世界 GeoJson光是中国一个国家的边界就包含了两万多个坐标点整个文件接近 50MB。直接丢给浏览器去解析肯定卡。更隐蔽的问题是许多下载站点只负责「转换」不负责「简化」原数据可能来自高精度测绘数据细到连一条河流的弯曲程度都记录了。那些对于地图展示不是必需的细节统统成了渲染的负担。所以在动手写页面之前先要建立一条基本判断标准如果一份 GeoJson 超过 10MB通常就需要先做顶点简化而不是直接进代码。减少顶点不只减小文件体积还能让浏览器渲染时省掉大量不必要的路径计算。2.2 从结构上看懂 GeoJsonFeatureCollection 和 Feature 的字段GeoJson 的类型不多最外层是 FeatureCollection它主要包含一个features数组。数组里每一个元素是一个 Feature 对象表示一个具体的地理实体——在这个资源里通常就是一个国家或地区。Feature 的关键字段是geometry和properties。先说 geometry它里面定义了typePolygon、MultiPolygon、Point 等和coordinates具体坐标数据。properties 则是一组自定义属性比如国家名称、ISO 国家码等。我给出一个最小示例方便看清层级关系{ type: FeatureCollection, features: [ { type: Feature, properties: { name: 示例国家A }, geometry: { type: Polygon, coordinates: [ [ [116.3, 39.9], [117.2, 39.2], [117.7, 40.0], [116.3, 39.9] ] ] } } ] }这个示例里只有一条 Featurecoordinates 的第一层是一个数组表示的是一个多边形边界。展开看内部每个元素都是一个[经度, 纬度]的坐标点。最外层数组里的第一个数组通常表示外边界如果还有后续数组则表示内部挖空的部分比如湖泊或某个飞地边界。明白这个层级后在写解析脚本时就不会被「数组套数组」绕晕。properties里的字段名在不同数据源里差异很大有的叫name有的叫NAME有的叫name_en使用时务必先打印出来看一遍。直接写死feature.properties.name很常见但字段名一旦不匹配地图上就会显示空白标签。2.3 坐标的第三层含义Polygon 和 MultiPolygon很多人第一次处理 GeoJson 时都会忽略geometry.type的影响。一个国家的边界并不是只有一个 Polygon。像一些国土包含飞地、海外领地或群岛的国家它的边界通常是由多个相互分离的多边形组成的此时 GeoJson 会用 MultiPolygon 来表示。这两者的坐标层级差了一层。Polygon 的 coordinates 长这样[[[经度,纬度], [经度,纬度], ...]]MultiPolygon 则长这样[[[[经度,纬度], [经度,纬度], ...], ...], ...]。也就是说MultiPolygon 内部先按「区块」分了一层每个区块再有自己的外环和内环。在实际开发里我见过有人把所有国家的坐标统一按geometry.coordinates[0]去取第一块结果遇到 MultiPolygon 结构的国家绘制出来就只剩一个局部形状看着就像国家「缺了一块」。要避免这个坑写脚本时就要先判断 type再决定遍历哪一层而不是统一处理。用一小段 Python 脚本可以快速把资源里所有国家的类型和坐标段数统计出来# inspect_geojson.py —— 查看每个国家的几何类型与区块数量 import json import sys with open(sys.argv[1], encodingutf-8) as f: data json.load(f) total_points 0 for feat in data[features]: geo feat[geometry] coords geo[coordinates] if geo[type] Polygon: total_points sum(len(ring) for ring in coords) elif geo[type] MultiPolygon: for poly in coords: total_points sum(len(ring) for ring in poly) print(feat[properties].get(name), geo[type], len(coords)) print(点总数:, total_points)运行方式是python inspect_geojson.py world.geojson。第一个参数是 GeoJson 文件路径脚本会一行行打印每个国家的名称、几何类型和坐标区块数最后统计全部点数。拿到这个输出就能判断当前数据的「重量级」——点总数超过两百万的话文件基本超过 20MB必须做压缩再做渲染。2.4 前置简化mapshaper 命令与压缩预算面对高精度的 GeoJson主流做法不是自己在程序里写抽稀算法而是先用 mapshaper 这类工具做顶点简化。mapshaper 是处理矢量地图数据的利器支持命令行也可以直接在网页版里拖拽操作。它内置了多种简化算法其中 Douglas-Peuckerdp算法在保留轮廓特征和压缩顶点数上表现比较均衡。一条最常用的简化命令如下mapshaper world.geojson -simplify dp 0.3% keep-shapes -o formatgeojson world_small.geojson参数含义拆开说dp指定使用 Douglas-Peucker 算法0.3%表示保留约 0.3% 的顶点这是简化力度数值越小简化幅度越大keep-shapes是一个安全选项避免某些小国家因为顶点被删掉太多而直接消失-o后面是输出文件路径和格式。简化力度怎么定我一般按场景来做世界地图全览时简化到 0.5% 到 3% 之间既能保住大陆轮廓也能兼顾加载速度做单国家大比例尺展示时不能太狠建议保留到 10% 左右否则国界看起来会像锯齿。简化完之后再用-info命令查看文件点数确认压缩效果mapshaper world_small.geojson -info输出里会显示每个 feature 的顶点数对比压缩前后的数字就能确定简化力度是否合适。这个步骤建议放在清理坐标系之后、接入页面之前。2.5 投影与坐标EPSG:4326 是省事的基础GeoJson 里存的是经纬度坐标标准约定是 EPSG:4326也就是 WGS84 坐标系。它的数值范围大致是经度 -180 到 180纬度 -90 到 90直接传给浏览器端的地图渲染库即可不需要额外做投影转换。但有些数据源提供的「GeoJson」实际上是已经被转换过的 Web 墨卡托坐标EPSG:3857坐标值动辄几百万直接按经纬度渲染时地图会缩放到一个奇怪的位置甚至出现在屏幕外。遇到这种情况常见做法是用 mapshaper 做坐标回退mapshaper source.geojson -proj wgs84 -o formatgeojson wgs84.geojson-proj wgs84会把投影强制转回到经纬度。我能给出的建议是下载的 GeoJson 先做一次「坐标范围检查」看看最大最小值是否落在经纬度合理区间。如果最小 X 是几百万就不要去改渲染代码先改数据。3. 把 GeoJson 跑起来ECharts 注册、单国家抽取与名称映射3.1 registerMap 的正确姿势与参数说明GeoJson 数据在 ECharts 里使用前必须先调用registerMap完成注册。很多初学者在这个环节翻车最常见的问题是注册名和配置项里的map字段不一致导致图表报「Map not found」或者干脆白屏。一个标准的地图初始化流程大致如下import * as echarts from echarts; const chart echarts.init(document.getElementById(mapContainer)); echarts.registerMap(world, worldJson); chart.setOption({ series: [{ type: map, map: world, roam: true, data: [ { name: 中国, value: 120 }, { name: 美国, value: 98 } ] }] });代码里echarts.registerMap(world, worldJson)的第一个参数是注册别名第二个参数是 GeoJson 对象。配置项里series.map的值必须和注册别名完全一致这里是world。roam: true开启缩放和拖拽。data数组里的name要和 GeoJson 中properties.name对应上只有这样对应区域的着色和数据值才能匹配。参数细节上还有一点值得说明如果 GeoJson 里的 name 是英文而业务数据里是中文那么两者直接做匹配永远对不上。此时要么改数据要么在注册前给 GeoJson 的 properties.name 批量替换成业务用的名称。我更推荐后一种做法因为数据层不太适合为中英文展示反复调整。3.2 从世界地图中抽取指定国家ISO 码脚本整套世界地图 GeoJson 有时候太大有的场景只需要展示某一个国家的局部区域比如用户点选某个国家后希望单独弹出一个该国家的地图。此时不需要去找单独的国家边界文件直接从世界地图里把对应 Feature 抽出来即可。一份世界 GeoJson 中每个国家的 properties 通常会包含 ISO 3166-1 alpha-3 代码比如 GBR 代表英国CHN 代表中国USA 代表美国。利用这个字段做匹配是最稳定的抽国方式# extract_by_iso.py —— 按 ISO 3166-1 alpha-3 从世界地图中提取指定国家 import json def extract_country(src_path, iso, out_path): with open(src_path, encodingutf-8) as f: data json.load(f) feature None for feat in data.get(features, []): props feat.get(properties, {}) # 兼容不同字段名ISO_A3 / iso_a3 if props.get(ISO_A3) iso or props.get(iso_a3) iso: feature feat break if feature is None: raise ValueError(fcountry not found: {iso}) output {type: FeatureCollection, features: [feature]} with open(out_path, w, encodingutf-8) as f: json.dump(output, f, ensure_asciiFalse) print(fextracted {iso} - {out_path}) if __name__ __main__: # 用法: python extract_by_iso.py world.geojson GBR gb.geojson import sys extract_country(sys.argv[1], sys.argv[2], sys.argv[3])这段脚本接收三个参数世界地图路径、ISO 代码、输出路径。脚本遍历所有 Feature匹配 properties 里的ISO_A3或iso_a3字段找到后把整个 Feature 放进新的 FeatureCollection 保存。这样抽取出来的单国地图没有多余数据体积小也省去了手动框选坐标的麻烦。要注意的是市面上的 GeoJson 中 ISO 字段有时不叫ISO_A3可能叫ISO_A3_EH或ADM0_A3。抽国之前先跑一下 2.3 节的统计脚本看完 properties 字段名再写匹配逻辑。我通常会在命令行里先执行一次python inspect_geojson.py world.geojson确认字段名后再执行抽国脚本这样能少走很多弯路。3.3 名称错位处理用 ISO 码关联业务数据国家名称错位是地图项目里一个非常隐蔽的坑。GeoJson 数据里的中文名称、英文名称、以及业务数据库里的名称往往不一致。比如 GeoJson 里叫United Kingdom业务数据里写的是英国直接在data数组里做 name 关联就无法给英国区域赋值。我把这类问题统称为「名称匹配黑匣子」——表面上看地图能渲染但某个区域颜色不变日志也不报错排查起来很费劲。后来我改成一种比较稳的做法不管 GeoJson 里的 name 是什么只把 ISO 国家码作为统一关联键。在实际处理中可以先用一个映射对象把业务数据里的国家码转换成 GeoJson 里的 name再交给 EChartsconst isoToName { GBR: 英国, CHN: 中国, USA: 美国 }; const mappedData rawData.map(d ({ name: isoToName[d.iso] || d.iso, value: d.value }));rawData是业务侧返回的数组每个元素包含iso和value。isoToName负责把 ISO 码转成和 GeoJson 一致的名称。但要彻底避免名称不匹配最稳妥的方式还是把 GeoJson 中所有 features 的 name 字段改为 ISO 码而不是中文或英文名。因为 ISO 码在一个标准资源里是唯一的不会出现「英国」和Britain的差异。改完之后mappedData里的name也直接存 ISO 码两边完全对齐。4. 常见问题避坑从白屏到国家错位的五条踩坑记录4.1 白屏与「Map not found」现象控制台报错提示某个地图未注册页面地图部分一片空白或者地图能渲染但点击缩放时没有任何反应。原因registerMap的注册名与配置项里的map字段不一致。比如注册时写的是registerMap(world, data)配置里却写成了map: worldMap。ECharts 找不到对应的地图资源干脆什么都不画。解决把两个名称统一为一个最好用一个常量维护避免手抖。我的做法是定义const MAP_NAME world注册时和配置时都用这个常量。如果是多语言切换场景还要注意按语言注册不同名称的地图不能互相覆盖。排查白屏时我一般会按「配置 → 数据 → 注册名 → 坐标」四步走先确认配置项没有语法错误再确认加载的 GeoJson 文件不是空的再检查注册名是否一致最后打开 GeoJson 看看坐标点是否在合理范围内。大多数白屏都能在四步内定位。4.2 文件解析报错与 UTF-8 BOM现象用fetch加载 GeoJson 后JSON.parse直接抛错或者本地开发正常部署到服务器后偶尔报解析失败。原因GeoJson 文件保存时带了 UTF-8 BOM 头或者数据里混入了 HTML 转义实体。BOM 是文件开头几个不可见字节JSON.parse不认转义实体常见于从网页直接复制的内容比如把双引号复制成了quot;导致 JSON 结构失效。解决写一个简单的修复脚本来统一清理这两种情况# fix_geojson.py —— 清理 BOM 与常见转义实体 import json import codecs def fix(path_in, path_out): raw open(path_in, rb).read() if raw.startswith(codecs.BOM_UTF8): raw raw[len(codecs.BOM_UTF8):] text raw.decode(utf-8) text text.replace(quot;, ).replace(amp;, ).replace(#39;, ) with open(path_out, w, encodingutf-8) as f: json.dump(json.loads(text), f, ensure_asciiFalse) print(done) if __name__ __main__: import sys fix(sys.argv[1], sys.argv[2])fix函数先把文件以二进制方式读入检测并去掉 BOM 头再处理几个高频转义字符最后用json.loads验证内容确实能正常解析。这一步相当于给数据上了一层保险只要是合法 JSON之后的加载就不会因为文件本身的问题翻车。4.3 地图变形或坐标漂移投影与经纬度分不清现象地图能渲染但整个形状被压扁、拉长或者显示位置完全不对有些区域跑到屏幕外。原因数据源给的坐标不是经纬度而是已经被投影成 planar 坐标常见是 Web 墨卡托。浏览器端的地图组件默认按经纬度处理拿着几百万的坐标直接做映射自然就「飞」出去了。解决先检查 GeoJson 中坐标数组的最小最大值。如果经度范围是 -180 到 180纬度范围是 -90 到 90说明是标准的 WGS84 经纬度不需要处理。如果坐标数值达到好几万或几百万就要用 mapshaper 把投影转回 WGS84mapshaper source.geojson -proj wgs84 -o formatgeojson fixed.geojson还有一种少见但真实存在的情况经度和纬度的数值被写反了。此时地图会出现水平翻转或者旋转 90 度的观感。常规做法是在转换脚本里把两个数值对调但我更建议重新找一份数据源因为手工修复几百个 MultiPolygon 的坐标顺序迟早会漏。4.4 移动端卡顿与数据体积简化预算不是玄学现象开发时用桌面浏览器看起来流畅部署到移动端后缩小地图时明显掉帧甚至直接白屏。原因移动端 GPU 和内存资源有限。全量 GeoJson 的顶点数量如果超过百万级渲染路径计算量会急剧上升尤其在每次缩放拖拽重新绘制时。解决回到第 2.4 节用 mapshaper 给数据做前置简化。简化预算不是固定的要按目标平台来定。桌面端可以宽松一些保留 3% 左右的顶点移动端建议压到 1% 以下并为地图页面增加一个「初始缩放级别」的控制让用户进入页面时不要带着全量顶点去做大范围渲染。第 2.3 节里的统计脚本在这里能派上用场如果压缩前点总数是 800 万压缩后只有 20 万移动端基本就不会卡了。我在项目里会把压缩后的点总数控制在 30 万以内然后配合异步加载等到地图即将展示时才拉取数据进一步减少首屏压力。4.5 国家名对不上业务数据旧地名与字段名差异现象地图主体渲染正常但某个国家区域始终不显示业务数据对应的颜色有些区域点击后弹出的名称还是旧版地名或英文别名。原因GeoJson 的properties.name与业务数据中的国家名称表述不一致。比如一边存的是苏联这种旧名一边存的是新国名或者一边是Russia一边是俄罗斯。ECharts 做 data 匹配时找不到对应项就会跳过该区域不报错也不提示。解决用第 3.3 节的 ISO 码映射思路把关联键从「名称」换成「ISO 码」展示名称单独用一张映射表控制。实际操作中我先整理一份完整的「ISO 码 → 业务名称」映射表再写一段逻辑把 GeoJson 的 name 统一替换成 ISO 码最后业务数据的 name 字段也转成 ISO 码。这样无论资源内部叫什么都无所谓关联不会断。下面是一张常用对照表供整理数据时参考ISO 3166-1 alpha-3GeoJson 常见 name业务展示名CHN中国中国GBRUnited Kingdom英国USAUnited States of America美国不管中文还是英文只要关联键统一用 ISO 码展示名就是纯展示层的事想怎么改都行不会再影响数据匹配。4.6 数据质量核对表拿到 GeoJson 后逐项检查把前面几条坑汇总成一张核对表每拿到一份新 GeoJson我都会在项目里过一遍检查项现象建议做法严重程度UTF-8 与 BOMJSON.parse 报错用 fix_geojson.py 清理高顶点数量白屏 / 卡顿用 mapshaper 简化高name 字段缺失地图无标签检查 properties补 name中ISO 字段命名抽国失败 / 数据错位先看字段名再匹配中坐标投影地图变形 / 漂移转回 WGS84高MultiPolygon 多次嵌套区块缺失按 geometry.type 分层解析中这张表不是摆设我建议把它当成数据接入的第一道工序。所有 GeoJson 在写页面代码之前先按表里的六项逐一确认能省掉后面至少一天的调试时间。5. 一个兜底技巧浏览器端边加载边精简5.1 快速顶点采样函数有些时候没法在命令行里跑 mapshaper比如临时做一个原型 Demo 或者数据由后端接口动态返回。这种情况我准备了一个 JavaScript 函数能在浏览器里直接对 GeoJson 做「应急式」顶点精简const simplifyGeoJson (orig, ratio 0.003) { const copy JSON.parse(JSON.stringify(orig)); copy.features.forEach((feat) { const geo feat.geometry; if (geo.type Polygon) { geo.coordinates geo.coordinates.map((ring) ring.filter((_, idx) idx % Math.round(1 / ratio) 0) ); } else if (geo.type MultiPolygon) { geo.coordinates geo.coordinates.map((poly) poly.map((ring) ring.filter((_, idx) idx % Math.round(1 / ratio) 0)) ); } }); return copy; };ratio参数控制采样间隔。默认0.003表示大约每 334 个点保留 1 个。要保留更多细节就调大 ratio比如0.01要更精简就调成0.001。这个函数的好处是纯 JS 实现不依赖外部工具缺点是不如 mapshaper 的 Douglas-Peucker 算法聪明——它只是均匀抽点可能会丢掉一些重要的拐点。所以我的使用场景很明确只在临时原型、接口数据演示时用。正式项目里我一定会回到 mapshaper优先做有损但保形的拓扑简化。5.2 最终推荐给数据入库的完整流程从拿到一份新 GeoJson 到真正跑进页面我推荐的完整流程是这样的用 fix_geojson.py 做 BOM 和转义清理确保能正常解析。用 inspect_geojson.py 打印国家名、几何类型、点总数确认字段名和坐标范围。用 mapshaper 做简化压缩到目标平台可接受的文件体积。用 extract_by_iso.py 抽出需要单独展示的国家存成独立文件。把 GeoJson 的 name 字段统一替换成 ISO 码业务数据也按 ISO 码关联。最后才进入 ECharts 的 registerMap 和 setOption。这套流程看着繁琐但每一步都是在消除一个真实的隐患。BOM 清理能避免解析报错顶点简化能避免移动端卡顿ISO 码统一能避免名称匹配黑匣子。我在一个项目上因为没有走这套流程直接用了全量高精度世界地图结果上线当天就收到用户反馈说地图缩放卡到没法操作。后来强制把数据先过一遍 mapshaper再重新发布问题立刻消失。从那以后我每次拿到新的 GeoJson 都强制走一遍这个流程先处理数据再写页面代码。希望帮到你。本文还有配套的精品资源点击获取 SEO 优化官网定制响应式建站教育培训建站