3

I'm quite new with python and matplotlib and this website helped me a lot. but I couldn't find a complete answer about this subject.

I would like to annotate the point of a 3d plot using matplotlib, so after some research I found this peace of code : Matplotlib: Annotating a 3D scatter plot

    import pylab
    from mpl_toolkits.mplot3d import Axes3D
    from mpl_toolkits.mplot3d import proj3d
    fig = pylab.figure()
    ax = fig.add_subplot(111, projection = '3d')
    x = y = z = [1, 2, 3]
    sc = ax.scatter(x,y,z)
    # now try to get the display coordinates of the first point

    x2, y2, _ = proj3d.proj_transform(1,1,1, ax.get_proj())

    label = pylab.annotate(
        "this", 
        xy = (x2, y2), xytext = (-20, 20),
        textcoords = 'offset points', ha = 'right', va = 'bottom',
        bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
        arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))

    def update_position(e):
        x2, y2, _ = proj3d.proj_transform(1,1,1, ax.get_proj())
        label.xy = x2,y2
        label.update_positions(fig.canvas.renderer)
        fig.canvas.draw()
    fig.canvas.mpl_connect('button_release_event', update_position)
    pylab.show()

but the thing is, I manage to update only the last label with this code, I tried to do some loop in the update_position(e) function. But I'm certainly missing something. So far I used the Axes3D.text(x, y, z, s, zdir) function, but it's not looking really good.

thanks !

Community
  • 1
  • 1
Adrien G.
  • 31
  • 2
  • Very similar to [this](http://stackoverflow.com/questions/12880254/python-plotting-3d-points-with-annotation) recent question. – cosmosis Oct 15 '12 at 23:32
  • not really, if you try the code from this post, when you rotate the plot the annotations stay still – Adrien G. Oct 16 '12 at 07:40

1 Answers1

4

I recently had the identical problem and inspiration for my solution has come from the solutions in:

  1. Matplotlib: Annotating a 3D scatter plot
  2. Annotate several points with one text in matplotlib

The solution is based on creating an array of "labels", then the position of each "label" gets updated in the update_position() function.

import numpy
from mpl_toolkits.mplot3d import proj3d
import matplotlib.pyplot as plt
import pylab  

def update_position(e):
    print "From update position"
    #Transform co-ordinates to get new 2D projection
    tX, tY, _ = proj3d.proj_transform(dataX, dataY, dataZ, ax.get_proj())
    for i in range(len(dataX)):
        label = labels[i]
        label.xy = tX[i],tY[i]
        label.update_positions(fig.canvas.renderer)
    fig.canvas.draw()
    return

#Input 3D Data
data = numpy.array([[3,6,2],[4,6,2],[2,9,2],[3,6,10],[6,1,5]])

#Separate into X, Y, Z for greater clarity
dataX = data[:,0]
dataY = data[:,1]
dataZ = data[:,2]

plt.close()
fig = plt.figure()
ax = fig.gca(projection='3d')

#3D scatter plot
ax.scatter(dataX, dataY, dataZ, marker = 'o', c='b')

#Transform co-ordinates to get initial 2D projection
tX, tY, _ = proj3d.proj_transform(dataX, dataY, dataZ, ax.get_proj())

#Array of labels
labels = []

#Loop through data points to initially annotate scatter plot
#and populate labels array
for i in range(len(dataX)):
    text='['+str(int(dataX[i]))+','+str(int(dataY[i]))+','+str(int(dataZ[i]))+']'
    label = ax.annotate(text,
            xycoords='data',
            xy = (tX[i], tY[i]), xytext = (-20, 20),
            textcoords = 'offset points', ha = 'right', va = 'top', fontsize=6,
            bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
            arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
    labels.append(label)
#Positions are updated when mouse button is released after rotation.
fig.canvas.mpl_connect('button_release_event', update_position)
fig.show()
Community
  • 1
  • 1
JaynieP
  • 41
  • 3