3d Surface animation(using two types data)

First, I am a Japanese student who are not good at English.
Sorry for my poor sentences.

I will make animation of tsunami.
I want to plot land data (bathymetry) and tsunami data together.

But I cannot make animation.
I can plot only bathymetry and initial data of tsunami.
I cannot move tsunami.

My code is shown below. If there is anyone to know, please give me some advice.

import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
import glob

# input and make data
bath = np.loadtxt('./bathymetry.dat',dtype=float)
bath = -1*bath[::-1]

bathymetry  =  go.Surface(z=bath,
                    #
                    colorscale='Greys',
                    cmin=-1000,cmax=2000,
                    showscale=False,
                    )

tsunamilist = glob.glob('./tsunami/*.dat')
tsunamilist.sort()

tnm = np.zeros((len(tsunamilist),len(bath[:,0]),len(bath[0,:])),dtype=float)

n = 0

for filename in tsunamilist:
    data_tnm = np.loadtxt(filename,dtype=float)
    data_tnm = data_tnm[::-1]
    data_tnm = np.where(data_tnm!=-99.0,data_tnm,np.nan) # no water point(tnm =-99.0)
    tnm[n] = data_tnm
    n += 1

tsunami_ini = go.Surface(z=tnm[0],
                       #
                       colorscale='RdBu',
                       cmin=-20,cmax=20,
                       )

datalist=[]

for i in range(10):
    tsunami = go.Surface(z=tnm[i],
                           #
                           colorscale='RdBu',
                           cmin=-20,cmax=20,
                           )
    thedata=[tsunami]
    datalist.append(thedata)

# define frames
frames = []

for k in range(10):
    frames.append(
        dict(
            data=datalist[k],
        )
    )


# make figure and plot
data=[bathymetry,tsunami_ini]

fig= go.Figure(data=data,frames=frames) 

py.iplot(fig,filename='test')

Hi @Karsy, welcome :slightly_smiling_face:

This sounds like a pretty interesting project! How are you displaying your plot right now? Are you using the Jupyter Notebook? If you are using the Jupyter Notebook, you could use a FigureWidget to display and then update a plot.

Here as an example that uses a slider and some other controls to update an existing FigureWidget: https://github.com/jonmmease/plotly_ipywidget_notebooks/blob/master/notebooks/Interact.ipynb

If you’d like more specific help, could post some sample data so that folks can reproduce the plot you’re trying to make?

-Jon

Thanks for your reaction, @jmmease.

I use Jupiter Notebook.
I don’t know FigureWidget, and I will try to use that.

Thank you.

I could plot for using Figure Widget.
But I cannot input frame data.

Sample code was uploaded this site.
https://github.com/Karsy-Kura/plotly_surface/blob/master/Sample.ipynb

Please tell me how to move wave if you know that.

Hi @Karsy, thanks for posting the example. Here’s what I was talking about

# Imports
import numpy as np
import plotly.plotly as py
import plotly.graph_objs as go
import ipywidgets as widgets
import time

# Compute surface data
x = np.linspace(-10,10,201)
y = np.linspace(-10,10,201)
X,Y = np.meshgrid(x,y)

z_bath = X+Y
z_tnm = np.zeros((10,201,201),dtype=float)

for t in range(10):
    tsunami = t*np.sin(X+t/10*np.pi)
    z_tnm[t] = tsunami

# Create FigureWidget and add surface trace
fig = go.FigureWidget()
surface = fig.add_surface(z=z_bath)

# Set axis ranges to fixed values to keep them from retting during animation
fig.layout.scene.zaxis.range = [-10, 10]
fig.layout.scene.yaxis.range = [0, 150]
fig.layout.scene.xaxis.range = [0, 150]
surface.cmin = -10
surface.cmax = 10

# Create function to update surface z property
def set_surface(i=0):
    surface.z = z=z_tnm[i]

# Display figure
fig

# Call function in a loop to animate
for i in range(10):
    set_surface(i)
    time.sleep(0.5)

Hope that helps!
-Jon

Thank you ,@jmmease.

Thanks to your code, I could move tsunami on Jupyter Notebook.

But my goal is to make one plotly file, not to display fig on Jupyter Notebook. Likely the last figure in this site.
https://plot.ly/python/animations/

Could you do it?

Hi @Karsy,

Here’s a standalone approach that uses the animation and frames support:

# Imports
import numpy as np
import plotly.plotly as py
import plotly.graph_objs as go
import ipywidgets as widgets
import time
from plotly.offline import init_notebook_mode, plot
init_notebook_mode()

# Compute surface data
x = np.linspace(-10,10,201)
y = np.linspace(-10,10,201)
X,Y = np.meshgrid(x,y)

z_bath = X+Y
z_tnm = np.zeros((10,201,201),dtype=float)

for t in range(10):
    tsunami = t*np.sin(X+t/10*np.pi)
    z_tnm[t] = tsunami

# Create FigureWidget and add surface trace
fig = go.Figure()
surface = fig.add_surface(z=z_bath)

# Set axis ranges to fixed values to keep them from retting during animation
fig.layout.scene.zaxis.range = [-10, 10]
fig.layout.scene.yaxis.range = [0, 150]
fig.layout.scene.xaxis.range = [0, 150]
surface.cmin = -10
surface.cmax = 10

frames = []
for i in range(10):
    frames.append(go.Frame(data=[{'type': 'surface', 'z': z_tnm[i]}]))

fig.frames = frames

fig.layout.updatemenus = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.55,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]

plot(fig)

Hope that helps!
-Jon

Thank you for your prompt response, @jmmease.

Thanks for you , I can move data of tsunami in one file.

Last question!
Surface of bathymetry data disappeared.
How can I prevent it?
Both of bathymetry and tsunami display together, I want to display always bathymetry.

Hi,@jmmease.
I succeed in displaying both of data bathymetry and tsunami!
The frame part is the same as your code, only the part of bath has been changed.
Finally code is shown below. Thank you very much!

# Imports
import numpy as np
import plotly.plotly as py
import plotly.graph_objs as go
import ipywidgets as widgets
import time
from plotly.offline import init_notebook_mode, plot
init_notebook_mode()

# Compute surface data
x = np.linspace(-10,10,201)
y = np.linspace(-10,10,201)
X,Y = np.meshgrid(x,y)

z_bath = X+Y
z_tnm = np.zeros((10,201,201),dtype=float)

for t in range(10):
    tsunami = t*np.sin(X+t/10*np.pi)
    z_tnm[t] = tsunami

# Create FigureWidget and add surface trace
#fig = go.Figure()
#surface = fig.add_surface(z=z_bath,name='bath')

# ----- change part ------ 
bath = go.Surface(z=z_bath)
fig = go.Figure(data=[bath,bath])

# Set axis ranges to fixed values to keep them from retting during animation
fig.layout.scene.zaxis.range = [-10, 10]
fig.layout.scene.yaxis.range = [0, 150]
fig.layout.scene.xaxis.range = [0, 150]
#surface.cmin = -10
#surface.cmax = 10

frames = []
for i in range(10):
    frames.append(go.Frame(data=[{'type': 'surface', 'z': z_tnm[i], 'name':'tsunami'}]))

fig.frames = frames

fig.layout.updatemenus = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.55,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]

plot(fig)

Great! Glad you got it working the way you wanted :slightly_smiling_face:

Hi, how can i add 2 surfaces to the frame ? i.e. i need 2 surfaces in the animation

below is the above code (1st version, but with 2 surface ), the frame gives error

Imports

import numpy as np
import plotly.plotly as py
import plotly.graph_objs as go
import ipywidgets as widgets
import time
from plotly.offline import init_notebook_mode, plot
init_notebook_mode()

Compute surface data

x = np.linspace(-10,10,201)
y = np.linspace(-10,10,201)
X,Y = np.meshgrid(x,y)

z_bath = X+Y
z_tnm = np.zeros((10,201,201),dtype=float)
z_tnm2 = np.zeros((10,201,201),dtype=float)

for t in range(10):
tsunami = tnp.sin(X+t/10np.pi)
z_tnm[t] = tsunami
###new
tsunami2 = 2tnp.cos(X+t/10*np.pi)
z_tnm2[t] = tsunami2

Create FigureWidget and add surface trace

fig = go.Figure()
surface = fig.add_surface(z=z_bath)
surface2 = fig.add_surface(z=z_bath) #new

Set axis ranges to fixed values to keep them from retting during animation

fig.layout.scene.zaxis.range = [-10, 10]
fig.layout.scene.yaxis.range = [0, 150]
fig.layout.scene.xaxis.range = [0, 150]
surface.cmin = -10
surface.cmax = 10

surface2.cmin = -10
surface2.cmax = 10

frames = []
for i in range(10):
frames.append(go.Frame(data=[{‘type’: ‘surface’, ‘z’: z_tnm[i]},{‘type’: ‘surface2’, ‘z’: z_tnm2[i]}]))

fig.frames = frames

fig.layout.updatemenus = [
{
‘buttons’: [
{
‘args’: [None, {‘frame’: {‘duration’: 500, ‘redraw’: False},
‘fromcurrent’: True, ‘transition’: {‘duration’: 300}}],
‘label’: ‘Play’,
‘method’: ‘animate’
},
{
‘args’: [[None], {‘frame’: {‘duration’: 0, ‘redraw’: False}, ‘mode’: ‘immediate’,
‘transition’: {‘duration’: 0}}],
‘label’: ‘Pause’,
‘method’: ‘animate’
}
],
‘direction’: ‘left’,
‘pad’: {‘r’: 10, ‘t’: 87},
‘showactive’: False,
‘type’: ‘buttons’,
‘x’: 0.55,
‘xanchor’: ‘right’,
‘y’: 0,
‘yanchor’: ‘top’
}
]

plot(fig)

thanks