You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何用Panel和Altair实现地图点选择联动折线图?

解决Altair+Panel地图点选联动折线图的方案

核心前提:对齐数据关联键

首先确保地图数据和折线数据存在唯一关联字段(比如id),避免用经纬度作为关联(可能存在重复)。示例数据结构:

  • 地图数据:包含idlatitudelongitudeprobability
  • 折线数据:包含idtimestampvalue(每个id对应一条时间序列)

步骤1:创建带点选交互的地图

用Altair的point_selection实现点击选点,绑定唯一关联字段确保精准选择:

import altair as alt
import panel as pn
import pandas as pd

# 示例数据
geo_df = pd.DataFrame({
    'id': [1,2,3],
    'latitude': [39.9, 31.2, 23.1],
    'longitude': [116.4, 121.5, 113.3],
    'probability': [0.8, 0.6, 0.9]
})

line_df = pd.DataFrame({
    'id': [1,1,1,2,2,2,3,3,3],
    'timestamp': pd.date_range('2024-01-01', periods=3).repeat(3),
    'value': [10,15,20,5,8,12,18,22,25]
})

# 创建点选交互:点击选中、双击清除,绑定id字段
point_select = alt.selection_point(fields=['id'], on='click', clear='dblclick', max_entries=1)

# 绘制交互地图
map_chart = alt.Chart(geo_df).mark_circle(size=100).encode(
    longitude='longitude:Q',
    latitude='latitude:Q',
    # 选中点标红,未选中蓝
    color=alt.condition(point_select, alt.value('red'), alt.value('blue')),
    tooltip=['id', 'probability']
).add_params(point_select).properties(width=400, height=400)

步骤2:编写折线图联动更新函数

用Panel的pn.bind绑定选择器,根据选中的id过滤折线数据生成对应图表:

def update_line_chart(selected):
    if not selected:
        # 未选择时显示提示图
        return alt.Chart(pd.DataFrame({'timestamp': [], 'value': []})).mark_line().encode(
            x='timestamp:T', y='value:Q'
        ).properties(title='请选择地图上的点')
    # 获取选中点的id
    selected_id = selected[0]['id']
    # 过滤对应时间序列数据
    filtered_df = line_df[line_df['id'] == selected_id]
    # 生成折线图
    return alt.Chart(filtered_df).mark_line().encode(
        x='timestamp:T',
        y='value:Q'
    ).properties(width=400, height=400, title=f'点ID {selected_id} 的时间序列')

# 绑定选择器与更新函数
line_chart = pn.bind(update_line_chart, selected=point_select)

步骤3:添加数据源切换菜单并布局应用

通过Panel下拉框切换数据源,同步更新地图和折线图的数据源:

# 数据源选择下拉框
data_source = pn.widgets.Select(options=['数据源A', '数据源B'], value='数据源A')

# 数据源切换逻辑(替换为实际加载逻辑)
def switch_data(source):
    if source == '数据源A':
        return geo_df, line_df
    else:
        # 模拟切换到数据源B
        new_geo = geo_df.copy()
        new_geo['probability'] += 0.1
        new_line = line_df.copy()
        new_line['value'] *= 1.2
        return new_geo, new_line

# 更新地图和折线图数据源
def update_map_and_line(source):
    new_geo, new_line = switch_data(source)
    # 更新地图数据
    updated_map = map_chart.transform_data(new_geo)
    # 更新全局折线数据(实际项目可改用类属性或闭包变量)
    global line_df
    line_df = new_line
    return updated_map

# 绑定数据源切换到地图
updated_map = pn.bind(update_map_and_line, source=data_source)

# 组装应用布局
app = pn.Column(
    data_source,
    pn.Row(updated_map, line_chart)
)

# 启动应用
app.servable()

常见问题排查

  • 如果联动无响应:检查关联字段id在两个数据集是否完全匹配,确保选择器fields参数指定的是关联字段而非经纬度。
  • 若出现多选中:在point_selection中添加max_entries=1限制单次选一个点。
  • 数据源切换后联动失效:确保切换数据源时同步更新了地图和折线图的底层数据,避免只更新其一。

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

火山引擎 最新活动