最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

angularjs - How can i display custom tooltip in Plotly.js and append traces with the same value if any, in a single tooltip - St

programmeradmin0浏览0评论

I have a Plotly graph with multiple traces which includes line as well as bar plots. If 2 traces have the same value at a point, the tooltip displays only the value of a single trace. I attempted to add custom tooltip for my traces, but it only displays a tooltip for a single trace, even when multiple traces have the same value. I also tried using the Plotly hover event, but it doesn't seem to work as expected; it gets overridden by the default tooltip.

const trace: any = [
  {
    x: this.data[this.group],
    y: this.data.Price,
    type: 'scatter',
    mode: 'lines+markers',
    name: 'Grid Price',
    line: { color: '#64323A', width: 3, shape: 'spline' },
    marker: { size: 7 },
    yaxis: 'y2',
    hovertemplate: '%{x}<br>Grid Price: %{customdata}<extra></extra>',
    customdata: this.data.Price.map((val: number) =>
      Number.isInteger(val) ? val.toFixed(0) : val.toFixed(3)) 
  },
  {
    x: this.data[this.group],
    y: this.data.Price.map(() => this.simulatorRun?.grid_used_to_firm_h2_output ? this.simulatorRun?.load_gain_price : 0),
    type: 'scatter',
    mode: 'lines+markers',
    name: 'Load Gain Price',
    line: { color: '#182D44', width: 3, shape: 'spline' },
    marker: { size: 7 },
    yaxis: 'y2',
    hovertemplate: '%{x}<br>Load Gain Price: %{customdata}<extra></extra>',
    customdata: this.data.Price.map(() =>
      this.simulatorRun?.grid_used_to_firm_h2_output ? this.simulatorRun?.load_gain_price : 0).map((val: string) => {
      const numVal = parseFloat(val); /// Convert string to a number
      return Number.isInteger(numVal) ? numVal.toFixed(0) : numVal.toFixed(3);
    })
  },
  {
    x: this.data[this.group],
    y: this.data.Price.map(() => this.simulatorRun?.grid_used_to_firm_h2_output ? this.simulatorRun?.load_shed_price : 0),
    type: 'scatter',
    mode: 'lines+markers',
    name: 'Load Shed Price',
    line: { color: '#273D1B', width: 3, shape: 'spline' },
    marker: { size: 7 },
    yaxis: 'y2',
    hovertemplate: '%{x}<br>Load Shed Price: %{customdata}<extra></extra>',
    customdata: this.data.Price.map(() =>
      this.simulatorRun?.grid_used_to_firm_h2_output ? this.simulatorRun?.load_shed_price : 0).map((val: string) => {
      const numVal = parseFloat(val); /// Convert string to a number
      return Number.isInteger(numVal) ? numVal.toFixed(0) : numVal.toFixed(3);
    })
  },
  {
    x: this.data[this.group],
    y: this.data.Price.map(() => this.simulatorRun?.sell_electricity ? this.simulatorRun?.electricity_price : 0),
    type: 'scatter',
    mode: 'lines+markers',
    name: 'Sell Electricity Price',
    line: { color: '#B38C3A', width: 3, shape: 'spline' },
    marker: { size: 7 },
    yaxis: 'y2',
    hovertemplate: '%{x}<br>Sell Electricity Price: %{customdata}<extra></extra>',
    customdata: this.data.Price.map(() => 
      this.simulatorRun?.sell_electricity ? this.simulatorRun?.electricity_price : 0).map((val: number) => 
        Number.isInteger(val) ? val.toFixed(0) : val.toFixed(3))
  },
  {
    x: this.data[this.group],
    y: this.data["Grid Withdrawals (MW)"],
    type: 'bar',
    name: 'Grid',
    marker: { color: '#046CC4' },
    yaxis: 'y1',
    hovertemplate: '%{x}<br>Grid: %{customdata}<extra></extra>',
    customdata: this.data["Grid Withdrawals (MW)"].map((val: number) => 
      Number.isInteger(val) ? val.toFixed(0) : val.toFixed(3))
  },
  {
    x: this.data[this.group],
    y: this.data["Grid Exports (MW)"],
    type: 'bar',
    name: 'Export',
    marker: { color: '#388454' },
    yaxis: 'y1',
    hovertemplate: '%{x}<br>Export: %{customdata}<extra></extra>',
    customdata: this.data["Grid Exports (MW)"].map((val: number) => 
      Number.isInteger(val) ? val.toFixed(0) : val.toFixed(3))
  },
];

/// Configure layout and responsive options
const commonTickfontSize = this.group === '48h' || this.group === 'Day' ? 12 : 14;  /// Adjust font size based on the group
const tickAngle = this.group === '48h' ? -30 : this.group === 'Day' ? -45 : 0;  /// Rotate for  48h and Day
const legendYPosition = this.group === '48h' ? -0.3 : this.group === 'Day' ? -0.55 : -0.2;  /// Adjust the legend position based on group

const layout: Partial<Plotly.Layout> = {
  hovermode: 'closest',
  title: {
    text: 'Grid Usage/Grid Price vs Time',
    font: { size: 14 },
    x: 0.5,
    xanchor: 'center',
  },
  xaxis: {
    tickvals: this.data[this.group],
    ticktext: this.data[this.group].map((val: any) => this.utilsService.getDateTextForChart(val, this.group)),
    tickfont: { size: commonTickfontSize, color: '#000000' },
    showgrid: true,
    tickangle: tickAngle,
  },
  yaxis: {
    title: {
      text: 'Operating Points (MW)',
      font: { size: 16, color: '#000000' },
      standoff: 30, /// Adds space between the title and the axis
    },
    tickfont: { size: 14, color: '#000000' },
    showgrid: false,
    rangemode: 'tozero'
  },
  yaxis2: {
    tickfont: { size: 14, color: '#000000' },
    overlaying: 'y',
    side: 'right',
    showgrid: false,
    rangemode: 'tozero'
  },
  annotations: [ ///reverse yaxis2 title
    {
      xref: 'paper',
      yref: 'paper',
      x: 1.05,
      y: 0.7,
      text: 'Grid Price ($/MWh)',
      showarrow: false,
      font: { size: 16 },
      textangle: '90' as any, /// Rotate the text
    }
  ],
  legend: {
    orientation: 'h',
    font: { size: 14, color: '#000000' },
    x: 0.5,
    y: legendYPosition,
    xanchor: 'center',
    yanchor: 'bottom',
  },
  dragmode: undefined, /// Disable dragging for zoom/pan
  barmode: 'stack',
  margin: { t: 50, b: 50, l: 50, r: 70 },
  bargap: 0.7,
  plot_bgcolor: '#F8F8F8',
  paper_bgcolor: '#F8F8F8',
};

const config: Partial<Plotly.Config> = {
  responsive: true,
  displayModeBar: true,
  modeBarButtonsToRemove: ['select2d', 'lasso2d', 'autoScale2d', 'zoomIn2d', 'zoomOut2d'] as Plotly.ModeBarDefaultButtons[], /// Disable box and lasso select, autoscale, zoom in, zoom out buttons
  toImageButtonOptions: {
    format: 'png',
    filename: 'Grid Usage/Grid Price vs Time',
    height: 500,
    width: 1300,
    scale: 1,
  },
  displaylogo: false,
};

Plotly.react(this.plot, trace, layout, config);
this.plotInited.emit({ plot: this.plot });

/// Event listeners for zoom and pan
this.plot.on('plotly_relayout', (eventData: any) => {
  if (eventData['xaxis.range[0]'] || eventData['xaxis.range[1]']) {
    this.plotZoomed.emit({ event: eventData });
  }
});

this.plot.on('plotly_click', (eventData: any) => {
  this.plotClicked.emit({ event: eventData, item: eventData.points });
});    

this.plot.on('plotly_hover', (eventData: any) => {
  const annotations: Partial<Plotly.Annotations>[] = [];

  eventData.points.forEach((point: any) => {
    const xVal = point.x;
    const yVal = point.y;
    const traceName = point.data.name;

    // Find traces with the same x and y values
    const tracesWithSameValue = eventData.points.filter(
      (p: any) => p.x === xVal && p.y === yVal
    );

    if (tracesWithSameValue.length > 1) {
      const tooltipContent = tracesWithSameValue
        .map(
          (trace: any) =>
            `${trace.data.name}: ${trace.y.toFixed(3)}`
        )
        .join('<br>');

      // Create a custom annotation
      annotations.push({
        x: xVal,
        y: yVal,
        text: tooltipContent,
        showarrow: false,
        font: { size: 12 },
        align: 'left' as 'left',
        bgcolor: 'rgba(255,255,255,0.8)',
        bordercolor: '#ccc',
        borderwidth: 1,
      });
    }
  });

  // Update the layout with custom annotations
  Plotly.relayout('chart', { annotations });
});

// Remove annotations on unhover
this.plot.on('plotly_unhover', () => {
  Plotly.relayout('chart', { annotations: [] });
});

}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论