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

  • 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]
               )