Bokeh update map tooltip using select or slider

不打扰是莪最后的温柔 提交于 2021-01-29 13:01:07

问题


I am trying to update a worldmap tooltip using a slicer or dropdown select. I got following question which sorted the most of the stuff for a Bokeh Slider custom JS callback


import pandas as pd
import random
from datetime import timedelta

df = pd.DataFrame({'base' : ["2017-01-01" for t in range(10000)],
    'Date' : [random.randint(0, 1035) for t in range(10000)], 
                   'Sales' : [random.random() for t in range(10000)]})
df['base'] = pd.to_datetime(df['base'])
df["Date2"] = df.apply(lambda x: x["base"] + timedelta(days=x['Date']), axis=1)
df.drop(['base', 'Date'], axis=1, inplace=True)
df.set_index('Date2', inplace=True)
df['month'] = df.index.month
df['year'] = df.index.year
df['day'] = df.index.day
df.head()

from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file

from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool


source1=df.groupby(['year','month','day'], as_index = False).sum()
source = source1[source1['year']== 2017]
sourcex = source[source['month'] ==1]
Overall=ColumnDataSource(source)
Curr=ColumnDataSource(sourcex)
boolinit = source['month']==1
view = CDSView(source=Overall, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('day', '@day'),('Sales','@{Sales}{0,0}')],
                   formatters = {'day': 'datetime','Sales': 'numeral'})

p =  figure(title='YEARLY SALES',  plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],  
toolbar_location="above")

r = p.vbar(x='day', top='Sales', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'



callback = CustomJS(args=dict(source=Overall, sc=Curr), code="""       
        var f = select.value;
        sc.data['day'] = [];
        sc.data['Sales'] = [];
        for (var i = 0; i <= source.get_length(); i++){
          if (source.data['month'][i] == f){
            sc.data['day'].push(source.data['day'][i])
            sc.data['Sales'].push(source.data['Sales'][i])
          }
        }
        sc.change.emit();
    """)
select = Select(options=["1","2","3"], title="Month", callback=callback)
callback.args["select"] = select

layout = column(select, p)
#Display plot inline in Jupyter notebook
output_notebook()
output_file("Filterdata.html")
show(layout)

Now, I replicated the same for a worldmap as below:

import pandas as pd
import geopandas as gpd
current_week = 4
shapefile = 'data/countries_110m/ne_110m_admin_0_countries.shp'
gdf = gpd.read_file(shapefile)[['ADMIN', 'ADM0_A3', 'geometry']]
gdf.columns = ['country', 'country_code', 'geometry']
gdf = gdf.drop(gdf.index[159])
df = pd.DataFrame({'Country':['India','India'],
              'SalesGain':['10%','20%'],
                   'Week':[4,5],
                   'Color':[0.2,0.4]
             })

import json
from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file
from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.palettes import brewer
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool,GeoJSONDataSource, LinearColorMapper, ColorBar

from bokeh.plotting import figure, output_file, show
output_file("worldmap.html")


merged = gdf.merge(df, left_on = 'country', right_on = 'Country', how = 'left')
merged_json = json.loads(merged.to_json())
json_data = json.dumps(merged_json)
geosource_all = GeoJSONDataSource(geojson =  json_data)

df_curr = df[df['Week']==current_week]
merged_curr = gdf.merge(df_curr, left_on = 'country', right_on = 'Country', how = 'left')
merged_json_curr = json.loads(merged_curr.to_json())
json_data_curr = json.dumps(merged_json_curr)
geosource_curr = GeoJSONDataSource(geojson =  json_data_curr)


# boolinit = merged['Week']!=current_week
boolinit = merged['Week']==current_week
view = CDSView(source=geosource_all, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('Country', '@Country'),('Sales','@SalesGain')])

#Define a sequential multi-hue color palette.
palette = brewer['YlGnBu'][8]
#Reverse color order so that dark blue is highest value
palette = palette[::-1]
#Instantiate LinearColorMapper that linearly maps numbers in a range, into a sequence of colors. Input nan_color.
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 12, nan_color = '#d9d9d9')
#Define custom tick labels for color bar.
tick_labels = {'0': '0', '2':'2%',  '4':'4%',  '6':'6%', '8':'8%','10':'10%','12':'12%'}
#Create color bar. 
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=6,width = 500, height = 20,
                     border_line_color=None,location = (0,0), orientation = 'horizontal', major_label_overrides = tick_labels)


#Create figure object.
p =  figure(title='Covid-19 Impact',  plot_width=900, plot_height=600, min_border=3,
            tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],toolbar_location="above")

p.title.text_font_size = '20pt'
p.title.text_color = "darkblue"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None


#Add patch renderer to figure. 
p.patches('xs','ys', source = geosource_curr,fill_color = {'field' :'Color', 'transform' : color_mapper},
          line_color = 'black', line_width = 0.25, fill_alpha = 1)
p.add_layout(color_bar, 'below')


callback = CustomJS(args=dict(source=geosource_all, sc=geosource_curr), code="""       
        var f = slider.value;
        sc.data['Country'] = [];
        sc.data['Week'] = [];
        sc.data['SalesGain'] = [];
        for (var i = 0; i <= source.get_length(); i++){
          if ((source.data['Week'][i] == f ) || (source.data['Country'][i] == null) ){
            sc.data['SalesGain'].push(source.data['SalesGain'][i])
            sc.data['Week'].push(source.data['Week'][i])
            sc.data['Country'].push(source.data['Country'][i])
          }
        }
        sc.change.emit();
    """)
# select = Select(options=["201951","201952","201953"], title="Week", callback=callback)
# callback.args["select"] = select
# layout = column(select, p)

slider = Slider(start=1, end=5, value=current_week, step=1, title="Month", callback=callback)
callback.args["slider"] = slider
layout = column(slider, p)

#Display plot inline in Jupyter notebook
output_notebook()

show(layout)

But in this case, as soon as I click on the slider, tooltip data vanish away. World map input file can be found here to smoothly run the code: https://github.com/CrazyDaffodils/Interactive-Choropleth-Map-Using-Python/tree/master/bokeh-app/data


回答1:


A tooltip disappears after a small delay after you move your mouse off the glyph that has triggered it.

Right now, Bokeh doesn't have any built-in way of changing that behavior. There's an open issue for that with a workaround that you might be able to adapt to your needs: https://github.com/bokeh/bokeh/issues/5724



来源:https://stackoverflow.com/questions/60838179/bokeh-update-map-tooltip-using-select-or-slider

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!