Reputation: 2325
I have a Bokeh scatter plot (bokeh==1.0.4
) using a polar projection. The radius of the points corresponds to RadiusSize
in the ColumnDataSource, and the color corresponds to DepthClass
.
I created a second "dummy" scatter plot to get a legend that corresponds to fill_color
(depth_legend
). I would like a second legend corresponding to the radius. The RadiusSize
data is integers ranging 1 through 5. I am looking for a second legend very similar to this matplotlib legend (see accepted answer).
Here is my relevant plotting code:
def make_scatter(event_data):
'''
Create a polar scatter figure
Parameters
----------
event_data: Pandas dataframe
Returns
-------
p: Bokeh Figure object
'''
cds = create_cds(event_data)
p = figure(
title="",
name="scatter_fig",
width=600, height=600,
tools='',
)
# Project data into polar coordinates
# customjs from bryevdv (https://github.com/bokeh/bokeh/issues/657)
polarx = CustomJSTransform(args=dict(source=cds), v_func='''
const new_xs = new Array(source.data.elev_project_vectors.length)
for(var i = 0; i < new_xs.length; i++) {
new_xs[i] = source.data.elev_project_vectors[i] * Math.sin(source.data.elev_angles[i] )
}
return new_xs
''')
polary = CustomJSTransform(args=dict(source=cds), v_func='''
const new_ys = new Array(source.data.elev_project_vectors.length)
for(var i = 0; i < new_ys.length; i++) {
new_ys[i] = source.data.elev_project_vectors[i] * Math.cos(source.data.elev_angles[i] )
}
return new_ys
''')
event_scatter = p.scatter(
x=transform('elev_project_vectors', polarx),
y=transform('elev_project_vectors', polary),
radius='RadiusSize',
fill_color='DepthClass', fill_alpha=1.0,
name='event_scatter',
source=cds)
# This is a dummy glyph just to have consistent colors for a custom legend
event_scatter_dummy = p.scatter(
x=[1,2,3],
y=[1,2,3],
radius=0,
fill_color=['green','yellow','red'], fill_alpha=1.0,
name='event_scatter_dummy',
)
depth_legend = Legend(items=[
LegendItem(label='crown < 12"', renderers=[event_scatter_dummy], index=0),
LegendItem(label='crown 12-44"', renderers=[event_scatter_dummy], index=1),
LegendItem(label='crown > 44"', renderers=[event_scatter_dummy], index=2),
])
p.add_layout(depth_legend)
return p
Here is a screenshot of the current Bokeh figure with the single legend corresponding to fill_color
:
Ideally, the second legend will have labels (R1,R2,R3,R4,R5) and a corresponding range of increasing radius circles. How can I get this second legend?
Upvotes: 2
Views: 1255
Reputation: 2325
I was able to hack this together by creating a dummy invisible circle, then creating and stacking together five separate Legend
instances that render the dummy circle. Each legend requires custom positioning using location
and label_standoff
to line up correctly. I then cycle through the circle glyphs and adjust their size so they correspond to the radius of the plotted data.
Not an ideal solution since the legend is not tied to the actual data, but it gets the job done visually.
Here is the Bokeh code:
event_radius_dummy_1 = p.circle(
1,1,
radius=0,
fill_alpha=0.0, line_color='black',
name='event_radius_dummy_1'
)
event_legend1 = Legend(items=[
LegendItem(label='R1', renderers=[event_radius_dummy_1])],
location=(20,554), label_standoff=10, label_height=3)
event_legend2 = Legend(items=[
LegendItem(label='R2', renderers=[event_radius_dummy_1])],
location=(14,532), label_standoff=5)
event_legend3 = Legend(items=[
LegendItem(label='R3', renderers=[event_radius_dummy_1])],
location=(8,507), label_standoff=0)
event_legend4 = Legend(items=[
LegendItem(label='R4', renderers=[event_radius_dummy_1])],
location=(2,479), label_standoff=-5)
event_legend5 = Legend(items=[
LegendItem(label='R5', renderers=[event_radius_dummy_1])],
location=(-4,447), label_standoff=-10)
event_legend_list = [event_legend1,event_legend2,event_legend3,event_legend4,event_legend5]
for legend in event_legend_list:
p.add_layout(legend)
size_list = [15,26,37,48,59]
index_list = [1,2,3,4,5]
for index, size in zip(index_list, size_list):
p.legend[index].glyph_height = size
p.legend[index].glyph_width = size
p.legend[index].padding = 0
p.legend[index].margin = 0
p.legend[index].border_line_alpha = 0
p.legend[index].background_fill_alpha = 0
Upvotes: 3