1

This SO question provides some help in order to keep annotations from overlapping on each other, but it just stacks the annotations upwards instead of keeping it as close to possible to the (x,y) position it was supposed to be at. Now it just stacks things straight up to where their original x,y position is meaningless.

The relevant part is this:

def get_text_positions(x_data, y_data, txt_width, txt_height):
a = zip(y_data, x_data)
text_positions = y_data.copy()
for index, (y, x) in enumerate(a):
    local_text_positions = [i for i in a if i[0] > (y - txt_height) 
                        and (abs(i[1] - x) < txt_width * 2) and i != (y,x)]
    if local_text_positions:
        sorted_ltp = sorted(local_text_positions)
        if abs(sorted_ltp[0][0] - y) < txt_height: #True == collision
            differ = np.diff(sorted_ltp, axis=0)
            a[index] = (sorted_ltp[-1][0] + txt_height, a[index][1])
            text_positions[index] = sorted_ltp[-1][0] + txt_height
            for k, (j, m) in enumerate(differ):
                #j is the vertical distance between words
                if j > txt_height * 2: #if True then room to fit a word in
                    a[index] = (sorted_ltp[k][0] + txt_height, a[index][1])
                    text_positions[index] = sorted_ltp[k][0] + txt_height
                    break
return text_positions

I think the magic is happening at the line j > txt_height, but I want to start moving things left and right if there is overlap.

Edit: I did not adjust anything from the outer for loop, because it looks to be calculating if any of the text positions are in the same 'neighborhood'. if local_text_positions: then runs if any of them are overlapping. np.diff is taking the first order difference... but I don't know what is going on after the j>txt_height like I mentioned in the question.

Community
  • 1
  • 1
Jared
  • 3,651
  • 11
  • 39
  • 64
  • 1
    Nice. So what have you tried so far? Where are you stuck? – hitzg Apr 27 '15 at 12:22
  • This is a nice idea. Have you dove in and mucked with stuff to see how the stacking code works? Also, have you worked out a napkin or whiteboard algorithm for how the spiral should work? Seems like you'll risk overlapping everything from the left if your labels are long. – cphlewis Apr 29 '15 at 23:23

1 Answers1

1

Check out my library which does this: https://github.com/Phlya/adjustText

import matplotlib.pyplot as plt
from adjustText import adjust_text
import numpy as np

np.random.seed(2016)

N = 50
scatter_data = np.random.rand(N, 3)
fig, ax = plt.subplots()
bubbles = ax.scatter(scatter_data[:, 0], scatter_data[:, 1],
           c=scatter_data[:, 2], s=scatter_data[:, 2] * 150)
labels = ['ano_{}'.format(i) for i in range(N)]
texts = []
for x, y, text in zip(scatter_data[:, 0], scatter_data[:, 1], labels):
    texts.append(ax.text(x, y, text))
adjust_text(texts, force_text=0.05, arrowprops=dict(arrowstyle="-|>",
                                                    color='r', alpha=0.5))
plt.show()

enter image description here

Phlya
  • 5,726
  • 4
  • 35
  • 54
  • No problem, but wish there was some test data in the question... Updated with an example! – Phlya Feb 10 '18 at 17:27