Banillie
Banillie

Reputation: 69

prevent text from overlapping data points and other text

I'm trying to find an intelligent solution to how text / annotations are placed into a matplotlib plt so they don't over lap with the data point being annotated. Code snip below. Apologies for long dict at the top. So far I've found adjustText which looks very promising, but I can't seem to get it working in this instance. The code below uses adjust_text(), but at the moment all text is being placed together in one part of the ax and I don't understand why. If you run without adjust_text() it places text roughly where it should be, but text is overlapping the data point in places, which I want to avoid. Grateful for any help.

fig, ax = plt.subplots(figsize=(10, 8))

dl_data = {
    "Center": {
        "axis": (0, 0),
        "tp": (0, 0),
        "r": 21.37311395187889,
        "colour": "#ffffff",
        "text": "Center",
        "fill": "solid",
        "ec": "#808080",
        "alignment": ("center", "center"),
    },
    "First": {
        "r": 6.758772077825972,
        "wlc": 45.681000000000004,
        "text": "First",
        "colour": "#FFFFFF",
        "fill": "dashed",
        "ec": "#808080",
        "alignment": ("center", "center"),
        "axis": (-68.82111180215705, -1.2642233142341064e-14),
        "tp": (-68.82111180215705, -1.2642233142341064e-14),
    },
    "Second": {
        "r": 18.979199140111263,
        "wlc": 360.21000000000004,
        "text": "Second",
        "colour": "#FFFFFF",
        "fill": "dashed",
        "ec": "#808080",
        "alignment": ("center", "center"),
        "axis": (-34.41055590107855, 59.600831137357034),
        "tp": (-34.41055590107855, 59.600831137357034),
    },
    "P1": {
        "r": 4.779173568725037,
        "wlc": 2.6,
        "colour": "#92a700",
        "text": "P1, £3",
        "fill": "solid",
        "ec": "#92a700",
        "axis": (-80.83697480558055, -1.4849511367261418e-14),
        "alignment": ("right", "top"),
        "tp": (-87.6161483743056, -1.6094825349031936e-14),
    },
    "P2": {
        "r": 4.779173568725037,
        "wlc": 0,
        "colour": "#ffba00",
        "text": "P2 has a long\nName, £0\n\n",
        "fill": "solid",
        "ec": "#ffba00",
        "axis": (-13.031791598544089, 30.17548646933409),
        "alignment": ("left", "top"),
        "tp": (-9.047093352116576, 24.691019844418072),
    },
    "P3": {
        "r": 4.779173568725037,
        "wlc": 0.21,
        "colour": "#92a700",
        "text": "P3 has a very,\nlong long long,\nname, £0 \n",
        "fill": "solid",
        "ec": "#92a700",
        "axis": (-55.78932020361301, 30.175486469334082),
        "alignment": ("right", "top"),
        "tp": (-59.77401845004052, 24.691019844418065),
    },
    "P4": {
        "r": 15.811388300841896,
        "wlc": 250,
        "colour": "#e77200",
        "text": "P4 also\nhas a longish\nname, £250\n",
        "fill": "solid",
        "ec": "#e77200",
        "axis": (-34.41055590107855, 95.97255740839438),
        "alignment": ("center", "center"),
        "tp": (-34.41055590107855, 113.78394570923628),
    },
    "P5": {
        "r": 4.779173568725037,
        "wlc": 6.6,
        "colour": "#92a700",
        "text": "P5 is medium,\n£7\n\n",
        "fill": "solid",
        "ec": "#92a700",
        "axis": (-69.00212318005225, 70.8403126698613),
        "alignment": ("right", "top"),
        "tp": (-75.44950037768407, 72.9351925104148),
    },
    "P6": {
        "r": 10.16857905510893,
        "wlc": 103.4,
        "colour": "#92a700",
        "text": "P6 is a very long name\nlike P4 is also,\n£100\n",
        "fill": "solid",
        "ec": "#92a700",
        "axis": (0.181011377895139, 70.8403126698613),
        "alignment": ("left", "top"),
        "tp": (11.754017782309209, 74.600610395285),
    },
}

ts = []
x_list = []
y_list = []
for c in dl_data.keys():
    circle = plt.Circle(
        dl_data[c]["axis"],  # x, y position
        radius=dl_data[c]["r"],
        fc=dl_data[c]["colour"],  # face colour
        ec=dl_data[c]["ec"],  # edge colour
        zorder=2,
    )
    ax.add_patch(circle)
    x = dl_data[c]["axis"][0]
    y = dl_data[c]["axis"][1]
    text = dl_data[c]["text"]
    if c in ["Center", "First", "Second"]:
        pass
    else:
        ts.append(ax.text(x, y, dl_data[c]["text"]))
        x_list.append(x)
        y_list.append(y)

adjust_text(
    ts,
    x=x_list,
    y=y_list,
    force_points=0.1,
    arrowprops=dict(arrowstyle="->", color="red"),
)

plt.axis("scaled")
plt.axis("off")
plt.show()

Upvotes: 0

Views: 836

Answers (1)

Stef
Stef

Reputation: 30639

There are two issues:

  1. adjust_text must called after all drawing is completed, i.e. plt.axis("scaled") must come before adjust_text, see docs:
Call adjust_text the very last, after all plotting (especially
anything that can change the axes limits) has been done.
  1. You must pass your circles as additional objects to be avoided: add_objects=objects
    ts = []
    x_list = []
    y_list = []
    objects = []
    for c in dl_data.keys():
        circle = plt.Circle(
            dl_data[c]["axis"],  # x, y position
            radius=dl_data[c]["r"],
            fc=dl_data[c]["colour"],  # face colour
            ec=dl_data[c]["ec"],  # edge colour
            zorder=2,
        )
        objects.append(circle)
        ax.add_patch(circle)
        x = dl_data[c]["axis"][0]
        y = dl_data[c]["axis"][1]
        text = dl_data[c]["text"]
        if c in ["Center", "First", "Second"]:
            pass
        else:
            ts.append(ax.text(x, y, dl_data[c]["text"].strip()))
            x_list.append(x)
            y_list.append(y)
    
    plt.axis("scaled")
    plt.axis("off")
    
    adjust_text(
        ts,
        add_objects=objects,
        arrowprops=dict(arrowstyle="->", color="red"),
    )

enter image description here

I couldn't manage to move the P6 text away from the green and orange circles, though.

Upvotes: 2

Related Questions