You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现带中间分组节点的Highcharts桑基图(航班数据场景)

实现Highcharts桑基图的完整三级数据流交互

原始航班数据

[
  {"from":"NY", "to":"Oslo", "company": "Norwegian", "flights":3},
  {"from":"NY", "to":"Paris", "company": "Norwegian", "flights":2},
  {"from":"Miami", "to":"Berlin", "company": "Norwegian", "flights":1},
  {"from":"Miami", "to":"London", "company": "SAS", "flights":1}
]

需求说明

需要实现Highcharts桑基图/冲积图,节点固定布局为:

  • 左侧:出发地(from)
  • 中间:航空公司(company)
  • 右侧:目的地(to)

要求图表能识别from=>company=>to的完整数据流,在鼠标悬停等交互中正确关联所有相关节点和连线(比如悬停NY时,需同时高亮Norwegian、Oslo、Paris以及对应的所有连线)。

当前实现的问题

通过伪造出发地=>航空公司、航空公司=>目的地的映射关系模拟外观后,交互存在缺陷:

  • 悬停出发地(如NY)时,仅能关联到中间的航空公司(Norwegian),无法直接关联到最终目的地(Oslo、Paris),因为出发地与目的地之间无直接数据连线,仅通过中间节点间接连接。

解决方案

要实现完整的三级数据流交互,需从数据处理和图表配置两方面入手:

1. 构建带完整链路标识的数据结构

桑基图基于两两节点连线,因此转换数据时需为每条连线添加完整路径标识,同时收集所有唯一节点:

const rawData = [/* 原始航班数据 */];
const nodes = new Set();
const links = [];

rawData.forEach(item => {
  const fullRoute = `${item.from}->${item.company}->${item.to}`;
  
  // 添加出发地到航空公司的连线,携带完整路径
  links.push({
    from: item.from,
    to: item.company,
    value: item.flights,
    fullRoute
  });
  
  // 添加航空公司到目的地的连线,携带完整路径
  links.push({
    from: item.company,
    to: item.to,
    value: item.flights,
    fullRoute
  });
  
  // 收集所有节点
  nodes.add(item.from);
  nodes.add(item.company);
  nodes.add(item.to);
});

// 转换为Highcharts所需的节点格式
const chartNodes = Array.from(nodes).map(name => ({ id: name, name }));

2. 自定义交互逻辑实现跨层级高亮

利用Highcharts的事件钩子,结合fullRoute字段实现跨层级的节点和连线高亮,同时自定义提示框内容:

Highcharts.chart('container', {
  series: [{
    type: 'sankey',
    nodes: chartNodes,
    data: links,
    events: {
      mouseOver: function(e) {
        const targetRoute = e.point?.fullRoute || e.target.options.fullRoute;
        const chart = this.chart;
        
        // 重置所有元素状态
        chart.series[0].points.forEach(point => point.setState(''));
        chart.series[0].nodes.forEach(node => node.setState(''));
        
        // 高亮同一路径的所有连线和节点
        chart.series[0].points.forEach(point => {
          if (point.fullRoute === targetRoute) {
            point.setState('hover');
            // 高亮对应节点
            chart.series[0].nodes.find(n => n.id === point.from).setState('hover');
            chart.series[0].nodes.find(n => n.id === point.to).setState('hover');
          }
        });
      },
      mouseOut: function() {
        // 恢复所有元素默认状态
        this.chart.series[0].points.forEach(point => point.setState(''));
        this.chart.series[0].nodes.forEach(node => node.setState(''));
      }
    },
    tooltip: {
      formatter: function() {
        const routeParts = this.point.fullRoute.split('->');
        return `<b>${routeParts[0]} → ${routeParts[1]} → ${routeParts[2]}</b><br/>航班数: ${this.point.value}`;
      }
    }
  }]
});

3. 固定节点层级(可选)

如果需要确保节点严格按出发地→航空公司→目的地的顺序排列,可在构建节点时指定列位置:

const chartNodes = Array.from(nodes).map(name => {
  let column = 0;
  if (rawData.some(item => item.company === name)) column = 1;
  if (rawData.some(item => item.to === name)) column = 2;
  return ({ id: name, name, column });
});

配置完成后,悬停任意节点或连线时,同一条完整路径的所有元素都会被高亮,提示框也会显示完整的航线信息。

内容的提问来源于stack exchange,提问作者siggemannen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:43:15