Matplotlib: Render Patch above Annotation

Viewed 243

I have several patches (circles) which are labeled with a text as annotation inside. The two circles are draggable so it is possible, that these overlap. The problem is, that the all circles are always rendered before all annotations, so that all annotations are rendered above the circles. This means, if circle 1 overlaps circle 2, the annotations of both circles are visible. This looks kinda weird and wrong. I tried to solve this with the use of the zorder parameter, but this parameter had no effect. After further studying the documentation, this behaviour seems to be hardcoded into the renderer and I haven't found any solution to change this behaviour.

This is the current way how I draw a single circle with annotation:

def draw_circle_with_annotation(ax, position, label, point_size):
    trans = ScaledTranslation(position[0], position[1], ax.transData)
    point = Circle((0, 0), point_size, transform=trans, picker=True)
    ax.add_patch(self.point)

    label = ax.annotate(label, xy=(0.5, 0.5), xycoords=point, fontsize=10, ha='center', va='center_baseline')

This function is called successively for each circle, so that the draw calls of the patches and the annotations are executed alternately. Still all annotations are rendered on top of all patches.

However, is there any way to draw a patch above an annotation?

1 Answers

Matplotlib first draws elements with the lowest zorder, and then subsequently the higher zorders on top. When elements have the same zorder, patches are draw before texts. The solution is to give every annotation a new, higher zorder:

import matplotlib.pyplot as plt
from matplotlib.transforms import ScaledTranslation
from matplotlib.patches import Circle
import numpy as np

zorder = 2

def draw_circle_with_annotation(ax, position, label, point_size):
    global zorder
    point = Circle(position, point_size, picker=True, facecolor='tomato', edgecolor='k', zorder=zorder)
    ax.add_patch(point)
    ax.annotate(label, xy=position, fontsize=14, ha='center', va='center_baseline', zorder=zorder)
    zorder += 1

fig, ax = plt.subplots()
for i in range(10):
    draw_circle_with_annotation(ax, np.random.uniform(10, [50, 25], 2), f'label\n{i + 1}', 5)
ax.autoscale()
ax.set_aspect('equal')
plt.show()

example plot

Related