How to implement dynamic datetime zoom-in levels on x-axis when there are 2+ level a-axis React plotly

15 views Asked by At

enter image description here

I'm working on a project where I need to display millions of data points associated with different stages. The requirement is to initially display only the year, then progressively include month, day, hour, minute, second, and millisecond based on the zoom-in level. Currently, I'm able to display datetime in the format YYYY-mm-dd : hh:mm:ss.ms, but this results in overcrowding on the x-axis as more data is displayed.

I've tried several approaches to implement this feature, but none of them have been successful. Do you have any ideas or suggestions on how to efficiently implement dynamic datetime zoom-in levels to handle large datasets? Any help would be greatly appreciated. Thank you!

Here's a snippet of code from my sandbox:

**import React, { useEffect, useState, useMemo } from "react";
import Plotly from "plotly.js-dist";
import createPlotlyComponent from "react-plotly.js/factory";
const Plot = createPlotlyComponent(Plotly);
const App = () => {
  const [chartKey, setChartKey] = useState(Date.now());
  const yaxis2 = {
    anchor: "x",
    overlaying: "y",
    showgrid: false,
    side: "right",
    title: {
      text: "Price per unit",
    },
  };
  const memoizedData = useMemo(() => {
    return [
      {
        name: "Fruits",
        type: "bar",
        x: [
          [
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 2",
            "stage 2",
            "stage 2",
            "stage 2",
          ],
          [
            "2023-10-11 09:25:57.0004",
            "2023-10-11 09:25:57.0005",
            "2023-10-11 09:25:57.0006",
            "2023-10-11 09:25:57.0008",
            "2023-10-11 09:25:57.0009",
            "2023-10-11 09:25:57.00010",
            "2023-10-11 09:25:57.00011",
            "2023-10-11 09:25:57.00012",
            "2023-10-11 09:25:57.0001",
            "2023-10-11 09:25:57.0002",
            "2023-10-11 09:25:57.0003",
            "2023-10-11 09:25:57.0004",
          ],
        ],
        y: [
          57246.0, 1587.0, 63189.0, 30680.0, 26071.0, 117519.0, 27519.0,
          42643.0, 8559.0, 22355.0, 59363.0, 62955.0,
        ],
      },
      {
        name: "Price",
        type: "scatter",
        x: [
          [
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 1",
            "stage 2",
            "stage 2",
            "stage 2",
            "stage 2",
          ],
          [
            "2023-10-11 09:25:57.0004",
            "2023-10-11 09:25:57.0005",
            "2023-10-11 09:25:57.0006",
            "2023-10-11 09:25:57.0008",
            "2023-10-11 09:25:57.0009",
            "2023-10-11 09:25:57.00010",
            "2023-10-11 09:25:57.00011",
            "2023-10-11 09:25:57.00012",
            "2023-10-11 09:25:57.0001",
            "2023-10-11 09:25:57.0002",
            "2023-10-11 09:25:57.0003",
            "2023-10-11 09:25:57.0004",
          ],
        ],
        y: [
          0.22991999, 0.27284184, 0.18438336, 0.24866362, 0.18280848,
          0.19382398, 0.17268069, 0.18802617, 0.17560463, 0.13415343,
          0.14773512, 0.31319196,
        ],
        yaxis: "y2",
      },
    ];
  }, []);
  const memoizedLayout = useMemo(() => {
    return {
      title: "DateTime Plot",
      height: 600,
      showlegend: true,
      yaxis: {
        showgrid: true,
        title: {
          text: "Fruits volume",
        },
      },
      yaxis2: yaxis2, // Assuming you want to include yaxis2 settings as well
      xaxis: {
        tickangle: -15,
        tickformat: "%Y-%m-%d",
        automargin: true, // Automatically adjust the margin to fit tick labels
        rangeslider: {
          visible: true,
        },
        rangeselector: {
          buttons: [
            {
              count: 1,
              label: "1d",
              step: "day",
              stepmode: "backward",
            },
            {
              count: 6,
              label: "6d",
              step: "day",
              stepmode: "backward",
            },
            {
              count: 1,
              label: "1m",
              step: "month",
              stepmode: "backward",
            },
            {
              step: "all",
            },
          ],
          x: 0,
          xanchor: "left",
          y: 1.2,
          yanchor: "top",
          font: {
            size: 10,
          },
          bgcolor: "rgba(0,0,0,0)",
          buttons: [
            {
              step: "all",
              label: "Reset Zoom",
            },
          ],
        },
        tickformatstops: [
          {
            dtickrange: [null, 1000], // less than 1 second
            value: "%H:%M:%S.%L ms",
          },
          {
            dtickrange: [1000, 60000], // 1 second - 1 minute
            value: "%H:%M:%S",
          },
          {
            dtickrange: [60000, 3600000], // 1 minute - 1 hour
            value: "%H:%M",
          },
          {
            dtickrange: [3600000, 86400000], // 1 hour - 1 day
            value: "%H:00",
          },
          {
            dtickrange: [86400000, 604800000], // 1 day - 1 week
            value: "%m-%d %H:%M",
          },
          {
            dtickrange: [604800000, "M1"], // 1 week - 1 month
            value: "%m-%d",
          },
          {
            dtickrange: ["M1", "M12"], // 1 month - 1 year
            value: "%Y-%m",
          },
          {
            dtickrange: ["M12", null], // greater than 1 year
            value: "%Y",
          },
        ],
      },
    };
  }, []);
  function setYAxisRange(gd, minY, maxY) {
    Plotly.relayout(gd, {
      "yaxis.range": [minY, maxY], // Set the desired range
    });
  }
  function setXAxisRange(gd, xData) {
    if (xData.length === 0) {
      // Handle case when there is no data
      Plotly.relayout(gd, {
        "xaxis.range": null, // Reset x-axis range
      });
    } else {
      // Calculate the x-axis range based on the data
      const minX = xData[0];
      const maxX = xData[xData.length - 1];
      // Set the initial tick format to display year-month-day
      let tickFormat = "%Y-%m-%d";
      Plotly.relayout(gd, {
        "xaxis.range": [minX, maxX], // Set x-axis range
        "xaxis.tickformat": tickFormat, // Set initial tick format
      });
      // Listen to changes in the x-axis range to dynamically update the tick format
      gd.on("plotly_relayout", (eventData) => {
        if (
          eventData &&
          eventData["xaxis.range[1]"] - eventData["xaxis.range[0]"] <
            24 * 60 * 60 * 1000 // Less than a day
        ) {
          // If zoomed in to less than a day, switch to hour-minute-second format
          tickFormat = "%H:%M:%S";
        } else {
          // Otherwise, display year-month-day
          tickFormat = "%Y-%m-%d";
        }
        Plotly.relayout(gd, {
          "xaxis.tickformat": tickFormat, // Update tick format dynamically
        });
      });
    }
  }
  const memoizedConfig = useMemo(() => {
    return {
      responsive: true,
      scrollZoom: true,
      displayModeBar: true,
      displaylogo: false,
      displaygird: false,
      showgrid: false,
      modeBarButtonsToRemove: ["autoscale"], // Remove 'Autoscale' button
      modeBarButtonsToAdd: [
        "sendDataToCloud",
        "hoverCompareCartesian",
        "lasso2d",
        {
          name: "Autoscale",
          icon: Plotly.Icons.autoscale,
          click: function (gd) {
            // Reset both x-axis and y-axis to their original ranges
            // setXAxisRange(gd, memoizedData[0].x[1]);
            setYAxisRange(gd, -50, 100);
            // Update the chart key to trigger a re-render
            setChartKey(Date.now());
          },
        },
      ],
    };
  }, []);
  const plotWithData = (
    <Plot
      key={chartKey}
      data={memoizedData ?? []}
      layout={memoizedLayout}
      config={memoizedConfig}
      style={{
        width: "100%",
        height: "100%",
      }}
      useResizeHandler
    />
  );
  return <div id="multi-categorical-axes-plot"> {plotWithData} </div>;
};
export default App;**
0

There are 0 answers