如何在Python Streamlit-Folium地图中点击弹窗后获取坐标?
问题分析与解决方案
你的核心问题在于前端JavaScript无法直接调用后端Python函数传递数据——Folium的JS代码运行在浏览器中,而Python代码运行在Streamlit服务器环境,两者没有直接的函数调用通道,这就是你遇到result未定义错误的根本原因。
下面提供两种可行的实现方案,根据你的需求选择:
方案一:利用st_folium内置交互(最简便)
如果不需要弹窗按钮触发,直接点击标记即可获取坐标并关联DataFrame数据,st_folium本身会返回用户的交互信息:
import folium import streamlit as st from streamlit_folium import st_folium import pandas as pd # 初始化地图 map = folium.Map(location=[38,-96.5], zoom_start=4) df.reset_index(drop=True,inplace=True) cords = df[["lat","lon"]].values.tolist() # 添加所有标记 for i in range(df.shape[0]): row = df.iloc[i] popup_content = f""" {row["name"]}<br> {row["date"]}<br> {row["city"]}, {row["state"]} """ folium.Marker(cords[i], popup=folium.Popup(popup_content, max_width=300)).add_to(map) # 渲染地图并获取交互数据 i_map = st_folium(map, width=700, height=450) # 处理点击事件 if i_map.get("last_object_clicked"): # 获取点击标记的坐标 lat = i_map["last_object_clicked"]["lat"] lon = i_map["last_object_clicked"]["lng"] st.write("选中的坐标:", (lat, lon)) # 匹配DataFrame中的对应数据 selected_row = df[(df["lat"] == lat) & (df["lon"] == lon)] if not selected_row.empty: st.write("该位置详细信息:") st.dataframe(selected_row)
方案二:弹窗按钮触发坐标传递(满足你的原始需求)
如果必须通过弹窗内的按钮触发,需要通过前端JS将坐标传递到Streamlit的会话状态(session_state),实现前后端数据同步:
import folium import streamlit as st from streamlit_folium import st_folium import pandas as pd import jinja2 # 初始化会话状态,存储选中的坐标 if "selected_coords" not in st.session_state: st.session_state.selected_coords = None map = folium.Map(location=[38,-96.5], zoom_start=4) df.reset_index(drop=True,inplace=True) cords = df[["lat","lon"]].values.tolist() for i in range(df.shape[0]): row = df.iloc[i] # 给每个弹窗的输入框设置唯一ID,避免多个标记冲突 popup_content = f""" <input type="text" value="{cords[i][0]}, {cords[i][1]}" id="location-{i}" readonly> <br> {row["name"]}<br> {row["date"]}<br> {row["city"]}, {row["state"]}<br> <button onclick="sendCoords({i})">Show More</button> """ folium.Marker(cords[i], popup=folium.Popup(popup_content, max_width=300)).add_to(map) # 添加JS脚本,将坐标发送到Streamlit会话状态 el = folium.MacroElement().add_to(map) el._template = jinja2.Template(""" {% macro script(this, kwargs) %} function sendCoords(index) { // 获取当前标记的坐标 var coordInput = document.getElementById("location-" + index); var coords = coordInput.value.trim(); // 发送坐标到Streamlit会话状态 window.parent.postMessage({ type: 'streamlit:setSessionState', key: 'selected_coords', value: coords }, '*'); } {% endmacro %} """) # 渲染地图 i_map = st_folium(map, width=700, height=450) # 展示选中的坐标及对应数据 if st.session_state.selected_coords: st.write("选中的坐标:", st.session_state.selected_coords) # 解析坐标字符串为浮点数 lat, lon = [float(x.strip()) for x in st.session_state.selected_coords.split(",")] # 匹配DataFrame数据 selected_row = df[(df["lat"] == lat) & (df["lon"] == lon)] if not selected_row.empty: st.write("该位置详细信息:") st.dataframe(selected_row)
关键注意点
- 避免ID冲突:多个标记的弹窗输入框必须设置唯一ID,否则JS会获取错误的坐标值。
- 会话状态同步:通过
window.parent.postMessage实现前端JS与后端Python的会话状态同步,这是Streamlit中前后端数据交互的标准方式。 - 坐标解析:处理坐标字符串时要注意去除空格,避免转换浮点数时出错。
内容的提问来源于stack exchange,提问作者Kyle Chitty23
相关产品推荐
相关产品推荐

