pythondata-visualizationbokehholoviewschord-diagram

inverted label text (half turn) for Chord diagram on holoviews with bokeh


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):

enter image description here

Second picture (objective):

enter image description here


Solution

  • 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.