# \[New\]Allow users to zoom into live graphs and maintain zoom for new data

**URL:** <https://community.plotly.com/t/new-allow-users-to-zoom-into-live-graphs-and-maintain-zoom-for-new-data/50408>\
**Category:** Dash Python\
**Tags:** show-and-tell\
**Created:** [February 24, 2021, 7:45pm UTC](https://community.plotly.com/t/new-allow-users-to-zoom-into-live-graphs-and-maintain-zoom-for-new-data/50408 "2021-02-24T19:45:19Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![dasher007](https://avatars.discourse-cdn.com/v4/letter/d/c57346/32.png) [@dasher007](https://community.plotly.com/u/dasher007)\
**Post date:** [February 24, 2021, 7:45pm UTC](https://community.plotly.com/t/new-allow-users-to-zoom-into-live-graphs-and-maintain-zoom-for-new-data/50408/1 "2021-02-24T19:45:19Z")

</div>

Hi guys,  
**posting again because i cant edit the old post anymore**  
I am following sentdex’s dash tutorial: [Vehicle Data App Example - Data Visualization GUIs with Dash and Python p.5](https://pythonprogramming.net/vehicle-data-visualization-application-dash-python-tutorial/) . I want to let the users to be able to zoom into any of the graphs and maintain that zoom for the new data coming in. The purpose is for the users to monitor one or more variables more closely if they wanted to. I looked at State and relayout data but i’m not able to implement it. Using this as reference: [How to save current zoom and position after filtering? - #2 by chriddyp](https://community.plotly.com/t/how-to-save-current-zoom-and-position-after-filtering/5310/2)  
Any help is appreciated! 😃

```auto
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State
import time
from collections import deque
import plotly.graph_objs as go
import random

app = dash.Dash('vehicle-data')

max_length = 5000
times = deque(maxlen=max_length)
oil_temps = deque(maxlen=max_length)
intake_temps = deque(maxlen=max_length)
coolant_temps = deque(maxlen=max_length)
rpms = deque(maxlen=max_length)
speeds = deque(maxlen=max_length)
throttle_pos = deque(maxlen=max_length)

data_dict = {"Oil Temperature":oil_temps,
"Intake Temperature": intake_temps,
"Coolant Temperature": coolant_temps,
"RPM":rpms,
"Speed":speeds,
"Throttle Position":throttle_pos}

def update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos):

    times.append(time.time())
    if len(times) == 1:
        #starting relevant values
        oil_temps.append(random.randrange(180,230))
        intake_temps.append(random.randrange(95,115))
        coolant_temps.append(random.randrange(170,220))
        rpms.append(random.randrange(1000,9500))
        speeds.append(random.randrange(30,140))
        throttle_pos.append(random.randrange(10,90))
    else:
        for data_of_interest in [oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos]:
            data_of_interest.append(data_of_interest[-1]+data_of_interest[-1]*random.uniform(-0.0001,0.0001))

    return times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos

times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos = update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)

app.layout = html.Div([
    html.Div([
        html.H2('Vehicle Data',
                style={'float': 'left',
                       }),
        ]),
    dcc.Dropdown(id='vehicle-data-name',
                 options=[{'label': s, 'value': s}
                          for s in data_dict.keys()],
                 value=['Coolant Temperature','Oil Temperature','Intake Temperature'],
                 multi=True
                 ),
    dcc.RadioItems(id='lock-zoom',
                    options=[{'label': i, 'value': i} for i in ['Lock View', 'Refresh View']],
                    value='Lock View'
                    ),
    html.Div(children=html.Div(id='graphs'), className='row'),
    dcc.Interval(
        id='graph-update',
        interval=1000,
        n_intervals=0),
    ], className="container",style={'width':'98%','margin-left':10,'margin-right':10,'max-width':50000})

@app.callback(
    Output('graphs','children'),
    [Input('vehicle-data-name', 'value'),
     Input('lock-zoom', 'value')],
    [Input('graph-update', 'n_intervals')],
     [State('graphs', 'relayoutData')]
)
def update_graph(data_names,lock_zoom, relayout_data, intervals):
    graphs = []
    update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)
    if len(data_names)>2:
        class_choice = 'col s12 m6 l4'
    elif len(data_names) == 2:
        class_choice = 'col s12 m6 l6'
    else:
        class_choice = 'col s12'

    for data_name in data_names:

        data = go.Scatter(
            x=list(times),
            y=list(data_dict[data_name]),
            name='Scatter',
            fill="tozeroy",
            fillcolor="#6897bb"
            )

        graphs.append(html.Div(dcc.Graph(
            id=data_name,
            animate=False,
            figure={'data': [data],'layout' : go.Layout(xaxis=dict(range=[min(times),max(times)]),
                                                        yaxis=dict(range=[min(data_dict[data_name]),max(data_dict[data_name])]),
                                                        margin={'l':50,'r':1,'t':45,'b':1},
                                                        title='{}'.format(data_name))}
            ), className=class_choice))
        
        print(relayout_data)
        if relayout_data and lock_zoom == 'Lock View':
            if 'xaxis.range[0]' in relayout_data:
                graphs['layout']['xaxis']['range'] = [
                    relayout_data['xaxis.range[0]'],
                    relayout_data['xaxis.range[1]']
                    ]
                if 'yaxis.range[0]' in relayout_data:
                    graphs['layout']['yaxis']['range'] = [
                        relayout_data['yaxis.range[0]'],
                        relayout_data['yaxis.range[1]']
                        ]

    return graphs

external_css = ["https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css"]
for css in external_css:
    app.css.append_css({"external_url": css})

external_js = ['https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js']
for js in external_css:
    app.scripts.append_script({'external_url': js})

if __name__ == ' __main__':
    app.run_server(debug=True)

```

---

<div class="post-metadata">

**Author:** ![xhlu](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/xhlu/32/12736_2.png) [@xhlu](https://community.plotly.com/u/xhlu)\
**Post date:** [February 25, 2021, 3:44am UTC](https://community.plotly.com/t/new-allow-users-to-zoom-into-live-graphs-and-maintain-zoom-for-new-data/50408/2 "2021-02-25T03:44:06Z")

</div>

Hi have you tried this: [📣 Preserving UI State, like Zoom, in dcc.Graph with uirevision with Dash](https://community.plotly.com/t/preserving-ui-state-like-zoom-in-dcc-graph-with-uirevision-with-dash/15793)

---

<div class="post-metadata">

**Author:** ![dasher007](https://avatars.discourse-cdn.com/v4/letter/d/c57346/32.png) [@dasher007](https://community.plotly.com/u/dasher007)\
**Post date:** [March 4, 2021, 5:50pm UTC](https://community.plotly.com/t/new-allow-users-to-zoom-into-live-graphs-and-maintain-zoom-for-new-data/50408/3 "2021-03-04T17:50:59Z")

</div>

Thanks xhlu. I did, I must’ve missed something. But setting `uirevision = True` in `go.Layout` helped me get there. Here’s the code:

```auto

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import time
from collections import deque
import plotly.graph_objs as go
import random

app = dash.Dash('vehicle-data')

max_length = 5000
times = deque(maxlen=max_length)
oil_temps = deque(maxlen=max_length)
intake_temps = deque(maxlen=max_length)
coolant_temps = deque(maxlen=max_length)
rpms = deque(maxlen=max_length)
speeds = deque(maxlen=max_length)
throttle_pos = deque(maxlen=max_length)

data_dict = {"Oil Temperature":oil_temps,
"Intake Temperature": intake_temps,
"Coolant Temperature": coolant_temps,
"RPM":rpms,
"Speed":speeds,
"Throttle Position":throttle_pos}

def update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos):

    times.append(time.time())
    if len(times) == 1:
        #starting relevant values
        oil_temps.append(random.randrange(180,230))
        intake_temps.append(random.randrange(95,115))
        coolant_temps.append(random.randrange(170,220))
        rpms.append(random.randrange(1000,9500))
        speeds.append(random.randrange(30,140))
        throttle_pos.append(random.randrange(10,90))
    else:
        for data_of_interest in [oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos]:
            data_of_interest.append(data_of_interest[-1]+data_of_interest[-1]*random.uniform(-0.0001,0.0001))

    return times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos

times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos = update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)

app.layout = html.Div([
    html.Div([
        html.H2('Vehicle Data',
                style={'float': 'left',
                       }),
        ]),
    dcc.Dropdown(id='vehicle-data-name',
                 options=[{'label': s, 'value': s}
                          for s in data_dict.keys()],
                 value=['Coolant Temperature','Oil Temperature','Intake Temperature'],
                 multi=True
                 ),
    html.Div(children=html.Div(id='graphs'), className='row'),
    dcc.Interval(
        id='graph-update',
        interval=3000,
        n_intervals=0),
    ], className="container",style={'width':'98%','margin-left':10,'margin-right':10,'max-width':50000})

@app.callback(
    Output('graphs','children'),
    [Input('vehicle-data-name', 'value'),
    Input('graph-update', 'n_intervals')]
)
def update_graph(data_names,intervals):
    graphs = []
    update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)
    if len(data_names)>2:
        class_choice = 'col s12 m6 l4'
    elif len(data_names) == 2:
        class_choice = 'col s12 m6 l6'
    else:
        class_choice = 'col s12'

    for data_name in data_names:

        data = go.Scatter(
            x=list(times),
            y=list(data_dict[data_name]),
            name='Scatter',
            fill="tozeroy",
            fillcolor="#6897bb"
            )

        graphs.append(html.Div(dcc.Graph(
            id=data_name,
            animate=False,
            figure={'data': [data],'layout' : go.Layout(uirevision=True, autosize=True, xaxis=dict(range=[min(times),max(times)]),
                                                        yaxis=dict(range=[min(data_dict[data_name]),max(data_dict[data_name])]),
                                                        title='{}'.format(data_name))}
            ), className=class_choice))

    return graphs

external_css = ["https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css"]
for css in external_css:
    app.css.append_css({"external_url": css})

external_js = ['https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js']
for js in external_css:
    app.scripts.append_script({'external_url': js})

if __name__ == ' __main__':
    app.run_server(debug=True)

```
