ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ECharts 世界地图实战:从 GeoJSON 注册到 visualMap 排错与 3D 球体实现

ECharts 世界地图实战:从 GeoJSON 注册到 visualMap 排错与 3D 球体实现 简介面向需要构建全球数据可视化地图的前端开发工程师、数据分析师及大屏展示设计人员这份压缩包提供了基于 ECharts 的完整世界地图 js 与 json 数据文件可直接用于人口、GDP、疫情、贸易、航班航线等地理分布场景的快速展示与二次开发。内含 77 个文件其中 39 个 js 脚本覆盖 ECharts 地图的实例化、交互配置、视觉映射与主题扩展等关键逻辑38 个 json 文件则保存世界各国的 GeoJSON 边界坐标数据二者配合即可实现从地图初始化、数据绑定到样式渲染的完整流程压缩包整体仅 3.42MB便于下载、分发与离线部署在资源占用与运行性能上表现友好。已有 1635 人浏览学习说明该资源在图表开发群体中获得了一定认可与检验。借助其中封装好的地图数据与配置示例使用者无需自行处理复杂的 GeoJSON 边界坐标也无需从零编写地图加载代码即可快速搭建带 tooltip 提示、visualMap 颜色分级、缩放平移等交互效果的世界地图并可按需调整国家名称与统计值同时支持自定义主题样式与高亮标注适合需要快速落地数据可视化项目的初中级开发者也为后续进阶学习 ECharts 高级地图特性提供了可直接改造的基础模板。 做全球化业务大屏世界地图几乎是个绕不开的组件。前阵子接了个海外业务分布需求要用 ECharts 把各国订单量铺到世界地图上。我原以为这事儿很简单——引入 echarts.min.js抄一段 map 配置就完事了。结果手动一跑页面先是白屏控制台甩出一行Map not exists等我把地图数据注册好、图表终于渲染出来了又发现所有国家的颜色一模一样完全看不出数据差异。回过头看用 ECharts 做世界地图本身不难难的是那些文档里没写透的细节地图 GeoJSON 从哪拿、怎么保证国家名和数据对得上、visualMap 为什么失灵、geo 和 series 里的 map 到底什么关系。这篇就把我实际跑通的完整链路拆开讲从拿数据到出图再到 echarts-gl 做 3D 球体每一步都给你能直接抄作业的代码和思路。适用读者正在做全球数据可视化大屏、海外运营看板、或者单纯想把世界地图跑通的开发者。你不需要用过 ECharts但最好有一点点 JavaScript 基础。1. 先解决没地图的问题世界地图数据的选型与清洗1.1 三种常见的地图数据来源ECharts 本身不携带任何地图数据这是设计上的选择——地图属于静态矢量数据体积不小如果打进主包首屏加载压力会明显变大。官方 4.x 时代还提供过 map/json 目录5.x 开始就完全交给开发者自己准备了。所以Map not exists这个报错的根因基本就是没把地图数据注册进去。现在拿到世界地图 GeoJSON 的渠道主要有三个DataV.GeoAtlas阿里云 DataV 官方的地图数据选择器可以按国家、省份下载 JSON 或 GeoJSON数据格式规范适合做中国地图和省市级地图。世界地图它也能给不过国家名的字段是中文和英文都带需要自己处理。ECharts 官方仓库以及社区维护的 map 数据仓库很多开源项目会定期整理 world GeoJSON字段通常保留了name、id、adcode这些属性命名风格比较接近 ECharts 的使用习惯我实际用得最多。第三方开源库比如世界地图 GeoJSON 合集、Natural Earth 数据数据精度普遍很高多边形顶点多、边界细但同样要自己写字段映射。选型的时候记住一个原则优先选带name字段的 GeoJSON且该name和你后端返回的国家名口径能对齐否则后面清洗数据会很痛苦。1.2 GeoJSON 里到底有什么GeoJSON 本身就是一个符合固定格式的 JSON 对象最外层通常是FeatureCollection里面有一个features数组每个feature代表一个区域{ type: FeatureCollection, features: [ { type: Feature, properties: { name: China, adcode: 100000 }, geometry: { type: MultiPolygon, coordinates: [ [[[经度, 纬度], [经度, 纬度]]] ] } } ] }ECharts 在注册地图时会遍历每个feature取properties.name有些数据源是properties.NAME或者properties.NAME_CHN作为该区域的名字键。后续你的series.data数组里name字段就是用来和它做匹配的。匹配不上数据就渲染不进对应的区域位置就会被跳过表现就是地图渲染了但颜色全空。1.3 国名不一致的通用处理办法这是世界地图项目里最容易踩的坑比地图数据本身麻烦得多。真实业务里后端接口返回的国家名可能是中文中国、美国可能是 ISO 国家代码CN、US可能是英文简称USA而世界地图 GeoJSON 里的name往往是官方英文名China、United States。我的做法是在拿到地图数据之后、注册之前先做一层名称归一化。GeoJSON 是数组结构洗数据非常方便// 假设从接口拿到的是中文国名把地图里的 name 改成中文也可以 // 但更推荐把接口数据映射成地图 name这样不影响地图原始数据 // 我这里选择改地图因为业务端已经约定按中文出数 const zhNameMap { China: 中国, United States: 美国, United Kingdom: 英国, Russia: 俄罗斯 // 其它国家继续补 }; worldGeoJson.features.forEach(feature { const enName feature.properties.name; if (zhNameMap[enName]) { feature.properties.name zhNameMap[enName]; } });这里要说明一下直接修改GeoJSON注册前的properties.name是完全合法的因为registerMap只对这个对象做一次读取和内部转换改完再注册就行。我还会把这个映射表单独抽成一个countryNameMap.js文件维护起来非常省事大屏项目里常见的中英文对照、简称全称对照都能放进去。2. 从零搭一个世界地图页面registerMap 与基础配置2.1 基础 HTML 骨架环境准备没什么特殊的一个div容器、一个 ECharts 的 JS 文件就够了。我用的是 CDN 方式开发时方便生产环境建议下载到本地或走公司的静态资源服务!DOCTYPE html html langzh-CN head meta charsetUTF-8 / title世界地图数据可视化/title style #map { width: 1000px; height: 600px; background: #0a1a2f; } /style /head body div idmap/div script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script script src./world.js/script script src./app.js/script /body /html一种常见的做法是把 GeoJSON 放进一个.js文件里全局挂一个变量比如world.js内部就是const worldGeoJson { ... }。这样可以借助普通script标签的同步加载保证执行顺序地图数据先就位app.js里再registerMap就不会找不到数据。2.2 registerMap 到底注册了什么echarts.registerMap(mapName, geoJson)做的事情简单说就是把一份 GeoJSON 变成 ECharts 内部可用的地图坐标系数据并挂在一个自定义名字下。这个名字就是后续配置里map字段要引用的键// 先注册 echarts.registerMap(world, worldGeoJson); // 再使用 const chart echarts.init(document.getElementById(map)); const option { series: [{ type: map, map: world }] }; chart.setOption(option);有个细节值得提醒setOption时不能把完整的 GeoJSON 塞进配置项。网上有些老文章会在option.geo.map里直接写一个对象这在旧版本可能能跑5.x 下是不靠谱的老老实实走registerMap最稳。2.3 用 series.map 展示数据这是最常规的出图方式数据直接挂在series.data每个数据项用name匹配国家value决定 visualMap 的颜色档位const option { tooltip: { trigger: item, formatter: function (params) { if (params.value) { return params.name params.value.toLocaleString() 条; } return params.name 暂无数据; } }, visualMap: { type: piecewise, pieces: [ { min: 10000, label: 10K 以上 }, { min: 5000, max: 9999, label: 5K - 9.9K }, { min: 1000, max: 4999, label: 1K - 4.9K }, { min: 1, max: 999, label: 1 - 999 } ], left: 20, bottom: 20, textStyle: { color: #fff } }, series: [{ type: map, map: world, roam: true, scaleLimit: { min: 1, max: 8 }, selectedMode: single, label: { show: false }, emphasis: { label: { show: true, color: #fff }, itemStyle: { areaColor: #ff9f43 } }, itemStyle: { areaColor: #1a3b5d, borderColor: #4a90d9, borderWidth: 0.6 }, data: [ { name: China, value: 12800 }, { name: United States, value: 9800 }, { name: Japan, value: 3200 } ] }] }; chart.setOption(option);这里有个新手特别容易忽略的点visualMap的min和max。如果你不显式设置ECharts 会按当前数据的最小最大值自动计算映射区间。数据差异特别大时比如最大值 10 万、最小值 5min会被压到 5max到 10 万视觉上 5000 和 50000 可能都显示成同一个深色看起来就像没区分度。用pieces分段就是人为控制区间把档位打散视觉效果会更直观。2.4 geo 组件和 series.map 怎么选很多人在这一步会纠结地图到底写在geo里还是写在series里它们都能渲染世界地图但定位完全不同series.map本身就是数据系列它直接消费data里的值配合visualMap自动上色也支持label、emphasis、select这些交互适合展示每个国家一个数值的场景。geo是一个坐标系组件它只提供底图不会自动消费业务数据。它的最大价值是作为其它系列的位置容器最常见的组合是geo scatter/effectScatter把点坐标画在地图上比如标记重点国家的首都、客户分布点。实际项目中我两种都用过选型的判断标准很简单如果颜色深浅代表某个指标选series.map如果重点在地图上的点和路线选geo做底图然后叠加scatter系列option { geo: { map: world, roam: true, itemStyle: { areaColor: transparent, borderColor: #5b9bd5 } }, series: [{ type: effectScatter, coordinateSystem: geo, data: [ { name: 北京, value: [116.4, 39.9, 100] }, { name: 伦敦, value: [-0.12, 51.5, 80] } ], symbolSize: function (val) { return Math.max(val[2] / 5, 4); } }] };scatter的data项里value是一个数组前两个元素是经纬度第三个及以后可以放业务值。coordinateSystem: geo是它和普通直角坐标系的区别所在。这种地图打点模式在大屏里出效果很快交互也轻。3. 让数据说话visualMap、缩放与图例的细节3.1 连续型与分段型的取舍visualMap有两种continuous和piecewise。世界地图这个场景只要数值跨度不是特别平滑我基本都用piecewise。原因有两个一是分段图例可以带文字标签比如高活跃区中活跃区领导看大屏时理解成本低二是分段能硬性拉出颜色差异不会出现上面说的全图一个色。分段配置的坑是需要保证pieces覆盖数据全量。比如数据里有 0 值甚至负数但你的pieces只定义了min: 1以上那么 0 值国家就没有对应颜色档位会显示成itemStyle.areaColor的底色。这不算报错但视觉上很容易误判为数据缺失。我的习惯是在pieces里显式加上一档{ min: 0, max: 1, label: 暂未开通 }把边界情况吃掉。连续型的优点在于数据落差小的场景比较细腻但地图上 200 多个国家连续渐变的图例在暗色大屏上其实挺考验配色。如果你确实要用建议这样写visualMap: { type: continuous, min: 0, max: 10000, inRange: { color: [#1a3b5d, #4a90d9, #ff9f43, #ff5e5e] } }3.2 地图缩放和默认视角控制世界地图不像中国地图区域跨度大默认视角下欧洲国家挤成一团label 一开更是糊到没法看。所以roam: true几乎是必开的它同时开启缩放和平移。scaleLimit用来限制缩放范围我一般设{ min: 1, max: 8 }避免用户放大到边界毛刺横飞的程度。如果你希望页面初始就聚焦到某个区域比如默认显示欧洲可以算一下目标区域的中心经纬度然后用center和zoom配合series: [{ type: map, map: world, center: [15, 50], // 大约欧洲区域 zoom: 2.5, roam: true }]center和zoom是 map 系列和 geo 组件都支持的属性直接写在初始化配置里就能生效。需要注意一旦用户手动拖拽过地图center就不再是初始化值如果你想做一个回到默认视角按钮可以用dispatchActionchart.dispatchAction({ type: geoRoam, zoom: 2.5, center: [15, 50] });3.3 地图上的点击与图例交互大屏项目里点击某个国家往往要联动右侧详情面板。ECharts 对 map 系列的点击事件已经做得很完备直接监听即可chart.on(click, function (params) { if (params.componentType series params.seriesType map) { // 拿到国家名和值 const countryName params.name; const value params.value; // 可以联动高亮 chart.dispatchAction({ type: downplay, seriesIndex: 0 }); chart.dispatchAction({ type: highlight, seriesIndex: 0, name: countryName }); } });dispatchAction这个 API 值得记住它允许你在图表渲染后继续控制和切换状态。我经常用它配合一个全局的国家列表鼠标移出列表时取消高亮鼠标移入时重新高亮达到双侧联动的效果。4. 排错专题世界地图最常见的五个翻车现场4.1 页面白屏控制台报 Map world not exists这是所有 ECharts 地图新手的第一道坎。报错的字面意思就是配置里map字段指向的名字没有被注册过。大概率是以下三种情况之一忘了调用echarts.registerMap(world, worldGeoJson)。调用了但mapName和配置里的字符串不一致比如注册时叫world配置里写成了worldMap。registerMap执行时worldGeoJson是undefined通常是加载异步数据和 JS 加载顺序没控制好。排查时直接打印一下worldGeoJson的 features 长度能迅速定位问题。fetch拿数据的话记得在.then里再注册和setOption。4.2 地图渲染出来了但所有国家颜色完全一样数据虽然渲染在地图上但颜色没有按值分层优先检查三处。第一visualMap是否真的生效确认是否漏写、或者seriesIndex指向了别的系列。第二min/max区间是否合理地覆盖了数据分布如果数据是 10000 到 12000 之间波动min默认取的可能是 10000max是 12000映射之后全图都是同一个深色非常正常。第三data里的name是否和地图里的name完全一致一旦有偏差那个国家的value不会报错但整张图也出不了颜色。我分享一个实用排查方法先给visualMap写死一个很小的min: 0, max: 1如果数据值都在 1 以上理论上所有国家都会被映射到最大色。这一步能快速验证是不是数据能对得上、但区间没调好。4.3 地图边界错乱、区域粘连或者 label 叠成一片边界错乱通常不是 ECharts 配置问题而是 GeoJSON 本身的精度或拓扑问题。不同来源的 GeoJSON 在边界处理上差异很大有的为了压缩体积做了简化放大到一定级别就会出现锯齿和毛边。解决方法是换一个更高精度的 GeoJSON或者限制scaleLimit的最大缩放值别让用户怼到太近。label 叠成一片则是另一个问题。世界地图 200 多个国家如果label.show一开始就开成true小国之间文字互相遮挡几乎不可避免。我的习惯是平铺时show: false只保留 hover 的emphasis.label.show。如果 ECharts 版本在 5.3 以上还可以用labelLayout: { hideOverlap: true }让引擎自动隐藏重叠标签效果也不错。4.4 应用数据是中文但地图 name 是英文这个问题我在 1.3 节已经给过处理方案维护一张中英文国名映射表在地图注册前批量改properties.name。实战中还有一种是接口返回 ISO 代码我同样用映射表转一遍代码逻辑完全一样。重要的不是改哪个方向而是所有数据源最后一次对齐时统一用哪个口径。建议在整个项目里只保留一种国家名表示方式传给 ECharts其他表示方式在数据层提前转换不要在渲染层到处改。4.5 大屏在低端设备上掉帧明显世界地图的 GeoJSON 本身数据量不算大大约 200 多个 features但如果叠加了effectScatter动画、3D 地图、多套visualMap和频繁的setOption低端设备照样扛不住。我在实际项目里的优化手段有三板斧地图和动画系列的 GeoJSON 用静态文件加载不走接口setOption更新数据时尽量只传series.data不要整棵 option 重传3D 地图上关闭 label或者只对重点区域开 label减少 GPU 渲染压力。5. 进阶向用 echarts-gl 把世界地图做成 3D 球体5.1 map3D 基础配置当你不再满足于平面地图想做出立体的 3D 地球效果就需要引入echarts-gl了。这东西本质上是 ECharts 的 WebGL 扩展支持map3D、scatter3D、bar3D等系列类型。第一次跑通map3D的配置长这样script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script script srchttps://cdn.jsdelivr.net/npm/echarts-gl2.0.9/dist/echarts-gl.min.js/script先把地图注册好然后配置map3D系列const option { backgroundColor: #0a1a2f, tooltip: {}, visualMap: { type: piecewise, pieces: [ { min: 10000, label: 10K 以上 }, { min: 1000, max: 9999, label: 1K - 9.9K }, { min: 1, max: 999, label: 1 - 999 } ], textStyle: { color: #fff } }, series: [{ type: map3D, map: world, regionHeight: 3, // 区域凸起高度 shading: lambert, // 光照模式 boxWidth: 100, // 整个球在容器中的尺寸 boxHeight: 80, environment: #000000, // 环境色 itemStyle: { color: #2a5caa, opacity: 0.9, borderWidth: 0.5, borderColor: #00d4ff }, label: { show: false }, data: [ { name: China, value: 12800 }, { name: United States, value: 9800 } ] }] };map3D和平面map的差异点很直观它是 3D 球体鼠标交互从拖拽平移变成了拖拽旋转首次坐标视角也完全不同center和zoom的配置逻辑和平面图不一致实际调试时多试几次就能找到手感。5.2 map3D 与 scatter3D 的联动做全球大屏的时候我更喜欢把map3D当底图用scatter3D或bar3D去做数据标记。比如只在重点国家上方打一根 3D 柱条柱条高度代表订单量视觉冲击力比平面图强很多series: [ { type: map3D, map: world, regionHeight: 2, shading: lambert, label: { show: false }, itemStyle: { color: #1b2c4f, opacity: 1, borderWidth: 0.8, borderColor: #3a6ea5 } }, { type: bar3D, coordinateSystem: geo3D, data: [ { name: China, value: [116.4, 39.9, 12800] }, { name: United States, value: [-77.0, 38.9, 9800] } ], barSize: 2, shading: lambert, itemStyle: { color: #ff9f43 } } ]这里有个容易忽略的点bar3D的坐标系统要写geo3D而不是geo而且value数组前两位是经纬度第三位才是高度值。如果配成geo数据不会报错但柱子根本不会出现在 3D 地图上。5.3 什么时候不建议上 3D虽然 3D 效果很炫但我还是想劝退一部分场景如果只是内部看板、数据变化频繁、需要快速看清数值差异2D 平面地图反而更高效。map3D的旋转交互在长时间盯屏时容易让人疲劳而且 WebGL 渲染在一些老设备上会有兼容性和性能问题。我自己的经验法则是3D 地图适合展厅、汇报、对外演示这种看一眼就 wow的场景日常运营分析还是用 2D 平面配一个清晰的visualMap分段图例信息获取效率最高。最后分享一个小技巧做世界地图前先把你业务里可能出现的所有国家名都列一遍不管是中文、英文、还是 ISO 代码都写进映射表里再核对一遍地图 GeoJSON 里的 name。这一步看起来琐碎但在后面省下的排查时间绝对不止一两个钟头。地图数据准备好了剩下的 ECharts 配置真的只是时间问题。本文还有配套的精品资源点击获取
返回列表