Box annotations with categorical axis

Hi,

I am trying to add a box annotation to my figure with categorical y-axis. The plot is not displayed, and we get the following browser error:

[bokeh 3.6.3] setting log level to: 'info'
bokeh-3.6.3.min.js:184  Uncaught (in promise) Error: BoxAnnotation(p6653).top given invalid value: "D"
    at N.validate (bokeh-3.6.3.min.js:184:2273)
    at N._update (bokeh-3.6.3.min.js:184:2013)
    at N.initialize (bokeh-3.6.3.min.js:184:1332)
    at F.initialize_props (bokeh-3.6.3.min.js:180:4419)
    at p._decode_object_ref (bokeh-3.6.3.min.js:213:4647)
    at p._decode (bokeh-3.6.3.min.js:213:1739)
    at bokeh-3.6.3.min.js:213:2209
    at g (bokeh-3.6.3.min.js:179:3808)
    at p._decode_plain_array (bokeh-3.6.3.min.js:213:2197)
    at p._decode (bokeh-3.6.3.min.js:213:888)

See the supplied example that reproduced the above error.

I found the following discussion and github issues:

I still cannot make it work neither I can find an example.

example:

from bokeh.plotting import figure
from bokeh.plotting import show
from bokeh.models import ColumnDataSource
from bokeh.models import BoxAnnotation


y_factor = ['A', 'B', 'C', 'D', 'E', 'F']

p = figure(
    width=1700,
    height=1000,
    background_fill_color="#DCDCDC", background_fill_alpha=0.4,
    y_range=y_factor
)
p.x_range.only_visible = True
box = BoxAnnotation(bottom='B', top='D', fill_alpha=0.2, fill_color='green')
p.add_layout(box)

data = {
    'y': ['A', 'B', 'C', 'D', 'E', 'F'],
    'x': [0, 10, 20, 30, 40, 50],
}
source = ColumnDataSource(data)

p.scatter(x='x', y='y', marker='circle', size=10, source=source)

show(p)

Bokeh info:

Python version        :  3.11.10 | packaged by Anaconda, Inc. | (main, Oct  3 2024, 07:22:26) [MSC v.1929 64 bit (AMD64)]
IPython version       :  (not installed)
Tornado version       :  6.4.1
NumPy version         :  1.26.4
Bokeh version         :  3.6.3
BokehJS static path   :  C:\Users\gilles.faure\AppData\Local\miniconda3\envs\EO_py3.11_truck_simulator\Lib\site-packages\bokeh\server\static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  Windows-10-10.0.22631-SP0

Please can you provide an example to show how to make it work
Thanks
Gilles

Internally, bokeh still holds numerical values for categorical axes. ‘A’ will be located at y = 0.5, and ‘B’ at y = 1.5 etc. So you can, as a workaround, just replace the letters with their respective numbers:

box = BoxAnnotation(bottom=1.5, top=3.5, fill_alpha=0.2, fill_color='green')

or if you want to get a bit more fancy:

box = BoxAnnotation(bottom=y_factor.index('B')+0.5, top=y_factor.index('D')+0.5, fill_alpha=0.2, fill_color='green')

Hopefully that helps…

Hrm that’s weird, the actual property type is left = Coordinate(...) which should include categorical factors, but the validation on the BokehJS side complains… :confused:

@Gilles can you make an issue about this? In general we are slowly moving away from having separate, different “annotation” classes, and instead just having normal glyphs for everything including annotations (i.e. “annotation is a thing you do, not a different thing”) so in the longer term the question for BoxAnnotation specifically is probably irrelevant. But we should keep this in mind for any missing test coverage.

You might also look to see if current “boxy” glyphs (e.g. rect, quad, block, bars) could satisfy your needs. Those definitely should work with categorical factors, though you might be relying on some features that are still exclusive to BoxAnnotation at present. YMMV

Issue raised:
BokehJS complains about categorical factors for the BoxAnnotation widget ¡ Issue #14520 ¡ bokeh/bokeh

1 Like