Use Inverted Colormaps on Select

Hi,
I am trying to implement inverted colormap from existing Bokeh colormaps into Select (or import some others from matplotlib or e.g. CMasher package). In this example I used invert_viridis = Viridis256[::-1] to create inverted colormap.

When I start bokeh serve, with this defined colormap it shows up nicely, however, if I use Select widget to chose different colormap and then get back to invert_viridis it gives me this error:

ValueError: expected an element of Seq(Color), got 'invert_viridis'

I believe this is because invert_viridis is not defined within Bokeh, like Viridis256, Cividis256 etc. Is it possible and how to implement other colormaps into Select so that they are recognized?

Minimum working example (changing colormap to a different one and then back to ‘invert_viridis’) will give mentioned error:

import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColorBar, LogTicker, HoverTool, ColumnDataSource, Select
from bokeh.plotting import figure
from bokeh.transform import log_cmap
from bokeh.util.hex import hexbin

from bokeh.palettes import Viridis256

data = {'x': np.random.randint(300, size=1000),
        'y': np.random.randint(200, size=1000),
        'z': np.random.randint(50, size=100)}


options = sorted(data.keys())

p = figure(tools='pan,box_zoom,box_select,reset,wheel_zoom, undo,redo,save',
           match_aspect=True, background_fill_color='white', toolbar_location="above")
p.grid.visible = False

source_bin = ColumnDataSource(data=dict(r=[], q=[], counts=[]))
hex_size = 10

    
bins = hexbin(data['x'], data['y'], hex_size)
source_bin.data = dict(r=bins.r, q=bins.q, counts=bins.counts)


invert_viridis = Viridis256[::-1]
cmap = log_cmap('counts', invert_viridis, 1, max(bins.counts))

# Colormaps
cbar = Select(title="Choose Colormap", value="invert_viridis",
              options=["invert_viridis", "Cividis256", "Viridis256"])

# Update colormap
def update_colorbar():
    cmap = log_cmap('counts', palette=cbar.value, low=1, high=max(bins.counts))
    color_bar.color_mapper.palette = cbar.value
    

cbar.on_change('value', lambda attr, old, new: update_colorbar())


h = p.hex_tile(q="q", r="r", size=hex_size, line_color=None,
           source=source_bin, fill_color=cmap)

color_bar = ColorBar(title="Counts", 
            color_mapper=cmap['transform'],
            location=(0, 0),
            ticker=LogTicker(),
            label_standoff=12,
            )

p.add_tools(HoverTool(tooltips=[('Counts', '@counts')],
                      mode='mouse',
                      point_policy='follow_mouse',
                      renderers=[h]))

p.add_layout(color_bar, 'right')

curdoc().add_root(row(column(cbar), p))

You need to have the callback pass the actual list of colors, not the string "invert_viridis"

Thank you for the reply.

If I place invert_viridis[:] into value and options then I get ValueError that it accepts strings:

ValueError: expected a value of type str, got ('#FDE724', '#FAE622', '#F8E621', '#F6E61F', '#F3E51E', '#F1E51C', '#EEE51B', '#ECE41A', '#E9E419', '#E7E419', '#E4E318', '#E1E318', '#DFE318', '#DCE218', '#DAE218', '#D7E219', '#D4E11A', '#D2E11

The select values can only be strings. Your will have to use a conditional inside the callback to check for special color map name and then pass the actual color sequence.

1 Like

Understood. I changed the function to do exactly that and it works.

Change made:

invert_viridis = Viridis256[::-1]

cmap = log_cmap('counts', invert_viridis[:], 1, max(bins.counts))


# Perceptually Uniform Sequential Colormaps - available in Bokeh
cbar = Select(title="Choose Colormap", value='test',
              options=['test', "Cividis256", "Viridis256"])


def update_colorbar():
    
    if cbar.value == 'test':
        cmap = log_cmap('counts', palette=invert_viridis[:], low=1, high=max(bins.counts))
        color_bar.color_mapper.palette = invert_viridis[:]

    else:
        cmap = log_cmap('counts', palette=cbar.value, low=1, high=max(bins.counts))
        color_bar.color_mapper.palette = cbar.value

Thank you.