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.