面向气象行业高级定制 Demo:类面向对象封装、远程气象 API 解析、自定义 SVG 图标绘制

发布时间:2026/10/30 3:44:09

面向气象行业高级定制 Demo:类面向对象封装、远程气象 API 解析、自定义 SVG 图标绘制 本气象预报图是 Highcharts 高级扩展能力的标杆 Demo充分证明其不局限于基础折线柱状可深度行业定制复杂复合型可视化解决气象系统多指标、多刻度、自定义图形绘制的开发难点。关于案例这是一个如何设置Highcharts图表的复杂演示动态源并通过绘制图像精灵、风箭头路径进行扩展在图表顶部添加第二个网格。演示的目的是启发开发人员可以超越基本的图表类型并展示该库的功能以编程方式进行扩展。这是演示程序所做的-以JSON服务的形式从www.yr.no加载天气预报。-当数据异步到达时创建一个Meteogram实例。我们有 创建Meteogram原型以提供有组织的结构与演示相关的不同方法和子程序。- parseYrData方法将www.yr.no中的数据解析为几个并行的 数组。这些阵列直接用作温度的数据选项降水和气压。-在此之后将构建options结构并生成图表解析后的数据。-在图表加载时天气图标和风向箭头的框架是使用自定义逻辑渲染。Highcharts 图表 简要Highcharts 提供完整扩展体系支撑行业级气象复合图原生多独立 Y 轴、windbarb 风羽专用系列、chart.renderer 自由手绘 SVG 图形、自定义图案填充、面向对象封装灵活解耦支持远程 AJAX 时序数据接入可一站式整合气温、降水、气压、风力、天气图标多维度气象指标是气象时序可视化最优前端方案。图表定位Highcharts气象图 这是一套高度行业定制化气象预报时序图并非基础图表核心亮点面向气象 API 做面向对象封装Meteogram类统一管理数据解析、配置生成、后置渲染多独立 Y 轴共存气温、降水量、海平面气压三套独立刻度5 种系列同画布气温平滑曲线、降水柱状、误差填充柱、气压点虚线、风羽风向风力箭头双层 X 时间轴小时细刻度 上层日期星期利用chart.renderer自定义绘制天气 SVG 图标、风羽分隔刻度线、区间边框远程 AJAX 拉取挪威气象 API 实时预报数据自动解析多时段预报内置天气编码字典映射几十种晴 / 雨 / 雪 / 雷 / 雾图标与提示文案支持横向滚动长时序、图案填充降水误差区间、正负气温分色曲线。完整JS示例代码/** * This is a complex demo of how to set up a Highcharts chart, coupled to a * dynamic source and extended by drawing image sprites, wind arrow paths * and a second grid on top of the chart. The purpose of the demo is to inpire * developers to go beyond the basic chart types and show how the library can * be extended programmatically. This is what the demo does: * * - Loads weather forecast from www.yr.no in form of a JSON service. * - When the data arrives async, a Meteogram instance is created. We have * created the Meteogram prototype to provide an organized structure of the * different methods and subroutines associated with the demo. * - The parseYrData method parses the data from www.yr.no into several parallel * arrays. These arrays are used directly as the data option for temperature, * precipitation and air pressure. * - After this, the options structure is built, and the chart generated with * the parsed data. * - On chart load, weather icons and the frames for the wind arrows are * rendered using custom logic. */ function Meteogram(json, container) { // Parallel arrays for the chart data, these are populated as the JSON file // is loaded this.symbols []; this.precipitations []; this.precipitationsError []; // Only for some data sets this.winds []; this.temperatures []; this.pressures []; // Initialize this.json json; this.container container; // Run this.parseYrData(); } /** * Mapping of the symbol code in yr.nos API to the icons in their public * GitHub repo, as well as the text used in the tooltip. * * https://api.met.no/weatherapi/weathericon/2.0/documentation */ Meteogram.dictionary { clearsky: { symbol: 01, text: Clear sky }, fair: { symbol: 02, text: Fair }, partlycloudy: { symbol: 03, text: Partly cloudy }, cloudy: { symbol: 04, text: Cloudy }, lightrainshowers: { symbol: 40, text: Light rain showers }, rainshowers: { symbol: 05, text: Rain showers }, heavyrainshowers: { symbol: 41, text: Heavy rain showers }, lightrainshowersandthunder: { symbol: 24, text: Light rain showers and thunder }, rainshowersandthunder: { symbol: 06, text: Rain showers and thunder }, heavyrainshowersandthunder: { symbol: 25, text: Heavy rain showers and thunder }, lightsleetshowers: { symbol: 42, text: Light sleet showers }, sleetshowers: { symbol: 07, text: Sleet showers }, heavysleetshowers: { symbol: 43, text: Heavy sleet showers }, lightsleetshowersandthunder: { symbol: 26, text: Light sleet showers and thunder }, sleetshowersandthunder: { symbol: 20, text: Sleet showers and thunder }, heavysleetshowersandthunder: { symbol: 27, text: Heavy sleet showers and thunder }, lightsnowshowers: { symbol: 44, text: Light snow showers }, snowshowers: { symbol: 08, text: Snow showers }, heavysnowshowers: { symbol: 45, text: Heavy show showers }, lightsnowshowersandthunder: { symbol: 28, text: Light snow showers and thunder }, snowshowersandthunder: { symbol: 21, text: Snow showers and thunder }, heavysnowshowersandthunder: { symbol: 29, text: Heavy snow showers and thunder }, lightrain: { symbol: 46, text: Light rain }, rain: { symbol: 09, text: Rain }, heavyrain: { symbol: 10, text: Heavy rain }, lightrainandthunder: { symbol: 30, text: Light rain and thunder }, rainandthunder: { symbol: 22, text: Rain and thunder }, heavyrainandthunder: { symbol: 11, text: Heavy rain and thunder }, lightsleet: { symbol: 47, text: Light sleet }, sleet: { symbol: 12, text: Sleet }, heavysleet: { symbol: 48, text: Heavy sleet }, lightsleetandthunder: { symbol: 31, text: Light sleet and thunder }, sleetandthunder: { symbol: 23, text: Sleet and thunder }, heavysleetandthunder: { symbol: 32, text: Heavy sleet and thunder }, lightsnow: { symbol: 49, text: Light snow }, snow: { symbol: 13, text: Snow }, heavysnow: { symbol: 50, text: Heavy snow }, lightsnowandthunder: { symbol: 33, text: Light snow and thunder }, snowandthunder: { symbol: 14, text: Snow and thunder }, heavysnowandthunder: { symbol: 34, text: Heavy snow and thunder }, fog: { symbol: 15, text: Fog } }; /** * Draw the weather symbols on top of the temperature series. The symbols are * fetched from yr.nos MIT licensed weather symbol collection. * https://github.com/YR/weather-symbols */ Meteogram.prototype.drawWeatherSymbols function (chart) { chart.series[0].data.forEach((point, i) { if (this.resolution 36e5 || i % 2 0) { const [symbol, specifier] this.symbols[i].split(_), icon Meteogram.dictionary[symbol].symbol ({ day: d, night: n }[specifier] || ); if (Meteogram.dictionary[symbol]) { chart.renderer .image( https://cdn.jsdelivr.net/gh/nrkno/yr-weather-symbols 8.0.1/dist/svg/${icon}.svg, point.plotX chart.plotLeft - 8, point.plotY chart.plotTop - 30, 30, 30 ) .attr({ zIndex: 5 }) .add(); } else { console.log(symbol); } } }); }; /** * Draw blocks around wind arrows, below the plot area */ Meteogram.prototype.drawBlocksForWindArrows function (chart) { const xAxis chart.xAxis[0]; for ( let pos xAxis.min, max xAxis.max, i 0; pos max 36e5; pos 36e5, i 1 ) { // Get the X position const isLast pos max 36e5, x Math.round(xAxis.toPixels(pos)) (isLast ? 0.5 : -0.5); // Draw the vertical dividers and ticks const isLong this.resolution 36e5 ? pos % this.resolution 0 : i % 2 0; chart.renderer .path([ M, x, chart.plotTop chart.plotHeight (isLong ? 0 : 28), L, x, chart.plotTop chart.plotHeight 32, Z ]) .attr({ stroke: chart.options.chart.plotBorderColor, stroke-width: 1 }) .add(); } // Center items in block chart.get(windbarbs).markerGroup.attr({ translateX: chart.get(windbarbs).markerGroup.translateX 8 }); }; /** * Build and return the Highcharts options structure */ Meteogram.prototype.getChartOptions function () { return { chart: { renderTo: this.container, marginBottom: 70, marginRight: 40, marginTop: 50, plotBorderWidth: 1, height: 310, alignTicks: false, scrollablePlotArea: { minWidth: 720 } }, defs: { patterns: [{ id: precipitation-error, path: { d: [ M, 3.3, 0, L, -6.7, 10, M, 6.7, 0, L, -3.3, 10, M, 10, 0, L, 0, 10, M, 13.3, 0, L, 3.3, 10, M, 16.7, 0, L, 6.7, 10 ].join( ), stroke: #68CFE8, strokeWidth: 1 } }] }, title: { text: Meteogram for London, England, align: left, style: { whiteSpace: nowrap, textOverflow: ellipsis } }, credits: { text: Forecast from a hrefhttps://yr.noyr.no/a, href: https://yr.no, position: { x: -40 } }, tooltip: { shared: true, headerFormat: small{point.x:%A, %b %e, %H:%M} - {point.point.to:%H:%M}/smallbr b{point.point.symbolName}/bbr }, xAxis: [{ // Bottom X axis type: datetime, tickInterval: 2 * 36e5, // two hours minorTickInterval: 36e5, // one hour tickLength: 0, gridLineWidth: 1, gridLineColor: rgba(128, 128, 128, 0.1), startOnTick: false, endOnTick: false, minPadding: 0, maxPadding: 0, offset: 30, showLastLabel: true, labels: { format: {value:%H} }, crosshair: true }, { // Top X axis linkedTo: 0, type: datetime, tickInterval: 24 * 3600 * 1000, labels: { format: {value:span stylefont-size: 12px; font-weight: bold%a/span %b %e}, align: left, x: 3, y: 8 }, opposite: true, tickLength: 20, gridLineWidth: 1 }], yAxis: [{ // temperature axis title: { text: null }, labels: { format: {value}°, style: { fontSize: 10px }, x: -3 }, plotLines: [{ // zero plane value: 0, color: var(--highcharts-neutral-color-20, #bbb), width: 1, zIndex: 2 }], maxPadding: 0.3, minRange: 8, tickInterval: 1, gridLineColor: rgba(128, 128, 128, 0.1) }, { // precipitation axis title: { text: null }, labels: { enabled: false }, gridLineWidth: 0, tickLength: 0, minRange: 10, min: 0 }, { // Air pressure allowDecimals: false, title: { // Title on top of axis text: hPa, offset: 0, align: high, rotation: 0, style: { fontSize: 10px, color: Highcharts.getOptions().colors[2] }, textAlign: left, x: 3 }, labels: { style: { fontSize: 8px, color: Highcharts.getOptions().colors[2] }, y: 2, x: 3 }, gridLineWidth: 0, opposite: true, showLastLabel: false }], legend: { enabled: false }, plotOptions: { series: { pointPlacement: between } }, series: [{ name: Temperature, data: this.temperatures, type: spline, marker: { enabled: false, states: { hover: { enabled: true } } }, tooltip: { pointFormat: span stylecolor:{point.color}\u25CF/span {series.name}: b{point.y}°C/bbr/ }, zIndex: 1, color: #FF3333, negativeColor: #48AFE8 }, { name: Precipitation, data: this.precipitationsError, type: column, color: url(#precipitation-error), yAxis: 1, groupPadding: 0, pointPadding: 0, tooltip: { valueSuffix: mm, pointFormat: span stylecolor:{point.color}\u25CF/span {series.name}: b{point.minvalue} mm - {point.maxvalue} mm/bbr/ }, grouping: false, dataLabels: { enabled: this.hasPrecipitationError, filter: { operator: , property: maxValue, value: 0 }, style: { fontSize: 8px, color: var(--highcharts-neutral-color-60, gray) } } }, { name: Precipitation, data: this.precipitations, type: column, color: #68CFE8, yAxis: 1, groupPadding: 0, pointPadding: 0, grouping: false, dataLabels: { enabled: !this.hasPrecipitationError, filter: { operator: , property: y, value: 0 }, style: { fontSize: 8px, color: var(--highcharts-neutral-color-60, gray) } }, tooltip: { valueSuffix: mm } }, { name: Air pressure, color: Highcharts.getOptions().colors[2], data: this.pressures, marker: { enabled: false }, shadow: false, tooltip: { valueSuffix: hPa }, dashStyle: shortdot, yAxis: 2 }, { name: Wind, type: windbarb, id: windbarbs, color: Highcharts.getOptions().colors[1], lineWidth: 1.5, data: this.winds, vectorLength: 18, yOffset: -15, tooltip: { valueSuffix: m/s } }] }; }; /** * Post-process the chart from the callback function, the second argument * Highcharts.Chart. */ Meteogram.prototype.onChartLoad function (chart) { this.drawWeatherSymbols(chart); this.drawBlocksForWindArrows(chart); }; /** * Create the chart. This function is called async when the data file is loaded * and parsed. */ Meteogram.prototype.createChart function () { this.chart new Highcharts.Chart(this.getChartOptions(), chart { this.onChartLoad(chart); }); }; Meteogram.prototype.error function () { document.getElementById(loading).innerHTML i classfa fa-frown-o/i Failed loading data, please try again later; }; /** * Handle the data. This part of the code is not Highcharts specific, but deals * with yr.nos specific data format */ Meteogram.prototype.parseYrData function () { let pointStart; if (!this.json) { return this.error(); } // Loop over hourly (or 6-hourly) forecasts this.json.properties.timeseries.forEach((node, i) { const x Date.parse(node.time), nextHours node.data.next_1_hours || node.data.next_6_hours, symbolCode nextHours nextHours.summary.symbol_code, to node.data.next_1_hours ? x 36e5 : x 6 * 36e5; if (to pointStart 48 * 36e5) { return; } // Populate the parallel arrays this.symbols.push(nextHours.summary.symbol_code); this.temperatures.push({ x, y: node.data.instant.details.air_temperature, // custom options used in the tooltip formatter to, symbolName: Meteogram.dictionary[ symbolCode.replace(/_(day|night)$/, ) ].text }); this.precipitations.push({ x, y: nextHours.details.precipitation_amount }); if (i % 2 0) { this.winds.push({ x, value: node.data.instant.details.wind_speed, direction: node.data.instant.details.wind_from_direction }); } this.pressures.push({ x, y: node.data.instant.details.air_pressure_at_sea_level }); if (i 0) { pointStart (x to) / 2; } }); // Create the chart when the data is loaded this.createChart(); }; // End of the Meteogram protype // On DOM ready... // Set the hash to the yr.no URL we want to parse if (!location.hash) { location.hash https://api.met.no/weatherapi/locationforecast/2.0/compact?lat51.50853lon-0.12574altitude25; } const url location.hash.substr(1); Highcharts.ajax({ url, dataType: json, success: json { window.meteogram new Meteogram(json, container); }, error: Meteogram.prototype.error, headers: { // Override the Content-Type to avoid preflight problems with CORS // in the Highcharts demos Content-Type: text/plain } });核心代码分层拆解1Meteogram 封装类架构数据存储数组气温、降水、误差降水、风力、气压、天气编码静态字典dictionary气象编码→图标文件名 中文 / 英文描述核心方法parseYrData解析气象 API JSON批量组装时序点位数据getChartOptions动态生成完整 Highcharts 多轴配置drawWeatherSymbols渲染顶部天气 SVG 图标drawBlocksForWindArrows绘制底部风羽分隔竖线createChart实例化图表onChartLoad图表渲染完成后执行自定义绘图。2多轴设计yAxis [0] 气温红色高温 / 蓝色低温0 度基准参考线yAxis [1] 降水柱状独立 0 起刻度yAxis [2] 海平面气压右侧虚线曲线独立单位 hPa。35 大数据系列spline 气温曲线column 降水误差区间图案填充column 标准降水量蓝色柱shortdash 气压时序windbarb 风羽系列自带风向角度、风速数值。4自定义渲染扩展Highcharts 核心扩展能力体现renderer.image远程加载官方天气 SVG 图标悬浮 Z 轴置顶renderer.path手绘底部时间分隔竖线用于风羽区块对齐自定义 SVG 图案defs.patterns实现降水误差斜纹填充图表加载完成回调二次绘图不侵入原生系列逻辑。5数据层Highcharts.ajax跨域请求气象开放 API自动读取 URL 哈希切换城市经纬度预报逐小时 / 6 小时预报自动截断 48 小时短期预报点位携带自定义拓展字段时段结束时间、天气文字说明。图表类型多轴复合型气象时序预报图Meteogram融合 spline /column/windbarb / 自定义渲染多独立 Y 轴、双层时间轴、自定义矢量绘图属于行业深度定制高级复合图表。核心业务功能48 小时精细化逐时段气象预报自动截断数据只展示 2 天短期预报支持 1 小时 / 6 小时两种预报粒度。多气象指标一体化展示气温曲线红色高温、蓝色零下低温0℃基准线直观区分冷暖降水柱状蓝色柱代表标准降水量斜纹填充柱代表降水预估误差区间海平面气压右侧虚线时序单位百帕风羽标记底部风羽箭头自动匹配风向、风速数值带分隔刻度。可视化气象状态图标 顶部自动渲染晴 / 雨 / 雪 / 雷 / 雾 SVG 图标日夜两套图标自动匹配时段。双层时间标尺 上层日期 星期宏观刻度下层24 小时小时细刻度兼顾全局与精细查看。超长时序横向滚动scrollablePlotArea窄屏自动生成横向滚动条完整展示两天连续预报。统一悬浮提示框 鼠标悬浮同时展示时段、天气状况、气温、降水、气压、风速多维度信息。适用行业场景气象监测平台、气象预报 Web 端、气象大屏风电、光伏场站气象工况预报看板景区、交通物流气象预警系统农业种植气象时序分析城市环境监测综合预报页面。
延伸阅读

更多相关文章

2026/10/30 3:42:53

小程序毕业设计-基于 SpringBoot + 微信小程序的美好食荐美食推荐系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/10/29 6:48:48

auto_py2to3路线图:未来功能规划与Python版本支持展望

auto_py2to3路线图:未来功能规划与Python版本支持展望 【免费下载链接】auto_py2to3 The ultimate purpose of the library is to enable automated and rapid code migration and provide automated testing. 项目地址: https://gitcode.com/openeuler/auto_py2t…

2026/10/30 3:41:02

USB蓝牙适配器音频断续的根因与系统级优化方案

1. 问题本质与真实场景还原USB蓝牙适配器播放音频不连续——这个标题背后藏着的不是一句简单的“卡顿”,而是一类在日常办公、居家学习、远程会议甚至车载娱乐中高频出现、却长期被误判为“设备质量差”或“系统不行”的典型链路故障。我接触过大量实际案例&#xf…

2026/10/30 3:41:02

台达DOPSoft 4.00.03安装与HMI工程实战指南

简介:本资源为台达全系列触摸屏专用编程软件DOPSoft安装包及配套文档,面向工业自动化工程师、PLC与HMI初学者及产线设备维护人员,解决台达人机界面程序开发、调试与兼容性适配等核心问题。压缩包共3个文件:主程序安装文件&#xf…

2026/10/30 3:41:02

Python实战:从城市边界数据推导农村边界的方法与避坑指南

城市边界和农村边界这两个概念,在地理信息处理里经常被混着用,但真正落到数据层面,它们完全是两套逻辑。我最近在做一个区域统计分析的小项目,需要把某片城区的周边农村范围圈出来,手头正好有一份全球城市边界数据集&a…

2026/10/30 3:41:02

Windows下DLL加载失败(ERROR_MOD_NOT_FOUND)排查指南

1. 问题现象与初步分析当你在Windows平台上调用LoadLibraryA("F:\\svnroot\\mccv.dll")时返回NULL,并且GetLastError()返回错误码126,这是一个典型的动态链接库加载失败场景。错误码126对应的是"ERROR_MOD_NOT_FOUND",即…

2026/10/30 3:41:02

扩散模型如何提升端到端自动驾驶的轨迹规划能力

1. 扩散模型在端到端自动驾驶中的潜力解析自动驾驶领域正在经历一场由深度学习驱动的革命,而扩散模型(Diffusion Models)作为生成式AI的最新代表,正在为轨迹规划带来范式转变。传统自动驾驶系统通常采用模块化架构,将感…

2026/10/30 3:36:01

NANDA:构建去中心化AI智能体网络的技术架构与应用

1. 项目概述:NANDA与去中心化AI的崛起NANDA(Networked Agents and Decentralized AI)是MIT媒体实验室Camera Culture团队主导的前沿研究项目,由Ramesh Raskar教授担任首席研究员。这个项目正在构建一个被称为"Agentic Web&qu…

2026/10/29 17:05:52

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/29 17:06:02

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/29 17:06:13

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/30 0:00:46

NCP1342高频准谐振反激控制器:从设计约束到验收的完整方案

简介:NCP1342 高频准谐振反激控制器应用介绍译文文档,面向电源设计工程师、硬件研发人员及开关电源学习者,帮助读者跨越英文原版资料的语言门槛,系统理解这款高集成度 QR Flyback 控制器的设计要点。文档围绕芯片核心特性展开&…

2026/10/30 0:00:46

NCP1342准谐振反激电源设计:从选型到验收的全流程解析

简介:这份文档面向电源设计工程师与开关电源学习者,系统梳理NCP1342高频准谐振反激控制器的应用要点,帮助读者快速理解这款高集成度离线电源变换器控制芯片的工作机制与设计方法。内容涵盖芯片核心特性、引脚功能定义、电气特性参数表、最大额…

2026/10/30 0:00:46

Web系统自动登出机制设计与实现全解析

1. 系统自动登出机制的设计初衷在各类Web应用和后台管理系统中,自动登出功能早已成为标配。这个看似简单的功能背后,实际上涉及用户安全、资源管理和体验优化的多重考量。作为从业十多年的老开发,我见过太多因为登出逻辑设计不当导致的安全事…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑