如何才能使图表上的缩放即使在加载新数据时也保持持久?

问题描述 投票:0回答:2

我有一个看起来像这样的图表-

import datetime

import dash
from dash import dcc, html
import plotly
from dash.dependencies import Input, Output
from pyorbital.orbital import Orbital
satellite = Orbital('TERRA')

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
    html.Div([
        html.H4('TERRA Satellite Live Feed'),
        html.Div(id='live-update-text'),
        dcc.Graph(id='live-update-graph'),
        dcc.Interval(
            id='interval-component',
            interval=10*1000, # in milliseconds
            n_intervals=0
        )
    ])
)


@app.callback(Output('live-update-text', 'children'),
              Input('interval-component', 'n_intervals'))
def update_metrics(n):
    lon, lat, alt = satellite.get_lonlatalt(datetime.datetime.now())
    style = {'padding': '5px', 'fontSize': '16px'}
    return [
        html.Span('Longitude: {0:.2f}'.format(lon), style=style),
        html.Span('Latitude: {0:.2f}'.format(lat), style=style),
        html.Span('Altitude: {0:0.2f}'.format(alt), style=style)
    ]


# Multiple components can update everytime interval gets fired.
@app.callback(Output('live-update-graph', 'figure'),
              Input('interval-component', 'n_intervals'))
def update_graph_live(n):
    satellite = Orbital('TERRA')
    data = {
        'time': [],
        'Latitude': [],
        'Longitude': [],
        'Altitude': []
    }

    # Collect some data
    for i in range(180):
        time = datetime.datetime.now() - datetime.timedelta(seconds=i*20)
        lon, lat, alt = satellite.get_lonlatalt(
            time
        )
        data['Longitude'].append(lon)
        data['Latitude'].append(lat)
        data['Altitude'].append(alt)
        data['time'].append(time)

    # Create the graph with subplots
    fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)
    fig['layout']['margin'] = {
        'l': 30, 'r': 10, 'b': 30, 't': 10
    }
    fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}

    fig.append_trace({
        'x': data['time'],
        'y': data['Altitude'],
        'name': 'Altitude',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 1, 1)
    fig.append_trace({
        'x': data['Longitude'],
        'y': data['Latitude'],
        'text': data['time'],
        'name': 'Longitude vs Latitude',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 2, 1)

    return fig


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

现在,如果我放大图表以查看特定的数据部分,然后数据刷新,我就会失去缩放位置。我希望即使在加载新数据时也能保持缩放位置。也许将更新设置为后台更新就是答案。

python python-3.x plotly plotly-dash plotly-python
2个回答
3
投票

看起来这应该可行。我添加了一个

dcc.Store
来保存每个子图的相关缩放信息,并且在重新加载时,我从该
dcc.Store
的数据中读取,以查看它是否有任何历史缩放信息可供加载。我使用这个来源作为指导 - https://community.plotly.com/t/how-to-save-current-zoom-and-position-after-filtering/5310.

import datetime

import dash
from dash import dcc, html
import plotly
from dash.dependencies import Input, Output, State
from pyorbital.orbital import Orbital
satellite = Orbital('TERRA')

external_stylesheets = [
    'https://codepen.io/chriddyp/pen/bWLwgP.css',
    {
        'href': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
        'rel': 'stylesheet',
        'integrity': 'sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO',
        'crossorigin': 'anonymous'
    }
]


app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
    html.Div([
        html.H4('TERRA Satellite Live Feed'),
        html.Div(id='live-update-text'),
        dcc.Graph(id='live-update-graph'),
        dcc.Interval(
            id='interval-component',
            interval=10*1000, # in milliseconds
            n_intervals=0
        ),
        dcc.Store(id='zoom_info')
    ])
)


@app.callback(
    Output('zoom_info', 'data'),
    [Input('live-update-graph', 'relayoutData'),
     Input('zoom_info', 'data')]
)
def update_zoom_info(relayout_data, zoom_info):
    if zoom_info is None:
        return relayout_data
    else:
        zoom_info.update(relayout_data)
        return zoom_info


@app.callback(Output('live-update-text', 'children'),
              Input('interval-component', 'n_intervals'))
def update_metrics(n):
    lon, lat, alt = satellite.get_lonlatalt(datetime.datetime.now())
    style = {'padding': '5px', 'fontSize': '16px'}
    return [
        html.Span('Longitude: {0:.2f}'.format(lon), style=style),
        html.Span('Latitude: {0:.2f}'.format(lat), style=style),
        html.Span('Altitude: {0:0.2f}'.format(alt), style=style)
    ]

# Multiple components can update everytime interval gets fired.
@app.callback(Output('live-update-graph', 'figure'),
              Input('interval-component', 'n_intervals'),
              State('zoom_info', 'data'))
def update_graph_live(n, zoom_info):
    satellite = Orbital('TERRA')
    data = {
        'time': [],
        'Latitude': [],
        'Longitude': [],
        'Altitude': []
    }

    # Collect some data
    for i in range(180):
        time = datetime.datetime.now() - datetime.timedelta(seconds=i*20)
        lon, lat, alt = satellite.get_lonlatalt(
            time
        )
        data['Longitude'].append(lon)
        data['Latitude'].append(lat)
        data['Altitude'].append(alt)
        data['time'].append(time)

    # Create the graph with subplots
    fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)
    fig['layout']['margin'] = {
        'l': 30, 'r': 10, 'b': 30, 't': 10
    }
    fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}

    fig.append_trace({
        'x': data['time'],
        'y': data['Altitude'],
        'name': 'Altitude',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 1, 1)
    fig.append_trace({
        'x': data['Longitude'],
        'y': data['Latitude'],
        'text': data['time'],
        'name': 'Longitude vs Latitude',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 2, 1)

    if zoom_info:
        for axis_name in ['axis', 'axis2']:
            if f'x{axis_name}.range[0]' in zoom_info:
                fig['layout'][f'x{axis_name}']['range'] = [
                    zoom_info[f'x{axis_name}.range[0]'],
                    zoom_info[f'x{axis_name}.range[1]']
                ]
            if f'y{axis_name}.range[0]' in zoom_info:
                fig['layout'][f'y{axis_name}']['range'] = [
                    zoom_info[f'y{axis_name}.range[0]'],
                    zoom_info[f'y{axis_name}.range[1]']
                ]
    return fig


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


0
投票

我尝试了@erap129的答案,效果很好,点赞。

我只是想添加另一种可能性: 在我的例子中,只需在

uirevision=True
区域添加
fig.update_layout(..)
也可以实现相同的行为(--> 始终保持缩放级别)。也许对于路过这里的人来说这也值得一试。

仍然可以定义哪些操作应该重置布局,请在此处阅读详细信息: https://community.plotly.com/t/preserving-ui-state-like-zoom-in-dcc-graph-with-uirevision-with-dash/15793

# none working code snippet, just to understand where to put uirevision

fig = go.Figure(go.Densitymapbox(lat=df['lat'],
                                 lon=df['lng'],
                                 z=np.log(df['dens']),
                                 zauto=False,
                                 radius=5,
                                 opacity=0.8,
                                 colorscale='YlOrRd_r',
                                 )
               )
fig.update_layout(mapbox_style=map_chosen,
                  mapbox_zoom=zoom,
                  mapbox_center=center,
                  margin=dict(t=0, b=0, l=0, r=0),  # top bottom left right
                  uirevision=True
                 )
fig.update_traces(showscale=False)

© www.soinside.com 2019 - 2024. All rights reserved.