
处理可视化大屏需求时全国天气地图是出现频率很高的一个场景。项目标题里的三个关键词——ECharts、天气接口、全国天气地图——基本就是整个技术栈用ECharts加载中国地图从天气数据开放平台拉取实时天气再用 visualMap 把温度或天气状态映射成地图上不同区域的颜色。它解决的核心问题是让一堆枯燥的城市天气数据变成一眼能看懂的空间分布。这个内容适合两类人一类是第一次用 ECharts 画地图的新手想搞明白地图 series、GeoJSON、visualMap 这些概念另一类是已经会画静态地图、但没有接过真实天气数据的开发者想看看接口对接、城市名对齐这些坑怎么填。我尽量把每一步都讲得能直接抄作业也会把实操中踩过的坑一并列出来。1. 整体设计思路先想清楚再动手1.1 为什么选 ECharts 而不是其他地图方案大家做地图可视化一般有几个选择在百度地图、高德地图这类底图上画覆盖物用 Leaflet 等前端库加载瓦片或者用 ECharts 的 map 系列。这个项目的场景是“全国天气分布”并不需要地图交互比如搜索路线、定位导航核心诉求是把省市级区域按数值涂上颜色所以选 ECharts 最省事。它不需要申请地图服务的 JS Key不需要等待底图瓦片加载只要有一份 GeoJSON马上就能画区域。如果需求变成“一个城市一个点鼠标滑过去就显示当前气温”ECharts 同样能用 scatter / effectScatter 系列做叠加。整体而言在有现成地图数据的情况下ECharts 的视觉映射、tooltip、自适应能力都够用而且渲染结果是 Canvas放到大屏上缩放也不发虚。选型时别忘了考虑维护成本用 Leaflet 再加地图服务虽然能做的地理操作更多但复杂度高不少还要管理底图配额。天气地图这种场景ECharts 是性价比最高的选择。方案上手难度适合场景主要成本ECharts map低区域着色、热力分布、数据大屏需要准备 GeoJSON 并处理名称匹配地图开放服务的 JS 库中需要搜索、路线、真实定位需要申请服务 Key有调用配额Leaflet 瓦片底图中高自定义底图、大量点位、地理交互瓦片服务、图层管理、插件调试1.2 数据流从天气接口到地图着色的链路整体数据流可以用一句话概括天气接口返回城市天气列表前端按城市名把气温或天气现象提取出来再放到 ECharts map 系列的 data 数组里最后由 visualMap 控制每个区域的颜色。中间最麻烦的不是请求而是“城市名对齐”。因为天气接口返回的城市名、省份名和地图 GeoJSON 里每个区域的 name 字段并不天然一致。比如接口里可能写“内蒙古”而 GeoJSON 里写的是“内蒙古自治区”接口里写“北京”地图里写“北京市”。差一个字ECharts 就不会把数据挂到对应区域上最终出现“地图全是一个颜色或者某些区域没有数据”的诡异效果。所以在设计阶段就要确定展示粒度。全国天气地图一般有两种做法一种是省级展示把全省的天气聚合后给省份上色另一种是市级展示ECharts 加载更细粒度的中国市级地图数据让每个城市独立上色。省级的好处是数据少、上屏快、视觉干净适合展示整体气温分布市级信息量大但 GeoJSON 体积更大而且天气接口需要能覆盖全国主要城市。大多数场景都会先做省级版本后面再叠加市级散点做补充。我下面的实操也以省级为例子展开市级只是换一份 GeoJSON 和映射逻辑。1.3 地图底图数据GeoJSON 从哪找、怎么用ECharts 的地图依赖 GeoJSON。GeoJSON 是一种把地理区域描述成多边形坐标的 JSON 文件每个区域有一堆经纬度围成的边界同时还带 name 属性。中国地图的 GeoJSON 在网上能找到很多版本但要注意几点尽量选近几年更新的因为有些地区的行政名称会调整旧数据会出现缺区域、名称对不上等问题选每个区域 name 都是标准全称比如“北京市”“内蒙古自治区”的方便后续和天气接口做匹配最后确认区域覆盖要完整演示大屏上缺一块很难看。GeoJSON 的获取方式一般有三种第一从 ECharts 官方示例和地图资源里拿现成的第二从开源地理数据仓库下载很多项目提供在线服务可以直接取省市级 GeoJSON第三自己用行政区划数据生成这个一般没必要。我建议的做法是直接下载一份全国省级 GeoJSON放在项目的静态资源目录里通过 import 或异步加载使用。不要每次都去在线服务拉地图数据因为大屏场景对加载速度有要求离线静态资源更可靠。还要注意文件体积一张省级中国地图压缩后一般几十 KB 到几百 KB完全可接受。2. 天气接口选型与数据预处理2.1 免费天气接口怎么选看这四点天气数据源的选择直接决定后面的工作量。有几点要评估是否需要注册和密钥免费额度够不够返回字段是否包含城市名、气温、天气现象以及是否支持跨域请求。国内能用的天气开放平台不少有的属于地图服务商有的属于专业天气服务商还有聚合类数据接口。它们一般都提供按城市名或城市 ID 查实时天气的接口差别主要在于免费版请求频率、数据延时和城市覆盖范围。个人开发和中小项目建议优先选有免费额度、文档清晰的平台不要一上来就选企业版收费接口。还有一条思路如果项目本身就有后端可以调自己后端封装好的天气服务。这样做的好处是密钥不暴露在前端也可以在后端统一做数据缓存避免每次刷新页面都去打免费接口的额度。免费接口一般有每秒或每日请求数限制全国几十个省市区前端直接循环请求很容易撞限。所以我在实际项目里通常是后端定时抓一次全国天气数据缓存到内存或缓存服务里前端只请求这一个聚合接口。如果你只是做着玩前端直接请求也行但一定要控制刷新频率。2.2 请求与解析把接口数据洗成标准结构请求接口这步不难真正要处理的是字段差异。有的接口叫 temp有的叫 temperature有的返回 city有的返回 province 加 city有的 weather 字段给“晴”有的给 sunny。所以第一步是定义你自己内部的数据结构把接口返回统一清洗成{ name, value }。下面是一个参考写法// 假设天气接口返回结构 // { city: 北京市, temp: 26, weather: 晴, updateTime: 2025-01-06 12:00 } async function fetchWeather() { const resp await fetch(https://weather-api.example.com/api/weather/cities); const result await resp.json(); return result; } function formatWeatherData(rawList) { // 第一层过滤字段不完整的数据 return rawList .filter(item item.city item.temp ! null) .map(item ({ name: toMapName(item.city), value: item.temp })); }清洗时建议做三层处理第一层过滤掉字段不完整的数据比如没有城市名或没有温度的记录直接跳过第二层做城市名归一化把“北京市”“北京”统一成地图数据里的标准名第三层如果是省级上色还需要把市级数据聚合成省级数据。聚合规则要根据业务定义比如你想展示最高气温就取该省所有城市的最高值想展示平均气温就做简单平均想展示天气现象可以先统计该省出现的各种天气现象次数取出现最多的作为省级天气。2.3 城市名对齐默认会踩的大坑我第一次做天气地图时以为把天气数据丢进 ECharts 就完事了结果地图上只有零星几个省有颜色。排查了半天原因是接口返回的很多城市名与 GeoJSON 里的 name 不一致。例如 GeoJSON 里的“广西壮族自治区”“内蒙古自治区”“新疆维吾尔自治区”是完整名称而天气接口里可能只给“广西”“内蒙古”“新疆”GeoJSON 里的直辖市是“北京市”“上海市”而接口返回的是“北京市市辖区”之类的字段。这些差异会让 ECharts 找不到对应区域从而把数据丢掉。解决办法是做一个映射表加归一的函数const nameMap { 北京: 北京市, 上海: 上海市, 天津: 天津市, 重庆: 重庆市, 内蒙古: 内蒙古自治区, 广西: 广西壮族自治区, 西藏: 西藏自治区, 新疆: 新疆维吾尔自治区, 宁夏: 宁夏回族自治区 }; function toMapName(rawName) { if (nameMap[rawName]) return nameMap[rawName]; const cleaned rawName .replace(/维吾尔自治区|壮族自治区|回族自治区|特别行政区/g, ) .replace(/省|市/g, ); return nameMap[cleaned] || rawName; }这里给一个实操建议先把天气接口返回的不重复城市列表打印出来再和 GeoJSON 里的 name 数组做一次批量比对生成覆盖全部差异的映射表。如果发现有 GeoJSON 里没出现的城市名就继续补充映射。这一步虽然枯燥但做完之后后面所有渲染都会顺。最怕的是不建映射表直接用两个数据源硬拼上线后时不时冒出几个空白区域查起来很痛苦。3. ECharts 地图核心配置与可视化设计3.1 注册地图并跑通第一个地图实例ECharts 里使用地图的第一件事是注册import * as echarts from echarts; import chinaGeoJson from ./assets/china.json; echarts.registerMap(china, chinaGeoJson); const chartDom document.getElementById(weather-map); const chart echarts.init(chartDom); const optionBasic { series: [{ type: map, map: china, data: [] }] }; chart.setOption(optionBasic);registerMap 接收两个参数第一个是自定义的地图名称第二个是 GeoJSON 对象。只要完成了注册series 里的 map 字段就可以引用这个名称。注意 registerMap 只需要执行一次不要在每次渲染数据时重复注册否则有概率出现地图数据被覆盖、控制台刷一堆警告的问题。初始化图表时最好先设置一个只有区域轮廓的 option确认地图能显示出来再去接天气数据。这样排查问题时能快速分清是地图问题还是数据问题。// 确认底图正常后再设置完整 option chart.setOption({ tooltip: { ... }, visualMap: { ... }, series: [{ type: map, map: china, data: weatherData }] });建议先用一个写死的 data 测试地图上色比如给“北京市”“上海市”赋不同 value打开页面确认这两个省有颜色再进入真实数据逻辑。3.2 map 系列的 data 格式name 和 value 的对应关系ECharts map 系列的 data 要求是一个对象数组每个对象必须有 name 和 value。name 必须与 GeoJSON 中某个区域的 name 字段完全一致value 就是你希望映射的数据值。比如省级温度地图的 data 可以是const weatherData [ { name: 北京市, value: 26 }, { name: 上海市, value: 28 }, { name: 广东省, value: 30 } ];这里有一个隐藏逻辑data 数组顺序不影响显示ECharts 会根据 name 自动关联对应区域。这也意味着如果天气接口返回了某个城市但 GeoJSON 里没有它这个数据会被忽略反过来如果 GeoJSON 里某个省份没有出现在 data 里该省份会按照视觉映射里未定义数据的状态显示为浅色或灰色。两种都不是我们想要的。所以数据清洗阶段尽量保证“地图有的区域数据也都有”可以给一些极端偏远的无数据地区设置默认值或者用后面的进阶散点方案补充。3.3 visualMap让温度颜色说话如果展示温度这种连续变量用 continuous 类型最合适。min 和 max 要根据你当天全国气温的合理区间来定不要写死成固定的 -20 到 40最好根据接口返回的真实数据动态计算 min 和 max否则冬天数据全挤在蓝绿色区间看起来一点都不直观。inRange 里的颜色数组按“低值到高值”的顺序排列暖色表示高温冷色表示低温符合直觉。visualMap: { type: continuous, min: -20, max: 40, text: [气温高, 气温低], calculable: true, itemWidth: 16, itemHeight: 120, left: 30, bottom: 30, inRange: { color: [#2c7bb6, #abd9e9, #ffffbf, #fdae61, #d7191c] } }如果展示的是“晴/多云/雨/雪”这种天气状态连续型就不合适了要用 piecewise 分段。可以把天气现象先映射成固定数值编码比如晴1、多云2、阴3、雨4、雪5再用数值区间做分段并把 pieces 的 label 写成人能看懂的天气名。这样既保证颜色正确tooltip 里也好格式化。visualMap: { type: piecewise, pieces: [ { value: 1, label: 晴, color: #f39c12 }, { value: 2, label: 多云, color: #1e90ff }, { value: 3, label: 阴, color: #95a5a6 }, { value: 4, label: 雨, color: #2ecc71 }, { value: 5, label: 雪, color: #bdc3c7 } ] }注意pieces 的区间不要重叠否则一个值会匹配多个片段容易出现控制台警告和取色不稳定。另一个提醒是piecewise 默认按数值区间匹配如果直接塞字符串 data展示效果会很奇怪所以上面示意里我特意把天气现象转成了数字编码。实际项目中这个编码转换可以在数据清洗阶段完成。3.4 tooltip、label 与地图交互细节很多教程会默认开启 label 显示也就是把省份名称直接印在色块上。实际看下来省级地图的地名显示还算能接受市级地图全开 label 会非常乱尤其长三角、珠三角这些小城市挤在一起最后就是一团黑字。建议默认不显示 label鼠标滑过看 tooltip。如果想要地图上能看到地名可以把 label 放在高亮的时候显示。tooltip: { trigger: item, formatter: function(params) { if (params.value null) { return params.name 暂无天气数据; } return params.name params.value ℃; } }, label: { show: false }, emphasis: { label: { show: true, color: #333 }, itemStyle: { areaColor: #ffd591 } }代码里还做了一件事value 为 null 时提示“暂无天气数据”避免出现undefined之类的难看文案。emphasis 里可以单独设置 hover 状态的高亮色这样鼠标移上去能清楚看出是哪个省。如果后续想开始缩放平移可以在 series 里加roam: true但要留意 visualMap 不会跟着一起缩放布局上最好给地图留足够空间。3.5 数据自动刷新与防白屏写法天气数据应该定时刷新但要注意几个问题一是免费天气接口有请求频率限制前端不要每秒刷二是定时器里要考虑接口失败的情况如果某次请求失败不要覆盖已有数据三是刷新时 setOption 的写法。下面是一个比较稳妥的刷新写法async function refreshWeather() { try { const raw await fetchWeather(); const data formatWeatherData(raw); const temps data.map(item item.value).filter(v v ! null); chart.setOption({ visualMap: { min: Math.min(...temps), max: Math.max(...temps) }, series: [{ data: data }] }); } catch (e) { console.warn(天气数据拉取失败保留上次数据, e); } } setInterval(refreshWeather, 5 * 60 * 1000);setOption 默认是合并模式也就是新配置会和旧配置自动合并一般不需要担心会把地图配置清掉。但如果你的 visualMap 类型在切换比如连续型和分段型切换合并模式会有残留项这种情况建议用chart.setOption(newOption, true)第二个参数表示完全替换不要让旧的 visualMap 残留影响新样式。另外页面放在大屏上时睡眠唤醒后图表容器尺寸可能变化记得监听窗口尺寸变化window.addEventListener(resize, () chart.resize());这个也建议做成防抖版本否则拖拽窗口时会频繁触发。4. 实操过程与踩坑记录4.1 建议按这个顺序跑通最小 Demo整个流程里最忌讳一上来就写很完整的逻辑。很多朋友把地图、接口、视觉映射全堆上去结果出了错不知道是地图注册失败、数据格式不对还是样式配置写错。我的建议是严格按下面顺序来准备一份全国省级 GeoJSON放到项目的静态目录。用echarts.registerMap注册地图。写一个固定的 optionseries 里只放一条 map 数据data 写死两个省份打开页面确认这两个省有颜色。接入天气接口把返回数据清洗成[{ name, value }]格式setOption 更新。调整 visualMap 和 tooltip把样式做完整。加上定时刷新和容错处理。第 3 步最关键。先跑通最小闭环后面每一步出错都能马上定位。我在实际做这类小项目时习惯先做“画板测试”也就是固定给几个省赋值不同温度验证地图能按颜色区分再接真实接口。这样一旦颜色不对就能判断是 visualMap 的问题还是真实数据的问题。4.2 地图上色失败的两种典型现象地图上部分区域不显示数据最常见原因就是 name 对不上。处理办法前面讲过这里再强调一个技巧在数据清洗阶段输出一份“天气接口城市名”和“地图 name 列表”的差异把多出来的、少掉的都打出来比对着建映射。这一步是体力活但做一次就够。直接在地图初始化后打印 GeoJSON 里的 name 也是常用办法因为 ECharts 注册后的地图数据可以通过调试工具查看。另一个现象是地图能渲染但整个地图都是同一种浅色好像数据没生效。这种情况大概率不是 name 的问题而是 visualMap 的 min 和 max 与数据范围不匹配。比如真实温度都在 25-35 之间你把 min 设为 -20、max 设为 40那所有省份的颜色都会被压到同一个水平上肉眼看起来就是没有区分。动态根据数据调整 min 和 max 就能解决。如果你用的是 weather 字符串分段映射那还要确认编码映射有没有做或者 pieces 的 value 是否和 data 的 value 类型一致。4.3 跨域、刷新与白屏的排查思路浏览器直接请求天气接口经常遇到跨域问题页面提示拿不到数据。解决办法有几个思路一是换一个支持跨域的接口或数据服务二是在前端开发服务器里配置转发把/weather开头的请求转发到真实天气服务地址三是写一个简单的后端中转由后端请求天气接口前端只请求自己的后端接口。第三种最推荐因为可以把免费接口的密钥安全地留在服务端还能做数据缓存。项目上线后前端访问的是静态资源或自己的服务器与第三方数据源之间的跨域问题必须在服务端或中转层解决不能只依赖开发阶段的转发配置。定时刷新白屏或者页面空白多数和定时器有关常见原因包括接口返回的 JSON 结构变了导致格式化函数报错页面逻辑中断或者使用chart.setOption时传入了错误的 data把地图撑坏了。建议在refreshWeather里全程 try/catch任何异常都 catch 住并打印不要让它冒泡到全局。还可以在刷新之前用 console.log 打印本次数据条数与城市列表快速判断是接口返回问题还是处理问题。4.4 常见问题排查速查表现象可能原因处理办法控制台报 Map china not existsregisterMap 未执行或 GeoJSON 加载失败确认地图文件加载完成再 setOption地图正常但只有几个省有颜色天气接口城市名与 GeoJSON 的 name 不一致用映射表统一城市名批量打印比对差异地图全是一种浅色数值分布没体现visualMap 的 min/max 范围过大按数据实际范围动态计算 min/max浏览器提示跨域天气接口限制浏览器跨域后端中转或开发服务器配置转发定时刷新后页面白屏刷新逻辑异常未捕获定时器回调全程 try/catch接口异常时保留旧数据显示出来的省名是英文或拼音GeoJSON 里 name 是拼音换用中文 name 的 GeoJSON或用 nameMap 转换label 显示太密集看不清市级地图全开 labellabel 默认关闭高亮时再显示这张表我建议截图存一份。后面如果做百度地图、高德地图这类区域可视化项目遇到的问题基本都是同一个模型数据名称对齐、数据范围、加载顺序、跨域把这四个方向检查一遍80% 的地图可视化问题都能定位。4.5 进阶操作从 Demo 变成可展示的产品如果只是地图上色两小时就能做完但要看起来像能上线的大屏还需要几个增强。第一叠加一层散点展示重点城市的实时天气。做法是在 map 系列之外再加一个 scatter 系列给它设置坐标系统用经纬度定位城市再让点大小和温度挂钩。示意代码如下series: [ { type: map, map: china, data: provinceData }, { type: scatter, coordinateSystem: geo, data: cityPoints, // [[经度, 纬度, 温度], ...] symbolSize: function(val) { return Math.max(4, val[2] / 5); }, itemStyle: { color: #fff } } ]这里 cityPoints 的第三个元素是温度点位会随温度变大变亮。不过要注意scatter 叠加需要地图配合 geo 坐标系如果直接用 series-map要把地图同时放到 geo 组件里或者使用 ECharts 的 geo 组件配置。这个配置比纯 map 稍微绕一点建议在官方示例里找地图散点图的例子对照着做。第二做一个右侧的城市榜单或底部的气温曲线和地图联动。比如鼠标滑过某个省时旁边列表显示该省天气。可以用 ECharts 的事件或 ZRender 事件做联动但更简单的是先用 tooltip 做比较细致的展示减少联动逻辑。对于大屏项目前端布局很讲究建议给地图留至少一半以上面积颜色分配要让用户第一眼看出“哪里热、哪里冷”其他文字信息全部弱化。第三如果公司有统一的暗色大屏主题可以把图表背景色、visualMap 颜色、tooltip 背景都统一成同一套深色文字用亮色地图边框改成暗灰色视觉效果会好很多。深色底上别用太深的蓝色否则区域边界会看不清。最后分享一个体会。做天气地图这类项目技术难度不算高真正折磨人的是数据对齐。天气接口、地图数据、字段命名三套东西各有各的脾气地图和接口数据一旦对不上排查起来很花时间。我的习惯是先把两边数据都打印出来做成映射表再写渲染逻辑。这套做法不止用在天气地图上任何涉及“外部数据 地图可视化”的项目都能复用。最后一个小技巧别把天气接口的密钥直接写在前端页面里额度很快就会被刷爆让后端做一次中转和缓存前面所有麻烦都会少一半。如果你能顺利跑通省级地图下一步可以挑战把 GeoJSON 换成市级版把散点层加进去再接一条气温变化曲线做出来的东西会比想象中更漂亮。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。