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

Dash Plotly地图填充区域与文本框交互的Callback实现求助

解决Dash中提交按钮触发地图填充区域更新的问题

我来帮你搞定这个问题!核心是调整Callback的触发逻辑,同时修正地图图表的坐标构建方式,具体步骤如下:

1. 调整Callback关联(关键!)

你之前的Callback是实时响应输入框变化,但我们需要点击提交按钮才触发更新,所以要把输入框的值从Input改成State,只把提交按钮的n_clicks作为Input。同时,输出目标要从output的children改成graphQ的figure,这样才能更新地图。

修改后的Callback代码:

@app.callback(
    Output("graphQ", "figure"),
    Input("submit_button", "n_clicks"),
    State("input1", "value"),  # Lat1
    State("input2", "value"),  # Long1
    State("input3", "value"),  # Lat2
    State("input4", "value"),  # Long2
    prevent_initial_call=True,  # 初始加载时不触发
)
def update_map(n_clicks, lat1, lon1, lat2, lon2):
    # 先处理输入为空的情况,避免报错
    if not all([lat1, lon1, lat2, lon2]):
        # 返回一个默认的空地图,或者提示用户输入完整
        return go.Figure(go.Scattermapbox()).update_layout(
            mapbox={'style': "stamen-terrain", 'center': {'lon': -73, 'lat': 46}, 'zoom': 5},
            showlegend=False,
            title="请输入完整的两组经纬度后点击提交"
        )
    
    # 构建闭合的多边形坐标(这里以两个点为对角的矩形为例,你可以根据需求调整)
    # 闭合路径需要首尾坐标一致,才能用fill="toself"填充
    lats = [lat1, lat1, lat2, lat2, lat1]
    lons = [lon1, lon2, lon2, lon1, lon1]
    
    # 生成地图图表
    fig = go.Figure(go.Scattermapbox(
        fill="toself",
        lon=lons,
        lat=lats,
        marker={'size': 10, 'color': "orange"},
        name="填充区域"
    ))
    
    # 更新地图布局,自动调整视野到填充区域
    fig.update_layout(
        mapbox={
            'style': "stamen-terrain",
            'center': {'lon': (lon1 + lon2)/2, 'lat': (lat1 + lat2)/2},  # 居中到两个点的中间
            'zoom': 8  # 可以根据实际情况调整缩放级别
        },
        showlegend=False,
        title="自定义填充区域"
    )
    
    return fig

2. 优化输入框代码(可选,但更简洁)

你现在的四个输入框函数几乎一样,可以合并成一个,减少重复代码,同时修正经纬度的合理取值范围:

def custom_input(paragraph_text, input_id, min_value=-90, max_value=90, step=0.01):
    # 纬度范围是-90到90,经度范围是-180到180,修正之前的不合理范围
    return html.Div(
        children=[
            html.P(paragraph_text, style={"paddingRight": 10}),
            dbc.Input(type="float", min=min_value, max_value=max_value, step=step, id=input_id),
        ],
        style={"display": "flex"},
    )

调用时只需区分经纬度的范围:

dbc.Col(custom_input("Lat1", "input1")),
dbc.Col(custom_input("Long1", "input2", min_value=-180, max_value=180)),
dbc.Col(custom_input("Lat2", "input3")),
dbc.Col(custom_input("Long2", "input4", min_value=-180, max_value=180)),

3. 关键知识点解释

  • State vs Input:State用来存储输入值,但不会触发Callback,只有Input(这里是提交按钮的n_clicks)变化时才会触发更新,完美符合“点击提交才更新”的需求。
  • 闭合多边形:fill="toself"需要路径是闭合的,所以我们把矩形的四个角坐标按顺序排列,最后回到起点,这样才能正确填充区域。
  • prevent_initial_call:设置为True可以避免页面加载时就触发Callback,因为初始时n_clicks是0,会触发一次,加上这个参数就不会了。

完整的整合代码示例

把上面的修改整合到你的现有代码里,完整代码如下:

import dash
from dash import html, dcc, Input, Output, State
import dash_bootstrap_components as dbc
import plotly.graph_objects as go

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

# 优化后的输入框函数
def custom_input(paragraph_text, input_id, min_value=-90, max_value=90, step=0.01):
    return html.Div(
        children=[
            html.P(paragraph_text, style={"paddingRight": 10}),
            dbc.Input(type="float", min=min_value, max_value=max_value, step=step, id=input_id),
        ],
        style={"display": "flex"},
    )

# 布局
app.layout = html.Div(children=[html.Div([
    html.H1("Query1", style={'text-align': 'center','fontSize': 30}),
    dbc.Row(
        [
            dbc.Col(dcc.Graph(id="graphQ", figure={}), md=8),
            dbc.Col(
                children=[
                    dbc.Row(
                        [
                            dbc.Col(custom_input("Lat1", "input1")),
                            dbc.Col(custom_input("Long1", "input2", min_value=-180, max_value=180)),
                        ],
                        style={"paddingBottom": 30},
                    ),
                    dbc.Row(
                        [
                            dbc.Col(custom_input("Lat2", "input3")),
                            dbc.Col(custom_input("Long2", "input4", min_value=-180, max_value=180)),
                        ],
                        style={"paddingBottom": 30},
                    ),
                    html.Div(id="output"),
                    html.Button('Submit', id='submit_button', n_clicks=0),
                ],
                md=4,
            ),
        ]
    ),
])])

# 地图更新的Callback
@app.callback(
    Output("graphQ", "figure"),
    Input("submit_button", "n_clicks"),
    State("input1", "value"),
    State("input2", "value"),
    State("input3", "value"),
    State("input4", "value"),
    prevent_initial_call=True,
)
def update_map(n_clicks, lat1, lon1, lat2, lon2):
    if not all([lat1, lon1, lat2, lon2]):
        return go.Figure(go.Scattermapbox()).update_layout(
            mapbox={'style': "stamen-terrain", 'center': {'lon': -73, 'lat': 46}, 'zoom': 5},
            showlegend=False,
            title="请输入完整的两组经纬度后点击提交"
        )
    
    # 构建闭合矩形的坐标
    lats = [lat1, lat1, lat2, lat2, lat1]
    lons = [lon1, lon2, lon2, lon1, lon1]
    
    fig = go.Figure(go.Scattermapbox(
        fill="toself",
        lon=lons,
        lat=lats,
        marker={'size': 10, 'color': "orange"},
    ))
    
    fig.update_layout(
        mapbox={
            'style': "stamen-terrain",
            'center': {'lon': (lon1 + lon2)/2, 'lat': (lat1 + lat2)/2},
            'zoom': 8
        },
        showlegend=False,
        title="自定义填充区域"
    )
    
    return fig

# 保留原来的输出Callback(可选,用来显示输入值)
@app.callback(
    Output("output", "children"),
    Input("submit_button", "n_clicks"),
    State("input1", "value"),
    State("input2", "value"),
    State("input3", "value"),
    State("input4", "value"),
    prevent_initial_call=True,
)
def update_output(n_clicks, input1,input2,input3,input4):
    return 'Lat1: {} and Long1: {}<br>Lat2: {} and Long2: {}'.format(input1, input2, input3, input4)

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

这样运行后,你输入两组经纬度,点击提交按钮,左侧地图就会显示对应的矩形填充区域啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 10:02:31