You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Matplotlib极坐标图中实现文本沿曲线排列?

在Matplotlib极坐标图中实现文本沿曲线排列

我想要在Matplotlib的极坐标图里实现文本沿曲线排列,以下是我的基础实现代码:

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)
# 定义角度(度转弧度)
theta = [i / 180 * np.pi for i in range(0, 360, angle_size)]
# 文本所在的径向半径
radius = [1] * no_labels
# 计算每个扇区的中点角度
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([])  # 移除默认x轴标签
ax.set_yticklabels([])  # 移除默认y轴标签

# 按标签数量划分网格线
lines = plt.thetagrids(range(0, 360, int(360/len(text))))

# 在每个扇区中点放置居中文本
for m, r, t in zip(mid_point, radius, text):
    ax.annotate(t, xy=[m, r], fontsize=12, ha="center", va="center")

这段代码生成的是普通居中文本的极坐标图。我尝试从零开始实现文本沿曲线排列,目前的进展如下:

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)
# 定义角度(度转弧度)
theta = [i / 180 * np.pi for i in range(0, 360, angle_size)]
# 文本所在的径向半径
radius = [1] * no_labels
# 计算每个扇区的中点角度
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)

# 按标签数量划分网格线
lines = plt.thetagrids(range(0, 360, int(360/len(text))))

ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_xticklabels([])  # 移除默认x轴标签
ax.set_yticklabels([])  # 移除默认y轴标签

# 尝试单个标签拆分字母排列
start_text = theta[0]
text2 = ["ELECTORAL PROCESS"]
spacing = len(text[0]) + 4
end_text = theta[1]

x = np.linspace(start_text, end_text, spacing)

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

这段代码实现了单个标签拆分字母的初步效果,但我需要循环处理所有标签,动态调整每个字母的旋转角度以贴合曲线,还要根据位置调整文本方向(比如部分区域文本正向,部分区域反向),这让问题变得复杂。


简洁实现代码

以下是可以批量处理所有标签、自动调整旋转角度和方向的完整代码:

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 = 360 / no_labels
radius = 1.05  # 文本所在的径向距离,稍大于网格半径更美观

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([])
ax.set_yticklabels([])
plt.thetagrids(range(0, 360, int(angle_size)))

for idx, label in enumerate(text):
    # 计算当前标签所在扇区的起始和结束角度(弧度)
    start_angle = idx * angle_size * np.pi / 180
    end_angle = (idx + 1) * angle_size * np.pi / 180
    # 生成每个字符对应的角度,均匀分布在扇区内
    char_angles = np.linspace(start_angle, end_angle, len(label))
    
    for char, angle in zip(label, char_angles):
        # 将极坐标角度转换为文本旋转角度(贴合曲线切线)
        rotation = np.rad2deg(angle)
        # 右半区(角度在90-270度之间,对应弧度π/2到3π/2)文本反向,避免倒置
        if np.pi/2 < angle < 3*np.pi/2:
            rotation += 180
        
        ax.text(angle, radius, char, 
                fontsize=10, 
                ha='center', va='center',
                rotation=rotation,
                rotation_mode='anchor')  # 基于锚点旋转,保证字符位置准确

plt.show()

参考代码原理解释

你提到的参考代码核心逻辑和上述实现一致,拆解来看:

  1. 文本拆分与角度分配:把每个标签拆分为单个字符,将当前扇区的角度范围均匀分配给每个字符,确保字符沿着扇区的弧线排列。
  2. 旋转角度计算:极坐标下的每个角度对应笛卡尔坐标系中的切线方向,文本的旋转角度需要和这个切线方向一致,才能贴合曲线。
  3. 方向调整:当文本位于极坐标的右半区(弧度π/2到3π/2)时,直接按切线方向旋转会导致文本倒置,因此需要额外增加180度旋转,让文本正向显示。
  4. 旋转模式设置:使用rotation_mode='anchor'确保文本以锚点为中心旋转,避免字符位置偏移。

内容的提问来源于stack exchange,提问作者ruthpozuelo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:52:03