Modify high and low of a color mapper.

I am trying to build a multi-dimensional image viewer using Panel and Bokeh and I would like to provide a slider to modify the scale of the intensities shown on the image.

For this, I would need to modify the color mapper of the Bokeh figure image. I first tried to set the argument color_mapper of fig.image to a string color_mapper so I could set it in a ColumnDataSource but it does not work.

Is that possible to set a new color mapper attached to a Bokeh image? Or even better to modify the high and low parameters (avoiding the unnecessary recreation of the color mapper object).

The same question also applies to the palette used in the ColorMapper . That would be nice to be able to ‘dynamically’ modify it.

Generally, to have the best chance of getting help, it is most effective to provide a code sample, and ideally a minimal complete example of what you have tried. For instance, it’s not clear from your description if this is a standalone HTML scenario (which means only CustomJS could be used) or a Bokeh server app (which means real Python callbacks could be used)

I am working in the Jupyter notebook. What I want to be able to do is the following:

import numpy as np

import panel as pn; pn.extension()
import bokeh as bk
from bokeh import plotting

image = np.random.random((128, 128))

fig = plotting.figure()

source = bk.models.ColumnDataSource(data={})

image_args = {}
image_args['image'] = 'image'
image_args['x'] = 'x'
image_args['y'] = 'y'
image_args['dw'] = 'dw'
image_args['dh'] = 'dh'
image_args['source'] = source
image_args['color_mapper'] = 'color_mapper'
fig.image(**image_args)

data = {}
data['image'] = [image]
data['x'] = [0]
data['y'] = [0]
data['dw'] = [128]
data['dh'] = [128]
data['color_mapper'] = bk.models.LinearColorMapper(palette=bk.palettes.Viridis256, low=0, high=1)
source.data = data

pn.Row(fig, sizing_mode="fixed")

I want to bind the color mapper to a data source so I can modify/update it later.

The above code fails with:

ValueError: expected an instance of type ColorMapper, got color_mapper of type str

@hadim The color mapper is not something that goes in a ColumnDataSource, so putting it in source.data will never be a correct thing to do. In fact, it is something that manipulates data in a data source. Have you looked at the Transforming Data section of the User’s Guide? That shows how you can use the higher level linear_cmap which is simpler and what I would suggest for most use cases.

Alternatively if you really need to use LinearColorMapper directly, there is an example of its use in examples/plotting/file/unemployment.py.

Either way, the mapper is something you set on the glyph, e.g.

p.rect(..., fill_color=linear_cmap('foo', ...))

# or

p.rect(..., fill_color=transform('foo', cmapper))

Thank you for your answer.

Mapping the mapper to a data source was a way to illustrate what I am trying to achieve. As said in my first post I am looking for a way to switch the color mapper of an image after its creation. I want to be able to modify both the high/low values and the colormap itself.

For example, I first create a Bokeh figure then add an image to it with a LinearColorMapper or linear_cmap. Then my plan is to have a slider or a button to allow the user to switch to different colormap and modify the high and low value of the mapper.

Ideally, I would like to modify the already existing Bokeh figure if it’s possible. It if it’s not possible then I would have to recreate the entire Bokeh figure each time.

Let me know if it’s not clear.

You can set e.g. my_mapper.high at any time. If you are in a Bokeh server application, you can set it from real Python code. If you have standalone HTML output (i.e. from output_file) then a CustomJS callback could set new property values. In general, views of Bokeh objects (i.e. plots, gylphs, whatever) are automatically reactive to changes set on them.

Here is a complete example:

from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import Button, ColumnDataSource, CustomJS, LinearColorMapper
from bokeh.plotting import figure
from bokeh.transform import transform

x = [1, 2, 3]
y = [2, 2, 2]
z = [1, 5, 9]

source = ColumnDataSource(data=dict(x=x, y=y, z=z), name="MY_SOURCE")

mapper = LinearColorMapper(palette=["red", "green", "blue"], low=0, high=10)

p = figure(plot_width=300, plot_height=300)
p.circle('x', 'y', size=30, color=transform('z', mapper), source=source)

b = Button()
b.js_on_click(CustomJS(args=dict(mapper=mapper), code="""
    mapper.palette = ["pink", "purple", "orange"]
"""))

show(column(p, b))

Ahah I was trying to access the color mapper from within the figure directly without thinking I could just keep track of the mapper instance. Of course it works fine :slight_smile:

Thanks again for your time!

1 Like

It is possible to dig through the plot to obtain the mapper, but it is much more work than just keeping a reference around and pass it in args.

1 Like