You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Dash中line_mapbox与时间序列图的双向交叉筛选

实现步骤

核心通过Dash的回调上下文判断交互触发源,同时绑定数据字段实现双向过滤,完整可运行代码如下:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash import callback_context
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go

app = dash.Dash()
server = app.server

np.random.seed(0)
# 随机虚拟数据
n = 100
time = np.linspace(0, 1, n)
latitude = 50 + 0.001 * np.cumsum(np.random.randn(n))
longitude = 2 + 0.001 * np.cumsum(np.random.randn(n))
altitude = (time - 0.5) ** 2
heartrate = 100 + np.cumsum(np.random.randn(n))
df = pd.DataFrame(
    {
        "time": time,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "heartrate": heartrate,
    }
)

# 初始地图配置,绑定time为自定义数据,开启选择模式
init_map_fig = px.line_mapbox(df, lat="latitude", lon="longitude", zoom=12, height=800, custom_data=['time'])
init_map_fig.update_layout(mapbox_style="stamen-terrain", clickmode='event+select', uirevision='constant')

app.layout = html.Div(
    [
        html.Div(
            [
                dcc.Graph(id="mymap", figure=init_map_fig),
            ]
        ),
        html.Div(
            [
                dcc.Graph(id="time-series"),
                dcc.Dropdown(
                    id="column",
                    options=[
                        {"label": i, "value": i} for i in ["altitude", "heartrate"]
                    ],
                    value="altitude",
                ),
            ]
        ),
    ]
)

@app.callback(
    [Output("mymap", "figure"),
     Output("time-series", "figure")],
    [Input("column", "value"),
     Input("time-series", "relayoutData"),
     Input("mymap", "selectedData")],
    prevent_initial_call=False
)
def update_both_charts(column, relayout_data, selected_data):
    filtered_df = df.copy()
    trigger_id = callback_context.triggered[0]['prop_id'] if callback_context.triggered else ''

    # 处理时间序列图刷选y轴区间的情况
    if trigger_id == 'time-series.relayoutData':
        if relayout_data and 'yaxis.range[0]' in relayout_data and 'yaxis.range[1]' in relayout_data:
            y_min = relayout_data['yaxis.range[0]']
            y_max = relayout_data['yaxis.range[1]']
            filtered_df = df[(df[column] >= y_min) & (df[column] <= y_max)]
    
    # 处理地图选点的情况
    elif trigger_id == 'mymap.selectedData':
        if selected_data and 'points' in selected_data and len(selected_data['points'])>0:
            time_list = [p['customdata'][0] for p in selected_data['points']]
            t_min, t_max = min(time_list), max(time_list)
            filtered_df = df[(df['time'] >= t_min) & (df['time'] <= t_max)]

    # 生成更新后的地图
    map_fig = px.line_mapbox(filtered_df, lat="latitude", lon="longitude", custom_data=['time'])
    map_fig.update_layout(mapbox_style="stamen-terrain", clickmode='event+select', uirevision='constant', height=800)

    # 生成更新后的时间序列图,开启刷选模式
    ts_fig = {
        "data": [go.Scatter(x=filtered_df['time'], y=filtered_df[column], mode='lines')],
        "layout": go.Layout(dragmode='select')
    }

    return map_fig, ts_fig

app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

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

使用说明

  • 时间序列图:按住鼠标左键框选y轴区间,地图会自动展示对应数据点的运动轨迹
  • 地图:按住shift+鼠标左键框选轨迹点,时间序列图会自动展示选中点对应时间区间的海拔/心率数据
  • 双击任意图表可重置选择状态,恢复展示全量数据

内容的提问来源于stack exchange,提问作者Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 18:18:05