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


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

{plotWithData}
;
};

export default App;