2

I would like to create a standalone document, similar to the server app example 'Crossfilter': to select different columns for coloring the circles and to update the colorbar as well.

I define a CustomJS with the code below, where I create a new LinearColorMapper with the calculated low and high values. (For debugging purposes, I intentionally add different palette than the one set via the Python code).

var low = Math.min.apply(Math,source.data[cb_obj.value]);
var high = Math.max.apply(Math,source.data[cb_obj.value]);
var color_mapper = new Bokeh.LinearColorMapper({palette:'Viridis5', low:low, high:high});
cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
color_bar.color_mapper = color_mapper;
source.change.emit();

As a result, when selecting the column, the circles become white, the line black, the ticks of the color bar change correctly, but the palette does not change.

Could you help me setting the proper attributes in the callback? Thank you in advance.

Original state, color mapper set from Python code

Original state, color mapper set from Python code

After selecting the column 'd'

After selecting the column 'd'

I created a "minimal working example" to show how far I got. The full project with the template it can be found here: https://github.com/pintergreg/bokehjscolormapperexample

pintergreg
  • 87
  • 3
  • 8
  • I found out that the problem is how I specify the color palette in the JS code. The name as string is wrong. If I pass the palette from the Python code to the CustomJS then the circles will have the desired color, but the colorbar does not change. ```python CustomJS(args=dict(cir=cir, source=source, color_bar=color_bar, pal=Viridis5), code=codec) ``` – pintergreg Mar 05 '19 at 11:33

1 Answers1

0

It looks like you cannot reference a colour pallet like this in BokehJS. Just pass Viridis5 variable to JS callback and it works (tested on Bokeh v1.0.4):

import pandas as pd
from bokeh.models import ColumnDataSource, ColorBar, Select, CustomJS
from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.palettes import Spectral5, Viridis5
from bokeh.transform import linear_cmap
from bokeh.embed import components
from jinja2 import Environment, FileSystemLoader

df = pd.DataFrame({"a": [2, 6, 5, 3, 7, 8, 1, 9, 2, 4],
                   "b": [3, 5, 7, 1, 0, 6, 5, 4, 2, 9],
                   "c": [11, 12, 13, 14, 11, 13, 15, 14, 15, 12],
                   "d": [21, 23, 24, 25, 21, 22, 23, 24, 25, 22]})
source = ColumnDataSource(df)

mapper = linear_cmap(field_name = "c", palette = Spectral5,
                     low = min(df["c"]), high = max(df["c"]))

fig = figure(plot_width = 400, plot_height = 400)

cir = fig.circle(x = "a", y = "b", size = 12,
                 source = source, line_color = mapper, color = mapper)
color_bar = ColorBar(color_mapper = mapper["transform"], width = 8,
                     location = (0, 0))
fig.add_layout(color_bar, "right")

codec = """
            var low = Math.min.apply(Math,source.data[cb_obj.value]);
            var high = Math.max.apply(Math,source.data[cb_obj.value]);
            var color_mapper = new Bokeh.LinearColorMapper({palette:viridis5, low:low, high:high});
            cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
            cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
            color_bar.color_mapper.low = low;
            color_bar.color_mapper.high = high;
            color_bar.color_mapper.palette = viridis5;
            source.change.emit();
        """
cb_cselect_c = CustomJS(args = dict(cir = cir, source = source, color_bar = color_bar, viridis5 = Viridis5),
                        code = codec)

c_select = Select(title = "Select variable for color: ", value = "None",
                  options = ["c", "d"], callback = cb_cselect_c)

layout = gridplot([[fig], [c_select]])
show(layout)

# env = Environment(loader=FileSystemLoader("."))
# template = env.get_template("template.html")
#
# script, div = components(layout)
#
# with open("output.html", "w") as f:
#     print(template.render(script=script, div=div), file=f)

You also need to manually add this line to header section of the generated HTML file:

<script type="text/javascript" src="http://cdn.bokeh.org/bokeh/release/bokeh-api-1.0.4.min.js"></script>
bigreddot
  • 33,642
  • 5
  • 69
  • 122
Tony
  • 7,767
  • 2
  • 22
  • 51
  • Yeah, that's the "workaround" I came up with (see my comment to the question). I was waiting for a confirmation that this cannot be done with JS only. Your version does not work for me (Bokeh 1.0.4), because the bokeh api js is not referenced in the html header (Thats why I used Jinja to render the output.) I pushed my full code to the 'solution' branch, and my solution to the color bar problem was to set the properties [one by one](https://github.com/pintergreg/bokehjscolormapperexample/blob/ffb0ca94a5d30c94f9d2127694837a81f54d4d86/minimal_workig_example.py#L34-L37). – pintergreg Mar 07 '19 at 15:42
  • Sorry I didn't read your comment. Actually the comments are not meant for that... And yes, I forgotten to mention the API BokehJS include is missing in Bokeh v1.0.4. I added this info to my answer. Please "vote up" if my answer was useful. – Tony Mar 07 '19 at 15:50
  • I appreciate your time, but first, your solution still does not fulfill all of the requirements (colorbar is not updated). Second, you're just copy-pasting _my solution_ into yours without matching the variable names... I used `pal` for palette... Third and most important thing, you cannot explain this situation, so I don't really find your answer useful, as my solution still better (and came first). – pintergreg Mar 07 '19 at 18:52
  • That's fair. But please remember that developers are looking for ready-made solutions here. Don't let them spend too much time reading comments to assembly that solution. Thanks – Tony Mar 08 '19 at 09:01
  • I tried adapting some of the code, but on `var color_mapper = new Bokeh.LinearColorMapper({palette:viridis5, low:low, high:high});` I'm getting `Uncaught TypeError: Bokeh.LinearColorMapper is not a constructor`. Is this still supported? – ManicMailman Jan 02 '20 at 16:43
  • You need to manually add the `bokeh-api-X.X.X.min.js` as described in the answer – Tony Jan 09 '20 at 14:23