ECharts 世界地图 GeoJSON 数据实战:注册、清洗与下钻避坑指南

发布时间:2026/10/14 14:54:54
ECharts 世界地图 GeoJSON 数据实战:注册、清洗与下钻避坑指南
简介这份资源面向使用 ECharts 进行地图可视化的前端开发者、数据分析人员及地理信息爱好者提供世界各国家与地区的 GeoJSON 地图数据解决地图底图数据获取困难、格式不统一的问题。压缩包为 zip 格式整体约 1.64MB内含各国地图 JSON 数据文件并附带使用示例方便直接引入 ECharts 的 map 或 geo 组件进行区域渲染与数据绑定。资源标签为 echarts适合需要快速搭建世界地图、国家分布图或区域热力图的场景也便于二次加工与自定义样式。目前已有 976 人学习下载说明其在同类地图数据资源中具备一定参考价值。读者可获得覆盖多国的标准化 GeoJSON 数据结合示例快速理解数据字段与接入方式减少自行采集与转换坐标的时间成本同时可参考在线生成地图 JSON 数据的链接按需获取更细粒度的区域边界提升可视化开发效率。1. 从一份世界国家 GeoJSON 合集说起ECharts 地图开发者的数据急救包做数据可视化的人多半遇到过这种场景项目要上线一张世界地图产品经理要求每个国家能单独高亮、能下钻、能按区域着色结果翻遍手头资料要么只有一张糊到看不清的 PNG要么就是某个国家缺一块、边界对不上。更头疼的是ECharts 从某个版本之后不再内置地图数据registerMap之前你得先有geoJSON。这份「世界国家 geojson 大全各国地图 json 数据[带例子].zip」解决的正是这个卡点——它把全球各个国家的地图边界整理成标准 GeoJSON 文件并且附带了可直接运行的示例省去你从公开数据源一个个扒、再手动清洗的功夫。适合谁前端做 ECharts 地图的、GIS 入门想拿现成边界练手的、以及需要批量生成国家维度地图配置的从业者。下面我按「数据长什么样 → 怎么接进 ECharts → 怎么批量处理 → 坑在哪」的顺序拆一遍。2. GeoJSON 与 ECharts 的对接原理为什么不是随便一个 json 都能画2.1 GeoJSON 的几何结构决定了渲染方式GeoJSON 本质是一个遵循 RFC 7946 的 JSON 对象核心字段是type、features。地图场景里最常打交道的是FeatureCollection里面每个Feature的geometry可能是Polygon单个多边形或MultiPolygon多个不相连的多边形比如岛屿国家。ECharts 的registerMap拿到这份数据后会把经纬度坐标投影到画布坐标系再交给geo组件或series-map渲染。这里有个容易被忽略的点ECharts 默认使用一种简化的墨卡托投影逻辑它并不关心你的 GeoJSON 是否符合严格的地理规范但非常在意坐标顺序。GeoJSON 标准规定坐标是[经度, 纬度]而有些从其他格式转换来的数据会写成[纬度, 经度]一旦顺序反了地图会整个翻转或挤成一条线。我一般拿到一份新数据先抽一个国家的第一个坐标点肉眼确认经度在 -180 到 180、纬度在 -90 到 90 之间再往下走。另一个关键字段是properties。ECharts 在series-map里通过name来匹配数据项而这个name默认取自properties.name。如果这份合集里某个国家的properties.name是英文而你的业务数据用的是中文国名那就对不上地图会显示但数据着色不生效。常见做法是在注册地图前先遍历features把properties.name统一成你业务里用的键。2.2 registerMap 的调用时机与作用域echarts.registerMap(world, geoJson)这个调用必须在setOption之前执行而且注册的名字要和series-map.map的值一致。很多人把注册写在组件mounted里但异步请求 GeoJSON 还没回来就setOption结果地图空白。稳妥的写法是把注册和setOption放在同一个then里或者用async/await串起来。import * as echarts from echarts; import worldGeoJson from ./geojson/world.json; // 注册必须在 setOption 之前且名字与 series.map 对应 echarts.registerMap(world, worldGeoJson); const chart echarts.init(document.getElementById(map)); // 假设业务数据里国家名用的是 properties.name 的英文值 const data [ { name: China, value: 100 }, { name: United States, value: 80 }, { name: Brazil, value: 60 } ]; chart.setOption({ series: [{ type: map, map: world, // 与 registerMap 的第一个参数一致 roam: true, // 允许缩放和平移 emphasis: { label: { show: true } }, data }] });上面这段代码里registerMap的第二个参数就是那份合集里某个国家或全球的 GeoJSON 对象。roam: true开启鼠标拖拽和滚轮缩放适合世界地图这种范围大的场景。data里的name必须和 GeoJSON 中properties.name完全一致大小写、空格都不能差。如果合集里用的是United States of America而你写United States那个国家就不会着色但边界依然显示——这是最迷惑人的地方地图看起来正常数据却没上去。2.3 单国地图与全球地图的取舍这份合集的价值在于「各国地图 json 数据」是分开的。做全球概览时用一份合并的 world GeoJSON 就够了但要做国家下钻比如点击中国后切换到中国省级地图那就需要单独加载对应国家的 GeoJSON。分开存储的好处是首屏只加载全球数据下钻时按需请求减少初始体积。常见做法是把每个国家的 GeoJSON 放在public/geojson/目录下文件名用 ISO 国家代码比如CN.json、US.json点击事件里根据代码动态fetch再registerMap。chart.on(click, async (params) { const countryCode codeMap[params.name]; // 业务维护的国名到代码映射 if (!countryCode) return; const res await fetch(/geojson/${countryCode}.json); const geoJson await res.json(); // 用国家代码注册避免和 world 冲突 echarts.registerMap(countryCode, geoJson); chart.setOption({ series: [{ type: map, map: countryCode, roam: true, data: [] // 这里换成该国的省级数据 }] }); });这段下钻逻辑里codeMap需要你自己维护因为 GeoJSON 的properties里不一定有 ISO 代码。如果合集里带了properties.iso_a3之类的字段那就可以直接用省去手工映射。注册时用国家代码而不是国名能避免中文名和英文名混用导致的覆盖问题。3. 批量处理与数据清洗把合集变成项目能直接吃的格式3.1 先摸清合集的目录结构和字段命名拿到压缩包后别急着往项目里塞先解压看目录。常见结构有两种一种是按国家分文件夹每个文件夹里一个geo.json另一种是平铺文件名就是国家名或代码。用命令行快速统计一下文件数量和大小分布心里有数。# 查看解压后的目录结构只看两层 find ./world-geojson -maxdepth 2 -type d | head -30 # 统计 json 文件数量和总大小 find ./world-geojson -name *.json | wc -l du -sh ./world-geojson # 抽查一个文件看 properties 里有哪些字段 cat ./world-geojson/CN.json | python -m json.tool | head -40find的-maxdepth 2避免目录太深刷屏du -sh看总体积决定是否需要按需加载。抽查文件时重点看properties里有没有name、name_zh、iso_a3这些字段它们决定了你后面怎么建映射表。如果只有英文名那就得自己准备一份中英文对照或者用name_zh字段如果有的话。3.2 用脚本统一 properties.name 并剔除冗余字段合集的原始数据往往带了一堆用不上的属性比如人口、面积、ISO 各种编码全量传给前端会白白增加体积。我一般写个 Node 脚本遍历所有 GeoJSON只保留name和iso_a3同时把name统一成业务需要的语言。const fs require(fs); const path require(path); const srcDir ./world-geojson; const outDir ./cleaned-geojson; // 只保留这两个属性其余丢弃 const KEEP_KEYS [name, iso_a3]; function cleanFeature(feature) { const props feature.properties || {}; const cleaned {}; KEEP_KEYS.forEach((k) { if (props[k] ! undefined) cleaned[k] props[k]; }); // 如果业务需要中文名这里做一次映射 if (props.name_zh) cleaned.name props.name_zh; return { ...feature, properties: cleaned }; } function walk(dir) { fs.readdirSync(dir).forEach((file) { const full path.join(dir, file); const stat fs.statSync(full); if (stat.isDirectory()) return walk(full); if (!file.endsWith(.json)) return; const geo JSON.parse(fs.readFileSync(full, utf-8)); if (geo.type FeatureCollection) { geo.features geo.features.map(cleanFeature); } else if (geo.type Feature) { Object.assign(geo, cleanFeature(geo)); } const outPath path.join(outDir, file); fs.mkdirSync(path.dirname(outPath), { recursive: true }); fs.writeFileSync(outPath, JSON.stringify(geo)); console.log(cleaned:, file); }); } fs.mkdirSync(outDir, { recursive: true }); walk(srcDir);脚本里KEEP_KEYS控制保留哪些属性按项目实际需要增减。cleanFeature里做了中文名覆盖前提是原始数据有name_zh。walk递归处理所有子目录输出到cleaned-geojson原数据不动方便对比。跑完之后体积通常能降三到五成具体看原始数据带了多少冗余字段。3.3 合并全球数据时注意 Feature 的粒度如果你需要一份全球地图把各国 GeoJSON 合并成一个FeatureCollection是最直接的做法。但要注意有些合集里「国家」的粒度不一致比如把某些地区单独拆成一个 Feature合并后可能出现重叠或缝隙。合并脚本里加一步去重和边界检查。const fs require(fs); const path require(path); const dir ./cleaned-geojson; const features []; const seen new Set(); fs.readdirSync(dir).forEach((file) { if (!file.endsWith(.json)) return; const geo JSON.parse(fs.readFileSync(path.join(dir, file), utf-8)); const list geo.type FeatureCollection ? geo.features : [geo]; list.forEach((f) { const key f.properties?.iso_a3 || f.properties?.name; if (!key || seen.has(key)) return; // 跳过无标识或重复的 seen.add(key); features.push(f); }); }); const world { type: FeatureCollection, features }; fs.writeFileSync(./world.json, JSON.stringify(world)); console.log(total features:, features.length);seen集合用iso_a3或name做去重键避免同一个国家被合并两次。如果某些 Feature 没有这两个字段直接跳过否则会在 ECharts 里产生无法匹配的幽灵区域。合并后features.length应该和你预期的国家数量大致吻合差太多就说明原始数据粒度有问题得回头检查。4. 避坑与排查地图不显示、数据不着色、下钻错位的常见原因4.1 地图一片空白控制台无报错现象registerMap调了setOption也执行了但容器里什么都没有控制台干净得让人发慌。原因最常见的是 GeoJSON 的type不是FeatureCollection而是单个Feature或者干脆是个数组。ECharts 的registerMap对这两种格式的处理方式不同传错了不会报错只是不渲染。另一个可能是series-map的map值和registerMap的名字不一致差一个字母都白搭。解决在registerMap之前打印geoJson.type确认是FeatureCollection。如果是单个 Feature手动包一层{ type: FeatureCollection, features: [geoJson] }。名字不一致的问题把registerMap和series.map的值抽成常量两边引用同一个变量。4.2 边界显示正常但数据着色不生效现象地图轮廓出来了但data里的值没反映到颜色上所有区域一个色。原因data里的name和 GeoJSON 里properties.name对不上。英文大小写、空格、缩写全称差异都会导致匹配失败。比如 GeoJSON 里是United States of America你写United StatesECharts 找不到对应区域数据就被忽略了。解决写个脚本把 GeoJSON 里所有properties.name导出来和你的业务数据做一次交集比对差集就是问题所在。统一命名后把映射关系固化到代码里别靠记忆。// 导出所有 name和业务数据比对 const names world.features.map((f) f.properties.name); const bizNames data.map((d) d.name); const missing bizNames.filter((n) !names.includes(n)); console.log(业务有但地图没有的:, missing);4.3 下钻后地图错位或显示上一个国家现象点击中国下钻地图确实变了但形状不对或者还是上一个国家的轮廓。原因registerMap注册的名字重复了。比如全球地图注册为world下钻时又用world注册了中国的数据ECharts 内部缓存被覆盖但series.map还指向world结果渲染的是新数据但按旧配置来。另一个可能是异步请求没回来就setOption用了旧的 GeoJSON。解决下钻时用唯一的注册名比如国家代码CN并且series.map同步改成CN。异步逻辑用await确保数据到位再setOption。如果下钻频繁可以在内存里缓存已注册的国家避免重复请求和注册。4.4 地图缩放后标签重叠或消失现象roam: true开启后放大到一定程度国家名称标签挤成一团或者干脆不显示。原因ECharts 的label默认在缩放时不做碰撞检测密集区域标签会重叠。另外label.show在emphasis里才开的话只有 hover 才显示平时看不到。解决在series-map顶层加label: { show: true, fontSize: 10 }并配合nameProperty指定用哪个字段做标签。如果还是挤可以关掉小国家的标签只在大区域显示或者用labelLayout做隐藏重叠。series: [{ type: map, map: world, roam: true, label: { show: true, fontSize: 10, formatter: (p) (p.value ? p.name : ) // 有数据的才显示 }, labelLayout: { hideOverlap: true // 自动隐藏重叠标签 }, data }]hideOverlap是 ECharts 5 之后支持的配置能自动处理标签重叠省去手动计算。formatter里根据value判断是否显示避免所有国家名都堆上去。4.5 打包后地图文件 404现象本地开发一切正常构建部署后地图不显示网络面板里 GeoJSON 请求 404。原因GeoJSON 放在src/assets里被 webpack 处理了但fetch的路径是按public目录写的构建后路径对不上。或者文件太大被构建工具忽略了。解决把 GeoJSON 统一放public/geojson/fetch用相对根路径/geojson/xx.json。如果必须放src用import静态引入让构建工具处理路径。大文件建议放public按需加载别打进 bundle。5. 进阶技巧用 nameMap 做多语言切换与按需加载优化5.1 nameMap 解决中英文切换不重启图表ECharts 的series-map支持nameMap可以在不重新注册地图的前提下把 GeoJSON 里的英文名映射成中文显示。这个特性在做多语言切换时特别省事——不用改数据只改配置。const nameMap { China: 中国, United States of America: 美国, Brazil: 巴西 }; chart.setOption({ series: [{ type: map, map: world, nameMap, // 显示时用中文匹配数据时仍用英文 data }] });nameMap的键是 GeoJSON 里的原始name值是显示用的名称。注意它只影响显示不影响data的匹配——data里的name仍然要用原始英文。切换语言时重新setOption换一份nameMap即可地图不用重新注册响应很快。5.2 按需加载与缓存策略全球 GeoJSON 动辄几 MB一次性加载会拖慢首屏。我的习惯是首屏只加载一份简化版的 world比如用 mapshaper 抽稀过的下钻时再加载完整版的国家数据。缓存用Map存已注册的 GeoJSON避免重复请求。const geoCache new Map(); async function loadAndRegister(code) { if (geoCache.has(code)) { echarts.registerMap(code, geoCache.get(code)); return; } const res await fetch(/geojson/${code}.json); const geo await res.json(); geoCache.set(code, geo); echarts.registerMap(code, geo); }geoCache用国家代码做键存的是原始 GeoJSON 对象。注册过的地图 ECharts 内部也有缓存但自己再存一份方便判断是否需要发请求。如果项目对体积敏感可以在构建时用工具把 GeoJSON 的坐标精度从 6 位降到 4 位体积能减不少肉眼几乎看不出差别。5.3 验证数据完整性的一个小脚本最后分享一个我每次拿到新 GeoJSON 合集都会跑的检查脚本用来确认没有空 Feature、没有坐标越界、没有重复 name。const fs require(fs); const path require(path); const dir ./cleaned-geojson; let total 0; const nameSet new Set(); fs.readdirSync(dir).forEach((file) { if (!file.endsWith(.json)) return; const geo JSON.parse(fs.readFileSync(path.join(dir, file), utf-8)); const features geo.type FeatureCollection ? geo.features : [geo]; features.forEach((f) { total; const name f.properties?.name; if (!name) console.warn(缺少 name:, file); if (nameSet.has(name)) console.warn(重复 name:, name); nameSet.add(name); // 抽查第一个坐标是否越界 const geom f.geometry; if (!geom) return console.warn(缺少 geometry:, name); const firstCoord geom.type Polygon ? geom.coordinates[0][0] : geom.coordinates[0][0][0]; const [lng, lat] firstCoord; if (lng -180 || lng 180 || lat -90 || lat 90) { console.warn(坐标越界:, name, firstCoord); } }); }); console.log(总 Feature 数:, total, 唯一 name 数:, nameSet.size);这个脚本会输出所有异常项total和nameSet.size不一致就说明有重复或缺失。坐标越界的检查只抽了第一个点够用来发现整体翻转的问题。从那以后我每次接入新的地图数据都强制走一遍这个检查宁可多花两分钟也不想上线后对着空白地图排查半天。希望帮到你。本文还有配套的精品资源点击获取