Bokeh Multi-Select Metrics for Hover Tooltip

Hi Bokeh Community,

Originally, I’m trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:

https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4

I follow the examples pretty much function by function with the exception of curdoc as I’m just trying to show a working example and not add to a bokeh server (?) I’m not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don’t think the javascript is working beneath it.

Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I’m doing this for a big graduating project for my masters!

Here’s my code
import pickle

import numpy as np

import pandas as pd

import geopandas as gpd

import bokeh

from bokeh.io import output_notebook, show

from bokeh.plotting import figure

from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS

from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select

from bokeh.io import output_file, show, curdoc, save

from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.plotting import gmap

from shapely.geometry import Polygon, Point

import matplotlib.pyplot as plt

from matplotlib.pyplot import *

from gmplot import gmplot

palette = palette[9]

palette.reverse()

color_mapper = LogColorMapper(palette=palette)

%matplotlib inline

df = pickle.load(open(“pickles/grid_df.pkl”, “rb”))

``
df.drop([“halibut_trawl_sites”, “marinas”, “oil_platforms”, “piers”], axis = 1, inplace = True)

#Convert columns to lists for column data source
xs = [df[“geometry”][i].exterior.xy[0].tolist() for i in range(df.shape[0])]

ys = [df[“geometry”][i].exterior.xy[1].tolist() for i in range(df.shape[0])]

biomass_kg = df.biomass.tolist()

water_depth_m = df.depth.tolist()

sst = df.mean_sst.tolist()

species = [df.critical_species[i][0][1] for i in range(df.shape[0])]

critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]

county = [df.county[i][0][1] for i in range(df.shape[0])]

county_distance = [df.county[i][0][2] for i in range(df.shape[0])]

pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]

pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]

estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]

estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]

avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]

avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]

qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]

qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]

unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]

ndvi = df.ndvi.tolist()

min_light = df.z_min_light.tolist()

mixed_l = df.z_mixedl.tolist()

floor_temp = df.floor_temp.tolist()

viable = df.viable.tolist()

boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]

boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]

shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]

shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]

shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]

shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]

aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]

admin_kelp = df.admin_kelp_bed.tolist()

#Gmap plotting - I’m hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key

def create_plot():

p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)



source = ColumnDataSource(data=dict(

    x = xs,

    y = ys,

    kelp = biomass_kg,

    bathymetry = water_depth_m,

    sst = sst,

    species = species,

    critical_species_distance = critical_species_distance,

    county = county,

    county_distance = county_distance,

    pretected_areas = pretected_areas,

    pretected_areas_distance = pretected_areas_distance,

    estab = estab,

    avg_emp = avg_emp,

    qcew = qcew,

    unemployment = unemployment,

    ndvi = ndvi,

    min_light = min_light,

    mixed_l = mixed_l,

    floor_temp = floor_temp,

    viable = viable,

    boat_launches = boat_launches,

    boat_launches_distance = boat_launches_distance,

    shoretype = shoretype,

    shoretype_distance = shoretype_distance,

    shoretype2 = shoretype2,

    shoretype2_distance = shoretype2_distance,

    aerial_kelp = aerial_kelp,

    admin_kelp = admin_kelp))

v = ms.value

patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper}, 

      fill_alpha=0.8, line_color = "white", line_width = 0.7)

p.add_glyph(source, patched)

tooltip_ms = [(x.replace("@", ""), x) for x in v]

p.add_tools(HoverTool(tooltips=tooltip_ms))

return p

def update(attribute, old, new):

l.children[1] = None

l.children[1] = create_plot()

#Help! Still won’t update :frowning:

ms.on_change(‘value’, update)

l = Column(ms, create_plot())

show(l)

``

grid_df.pkl (725 KB)

Hi,

By calling show, you are creating a standalone HTML/JS document that is not connected to any persistent python process. So there is nothing to run your Python update callback. Use of real python callbacks requires running on the Bokeh server (that is the purpose of the Bokeh server, to be the thing that runs real Python callbacks). You can find more information about the bokeh server here:

  https://bokeh.pydata.org/en/latest/docs/user_guide/server.html

If you don't want to use the Bokeh server then you will be limited to CustomJS callbacks, which only execute JavaScript code in the browser.

Thanks,

Bryan

···

On Jul 29, 2018, at 12:56, [email protected] wrote:

Hi Bokeh Community,

Originally, I'm trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:
https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4

I follow the examples pretty much function by function with the exception of curdoc as I'm just trying to show a working example and not add to a bokeh server (?) I'm not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don't think the javascript is working beneath it.

Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I'm doing this for a big graduating project for my masters!

Here's my code
import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

df = pickle.load(open("pickles/grid_df.pkl", "rb"))
df.drop(["halibut_trawl_sites", "marinas", "oil_platforms", "piers"], axis = 1, inplace = True)

#Convert columns to lists for column data source
xs = [df["geometry"][i].exterior.xy[0].tolist() for i in range(df.shape[0])]
ys = [df["geometry"][i].exterior.xy[1].tolist() for i in range(df.shape[0])]
biomass_kg = df.biomass.tolist()
water_depth_m = df.depth.tolist()
sst = df.mean_sst.tolist()
species = [df.critical_species[i][0][1] for i in range(df.shape[0])]
critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]
county = [df.county[i][0][1] for i in range(df.shape[0])]
county_distance = [df.county[i][0][2] for i in range(df.shape[0])]
pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]
pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]
estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]
estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]
avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]
avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]
qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]
qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]
unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]
ndvi = df.ndvi.tolist()
min_light = df.z_min_light.tolist()
mixed_l = df.z_mixedl.tolist()
floor_temp = df.floor_temp.tolist()
viable = df.viable.tolist()
boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]
boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]
shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]
shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]
shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]
shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]
aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]
admin_kelp = df.admin_kelp_bed.tolist()

#Gmap plotting - I'm hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key

def create_plot():
    p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)
    
    source = ColumnDataSource(data=dict(
        x = xs,
        y = ys,
        kelp = biomass_kg,
        bathymetry = water_depth_m,
        sst = sst,
        species = species,
        critical_species_distance = critical_species_distance,
        county = county,
        county_distance = county_distance,
        pretected_areas = pretected_areas,
        pretected_areas_distance = pretected_areas_distance,
        estab = estab,
        avg_emp = avg_emp,
        qcew = qcew,
        unemployment = unemployment,
        ndvi = ndvi,
        min_light = min_light,
        mixed_l = mixed_l,
        floor_temp = floor_temp,
        viable = viable,
        boat_launches = boat_launches,
        boat_launches_distance = boat_launches_distance,
        shoretype = shoretype,
        shoretype_distance = shoretype_distance,
        shoretype2 = shoretype2,
        shoretype2_distance = shoretype2_distance,
        aerial_kelp = aerial_kelp,
        admin_kelp = admin_kelp))

    v = ms.value
    patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
          fill_alpha=0.8, line_color = "white", line_width = 0.7)
    p.add_glyph(source, patched)
    tooltip_ms = [(x.replace("@", ""), x) for x in v]
    p.add_tools(HoverTool(tooltips=tooltip_ms))
    return p

def update(attribute, old, new):
    l.children[1] = None
    l.children[1] = create_plot()

#Help! Still won't update :frowning:
ms.on_change('value', update)
l = Column(ms, create_plot())
show(l)

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/602d6872-60fa-4739-9691-81c8ed066923%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.
<grid_df.pkl>

Thank you Bryan, you are a Godsend

For now, I’m going to continue with the JS standalone route. Would you or anyone help me as I have the code that should theoretically work, but there’s a small disconnect?

I start plotting my gmaps image and creating a widget for multi_select. I create tooltip_values as the metric values that pass into hover tooltip. I mask it in a ColumnDataSource function because the callback function likes it in a Dict(String, Instance(Model)) format. (I’m open to a slicker way to skip the CDS function). Note that source is used as a list comprehension for the actual tooltips with labels and values. It’s also first defined, and used only once right after within the tooltip values.

I make the callback JS function by taking in source2 and multi_select values, and simply replace the source2 values with the selected values. The console logs show that the values check out. Apparently, source values are only updating correctly within the JS, but not back out to hover tooltip’s source. Do I need to call a function to replot within after the initial JS callback?

import pickle

import numpy as np

import pandas as pd

import geopandas as gpd

import bokeh

from bokeh.io import output_notebook, show

from bokeh.plotting import figure

from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS

from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select

from bokeh.io import output_file, show, curdoc, save

from bokeh.plotting import gmap

from shapely.geometry import Polygon, Point

import matplotlib.pyplot as plt

from matplotlib.pyplot import *

from gmplot import gmplot

palette = palette[9]

palette.reverse()

color_mapper = LogColorMapper(palette=palette)

%matplotlib inline

output_file(“combinedplot.html”)

map_options = GMapOptions(lat=32.88, lng=-118.460745, map_type=“roadmap”, zoom=8)

#Gmaps API key: https://developers.google.com/maps/documentation/javascript/get-api-key

p = gmap(“YOUR_OWN_KEY”, map_options, title=“CA Kelp Areas - Big Squares, Combined Data”, height = 600, width = 600)

source = ColumnDataSource(data=dict(

x = xs,

y = ys,

kelp = biomass_kg,

bathymetry = water_depth_m,

sst = sst,

species = species,

critical_species_distance = critical_species_distance,

county = county,

county_distance = county_distance,

pretected_areas = pretected_areas,

pretected_areas_distance = pretected_areas_distance,

estab = estab,

avg_emp = avg_emp,

qcew = qcew,

unemployment = unemployment,

ndvi = ndvi,

min_light = min_light,

mixed_l = mixed_l,

floor_temp = floor_temp,

viable = viable,

boat_launches = boat_launches,

boat_launches_distance = boat_launches_distance,

shoretype = shoretype,

shoretype_distance = shoretype_distance,

shoretype2 = shoretype2,

shoretype2_distance = shoretype2_distance,

aerial_kelp = aerial_kelp,

admin_kelp = admin_kelp))

patched = Patches(xs=“x”, ys=“y”, fill_color= {“field”: “kelp”, “transform”:color_mapper},

      fill_alpha=0.8, line_color = "white", line_width = 0.7)

p.add_glyph(source, patched)

tooltip_values = [’@kelp’,

                 '@bathymetry',

                 '@sst',

                 '@species',

                 '@species_distance',

                 '@county',

                 '@county_distance',

                 '@pretected_areas',

                 '@pretected_areas_distance',

                 '@estab',

                 '@avg_emp',

                 '@qcew',

                 '@unemployment',

                 '@ndvi',

                 '@min_light',

                 '@mixed_l',

                 '@floor_temp',

                 '@viable',

                 '@boat_launches',

                 '@boat_launches_distance',

                 '@shoretype',

                 '@shoretype_distance',

                 '@shoretype2',

                 '@shoretype2_distance',

                 '@aerial_kelp',

                 '@admin_kelp']

source2 = ColumnDataSource(data = dict(value = tooltip_values))

p.add_tools(HoverTool(tooltips=[(i.replace("@", “”), i) for i in source2.data[“value”]]))

multi_select = MultiSelect(title=“Metrics Hover:”,

                       value=["@kelp"],

                       options=tooltip_values)

callback_m = CustomJS(args=dict(source2=source2, multi_select=multi_select), code="""

var tt = source2.data.value;

console.log("tt", tt)

var value = cb_obj.value;

console.log("ms value", value)

tt = value;

console.log("post change", tt)

console.log("source2 emitting", source2)

source2.change.emit();

""")

multi_select.js_on_change(‘value’, callback_m)

show(Column(multi_select, p))

``

···

On Sunday, July 29, 2018 at 1:04:40 PM UTC-7, Bryan Van de ven wrote:

Hi,

By calling show, you are creating a standalone HTML/JS document that is not connected to any persistent python process. So there is nothing to run your Python update callback. Use of real python callbacks requires running on the Bokeh server (that is the purpose of the Bokeh server, to be the thing that runs real Python callbacks). You can find more information about the bokeh server here:

    [https://bokeh.pydata.org/en/latest/docs/user_guide/server.html](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html)

If you don’t want to use the Bokeh server then you will be limited to CustomJS callbacks, which only execute JavaScript code in the browser.

Thanks,

Bryan

On Jul 29, 2018, at 12:56, [email protected] wrote:

Hi Bokeh Community,

Originally, I’m trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:

https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4

I follow the examples pretty much function by function with the exception of curdoc as I’m just trying to show a working example and not add to a bokeh server (?) I’m not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don’t think the javascript is working beneath it.

Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I’m doing this for a big graduating project for my masters!

Here’s my code

import pickle

import numpy as np

import pandas as pd

import geopandas as gpd

import bokeh

from bokeh.io import output_notebook, show

from bokeh.plotting import figure

from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS

from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row

from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select

from bokeh.io import output_file, show, curdoc, save

from bokeh.plotting import gmap

from shapely.geometry import Polygon, Point

import matplotlib.pyplot as plt

from matplotlib.pyplot import *

from gmplot import gmplot

palette = palette[9]

palette.reverse()

color_mapper = LogColorMapper(palette=palette)

%matplotlib inline

df = pickle.load(open(“pickles/grid_df.pkl”, “rb”))

df.drop([“halibut_trawl_sites”, “marinas”, “oil_platforms”, “piers”], axis = 1, inplace = True)

#Convert columns to lists for column data source

xs = [df[“geometry”][i].exterior.xy[0].tolist() for i in range(df.shape[0])]

ys = [df[“geometry”][i].exterior.xy[1].tolist() for i in range(df.shape[0])]

biomass_kg = df.biomass.tolist()

water_depth_m = df.depth.tolist()

sst = df.mean_sst.tolist()

species = [df.critical_species[i][0][1] for i in range(df.shape[0])]

critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]

county = [df.county[i][0][1] for i in range(df.shape[0])]

county_distance = [df.county[i][0][2] for i in range(df.shape[0])]

pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]

pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]

estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]

estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]

avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]

avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]

qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]

qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]

unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]

ndvi = df.ndvi.tolist()

min_light = df.z_min_light.tolist()

mixed_l = df.z_mixedl.tolist()

floor_temp = df.floor_temp.tolist()

viable = df.viable.tolist()

boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]

boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]

shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]

shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]

shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]

shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]

aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]

admin_kelp = df.admin_kelp_bed.tolist()

#Gmap plotting - I’m hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key

def create_plot():

p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)
source = ColumnDataSource(data=dict(
    x = xs,
    y = ys,
    kelp = biomass_kg,
    bathymetry = water_depth_m,
    sst = sst,
    species = species,
    critical_species_distance = critical_species_distance,
    county = county,
    county_distance = county_distance,
    pretected_areas = pretected_areas,
    pretected_areas_distance = pretected_areas_distance,
    estab = estab,
    avg_emp = avg_emp,
    qcew = qcew,
    unemployment = unemployment,
    ndvi = ndvi,
    min_light = min_light,
    mixed_l = mixed_l,
    floor_temp = floor_temp,
    viable = viable,
    boat_launches = boat_launches,
    boat_launches_distance = boat_launches_distance,
    shoretype = shoretype,
    shoretype_distance = shoretype_distance,
    shoretype2 = shoretype2,
    shoretype2_distance = shoretype2_distance,
    aerial_kelp = aerial_kelp,
    admin_kelp = admin_kelp))
v = ms.value
patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
      fill_alpha=0.8, line_color = "white", line_width = 0.7)
p.add_glyph(source, patched)
tooltip_ms = [(x.replace("@", ""), x) for x in v]
p.add_tools(HoverTool(tooltips=tooltip_ms))
return p

def update(attribute, old, new):

l.children[1] = None
l.children[1] = create_plot()

#Help! Still won’t update :frowning:

ms.on_change(‘value’, update)

l = Column(ms, create_plot())

show(l)


You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/602d6872-60fa-4739-9691-81c8ed066923%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

<grid_df.pkl>

Hi,

I'm sorry, I don't really understand what you are trying to accomplish. Are you trying to have the set of displayed tooltips change dynamically based on another Widget? If so, they you should know that the configured tooltips have nothing to do with any CDS, so updating a CDS will never cause a different sent of tooltips to be shown.

The configuration of tooltips to show is given by the .tooltips property of the hover tool, which is a list of Tooltip objects. The python syntax of supplying a list of tuples is a python-only convenience API, over this lower level data structure. But to change it from a JS callback you will have to edit the .tooltips property of the HoverTool manually.

Bryan

···

On Jul 29, 2018, at 15:04, Robert Deng <[email protected]> wrote:

Thank you Bryan, you are a Godsend

For now, I'm going to continue with the JS standalone route. Would you or anyone help me as I have the code that should theoretically work, but there's a small disconnect?

I start plotting my gmaps image and creating a widget for multi_select. I create tooltip_values as the metric values that pass into hover tooltip. I mask it in a ColumnDataSource function because the callback function likes it in a Dict(String, Instance(Model)) format. (I'm open to a slicker way to skip the CDS function). Note that source is used as a list comprehension for the actual tooltips with labels and values. It's also first defined, and used only once right after within the tooltip values.

I make the callback JS function by taking in source2 and multi_select values, and simply replace the source2 values with the selected values. The console logs show that the values check out. Apparently, source values are only updating correctly within the JS, but not back out to hover tooltip's source. Do I need to call a function to replot within after the initial JS callback?

import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

output_file("combinedplot.html")

map_options = GMapOptions(lat=32.88, lng=-118.460745, map_type="roadmap", zoom=8)

#Gmaps API key: https://developers.google.com/maps/documentation/javascript/get-api-key
p = gmap("YOUR_OWN_KEY", map_options, title="CA Kelp Areas - Big Squares, Combined Data", height = 600, width = 600)

source = ColumnDataSource(data=dict(
   x = xs,
   y = ys,
   kelp = biomass_kg,
   bathymetry = water_depth_m,
   sst = sst,
   species = species,
   critical_species_distance = critical_species_distance,
   county = county,
   county_distance = county_distance,
   pretected_areas = pretected_areas,
   pretected_areas_distance = pretected_areas_distance,
   estab = estab,
   avg_emp = avg_emp,
   qcew = qcew,
   unemployment = unemployment,
   ndvi = ndvi,
   min_light = min_light,
   mixed_l = mixed_l,
   floor_temp = floor_temp,
   viable = viable,
   boat_launches = boat_launches,
   boat_launches_distance = boat_launches_distance,
   shoretype = shoretype,
   shoretype_distance = shoretype_distance,
   shoretype2 = shoretype2,
   shoretype2_distance = shoretype2_distance,
   aerial_kelp = aerial_kelp,
   admin_kelp = admin_kelp))

patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
          fill_alpha=0.8, line_color = "white", line_width = 0.7)
p.add_glyph(source, patched)
tooltip_values = ['@kelp',
                    '@bathymetry',
                    '@sst',
                    '@species',
                    '@species_distance',
                    '@county',
                    '@county_distance',
                    '@pretected_areas',
                    '@pretected_areas_distance',
                    '@estab',
                    '@avg_emp',
                    '@qcew',
                    '@unemployment',
                    '@ndvi',
                    '@min_light',
                    '@mixed_l',
                    '@floor_temp',
                    '@viable',
                    '@boat_launches',
                    '@boat_launches_distance',
                    '@shoretype',
                    '@shoretype_distance',
                    '@shoretype2',
                    '@shoretype2_distance',
                    '@aerial_kelp',
                    '@admin_kelp']

source2 = ColumnDataSource(data = dict(value = tooltip_values))
p.add_tools(HoverTool(tooltips=[(i.replace("@", ""), i) for i in source2.data["value"]]))

multi_select = MultiSelect(title="Metrics Hover:",
                          value=["@kelp"],
                          options=tooltip_values)

callback_m = CustomJS(args=dict(source2=source2, multi_select=multi_select), code="""
   var tt = source2.data.value;
   console.log("tt", tt)
   var value = cb_obj.value;
   console.log("ms value", value)
   tt = value;
   console.log("post change", tt)
   console.log("source2 emitting", source2)
   source2.change.emit();
   """)
multi_select.js_on_change('value', callback_m)

show(Column(multi_select, p))

On Sunday, July 29, 2018 at 1:04:40 PM UTC-7, Bryan Van de ven wrote:
Hi,

By calling show, you are creating a standalone HTML/JS document that is not connected to any persistent python process. So there is nothing to run your Python update callback. Use of real python callbacks requires running on the Bokeh server (that is the purpose of the Bokeh server, to be the thing that runs real Python callbacks). You can find more information about the bokeh server here:

        https://bokeh.pydata.org/en/latest/docs/user_guide/server.html

If you don't want to use the Bokeh server then you will be limited to CustomJS callbacks, which only execute JavaScript code in the browser.

Thanks,

Bryan

> On Jul 29, 2018, at 12:56, [email protected] wrote:
>
> Hi Bokeh Community,
>
> Originally, I'm trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:
> https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4
>
> I follow the examples pretty much function by function with the exception of curdoc as I'm just trying to show a working example and not add to a bokeh server (?) I'm not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don't think the javascript is working beneath it.
>
> Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I'm doing this for a big graduating project for my masters!
>
> Here's my code
> import pickle
> import numpy as np
> import pandas as pd
> import geopandas as gpd
>
> import bokeh
> from bokeh.io import output_notebook, show
> from bokeh.plotting import figure
> from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
> from bokeh.palettes import YlGn as palette
> from bokeh.layouts import Column, widgetbox, Row
> from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
> from bokeh.io import output_file, show, curdoc, save
> from bokeh.plotting import gmap
> from shapely.geometry import Polygon, Point
> import matplotlib.pyplot as plt
> from matplotlib.pyplot import *
> from gmplot import gmplot
> palette = palette[9]
> palette.reverse()
> color_mapper = LogColorMapper(palette=palette)
> %matplotlib inline
>
> df = pickle.load(open("pickles/grid_df.pkl", "rb"))
> df.drop(["halibut_trawl_sites", "marinas", "oil_platforms", "piers"], axis = 1, inplace = True)
>
> #Convert columns to lists for column data source
> xs = [df["geometry"][i].exterior.xy[0].tolist() for i in range(df.shape[0])]
> ys = [df["geometry"][i].exterior.xy[1].tolist() for i in range(df.shape[0])]
> biomass_kg = df.biomass.tolist()
> water_depth_m = df.depth.tolist()
> sst = df.mean_sst.tolist()
> species = [df.critical_species[i][0][1] for i in range(df.shape[0])]
> critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]
> county = [df.county[i][0][1] for i in range(df.shape[0])]
> county_distance = [df.county[i][0][2] for i in range(df.shape[0])]
> pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]
> pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]
> estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]
> estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]
> avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]
> avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]
> qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]
> qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]
> unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]
> ndvi = df.ndvi.tolist()
> min_light = df.z_min_light.tolist()
> mixed_l = df.z_mixedl.tolist()
> floor_temp = df.floor_temp.tolist()
> viable = df.viable.tolist()
> boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]
> boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]
> shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]
> shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]
> shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]
> shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]
> aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]
> admin_kelp = df.admin_kelp_bed.tolist()
>
> #Gmap plotting - I'm hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key
>
> def create_plot():
> p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)
>
> source = ColumnDataSource(data=dict(
> x = xs,
> y = ys,
> kelp = biomass_kg,
> bathymetry = water_depth_m,
> sst = sst,
> species = species,
> critical_species_distance = critical_species_distance,
> county = county,
> county_distance = county_distance,
> pretected_areas = pretected_areas,
> pretected_areas_distance = pretected_areas_distance,
> estab = estab,
> avg_emp = avg_emp,
> qcew = qcew,
> unemployment = unemployment,
> ndvi = ndvi,
> min_light = min_light,
> mixed_l = mixed_l,
> floor_temp = floor_temp,
> viable = viable,
> boat_launches = boat_launches,
> boat_launches_distance = boat_launches_distance,
> shoretype = shoretype,
> shoretype_distance = shoretype_distance,
> shoretype2 = shoretype2,
> shoretype2_distance = shoretype2_distance,
> aerial_kelp = aerial_kelp,
> admin_kelp = admin_kelp))
>
> v = ms.value
> patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
> fill_alpha=0.8, line_color = "white", line_width = 0.7)
> p.add_glyph(source, patched)
> tooltip_ms = [(x.replace("@", ""), x) for x in v]
> p.add_tools(HoverTool(tooltips=tooltip_ms))
> return p
>
> def update(attribute, old, new):
> l.children[1] = None
> l.children[1] = create_plot()
>
> #Help! Still won't update :frowning:
> ms.on_change('value', update)
> l = Column(ms, create_plot())
> show(l)
>
>
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> To post to this group, send email to [email protected].
> To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/602d6872-60fa-4739-9691-81c8ed066923%40continuum.io.
> For more options, visit https://groups.google.com/a/continuum.io/d/optout.
> <grid_df.pkl>

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to bokeh[email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/86c40ff0-2c68-4bcf-b550-831489e02ce6%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Just to better clarify, I would like to update / reduce the amount of metrics shown in the hover tooltips on the map using my multi select widget. If I select 3 metrics, I would like to simply see 3 metrics.

So now, I know CDS is not the right way to update dynamic tooltips. Can you go a little more into detail on how to update .tooltips using the JS callback function? Where would it live - within or outside the JS Callback?

···

On Jul 29, 2018, at 3:15 PM, Bryan Van de ven <[email protected]> wrote:

Hi,

I'm sorry, I don't really understand what you are trying to accomplish. Are you trying to have the set of displayed tooltips change dynamically based on another Widget? If so, they you should know that the configured tooltips have nothing to do with any CDS, so updating a CDS will never cause a different sent of tooltips to be shown.

The configuration of tooltips to show is given by the .tooltips property of the hover tool, which is a list of Tooltip objects. The python syntax of supplying a list of tuples is a python-only convenience API, over this lower level data structure. But to change it from a JS callback you will have to edit the .tooltips property of the HoverTool manually.

Bryan

On Jul 29, 2018, at 15:04, Robert Deng <[email protected]> wrote:

Thank you Bryan, you are a Godsend

For now, I'm going to continue with the JS standalone route. Would you or anyone help me as I have the code that should theoretically work, but there's a small disconnect?

I start plotting my gmaps image and creating a widget for multi_select. I create tooltip_values as the metric values that pass into hover tooltip. I mask it in a ColumnDataSource function because the callback function likes it in a Dict(String, Instance(Model)) format. (I'm open to a slicker way to skip the CDS function). Note that source is used as a list comprehension for the actual tooltips with labels and values. It's also first defined, and used only once right after within the tooltip values.

I make the callback JS function by taking in source2 and multi_select values, and simply replace the source2 values with the selected values. The console logs show that the values check out. Apparently, source values are only updating correctly within the JS, but not back out to hover tooltip's source. Do I need to call a function to replot within after the initial JS callback?

import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

output_file("combinedplot.html")

map_options = GMapOptions(lat=32.88, lng=-118.460745, map_type="roadmap", zoom=8)

#Gmaps API key: https://developers.google.com/maps/documentation/javascript/get-api-key
p = gmap("YOUR_OWN_KEY", map_options, title="CA Kelp Areas - Big Squares, Combined Data", height = 600, width = 600)

source = ColumnDataSource(data=dict(
  x = xs,
  y = ys,
  kelp = biomass_kg,
  bathymetry = water_depth_m,
  sst = sst,
  species = species,
  critical_species_distance = critical_species_distance,
  county = county,
  county_distance = county_distance,
  pretected_areas = pretected_areas,
  pretected_areas_distance = pretected_areas_distance,
  estab = estab,
  avg_emp = avg_emp,
  qcew = qcew,
  unemployment = unemployment,
  ndvi = ndvi,
  min_light = min_light,
  mixed_l = mixed_l,
  floor_temp = floor_temp,
  viable = viable,
  boat_launches = boat_launches,
  boat_launches_distance = boat_launches_distance,
  shoretype = shoretype,
  shoretype_distance = shoretype_distance,
  shoretype2 = shoretype2,
  shoretype2_distance = shoretype2_distance,
  aerial_kelp = aerial_kelp,
  admin_kelp = admin_kelp))

patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
         fill_alpha=0.8, line_color = "white", line_width = 0.7)
p.add_glyph(source, patched)
tooltip_values = ['@kelp',
                   '@bathymetry',
                   '@sst',
                   '@species',
                   '@species_distance',
                   '@county',
                   '@county_distance',
                   '@pretected_areas',
                   '@pretected_areas_distance',
                   '@estab',
                   '@avg_emp',
                   '@qcew',
                   '@unemployment',
                   '@ndvi',
                   '@min_light',
                   '@mixed_l',
                   '@floor_temp',
                   '@viable',
                   '@boat_launches',
                   '@boat_launches_distance',
                   '@shoretype',
                   '@shoretype_distance',
                   '@shoretype2',
                   '@shoretype2_distance',
                   '@aerial_kelp',
                   '@admin_kelp']

source2 = ColumnDataSource(data = dict(value = tooltip_values))
p.add_tools(HoverTool(tooltips=[(i.replace("@", ""), i) for i in source2.data["value"]]))

multi_select = MultiSelect(title="Metrics Hover:",
                         value=["@kelp"],
                         options=tooltip_values)

callback_m = CustomJS(args=dict(source2=source2, multi_select=multi_select), code="""
  var tt = source2.data.value;
  console.log("tt", tt)
  var value = cb_obj.value;
  console.log("ms value", value)
  tt = value;
  console.log("post change", tt)
  console.log("source2 emitting", source2)
  source2.change.emit();
  """)
multi_select.js_on_change('value', callback_m)

show(Column(multi_select, p))

On Sunday, July 29, 2018 at 1:04:40 PM UTC-7, Bryan Van de ven wrote:
Hi,

By calling show, you are creating a standalone HTML/JS document that is not connected to any persistent python process. So there is nothing to run your Python update callback. Use of real python callbacks requires running on the Bokeh server (that is the purpose of the Bokeh server, to be the thing that runs real Python callbacks). You can find more information about the bokeh server here:

       https://bokeh.pydata.org/en/latest/docs/user_guide/server.html

If you don't want to use the Bokeh server then you will be limited to CustomJS callbacks, which only execute JavaScript code in the browser.

Thanks,

Bryan

On Jul 29, 2018, at 12:56, [email protected] wrote:

Hi Bokeh Community,

Originally, I'm trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:
https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4

I follow the examples pretty much function by function with the exception of curdoc as I'm just trying to show a working example and not add to a bokeh server (?) I'm not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don't think the javascript is working beneath it.

Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I'm doing this for a big graduating project for my masters!

Here's my code
import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

df = pickle.load(open("pickles/grid_df.pkl", "rb"))
df.drop(["halibut_trawl_sites", "marinas", "oil_platforms", "piers"], axis = 1, inplace = True)

#Convert columns to lists for column data source
xs = [df["geometry"][i].exterior.xy[0].tolist() for i in range(df.shape[0])]
ys = [df["geometry"][i].exterior.xy[1].tolist() for i in range(df.shape[0])]
biomass_kg = df.biomass.tolist()
water_depth_m = df.depth.tolist()
sst = df.mean_sst.tolist()
species = [df.critical_species[i][0][1] for i in range(df.shape[0])]
critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]
county = [df.county[i][0][1] for i in range(df.shape[0])]
county_distance = [df.county[i][0][2] for i in range(df.shape[0])]
pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]
pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]
estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]
estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]
avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]
avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]
qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]
qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]
unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]
ndvi = df.ndvi.tolist()
min_light = df.z_min_light.tolist()
mixed_l = df.z_mixedl.tolist()
floor_temp = df.floor_temp.tolist()
viable = df.viable.tolist()
boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]
boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]
shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]
shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]
shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]
shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]
aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]
admin_kelp = df.admin_kelp_bed.tolist()

#Gmap plotting - I'm hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key

def create_plot():
   p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)

   source = ColumnDataSource(data=dict(
       x = xs,
       y = ys,
       kelp = biomass_kg,
       bathymetry = water_depth_m,
       sst = sst,
       species = species,
       critical_species_distance = critical_species_distance,
       county = county,
       county_distance = county_distance,
       pretected_areas = pretected_areas,
       pretected_areas_distance = pretected_areas_distance,
       estab = estab,
       avg_emp = avg_emp,
       qcew = qcew,
       unemployment = unemployment,
       ndvi = ndvi,
       min_light = min_light,
       mixed_l = mixed_l,
       floor_temp = floor_temp,
       viable = viable,
       boat_launches = boat_launches,
       boat_launches_distance = boat_launches_distance,
       shoretype = shoretype,
       shoretype_distance = shoretype_distance,
       shoretype2 = shoretype2,
       shoretype2_distance = shoretype2_distance,
       aerial_kelp = aerial_kelp,
       admin_kelp = admin_kelp))

   v = ms.value
   patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
         fill_alpha=0.8, line_color = "white", line_width = 0.7)
   p.add_glyph(source, patched)
   tooltip_ms = [(x.replace("@", ""), x) for x in v]
   p.add_tools(HoverTool(tooltips=tooltip_ms))
   return p

def update(attribute, old, new):
   l.children[1] = None
   l.children[1] = create_plot()

#Help! Still won't update :frowning:
ms.on_change('value', update)
l = Column(ms, create_plot())
show(l)

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/602d6872-60fa-4739-9691-81c8ed066923%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.
<grid_df.pkl>

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/86c40ff0-2c68-4bcf-b550-831489e02ce6%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/PPCEzgLPSwo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/EE9A9C35-C983-4D03-AB9A-867AC9C9EE4A%40anaconda.com.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Here is a complete example of updating tooltips from a multi-select. As a gentle suggestion, it is always better to try to start as simple as possible, to tackle independent requirements one at a time with toy test cases to gain understanding. This also helps to keep support questions as focused and minimal as possible, which will greatly increase the chance that someone can/will help.

    from bokeh.io import show
    from bokeh.layouts import column
    from bokeh.models import CustomJS, HoverTool, MultiSelect
    from bokeh.plotting import figure

    data = dict(x=[1,2,3], y=[1,2,4], foo=list("abc"))

    p = figure()
    p.circle('x', 'y', size=20, alpha=0.6, source=data) # dict converted to CDS internally

    hover = HoverTool(tooltips=[("x", "@x")])
    p.add_tools(hover)

    callback = CustomJS(args=dict(hover=hover), code="""
    hover.tooltips =
    const value = cb_obj.value
    for (i=0; i<value.length; ++i) {
        const name = value[i]
        hover.tooltips.push([name, "@"+name])
    }
    """)

    select = MultiSelect(title="Chose", value=["x"], options=["x", "y", "foo"])
    select.js_on_change('value', callback)

    show(column(select, p))

Thanks,

Bryan

···

On Jul 29, 2018, at 15:18, Robert Deng <[email protected]> wrote:

Just to better clarify, I would like to update / reduce the amount of metrics shown in the hover tooltips on the map using my multi select widget. If I select 3 metrics, I would like to simply see 3 metrics.

So now, I know CDS is not the right way to update dynamic tooltips. Can you go a little more into detail on how to update .tooltips using the JS callback function? Where would it live - within or outside the JS Callback?

On Jul 29, 2018, at 3:15 PM, Bryan Van de ven <[email protected]> wrote:

Hi,

I'm sorry, I don't really understand what you are trying to accomplish. Are you trying to have the set of displayed tooltips change dynamically based on another Widget? If so, they you should know that the configured tooltips have nothing to do with any CDS, so updating a CDS will never cause a different sent of tooltips to be shown.

The configuration of tooltips to show is given by the .tooltips property of the hover tool, which is a list of Tooltip objects. The python syntax of supplying a list of tuples is a python-only convenience API, over this lower level data structure. But to change it from a JS callback you will have to edit the .tooltips property of the HoverTool manually.

Bryan

On Jul 29, 2018, at 15:04, Robert Deng <[email protected]> wrote:

Thank you Bryan, you are a Godsend

For now, I'm going to continue with the JS standalone route. Would you or anyone help me as I have the code that should theoretically work, but there's a small disconnect?

I start plotting my gmaps image and creating a widget for multi_select. I create tooltip_values as the metric values that pass into hover tooltip. I mask it in a ColumnDataSource function because the callback function likes it in a Dict(String, Instance(Model)) format. (I'm open to a slicker way to skip the CDS function). Note that source is used as a list comprehension for the actual tooltips with labels and values. It's also first defined, and used only once right after within the tooltip values.

I make the callback JS function by taking in source2 and multi_select values, and simply replace the source2 values with the selected values. The console logs show that the values check out. Apparently, source values are only updating correctly within the JS, but not back out to hover tooltip's source. Do I need to call a function to replot within after the initial JS callback?

import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

output_file("combinedplot.html")

map_options = GMapOptions(lat=32.88, lng=-118.460745, map_type="roadmap", zoom=8)

#Gmaps API key: https://developers.google.com/maps/documentation/javascript/get-api-key
p = gmap("YOUR_OWN_KEY", map_options, title="CA Kelp Areas - Big Squares, Combined Data", height = 600, width = 600)

source = ColumnDataSource(data=dict(
x = xs,
y = ys,
kelp = biomass_kg,
bathymetry = water_depth_m,
sst = sst,
species = species,
critical_species_distance = critical_species_distance,
county = county,
county_distance = county_distance,
pretected_areas = pretected_areas,
pretected_areas_distance = pretected_areas_distance,
estab = estab,
avg_emp = avg_emp,
qcew = qcew,
unemployment = unemployment,
ndvi = ndvi,
min_light = min_light,
mixed_l = mixed_l,
floor_temp = floor_temp,
viable = viable,
boat_launches = boat_launches,
boat_launches_distance = boat_launches_distance,
shoretype = shoretype,
shoretype_distance = shoretype_distance,
shoretype2 = shoretype2,
shoretype2_distance = shoretype2_distance,
aerial_kelp = aerial_kelp,
admin_kelp = admin_kelp))

patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
        fill_alpha=0.8, line_color = "white", line_width = 0.7)
p.add_glyph(source, patched)
tooltip_values = ['@kelp',
                  '@bathymetry',
                  '@sst',
                  '@species',
                  '@species_distance',
                  '@county',
                  '@county_distance',
                  '@pretected_areas',
                  '@pretected_areas_distance',
                  '@estab',
                  '@avg_emp',
                  '@qcew',
                  '@unemployment',
                  '@ndvi',
                  '@min_light',
                  '@mixed_l',
                  '@floor_temp',
                  '@viable',
                  '@boat_launches',
                  '@boat_launches_distance',
                  '@shoretype',
                  '@shoretype_distance',
                  '@shoretype2',
                  '@shoretype2_distance',
                  '@aerial_kelp',
                  '@admin_kelp']

source2 = ColumnDataSource(data = dict(value = tooltip_values))
p.add_tools(HoverTool(tooltips=[(i.replace("@", ""), i) for i in source2.data["value"]]))

multi_select = MultiSelect(title="Metrics Hover:",
                        value=["@kelp"],
                        options=tooltip_values)

callback_m = CustomJS(args=dict(source2=source2, multi_select=multi_select), code="""
var tt = source2.data.value;
console.log("tt", tt)
var value = cb_obj.value;
console.log("ms value", value)
tt = value;
console.log("post change", tt)
console.log("source2 emitting", source2)
source2.change.emit();
""")
multi_select.js_on_change('value', callback_m)

show(Column(multi_select, p))

On Sunday, July 29, 2018 at 1:04:40 PM UTC-7, Bryan Van de ven wrote:
Hi,

By calling show, you are creating a standalone HTML/JS document that is not connected to any persistent python process. So there is nothing to run your Python update callback. Use of real python callbacks requires running on the Bokeh server (that is the purpose of the Bokeh server, to be the thing that runs real Python callbacks). You can find more information about the bokeh server here:

      https://bokeh.pydata.org/en/latest/docs/user_guide/server.html

If you don't want to use the Bokeh server then you will be limited to CustomJS callbacks, which only execute JavaScript code in the browser.

Thanks,

Bryan

On Jul 29, 2018, at 12:56, [email protected] wrote:

Hi Bokeh Community,

Originally, I'm trying to create a multi-select widget with metrics options to toggle on the hover over on a gmap plot. I was inspired by this:
https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/4zwyPhPlTn4

I follow the examples pretty much function by function with the exception of curdoc as I'm just trying to show a working example and not add to a bokeh server (?) I'm not sure if that has any bearing on this situation. Basically it plots and the values are read in well, but I don't think the javascript is working beneath it.

Hopefully this loads for you guys, if anything breaks, lmk. Would really appreciate this as I'm doing this for a big graduating project for my masters!

Here's my code
import pickle
import numpy as np
import pandas as pd
import geopandas as gpd

import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool, LogColorMapper, GMapOptions, GMapPlot, Patches, HoverTool, CustomJS
from bokeh.palettes import YlGn as palette
from bokeh.layouts import Column, widgetbox, Row
from bokeh.models.widgets import Slider, Button, CheckboxButtonGroup, CheckboxGroup, TableColumn, Panel, DataTable, DateFormatter, TableColumn, MultiSelect, Select
from bokeh.io import output_file, show, curdoc, save
from bokeh.plotting import gmap
from shapely.geometry import Polygon, Point
import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from gmplot import gmplot
palette = palette[9]
palette.reverse()
color_mapper = LogColorMapper(palette=palette)
%matplotlib inline

df = pickle.load(open("pickles/grid_df.pkl", "rb"))
df.drop(["halibut_trawl_sites", "marinas", "oil_platforms", "piers"], axis = 1, inplace = True)

#Convert columns to lists for column data source
xs = [df["geometry"][i].exterior.xy[0].tolist() for i in range(df.shape[0])]
ys = [df["geometry"][i].exterior.xy[1].tolist() for i in range(df.shape[0])]
biomass_kg = df.biomass.tolist()
water_depth_m = df.depth.tolist()
sst = df.mean_sst.tolist()
species = [df.critical_species[i][0][1] for i in range(df.shape[0])]
critical_species_distance = [df.critical_species[i][0][2] for i in range(df.shape[0])]
county = [df.county[i][0][1] for i in range(df.shape[0])]
county_distance = [df.county[i][0][2] for i in range(df.shape[0])]
pretected_areas = [df.pretected_areas[i][0][1] for i in range(df.shape[0])]
pretected_areas_distance = [df.pretected_areas[i][0][2] for i in range(df.shape[0])]
estab = [df.nes_estab_pct[i][0][1] for i in range(df.shape[0])]
estab_perc = [df.nes_estab_pct[i][0][2] for i in range(df.shape[0])]
avg_emp = [df.annual_avg_emplvl[i][0][1] for i in range(df.shape[0])]
avg_emp_perc = [df.annual_avg_emplvl[i][0][2] for i in range(df.shape[0])]
qcew = [df.qcew_emp_pct[i][0][1] for i in range(df.shape[0])]
qcew_perc = [df.qcew_emp_pct[i][0][2] for i in range(df.shape[0])]
unemployment = [df.unemployment_rate[i][0][1] for i in range(df.shape[0])]
ndvi = df.ndvi.tolist()
min_light = df.z_min_light.tolist()
mixed_l = df.z_mixedl.tolist()
floor_temp = df.floor_temp.tolist()
viable = df.viable.tolist()
boat_launches = [df.boat_launches[i][0][1] for i in range(df.shape[0])]
boat_launches_distance = [df.boat_launches[i][0][2] for i in range(df.shape[0])]
shoretype = [df.shoretype[i][0][1] for i in range(df.shape[0])]
shoretype_distance = [df.shoretype[i][0][2] for i in range(df.shape[0])]
shoretype2 = [df.shoretype2[i][0][1] for i in range(df.shape[0])]
shoretype2_distance = [df.shoretype2[i][0][2] for i in range(df.shape[0])]
aerial_kelp = [df.aerial_kelp[i][0][2] for i in range(df.shape[0])]
admin_kelp = df.admin_kelp_bed.tolist()

#Gmap plotting - I'm hiding my google api key, but you could easily get one here: https://developers.google.com/maps/documentation/javascript/get-api-key

def create_plot():
  p = gmap("YOUR_OWN_GOOGLE_API_KEY", map_options, title="Combined Data", height = 600, width = 600)

  source = ColumnDataSource(data=dict(
      x = xs,
      y = ys,
      kelp = biomass_kg,
      bathymetry = water_depth_m,
      sst = sst,
      species = species,
      critical_species_distance = critical_species_distance,
      county = county,
      county_distance = county_distance,
      pretected_areas = pretected_areas,
      pretected_areas_distance = pretected_areas_distance,
      estab = estab,
      avg_emp = avg_emp,
      qcew = qcew,
      unemployment = unemployment,
      ndvi = ndvi,
      min_light = min_light,
      mixed_l = mixed_l,
      floor_temp = floor_temp,
      viable = viable,
      boat_launches = boat_launches,
      boat_launches_distance = boat_launches_distance,
      shoretype = shoretype,
      shoretype_distance = shoretype_distance,
      shoretype2 = shoretype2,
      shoretype2_distance = shoretype2_distance,
      aerial_kelp = aerial_kelp,
      admin_kelp = admin_kelp))

  v = ms.value
  patched = Patches(xs="x", ys="y", fill_color= {"field": "kelp", "transform":color_mapper},
        fill_alpha=0.8, line_color = "white", line_width = 0.7)
  p.add_glyph(source, patched)
  tooltip_ms = [(x.replace("@", ""), x) for x in v]
  p.add_tools(HoverTool(tooltips=tooltip_ms))
  return p

def update(attribute, old, new):
  l.children[1] = None
  l.children[1] = create_plot()

#Help! Still won't update :frowning:
ms.on_change('value', update)
l = Column(ms, create_plot())
show(l)

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/602d6872-60fa-4739-9691-81c8ed066923%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.
<grid_df.pkl>

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/86c40ff0-2c68-4bcf-b550-831489e02ce6%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/PPCEzgLPSwo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/EE9A9C35-C983-4D03-AB9A-867AC9C9EE4A%40anaconda.com.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/02EE33F8-32B3-49EB-9328-2FC5CE52AAC1%40gmail.com.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Bryan,

Huge thanks for the help - I got it to work and it’s phenomenal!

···

On Jul 29, 2018, at 3:47 PM, Bryan Van de ven [email protected] wrote:

data = dict(x=[1,2,3], y=[1,2,4], foo=list(“abc”))