Search code examples
pythonmatplotlibplot-annotationspolar-plot

Curve text around a polar plot


I am trying to curve text on a polar chart in matplotlib.

Here is an example of my case:

import matplotlib.pyplot as plt
import numpy as np
   
text = ["Electoral Process", "Political Pluralism", "Rule of Law",
            "Freedom of expression", "Freedom of believe"]

no_labels =len(text)
angle_size = int(360/no_labels)
#define the number of angles in degrees and convert it to radians 
theta = [i/180*np.pi for i in range(0, 360,angle_size) ]
#where to put the labels on the radial axis
radius = [1]*no_labels
#Find the mid point of each angle
mid_point =[i/180*np.pi for i in range(int(angle_size/2), 360, angle_size)]

fig, ax = plt.subplots(subplot_kw={'projection': 'polar'},figsize=(10, 6), facecolor="white")

ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_xticklabels([]) #remove the original xlabels
ax.set_yticklabels([])  #remove the original ylabels

# Arrange the grid into number of sales equal parts in degrees
lines = plt.thetagrids(range(0, 360, int(360/len(text))))

#Place the text in the midle of each pie
for m, r, t in zip(mid_point,radius, text):
    ax.annotate(t, xy=[m, r], fontsize=12,  ha="center", va="center",  )

Which generates this:

enter image description here

I have found other posts that do that, but I dont understand the code so I am trying to build it from scratch. This is what I got so far:

import numpy as np 
import matplotlib as mpl 

text = ["Electoral Process", "Political Pluralism", "Rule of Law",
            "Freedom of expression", "Freedom of believe"]

no_labels =len(text)
angle_size = int(360/no_labels)
#define the number of angles in degrees and convert it to radians 
theta = [i/180*np.pi for i in range(0, 360,angle_size) ]
#where to put the labels on the radial axis
radius = [1]*no_labels
#Find the mid point of each angle
mid_point =[i/180*np.pi for i in range(int(angle_size/2), 360, angle_size)]

fig,ax=plt.subplots(subplot_kw={'projection': 'polar'},figsize=(10,6), dpi=100)


# Arrange the grid into number of sales equal parts in degrees
lines = plt.thetagrids(range(0, 360, int(360/len(text))))

ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_xticklabels([]) #remove the original xlabels
ax.set_yticklabels([])  #remove the original ylabels

#start with one label

start_text = theta[0]
text2=["ELECTORAL PROCESS"]
spacing=len(text[0])+4
end_text = theta[1]

x= np.linspace(start_text, end_text, spacing)
y= [1, 0.5]

for txt in text2:
    print(txt)
    for a,tx in zip(x,txt):
        ax.text(a, 1.05 ,tx,rotation=-28, fontsize= 8, ha="center", va= "center"),
print(a,tx)

Which produces this: enter image description here

Now I have to iterate it for every label, rotate each letter to follow the curve (using a hardcoded angle at the moment) and also write upwards or downwards depending on where the code is and it seems like I might be complicating things.

Anyone has done this using "simple code" or can explain how the code below works?

Similar problem but dont understand the code


Solution

  • Thanks to @aradhna I managed to fix the code. (Thanks a million) Here it is!

    import numpy as np 
    import matplotlib.pyplot as plt
    
    text = ["Electoral Process", "Political Pluralism", "Rule of Law",
                "Freedom of expression", "Freedom of believe"]
    
    no_labels =len(text)
    angle_size = int(360/no_labels)
    #define the number of angles in degrees and convert it to radians 
    theta = [i/180*np.pi for i in range(0, 360,angle_size) ]
    #Find the mid point of each angle
    mid_point =[i/180*np.pi for i in range(int(angle_size/2), 360, angle_size)]
    
    fig,ax=plt.subplots(subplot_kw={'projection': 'polar'},figsize=(10,6), dpi=100)
    
    
    # Arrange the grid into number of text
    lines = plt.thetagrids(range(0, 360, int(360/len(text))))
    
    
    ax.set_theta_zero_location('N')
    ax.set_theta_direction(-1)
    ax.set_xticklabels([]) #remove the original xlabels
    ax.set_yticklabels([])  #remove the original ylabels
    
    for txt,  midpoint in zip(text, mid_point):
        #count nr letters divide by 2 convert to radians and substract from midpoiint
        center_pos = midpoint-np.deg2rad(len(txt)/2+4)
        #print(midpoint, text, a)
        for i in range(len(txt)):     
            #print(txt[i], midpoint, a)      
            ax.text(center_pos, 1.04 ,txt[i].capitalize(),rotation=-np.rad2deg(center_pos), fontsize= 8, ha="center", va= "center")
            center_pos += 0.032 #adds radians to the next275 letter
        
    

    Here is the result:

    enter image description here