Reputation: 1933
In the example in https://holoviews.org/reference/elements/bokeh/Chord.html , how can I change the orientation of the label text 180° in order to avoid rotations that flip the label text upside down in the left semicircle (see pictures). The Backend is bokeh. The label text would be much more readable with this change.
Here is what I have done so far:
import numpy as np
def rotate_label(plot, element):
angles = plot.handles['text_1_source'].data['angle']
angles[np.where((angles < -1.5707963267949) | (angles > 1.5707963267949))] += 3.1415926535898
chord.opts(cmap='Category20b',
edge_cmap='Category20b',
edge_color=dim('source').str(),
labels='index',
node_color=dim('index').str(),
hooks=[rotate_label]
)
First picture (current):
Second picture (objective):
Upvotes: 0
Views: 1025
Reputation: 26
Thanks to @mouwsy. I make an updated version to fix the text_align problem:
import numpy as np
def rotate_label(plot, element):
white_space = " "
angles = plot.handles['text_1_source'].data['angle']
characters = np.array(plot.handles['text_1_source'].data['text'])
text_align = []
for angle in angles:
if angle < -1.5707963267949 or angle > 1.5707963267949:
text_align.append('right')
else:
text_align.append('left')
# Update text with whitespace based on angle
plot.handles['text_1_source'].data['text'] = np.array([
x + white_space if x in characters[np.where((angles < -1.5707963267949) | (angles > 1.5707963267949))]
else white_space + x
for x in plot.handles['text_1_source'].data['text']
])
# Rotate labels that are upside down
angles[np.where((angles < -1.5707963267949) | (angles > 1.5707963267949))] += 3.1415926535898
# Set text alignment for each label individually
plot.handles['text_1_source'].data['text_align'] = text_align
plot.handles['text_1_glyph'].text_align = 'text_align'
chord.opts(cmap='Category20b',
edge_cmap='Category20b',
edge_color=dim('source').str(),
labels='index',
node_color=dim('index').str(),
hooks=[rotate_label]
)
Upvotes: 1
Reputation: 1933
This was quite difficult. If the label text is just a few characters long this solution with a hook might be appropriate. I am sure the code could be written clearer, but I have no experience with numpy.
import numpy as np
def rotate_label(plot, element):
white_space = " "
angles = plot.handles['text_1_source'].data['angle']
characters = np.array(plot.handles['text_1_source'].data['text'])
plot.handles['text_1_source'].data['text'] = np.array([x + white_space if x in characters[np.where((angles < -1.5707963267949) | (angles > 1.5707963267949))] else x for x in plot.handles['text_1_source'].data['text']])
plot.handles['text_1_source'].data['text'] = np.array([white_space + x if x in characters[np.where((angles > -1.5707963267949) | (angles < 1.5707963267949))] else x for x in plot.handles['text_1_source'].data['text']])
angles[np.where((angles < -1.5707963267949) | (angles > 1.5707963267949))] += 3.1415926535898
plot.handles['text_1_glyph'].text_align = "center"
chord.opts(cmap='Category20b',
edge_cmap='Category20b',
edge_color=dim('source').str(),
labels='index',
node_color=dim('index').str(),
hooks=[rotate_label]
)
The variable white_space
contains several whitespace characters that can be adjusted, if necessary, to position the label text.
It's not ideal with centered label text (plot.handles['text_1_glyph'].text_align = "center"
). The better solution would be to align the label text of left semicirlce to right
and the label text of right semicircle to left
. But I don't know how to do that.
Upvotes: 1