Bokeh滑块组件无法更新地理地图绘图问题求助
问题原因和修正方案
核心错误点
update_map回调函数中调用plot_area生成了新的绘图对象,但没有将新对象替换到已挂载的布局中,原有页面上的绘图实例完全没有被修改,自然不会有更新效果。
get_dataset函数存在参数名笔误:形参声明为dataframe,但函数内部调用时用的是未定义的geodataframe变量,会直接抛出变量不存在的错误。
- 绘图逻辑设计不合理:每次触发更新都重新创建整个绘图实例、瓦片图层、颜色条等组件,不仅效率极低,也不符合Bokeh Server的更新逻辑——正确做法是复用已有的绘图实例,仅更新数据源的内容。
plot_area函数内部写了导入语句,虽然不直接导致报错,但不符合Python编码规范,也会影响运行效率。
修正步骤
1. 修正get_dataset函数
def get_dataset(dataframe, column, geometry_column, val): # 把函数内的geodataframe改成参数名dataframe g = dataframe[dataframe[column] == val] g_gdf = gpd.GeoDataFrame(g, geometry=geometry_column) g_gdf.crs = 'EPSG:3857' return g_gdf
2. 重构绘图逻辑,抽离可复用组件
不要每次更新都重建整个图,我们把数据源、绘图实例、patch渲染器都抽出来复用:
# 所有导入统一放到文件顶部 from bokeh.plotting import figure from bokeh.tile_providers import get_provider from bokeh.models import GeoJSONDataSource, ColorBar, BasicTicker, LinearColorMapper from bokeh.models.widgets import Slider, Paragraph from bokeh.layouts import row, widgetbox from bokeh.io import curdoc import geopandas as gpd # 先计算全局颜色映射范围,避免每次更新颜色条范围变化 global_min = precipitacao_por_bacia['val'].min() global_max = precipitacao_por_bacia['val'].max() # 初始化数据源 initial_g = get_dataset(precipitacao_por_bacia,'datahora','st_astext',dias[0]) initial_g.reset_index(drop=True,inplace=True) initial_g['datahora'] = initial_g['datahora'].apply(lambda x: str(x)) poligonos_ds = GeoJSONDataSource(geojson=initial_g.to_json()) # 初始化绘图实例,仅创建一次 tile_provider = get_provider('CARTODBPOSITRON_RETINA') p = figure(title='Precipitação ', plot_width=500, plot_height=500, tooltips=[('Bacia','@bacia_nome'), ('precipitação','@val')]) p.add_tile(tile_provider) p.axis.visible = False # 添加颜色映射和图层,仅创建一次 color_mapper = LinearColorMapper(palette='Viridis256', low=global_min, high=global_max) color_bar = ColorBar(color_mapper=color_mapper, ticker=BasicTicker(), location=(0,0), label_standoff=12) p.patches('xs','ys', source=poligonos_ds, fill_alpha=1, line_width=0.5, line_color='black', fill_color={'field' :'val', 'transform': color_mapper}) p.add_layout(color_bar, 'left') # 重写update回调,仅更新数据源 def update_map(attrname, old, new): g = get_dataset(precipitacao_por_bacia,'datahora','st_astext',dias[slider.value]) g.reset_index(drop=True,inplace=True) g['datahora'] = g['datahora'].apply(lambda x: str(x)) # 直接更新数据源的geojson属性即可触发绘图更新 poligonos_ds.geojson = g.to_json() output.text = str(g['datahora'][0]) # 控件和布局逻辑保持不变 slider = Slider(title="Day", value=0, start=0, end=409) output = Paragraph() slider.on_change('value', update_map) layout = row(p, widgetbox(slider, output)) curdoc().add_root(layout)
补充说明
如果你的应用需要根据过滤后的数据动态调整颜色条范围,可以在update回调中同步修改color_mapper.low和color_mapper.high属性即可。
内容的提问来源于stack exchange,提问作者Gabriel Lima
相关产品推荐
相关产品推荐

