Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotating an annotation with Matplotlib

I am currently doing some "fancy" annotation in Matplotlib:

import matplotlib.pyplot as plt
ax = plt.subplot(111)
plt.axis('equal')
src_pos, dst_pos = (0, 0), (1, 1)
src_patch = plt.Rectangle(src_pos, .25, .25, color='r')
ax.add_patch(src_patch)
dst_patch = plt.Circle(dst_pos, .25, color='b')
ax.add_patch(dst_patch)
arrowprops = dict(
    arrowstyle='<-', 
    connectionstyle='arc3,rad=0.3', 
    patchA=dst_patch, 
    patchB=src_patch, 
    shrinkA=1, 
    shrinkB=1)
ant = ax.annotate('', src_pos, dst_pos, arrowprops=arrowprops)
plt.draw()
plt.show()

Now I'd like to add a bit of text next to the arrow. The output should look like:

enter image description here

I am interested in a general solution in which the text placement is chosen programmatically based on the renderer's choice of arrow path. E.g.,

  • The text should be placed appropriately for unknown values of src_pos and dst_pos.
  • The text should be placed appropriately regardless of how the arrowprops are set and which connectionstyle is used.
  • I would prefer to not have to call plt.draw() until the end of the script.

Thanks!

like image 373
BrianTheLion Avatar asked Dec 02 '13 00:12

BrianTheLion


1 Answers

Just looking at the http://matplotlib.org/users/annotations_intro.html page. It seems that the text can be placed independently of the arrow if you know the coordinates. Add this before the draw() command:

ant2 = ax.annotate('this is\nthe text\ni want\nto add', xy=(0.1, 0.8), xytext=(0.1, 0.8), horizontalalignment='left', verticalalignment='top')

In terms of a generalized solution, it would be as complex as the original annotate because there are quite a few different kinds of things that are created depending on the parameters given to annotate. For instance if the arrowstyle is not specified then a YAArow patch instance is created. You can call YAArow's get_path() but a path is of limited usefulness since it's more or less just a collection of points. You would need to write a "find_middle_of_path()" which would need to take into account that paths don't necessarily need to be contiguous, and that sometimes all of the attributes of the path are not filled out by methods that use it, etc. An example of when an arrow path would be non-contiguous would be when the middle of the arrow path is clipped by the edge of the figure.

If arrowstyle is specified then a FancyArrowPatch instance is created. If a FancyArrowPatch is created, you can call get_path_in_displaycoord() to get the path with the same caveat as for YAArow patch path.

IMHO you are better off creating a number of rather simple routines that call annotate two times, one for the arrow and one for the text that correspond with the probably limited ways that you really want to use annotate.

like image 73
Troy Rockwood Avatar answered Oct 17 '22 02:10

Troy Rockwood