Highcharts Dashboards技巧|实时气象仪表盘 Demo 完整代码

Highcharts Dashboards技巧|实时气象仪表盘 Demo 完整代码 Highcharts Dashboards 实时气象演示仪表盘是一套多组件联动、地图 数据 图表 KPI 一体化的实战型可视化方案基于挪威气象研究所真实预报接口打造可直接用于气象监控、物联网大屏、环境监测、运维指挥中心等场景。Highcharts.setOptions({ chart: { styledMode: true }, lang: { locale: en-us } }); // Index of the data to be displayed in map, KPI and spline chart. // The number is an offset from the current hour. const rangeConfig { first: 0, // 0: current observation hours: 24 // max: 19 days }; // The current date is used in several charts const currentDay Highcharts.dateFormat(%B %e, %Y, Date.now()); // Windbarb series object (deleted when not showing wind) let windbarbSeries null; // Weather parameter configuration const paramConfig { temperature: { name: temperature, descr: Temperature, unit: ˚C, min: -20, max: 40, floatRes: 1, chartType: spline, colorStops: [ [0.0, #4CAFFE], [0.3, #53BB6C], [0.5, #DDCE16], [0.6, #DF7642], [0.7, #DD2323] ] }, wind: { name: wind, descr: Wind speed, unit: m/s, min: 0, max: 30, // Hurricane 32.7 floatRes: 1, chartType: area, colorStops: [ [0.0, #C0CCC0], [0.25, #CCCC99], [0.50, #99CC66], [0.75, #336633] ] }, windDir: { // Used by Windbarb and grid only name: windDir, descr: Wind dir., unit: degr. }, precipitation: { name: precipitation, descr: Precipitation, unit: mm, min: 0, max: 10, // per hour floatRes: 1, chartType: column, colorStops: [ [0.0, #CCCCCC], [0.1, #CCCCAA], [0.2, #9999AA], [0.8, #0000CC] ] }, getColumnHeader: function (param, unit true) { const info this[param]; // First letter uppercase const name info.descr; if (unit) { return ${name} ${info.unit}; } return name; } }; // Geolocation info: https://www.maps.ie/coordinates.html // Selection of weather stations to display data from const locations { mapUrl: https://code.highcharts.com/mapdata/custom/north-america-no-central.topo.json, default: New York, points: [ [station, lat, lon, elevation, timeDifference], [New York, 40.71, -74.01, 10, 5], [San Diego, 32.71, -117.16, 36, 8], [Anchorage, 61.22, -149.89, 0, 9], [Winnipeg, 49.90, -97.14, 236, 6], [Monterrey, 25.68, -99.13, 540, 6], [Baracoa, 20.35, -74.50, 15, 5] ] }; function getTimeDifference(station) { const location locations.points.find(point point[0] station); return location ? location[4] : 0; // Default to 0 if station not found } // Application configuration (weather station data set data provider) const weatherStationConfig { location: locations, // Weather station data set // Base URL for weather forecast baseUrl: https://api.met.no/weatherapi/locationforecast/2.0/compact, // Build the full URL for accessing the data buildUrl: function (station) { for (let i 1; i this.location.points.length; i) { const point this.location.points[i]; if (point[0] station) { return this.baseUrl ?lat${point[1]}lon${point[2]}altitude${point[3]}; } } return null; } }; // Convert a cell value to a number. function convertToNumber(value, useNaN) { switch (typeof value) { case boolean: return value ? 1 : 0; case number: return (isNaN(value) !useNaN ? null : value); default: value parseFloat(${value ?? }); return (isNaN(value) !useNaN ? null : value); } } // Retrieve a connector data table. async function getConnectorTable(dataPool, connectorId) { return dataPool .getConnector(connectorId) .then(connector connector.getTable()); } // Common options for KPI charts // - Adapted from https://www.highcharts.com/demo/dashboards/climate const kpiGaugeOptions { chart: { spacing: [8, 8, 8, 8], type: solidgauge }, pane: { background: { innerRadius: 90%, outerRadius: 120%, shape: arc }, center: [50%, 70%], endAngle: 90, startAngle: -90 }, yAxis: { labels: { distance: 105%, y: 5, align: auto }, lineWidth: 2, minorTicks: false, tickWidth: 2, tickAmount: 2, visible: true }, series: [{ data: null, // Populated later dataLabels: { format: {y:.1f}, y: -25 }, enableMouseTracking: false, innerRadius: 90%, radius: 120%, clip: false }], accessibility: { typeDescription: The gauge chart with 1 data point. } }; // Launches the Dashboards application async function setupDashboard() { let activeStation weatherStationConfig.location.default; let activeParam paramConfig.temperature; const board await Dashboards.board(container, { dataPool: { connectors: [ { id: Stations, type: JSON, firstRowAsNames: true, data: weatherStationConfig.location.points } ] }, // GUI based on https://www.highcharts.com/demo/dashboards/climate gui: { layouts: [{ rows: [{ cells: [{ // Top left id: station-map }, { // Top right id: kpi-layout, layout: { rows: [{ cells: [{ id: html-geo-info }] }, { cells: [{ id: kpi-temperature }, { id: kpi-wind }, { id: kpi-precipitation }] }] } }] }, { cells: [{ // Bottom left id: forecast-grid }, { // Bottom right id: forecast-chart }] }] }] }, // Adapted from https://www.highcharts.com/demo/dashboards/climate components: [ { renderTo: station-map, type: Highcharts, chartConstructor: mapChart, chartOptions: { chart: { map: await fetch(weatherStationConfig.location.mapUrl) .then(response response.json()) }, title: { text: paramConfig.getColumnHeader(temperature) }, colorAxis: { startOnTick: false, endOnTick: false, max: activeParam.max, min: activeParam.min, stops: activeParam.colorStops }, legend: { enabled: false }, mapNavigation: { buttonOptions: { verticalAlign: bottom }, enabled: true, enableMouseWheelZoom: true }, mapView: { maxZoom: 4 }, series: [{ type: map, name: Weather Station Map }, { type: mappoint, name: Stations, data: [], allowPointSelect: true, dataLabels: [{ align: left, crop: false, enabled: true, format: {point.name}, padding: 0, verticalAlign: top, x: -2, y: 2 }, { crop: false, enabled: true, format: {point.y:.0f}, inside: true, padding: 0, verticalAlign: bottom, y: -16 }], events: { click: function (e) { const station e.point.name; if (station ! activeStation) { // New station selected activeStation station; updateBoard( board, activeStation, activeParam.name, false, // Parameter unchanged true // Station changed ); } else { // Re-select (otherwise marker is reset) setActiveStation(); } } }, marker: { enabled: true, lineWidth: 2, radius: 12, symbol: mapmarker, states: { select: { radiusPlus: 4 } } }, tooltip: { footerFormat: , headerFormat: , pointFormat: ( b{point.name}/bbr Elevation: {point.custom.elevation} mbr {point.y:.1f} {point.custom.unit} ) } }], tooltip: { shape: rect, distance: -60, useHTML: true, stickOnContact: true }, lang: { accessibility: { chartContainerLabel: Weather stations. Highcharts Interactive Map. } }, accessibility: { description: The chart is displaying forecasted temperature., point: { valueDescriptionFormat: {value} degrees celsius, {xDescription}, Location } } } }, { renderTo: html-geo-info, type: HTML, elements: [{ tagName: div, // textContent of children populated dynamically children: [{ tagName: h2 }, { tagName: div, attributes: { id: geo-info }, children: [{ tagName: p, attributes: { id: lon, name: Longitude } }, { tagName: p, attributes: { id: lat, name: Latitude } }, { tagName: p, attributes: { id: elevation, name: Elevation } }] }] }] }, { renderTo: kpi-temperature, type: KPI, columnId: temperature, chartOptions: { chart: kpiGaugeOptions.chart, pane: kpiGaugeOptions.pane, title: { text: paramConfig.getColumnHeader( temperature, false ) (latest), verticalAlign: bottom }, yAxis: { ...kpiGaugeOptions.yAxis, min: paramConfig.temperature.min, max: paramConfig.temperature.max, accessibility: { description: paramConfig.getColumnHeader( temperature ) } }, series: [{ ...kpiGaugeOptions.series[0], dataLabels: { format: {y:.1f} paramConfig.temperature.unit, y: kpiGaugeOptions.series[0].dataLabels.y } }] }, events: { click: function () { // Update dashboard activeParam paramConfig.temperature; // Parameter changed, station unchanged updateBoard( board, activeStation, temperature, true, false ); } }, states: { active: { enabled: true, isActive: true }, hover: { enabled: true } } }, { renderTo: kpi-wind, type: KPI, columnId: wind, chartOptions: { chart: kpiGaugeOptions.chart, pane: kpiGaugeOptions.pane, title: { text: paramConfig.getColumnHeader( wind, false ) (latest), verticalAlign: bottom }, yAxis: { ...kpiGaugeOptions.yAxis, min: paramConfig.wind.min, max: paramConfig.wind.max, accessibility: { description: paramConfig.getColumnHeader(wind) } }, series: [{ ...kpiGaugeOptions.series[0], dataLabels: { format: {y:.1f} paramConfig.wind.unit, y: kpiGaugeOptions.series[0].dataLabels.y } }] }, events: { click: function () { // Update board activeParam paramConfig.wind; // Parameter changed, station unchanged updateBoard(board, activeStation, wind, true, false); } }, states: { active: { enabled: true }, hover: { enabled: true } } }, { renderTo: kpi-precipitation, type: KPI, columnId: precipitation, chartOptions: { chart: kpiGaugeOptions.chart, pane: kpiGaugeOptions.pane, title: { text: paramConfig.getColumnHeader( precipitation, false ) (next 24 hours), verticalAlign: bottom }, yAxis: { ...kpiGaugeOptions.yAxis, min: paramConfig.precipitation.min, max: 50, // Per 24 hours accessibility: { description: paramConfig.getColumnHeader( precipitation ) } }, series: [{ ...kpiGaugeOptions.series[0], dataLabels: { format: {y:.1f} paramConfig.precipitation.unit, y: kpiGaugeOptions.series[0].dataLabels.y } }] }, events: { click: function () { // Update board activeParam paramConfig.precipitation; // Parameter changed, station unchanged updateBoard( board, activeStation, precipitation, true, false ); } }, states: { active: { enabled: true }, hover: { enabled: true } } }, { renderTo: forecast-grid, type: Grid, title: { enabled: true, text: Forecast for currentDay }, sync: { highlight: true }, gridOptions: { credits: { enabled: false }, columns: [{ id: time, header: { format: Local Time }, cells: { formatter: function () { return Highcharts.dateFormat( %I:%M %p, this.value ); } } }, { id: temperature, header: { format: paramConfig.getColumnHeader(temperature) }, cells: { format: {value:.1f} } }, { id: wind, header: { format: paramConfig.getColumnHeader(wind) }, cells: { format: {value:.1f} } }, { id: windDir, header: { format: paramConfig.getColumnHeader(windDir) }, cells: { format: {value:.0f} {log} } }, { id: precipitation, header: { format: paramConfig.getColumnHeader(precipitation) }, cells: { format: {value:.1f} } }] } }, { renderTo: forecast-chart, type: Highcharts, sync: { highlight: true }, chartOptions: { chart: { spacing: [40, 40, 40, 10], styledMode: true, type: activeParam.chartType }, credits: { enabled: true, href: https://api.met.no/weatherapi/locationforecast/2.0/documentation, text: MET Norway }, legend: { enabled: false }, colorAxis: { startOnTick: false, endOnTick: false, max: activeParam.max, min: activeParam.min, stops: activeParam.colorStops }, plotOptions: { series: { marker: { enabled: true, symbol: circle }, tooltip: { headerFormat: span stylefont-size: 10px {point.x:%B %e, %Y, %l:%M %p}/spanbr/, pointFormatter: function () { return this.y activeParam.unit; } } }, windbarb: { tooltip: { headerFormat: span stylefont-size: 10px {point.x:%B %e, %Y, %l:%M %p}/spanbr/, pointFormat: b{point.value:.2f} m/s/b // eslint-disable-next-line max-len ({point.beaufort})br/Wind direction: // eslint-disable-next-line max-len {point.series.directionData.(point.index)::.0f}, pointFormatter: null } } }, title: { margin: 20, x: 15, y: 5 }, subtitle: { text: ---- }, xAxis: { type: datetime, labels: { format: {value:%l:%M %p}, accessibility: { description: Hours, minutes } } }, yAxis: { title: { enabled: false }, accessibility: { description: activeParam.unit } }, lang: { accessibility: { chartContainerLabel: Weather stations. Highcharts Interactive Map. } }, accessibility: { description: The chart is displaying forecasted temperature, wind or precipitation. } } }] }, true); const dataPool board.dataPool; const stationsTable await getConnectorTable(dataPool, Stations); const stationRows stationsTable.getRowObjects(); // Add weather station sources for (let i 0, iEnd stationRows.length; i iEnd; i) { const station stationRows[i].station; const url weatherStationConfig.buildUrl(station); if (!url) { continue; } dataPool.setConnectorOptions({ id: station, type: JSON, firstRowAsNames: false, dataUrl: url, // Pre-parsing for filtering incoming data beforeParse: function (data) { const retData []; const forecastData data.properties.timeseries; // Get the time difference for the current station const timeDifference getTimeDifference(station); for (let i 0; i rangeConfig.hours; i) { const item forecastData[i]; // Instant data const instantData item.data.instant.details; // Data for the next hour const hourSpan item.data.next_1_hours.details; // Convert UTC time to local time const localTime new Date(item.time).getTime() - timeDifference * 3600000; // Picks the parameters this application uses retData.push({ time: localTime, temperature: instantData.air_temperature, precipitation: hourSpan.precipitation_amount, wind: instantData.wind_speed, windDir: instantData.wind_from_direction }); } return retData; } }); } // Update map (series 0 is the world map, series 1 the weather data) const mapChart getMapChart(board); // Load active weather station await addStationToMap(board, stationsTable, mapChart, activeStation); await updateBoard(board, activeStation, activeParam.name); // Select active weather station on the map setActiveStation(); // Load additional cities for (let i 0; i stationRows.length; i) { const station stationRows[i].station; if (station ! activeStation) { await addStationToMap(board, stationsTable, mapChart, station); } } // HELPER functions // Get map chart function getMapChart(board) { // Update map (series 0 is the world map, series 1 the weather data) return board.mountedComponents[0].component.chart.series[1]; } // Select a weather station on the map function setActiveStation() { const mapData getMapChart(board).data; for (let i 0; i mapData.length; i) { if (mapData[i].name activeStation) { mapData[i].select(); break; } } } } // Add weather station to map async function addStationToMap(board, stationsTable, stationMap, station) { const forecastTable await getConnectorTable(board.dataPool, station); const stationInfo stationsTable.getRowObject( stationsTable.getRowIndexBy(station, station) ); // Add weather station to world map stationMap.addPoint({ name: stationInfo.station, lat: stationInfo.lat, lon: stationInfo.lon, custom: { elevation: stationInfo.elevation, unit: paramConfig.temperature.unit }, // First item in current data set y: forecastTable.columns.temperature[rangeConfig.first] }); } // Get observation (accumulated or latest) function getObservation(forecastTable, param) { if (param precipitation) { let sum 0; for (let i rangeConfig.first; i rangeConfig.hours; i) { sum forecastTable.columns[param][i]; } return Math.round(sum); } return forecastTable.columns[param][rangeConfig.first]; } // Update board after changing weather station or parameter (measurement type) async function updateBoard( board, station, paramName, paramChanged true, stationChanged true ) { // Parameter info const param paramConfig[paramName]; // Data access const dataPool board.dataPool; // Geographical information const stationsTable await getConnectorTable(dataPool, Stations); const [ // The order here must be the same as in the component // definition in the Dashboard. stationMap, htmlGeoInfo, kpiTemperature, kpiWind, kpiRain, selectionGrid, cityChart ] board.mountedComponents.map(c c.component); // Common to map and chart const colorAxis { min: param.min, max: param.max, stops: param.colorStops }; // Update forecast chart const options cityChart.chartOptions; const isWind paramName wind; const title isWind ? Wind : paramConfig.getColumnHeader(paramName, false); options.title.text title forecast for station; options.subtitle.text currentDay; options.colorAxis colorAxis; options.chart.type param.chartType; options.xAxis.offset isWind ? 40 : 0; // Allow space for Windbarb const chart cityChart.chart; // Handle auxiliary series (Windbarb) if (windbarbSeries ! null) { // Remove any existing Windbarb series await windbarbSeries.remove(); windbarbSeries null; } await cityChart.update({ connector: { id: station, columnAssignment: [{ seriesId: paramName, data: [time, paramName] }] }, chartOptions: options }); if (isWind) { // Add Windbarb series const forecastTable await getConnectorTable(dataPool, station); // Create series data const data []; for (let i rangeConfig.first; i rangeConfig.hours; i) { const windSpeed forecastTable.columns.wind[i]; const windDir forecastTable.columns.windDir[i]; data.push([windSpeed, windDir]); } // Add series (and store for removal) windbarbSeries await chart.addSeries({ type: windbarb, pointStart: forecastTable.columns.time[0], pointInterval: 36e5, data: data }); } if (paramChanged) { // Parameters update: e.g. temperature - precipitation. // Affects: map // Update all map points (series 1: weather data) const mapPoints stationMap.chart.series[1].data; for (let i 0, iEnd mapPoints.length; i iEnd; i) { // Forecast for station const station mapPoints[i].name; const forecastTable await getConnectorTable(dataPool, station); const elevation convertToNumber(stationsTable.getCell( elevation, stationsTable.getRowIndexBy(station, station) )); mapPoints[i].update({ y: getObservation(forecastTable, paramName), custom: { elevation: elevation, unit: param.unit } }, false); } // Update map properties and redraw stationMap.chart.update({ colorAxis: colorAxis, title: { text: paramConfig.getColumnHeader(paramName) } }); } if (stationChanged) { // Weather station changed: e.g. New York - Winnipeg // Affects: KPIs and grid. const forecastTable await getConnectorTable(dataPool, station); await kpiTemperature.update({ value: getObservation(forecastTable, temperature) }); await kpiWind.update({ value: getObservation(forecastTable, wind) }); await kpiRain.update({ value: getObservation(forecastTable, precipitation) }); // Update geo-info HTML const options htmlGeoInfo.getOptions(); const html options.elements[0]; const stationRow stationsTable.getRowIndexBy(station, station); html.children[0].textContent Forecast for station; const geoInfo html.children[1].children; for (let i 0; i geoInfo.length; i) { const attr geoInfo[i].attributes; const value convertToNumber(stationsTable.getCell(attr.id, stationRow)); const unit attr.id elevation ? m. : degr.; geoInfo[i].textContent ${attr.name}: ${value} ${unit}; } await htmlGeoInfo.update(options); // Update grid await selectionGrid.update({ connector: { id: station } }); } } // Launch the application setupDashboard();一、核心功能演示系统展示北美地区多个气象监测站的实时观测数据 未来 24 小时天气预报第一行数据为最新实况观测后续数据为未来时段预测值支持地图选点、参数切换、全局数据联动刷新。二、使用方式点击地图标记点→ 切换不同气象监测站点击 KPI 仪表盘→ 切换查看温度、风速、降雨量等气象参数所有组件全局实时联动无需刷新页面三、Dashboards 组件构成六大核心组件协同Highcharts Maps 地图组件显示气象站位置支持点击选点Tooltip 展示实时关键指标。HTML 面板组件展示当前选中气象站名称、位置、经纬度等基础信息。KPI 仪表盘组件实时显示当前温度、风速、未来 24 小时预计降水量三大核心指标。DataGrid 数据表格展示未来 24 小时完整气象参数明细支持查看全维度时序数据。Highcharts 专业图表根据参数自动切换展示温度 → 曲线图风速 → 风向箭头 / 柱状图降水量 → 面积图全局数据中枢统一数据源支持地图、KPI、表格、图表跨组件双向联动。四、数据集说明数据来源专业气象机构预报数据包含参数温度、风速、风向、降水量覆盖范围北美地区多个气象站点时间跨度默认展示最新观测 未来 24 小时完整数据集长达 19 天时间格式自动适配时区使用美式本地化时间展示五、可自定义扩展能力增删 / 修改气象站点位置与信息切换不同区域 / 不同类型的监测站点自定义时间跨度默认 24 小时可扩展至 19 天自定义图表类型、配色、KPI 样式、表格列自定义联动规则与展示参数六、适用场景气象监控大屏物联网环境监测农业 / 水利 / 能源监测指挥中心可视化实时数据仪表盘多维度数据分析平台一句话总结Highcharts Dashboards 气象 Demo 是地图 KPI 数据表格 专业图表的顶级联动示范真正实现一套数据、全局联动、多端展示、实时更新是企业级数据仪表盘的最佳实践模板。