predict-idlab / plotly-resampler

Visualize large time series data with plotly.py
https://predict-idlab.github.io/plotly-resampler/latest
MIT License
1k stars 67 forks source link

Don't automatically add `axis: {"autorange": True, "range": None}` when converting existing figures #223

Closed remulasce closed 1 year ago

remulasce commented 1 year ago

In figure_resampler_interface.py#149

I'm generating some nested graphs that are meant to always display data within the same range. For my case it's telemetry from my car, and let's imagine the example of Oil Temp. I know the oil temp can only be within the range [40, 100] during operation, so my oil temp graph is set to always have that range. That way, any time I open a trace, I can tell at a glance if the line is near the top of the graph, it must mean bad things.

When I add FigureResampler to my existing Dash app, it completely breaks my careful construction of the graph and throws a global 'autorange' onto it. This both loses the initial fixed range, and also continuously re-ranges the Y axis whenever I scroll horizontally

graph_things.py

# Make all my various graphs. Copy pasted segments.
def plot_car_trace():
    fig = subplots.make_subplots(rows=subplot_rows, cols=subplot_cols, shared_xaxes=True, shared_yaxes=False, ...)

    fig.add_trace(oil_pump_scatter(), row=1, col=1)
    fig.update_yaxes(row=1, col=1, range= [40, 100])

    fig.add_trace(vehicle_speed_scatter(), row=2, col=1)
    fig.update_yaxes(row=2, col=1, range= [0, 200])

    # don't even let the user mess up the ranges
    fig.update_yaxes(fixedrange=True, autorange=False)
    fig.update_xaxes(autorange=False, range=[0, 5 * 60])  # Show 5 minutes of data
    return fig

dash_app.py

app = DashProxy(__name__, transforms=[ServersideOutputTransform()])
# etc

@app.callback(...)
def update_figure(trace_hash, trace_json, filename):
    fig = FigureResampler(plot_car_trace())
    return fig

# as per examples
def update_fig(relayoutdata, fig):
    if fig is None:
        return no_update
    return fig.construct_update_data(relayoutdata)

app.run(debug=True, port=9023, dev_tools_hot_reload=False)

I've split my code into two files, since I don't necessarily always want to use Dash to display the Figure. So I don't want to, eg. set the FigureResampler(subplots.make_subplots()), and then separately add each trace to it, or reset the axis ranges in a second step.

Is there a way I can disable autozoom when simply wrapping an existing Figure using FigureResampler(existingFig)?

jvdd commented 1 year ago

Hey @remulasce,

Thanks for submitting this clear issue! I agree that it does not make a lot of sense that the layout is not fully copied when wrapping into a Figure(Widget)Resampler.

I think removing this code from the constructor (as I do in #228) will fix this issue. https://github.com/predict-idlab/plotly-resampler/blob/c9054781cdf82d614f194d3fee6b9d3e0be84278/plotly_resampler/figure_resampler/figure_resampler_interface.py#L163-L169

To be honest, I am not entirely sure why we added this code in the constructor in the first place..

remulasce commented 1 year ago

I recall attempting to comment out the lines myself and having a problem where the bounds update event dash callback was throwing, possibly due to the bounds not being provided in a raw translation update? I got it kind of working by disabling Y autoscale but not X.

In the end I found the latency of the server having to receive an update, then push new trace data to the client, to be too long when scrolling around and I didn't want to have to set up a whole memory cache system and also fix this issue. Instead I started using a ScatterGL plot, which handles my requirements at full resolution and framerate without lag.