7

I am trying to draw a graph where every node is automatically placed 1 unit below its parent. Here is an example:

enter image description here

Each node is some text surrounded by a bounding box, which can have a different size and may contain a lot of values (for example, box 3 is very long). I need to obtain the bottom coordinate of each parent node and draw the child node 1 unit below that. However, my problem is that the reported coordinates of the bounding boxes are not correct. In the above example, I expect box 4 to be 1 unit below box 3, but the bottom coordinate of box 3 is reported as -1.5 instead of -2.3. Box 4 is now drawn too close to its parent. I am using Python 2.7.13. Here is my code:

import matplotlib.pyplot as plt

#instantiate plot
plt.axes()
ax = plt.gca()
ax.cla()

#simple setup: draw some nodes at predefined positions
t1 = ax.text(0, 0, '1', ha='center', va='top', color='black', bbox=dict(facecolor='#ccfff5', edgecolor='black',boxstyle='round,pad=0.5'),fontsize=8)
t2 = ax.text(-1, -1, '2', ha='center', va='top', color='black', bbox=dict(facecolor='#ccfff5', edgecolor='black',boxstyle='round,pad=0.5'),fontsize=8)
t3 = ax.text(1, -1, '3\n\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM', ha='center', va='top', color='black', bbox=dict(facecolor='#ccfff5', edgecolor='black',boxstyle='round,pad=0.5'),fontsize=8)

#draw edges between the nodes
plt.plot([0, 0],[0,0], zorder=1, color='#404040', linewidth=1)
plt.plot([0, -1],[0,-1], zorder=1, color='#404040', linewidth=1)
plt.plot([0, 1],[0,-1], zorder=1, color='#404040', linewidth=1)

#Obtain the dimensions of box 3 so that we can place box 4 properly underneath box 3
canvas = ax.figure.canvas
r = canvas.get_renderer()
transf = ax.transData.inverted()
bb = t3.get_window_extent(renderer = r)
bb_datacoords = bb.transformed(transf)

#The y0 coordinate of box 3 is not the expected bottom
print "box coordinates: ", bb_datacoords #Bbox(x0=0.978654233871, y0=-1.5130952381, x1=1.02134576613, y1=-1.0)
print "bottom of box 3: ", bb_datacoords.y0 #-1.5130952381

#define the top y coordinate of box 4
newY = (bb_datacoords.y0 - 1)
print "top position of box 4: ", newY #-2.5130952381
#the bottom coordinate of box 3
newPreviousY = bb_datacoords.y0
print "bottom position of box 3: ", newPreviousY #-1.5130952381

#Add box 4 1 unit below box 3
t4 = ax.text(1, newY, '4', ha='center', va='top', color='black', bbox=dict(facecolor='#ccfff5', edgecolor='black',boxstyle='round,pad=0.5'),fontsize=8)
plt.plot([1, 1],[newPreviousY,newY], zorder=1, color='#404040', linewidth=1)

plt.show()

I have been able to get closer to solving this problem by disabling axis limits before drawing anything in the plot and setting manual limits as:

plt.xlim(-1, 1)
plt.ylim(-6, 1)
plt.autoscale(False)

Box 4 is then still not drawn exactly 1 unit below box 3 (but I think that’s padding issues), but it works almost as expected. However, setting the axis limits beforehand is not ideal, because I need to draw the trees automatically and do not know what the plot limits should be at that point.

How can I obtain the correct bottom coordinate of every box and place a new box 1 unit below it?

SparkAndShine
  • 17,001
  • 22
  • 90
  • 134
pm47582
  • 71
  • 2
  • Have you tried the solution to [this](https://stackoverflow.com/questions/22667224/matplotlib-get-text-bounding-box-independent-of-backend?rq=1) question? (tl;dr : text Bbox isn't final until rendered, and backends render differently; some hacks required). – cphlewis Aug 17 '17 at 20:04

0 Answers0