0

Problem:

I am trying to plot several images in Bokeh with the exact same colorbar. But it seems to be impossible. In this minimal example, even if I set the same values low and high for the 2 color bars, the colors on the image remain the same (even if the arrays field1 and field2 are different from each other by a factor of 10).

Minimal example (jupyter notebook):

import numpy as np
from bokeh.palettes import diverging_palette, gray
from bokeh.plotting import figure
from bokeh.models import LinearColorMapper, ColorBar
from bokeh.io import show, output_notebook
from bokeh.layouts import row

output_notebook()

# Define diverging colorbar (black on the edges and white in zero)

palette = diverging_palette(gray(256), gray(256), n=500)
color_mapper = LinearColorMapper(palette=palette, low=-10, high=10, low_color='#000000', high_color='#000000')
color_bar = ColorBar(color_mapper=color_mapper)

# First image

X1, Y1 = np.meshgrid(np.linspace(-1,1,10), np.linspace(-1,1,10))
field1 = X1

print('Field 1:')
print(np.round(field1, 2))

fig1 = figure(title='Field 1', x_range=(-1,1), y_range=(-1,1), width=450, height=250)
fig1.image([field1], x=-1, y=-1, dw=2, dh=2, palette=palette)

fig1.add_layout(color_bar, 'right')

# Second image (with values multiplied by 10, but same colorbar and same plotting parameters)

field2 = 10*field1

print('Field 2:')
print(np.round(field2, 2))

fig2 = figure(title='Field 2', x_range=(-1,1), y_range=(-1,1), width=450, height=250)
fig2.image([field2], x=-1, y=-1, dw=2, dh=2, palette=palette)

fig2.add_layout(color_bar, 'right')

show(row(fig1, fig2))

Output:

Expected output:

As I've set low=-10 and high=10 for both the color bars, I would expect the first image to be all white because all the values in the array field1 are between -1 and 1. The second image is OK to me.

I've already checked the issue How to extend colorbar for 'out-of-range' values in Bokeh or Holoview? and the PR https://github.com/bokeh/bokeh/pull/10781 but it did not help much... Does anyone know the right solution or an easy workaround? Thanks in advance :)

NB: I do not want to use matplotlib because it is too slow for my use case as I am using a slider (I want to plot a sequence of different images with the exact same colorbar).

V. Foy
  • 215
  • 1
  • 2
  • 8
  • 1
    Passing a palette to `image` is a convenience. In actuality, `Image` has a `color_mapper` property set to a `ColorMapper` instance. If you pass a `palette`, what that does is cause a `ColorMapper` to be created for you, which by default will "auto-range" to the data to be colormapped. You should either configure the glyph's color mapper to be identical to the one the color bar has, or (probably better) the color mapper sholud be able to be shared between the color bar and the image glyph so that they always agree. – bigreddot Mar 03 '22 at 02:09

1 Answers1

0

Hey @bigreddot thanks for the comment! Indeed, I've replaced palette=palette with color_mapper=color_mapper in the Image creations, and it worked well! Here is the new script:

import numpy as np
from bokeh.palettes import diverging_palette, gray
from bokeh.plotting import figure
from bokeh.models import LinearColorMapper, ColorBar
from bokeh.io import show, output_notebook
from bokeh.layouts import row

output_notebook()

# Define diverging colorbar (black on the edges and white in zero)

palette = diverging_palette(gray(256), gray(256), n=500)
color_mapper = LinearColorMapper(palette=palette, low=-10, high=10, low_color='#000000', high_color='#000000')
color_bar = ColorBar(color_mapper=color_mapper)

# First image

X1, Y1 = np.meshgrid(np.linspace(-1,1,10), np.linspace(-1,1,10))
field1 = X1

print('Field 1:')
print(np.round(field1, 2))

fig1 = figure(title='Field 1', x_range=(-1,1), y_range=(-1,1), width=450, height=250)
img1 = fig1.image([field1], x=-1, y=-1, dw=2, dh=2, color_mapper=color_mapper)

fig1.add_layout(color_bar, 'right')

# Second image (with values multiplied by 10, but same colorbar and same plotting parameters)

field2 = 10*field1

print('Field 2:')
print(np.round(field2, 2))

fig2 = figure(title='Field 2', x_range=(-1,1), y_range=(-1,1), width=450, height=250)
fig2.image([field2], x=-1, y=-1, dw=2, dh=2, color_mapper=color_mapper)

fig2.add_layout(color_bar, 'right')

show(row(fig1, fig2))

And here is the new output: enter image description here

Exactly as expected, thank you so much :)

V. Foy
  • 215
  • 1
  • 2
  • 8