2

This may be a bit crazy, but I'm trying to create a clickable image map of a 3d scatter plot using matplotlib v1.1.0. I've read how to do it for 2d plots (c.f. this blog), but 3d has mystified me. The basic problem is I don't know how to get the display coordinates for a 3d axes.

In the 2d case, in order to get the click points correctly located over the scatter points, you need to convert each scatter point from data units to display units. That seems fairly straight forward using ax.transData. I was hoping this would also work for 3d axes, but it appears not. For example, here's what I have tried to do:

# create the plot
from mpl_toolkits.mplot3d import Axes3D
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
sc.axes.transData.transform((1,1,1))

The last line gives me an "Invalid vertices array" error, however. It only works if you pass it a tuple of two points, e.g., (1,1), but that doesn't make sense for a 3d plot. There must be a method somewhere that converts the 3d projection to 2d display coordinates, but after wracking the internet for a couple hours I can't find it. Does anyone know how to correctly do this?

ccap83
  • 21
  • 2

1 Answers1

3

You can use proj3d.proj_transform() to project 3D coordinate to 2D. call ax.get_proj() to get the transform matrix:

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)

#####################    
x2, y2, _ = proj3d.proj_transform(1, 1, 1, ax.get_proj())
print x2, y2   # project 3d data space to 2d data space
print ax.transData.transform((x2, y2))  # convert 2d space to screen space
#####################
def on_motion(e):
    # move your mouse to (1,1,1), and e.xdata, e.ydata will be the same as x2, y2
    print e.x, e.y, e.xdata, e.ydata  
fig.canvas.mpl_connect('motion_notify_event', on_motion)
pylab.show()
Zephyr
  • 11,891
  • 53
  • 45
  • 80
HYRY
  • 94,853
  • 25
  • 187
  • 187