简体   繁体   中英

Annotate 3D scatter plot on pick event

I want to show annotations on a 3D scatter plot when the user clicks on a point.

The code I have shows the annotation once I move the plot after I click on a point.

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

fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')

x = [1, 2, 3]
y = [1, 2, 3]
z = [1, 2, 3]

scatter = ax.scatter(x,y,z,picker=True)

def annotate_onclick(event):

    point_index = int(event.ind)
    print(point_index)

    proj = ax.get_proj()

    x_p, y_p, _ = proj3d.proj_transform(x[point_index], y[point_index], z[point_index], proj)

    plt.annotate(str(point_index), xy=(x_p, y_p))


fig.canvas.mpl_connect('pick_event', annotate_onclick)

plt.show()

How can I make the annotation appear as soon as the user clicks on a point, without having to move the plot?

Add fig.canvas.draw_idle() at the end of your callback function to force the re-drawing of the new annotation.

Hmm when using Matplotlib 3.4 the index flips depending on the orientation of the 3dgraph on the screen, and it throws this whole thing off. Run the code rotate graph by 180 degrees and click on the points. It's awful.

#!/usr/bin/env python3

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


import matplotlib
print('matplotlib: {}'.format(matplotlib.__version__))

fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')

x = [0, 2, 0,0]
y = [0, 2, 0,2]
z = [0, 2, 2,0]

scatter = ax.scatter(x,y,z,picker=True)

def chaos_onclick(event):

    point_index = int(event.ind)
    print(point_index)

    #proj = ax.get_proj()
    #x_p, y_p, _ = proj3d.proj_transform(x[point_index], y[point_index], z[point_index], proj)
    #plt.annotate(str(point_index), xy=(x_p, y_p))
    
    print("X=",x[point_index], " Y=",y[point_index], " Z=",z[point_index], " PointIdx=", point_index)


fig.canvas.mpl_connect('pick_event', chaos_onclick)
plt.show()

OK here's a workaround with info gathered from Matplotlib: Annotating a 3D scatter plot .

#!/usr/bin/env python3

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

import matplotlib
print('matplotlib: {}'.format(matplotlib.__version__))

fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')

x = [0, 2, 0,0]
y = [0, 2, 0,2]
z = [0, 2, 2,0]

scatter = ax.scatter(x,y,z,picker=True)

ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')


def onMouseMotion(event):
  print(event)

def chaos_onclick(event):

    print(dir(event.mouseevent))
    xx=event.mouseevent.x
    yy=event.mouseevent.y
    
    #magic from https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
    x2, y2, z2=proj3d.proj_transform(x[0], y[0], z[0], plt.gca().get_proj())
    x3, y3 = ax.transData.transform((x2, y2))
    #the distance
    d=np.sqrt ((x3 - xx)**2 + (y3 - yy)**2)
    
    print ("distance=",d)
    
    
    #find the closest by searching for min distance.
    #All glory to https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
    imin=0
    dmin=10000000
    for i in range(len(x)):
      #magic from https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
      x2, y2, z2=proj3d.proj_transform(x[i], y[i], z[i], plt.gca().get_proj())
      x3, y3 = ax.transData.transform((x2, y2))
      #the distance magic from https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
      d=np.sqrt ((x3 - xx)**2 + (y3 - yy)**2)
      #We find the distance and also the index for the closest datapoint
      if d< dmin:
        dmin=d
        imin=i
        
      #print ("i=",i," d=",d, " imin=",imin, " dmin=",dmin)
    
    # gives the incorrect data point index
    point_index = int(event.ind)

    print("Xfixed=",x[imin], " Yfixed=",y[imin], " Zfixed=",z[imin], " PointIdxFixed=", imin)
    print("Xbroke=",x[point_index], " Ybroke=",y[point_index], " Zbroke=",z[point_index], " PointIdx=", point_index)

fig.canvas.mpl_connect('pick_event', chaos_onclick)
#fig.canvas.mpl_connect('motion_notify_event', onMouseMotion)  # on mouse motion


plt.show()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM