🏥 🏭 Working on the COVID-19 response in Canada? Plotly & the Canadian government can help you and your organization. Learn more and get in touch.

Trying to add a png/jpg image to a 3D surface graph (R)

Hi there,

I am plotting a 3D surface image (similar to the volcano data example):

plot_ly(z = ~volcano) %>% add_surface()

The only difference is that my data is related to geographical coordinates (on the x and y axes). What I would like to now do is overlay/underlay a real map onto this graph as an additional surface, either as map tiles or even just as a plain jpg/png file.

I’ve downloaded a detailed map image with the exact same co-ordinates/dimensions as my plot. I’ve already spent many hours trying to figure out how to add an image as a flat surface layer to the 3D plot, but have not yet succeeded.

I can see how to add additional plotted surfaces by adding:

%>% add_surface(z = z_matrix)

but specifying an image here doesn’t work (needs a matrix).

I’ve also looked into trying to add it as a background image, as you would in a 2D plot, but this doesn’t work in 3D plots.

I’ve seen a similar implementation in python of mapping an image onto a surface (thanks to a tip by @jackparmer via github), but am still having trouble figuring out how to map an image onto a 3d surface using surfacecolor.

Has anyone done something similar? Is there any other way this can be achieved?

Hi renegademonkey

I need the same thing. Just load an image in a 3d chart. It’s like a flat surface that “dissects” the surface. Not a background image.
Did you have success with it?
Anybody else did succeed in the last 3 years with it?

Thanks in advance,
costef

Hi @costef,

I can explain how to intersect a surface by an image, via plotly.py, not R:

  • suppose that you read an image as an array of shape (m, n, 3) or (m, n, 4);
  • take one of the first three arrays, image[: ,: k], k =0, 1 or 2, let us say, img = image[:,:, 1]
  • define a planar surface that fits the image resolution and map its points onto a colorscale (usually ‘gray’ or ‘gray_r’, according to the values of img, i.e. surfacecolor=img.
import numpy as np
import plotly.graph_objects as go
import skimage.io as sio

x = np.linspace(-2,2, 128)
x, z = np.meshgrid(x,x)
y = np.sin(x**2*z)

fig = go.Figure(go.Surface(x=x, y=y, z=z,
                           colorscale='RdBu', 
                           showscale=False))
image = sio.imread ("https://raw.githubusercontent.com/empet/Discrete-Arnold-map/master/Images/cat-128.jpg") 
print(image.shape)
img = imag[:,:, 1] 
Y = 0.5 * np.ones(y.shape)
fig.add_surface(x=x, y=Y, z=z, 
                surfacecolor=np.flipud(img), 
                colorscale='matter_r', 
                showscale=False)
fig.update_layout(width=600, height=600, 
                  scene_camera_eye_z=0.6, 
                  scene_aspectratio=dict(x=0.9, y=1, z=1));
fig.show()

Incidentally in this case the cat looks natural colormapped to the colorscale 'matter_r`. If you cannot find
a colorscale that is suitable for your image use ‘gray’ or its reversed version:

fig.data[1].update(colorscale='gray');

Hi @empet

from which planet do you come from ???
Great !!!
This is exactly what I need: an image that intersects a surface.
Thanks a lot for providing a piece of source code.

I’m going to “translate” it in plotly.js (Javascript), then to put the cat in horizontal and then I’m done.
Are you maybe good in Javascript to help me with the porting?

Thanks a lot again,
costef

1 Like