# PlotlyJS.jl question

**URL:** <https://discourse.julialang.org/t/plotlyjs-jl-question/90403>\
**Category:** Visualization\
**Tags:** plotting\
**Created:** [November 15, 2022, 3:55pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403 "2022-11-15T15:55:50Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Djamil\_Lakhdar-Hamin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djamil_lakhdar-hamin/32/19910_2.png) [@Djamil\_Lakhdar-Hamin](https://discourse.julialang.org/u/Djamil_Lakhdar-Hamin)\
**Post date:** [November 15, 2022, 3:55pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/1 "2022-11-15T15:55:50Z")

</div>

What if I am trying to animate the evolution of a surface after the execution of some command that modifies the z component of the surface? I have written the following but the trace does not update when you run the code :

```julia

using PlotlyJS

mutable struct Node 

    #=

    =#

    x_::Real
    y_::Real
    potential::Real
    edge_flag::Bool ## if 1 then the node is on an edge
    # adjacency_list::Array{Real,1}

end 

mutable struct Lattice

     #=

    =#

    length::Integer
    n_grids::Integer 
    lattice_grid::Array{Node}
end 

# number-> √number* √number==number ? true : return false 

function checkperfsquare(number::Integer)

     #=

    =#

    if √number* √number==number 
        return true 
    else 
        return false
    end 
end 

function checkifedge(x,y,L)

    #=

    =#

    if (x>=0 || x<=L) && (y>=0 && y<=L)
        if (x==L || x==0 || y==L || y==0) || (x>L/2 && y<L/2)
            return true 
        else 
            return false 
        end 
    else 
        return false 
    end 
end 

function make_lattice_node(L::Integer,n_grids::Integer,initial_potential::Real)

    #=

   =#

   default_potential=0
   if n_grids>0 && checkperfsquare(n_grids) 
    # nsquare=√n_grids
       incre=L/√n_grids
       lattice_grid=[y==L ? Node(x,y,initial_potential,checkifedge(x,y,L)) : Node(x,y,default_potential,checkifedge(x,y,L)) for x=0-incre:incre:L+incre,y=0-incre:incre:L+incre]
       return Lattice(L,n_grids,lattice_grid)

   else 
       error("Use integer which is a perfect square\n")
       return 0
   end 
end 

function average_over_neighbors(grid::Array{Node},i,j,a)

    #=

    =#

    return 1/4a^2*(grid[i-1,j].potential+grid[i+1,j].potential+grid[i,j-1].potential+grid[i,j+1].potential)

end 

function relax(lattice::Lattice,L::Real,n_grids::Integer)

    #=

    =#

        m,n=size(lattice.lattice_grid)
        grid=lattice.lattice_grid
        a=L/√n_grids

        for i=2:n-1,j=2:m-1
            node=grid[i,j]
            # @show node
            if node.edge_flag==false
               node.potential=average_over_neighbors(grid,i,j,a)
            end 
        end 

end 

# function relax_dont(lattice::Lattice,trials::Real)

# #=

# =#

# Δ=0
# while Δ<trials  
# # @show Δ
# relax(lattice) 
# # @show lattice.lattice_grid
# Δ=Δ+1
# end
# end 

function topographical_map(lattice::Lattice)

    z_data=[node.potential for node in lattice.lattice_grid[2:end-1,2:end-1]]

    return surface(
        x=[node.x_ for node in lattice.lattice_grid[2:end-1,2:end-1]],
        y=[node.y_ for node in lattice.lattice_grid[2:end-1,2:end-1]],
        z=z_data,
        contours_z=attr(
            show=true,
            usecolormap=true,
            highlightcolor="limegreen",
            project_z=true
        )
    )

end

function animate(lattice::Lattice,trials::Integer,n_grids::Integer)

    trace = topographical_map(lattice)
    n_frames = trials
    frames = Vector{PlotlyFrame}(undef, n_frames)
    for k in 1:n_frames
        # @show Δ
        relax(lattice,lattice.length,n_grids) 
        frames[k]=frame(data=(attr(z=[node.potential for node in lattice.lattice_grid[2:end-1,2:end-1]])),
        layout=attr(title_text="Iteration $k"), #update title
        name="fr$k", #frame name; it is passed to slider 
        traces=[0] # this means that the above data update the first trace (here the unique one) 
        ) 
    end

    updatemenus = [attr(type="buttons", 
                        active=0,
                        y=1.2, #(x,y) button position 
                        x=1.2,
                        buttons=[attr(label="Play",
                                    method="animate",
                                    args=[nothing,
                                            attr(frame=attr(duration=1, 
                                                            redraw=true),
                                                transition=attr(duration=0),
                                                fromcurrent=true,
                                                mode="immediate"
                                                            )])])];

    sliders = [attr(active=0, 
                    minorticklen=0,
                    
                    steps=[attr(label="f$k",
                                method="animate",
                                args=[["fr$k"], # match the frame[:name]
                                    attr(mode="immediate",
                                        transition=attr(duration=1),
                                        frame=attr(duration=1, 
                                                    redraw=true))
                                    ]) for k in 1:n_frames ]
                )];    

    layout = Layout(title_text="Finding Pontential Via Relaxation", title_x="x",
        width=1000, height=1000,
                xaxis_range=[0, lattice.length+5], 
                yaxis_range=[0, lattice.length+5],
                updatemenus=updatemenus,
                sliders=sliders)

    Plot(trace, layout, frames)

end

latt=make_lattice_node(16,16,10)
animate(latt,100,16)

```

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [November 16, 2022, 7:49pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/2 "2022-11-16T19:49:37Z")

</div>

1. replace the `frame` definition by:

```julia
frames[k]=frame(data=[attr(z=[node.potential for node in lattice.lattice_grid[2:end-1,2:end-1]])],
                        layout=attr(title_text="Iteration $k"), #update title
                        name="fr$k", #frame name; it is passed to slider 
                        traces=[0] # this means that the above data update the first trace (here the unique one) 
        ) 

```

i.e. `data` is a vector.

1. `xaxis` is a `scene` property, and `scene` is missing from your layout definition. The right definition is:

```julia
layout = Layout(title_text="Finding Pontential Via Relaxation", title_x="x",
        width=700, height=500,
                scene=attr(xaxis_range=[0, lattice.length+5], 
                yaxis_range=[0, lattice.length+5]),
                updatemenus=updatemenus,
                sliders=sliders)

```

1. with these changes the animation works, but the surface z-variation is unnoticeable because, for example, if `fig = Plot(trace, layout, frames)`  
we have the difference:

```julia
fig.frames[50].data[1].z .- fig.frames[1].data[1].z    
5×5 Matrix{Real}:
 0 0 0 0 0
 0 4.00604e-5 0.00252263 0.002559 0
 0 4.12361e-5 0.00259905 0.00256199 0
 0 0 0.002522 7.94373e-5 0
 0 0 0 0 0

```

and

```julia
fig.frames[50].data[1].z .-fig.frames[49].data[1].z    
5×5 Matrix{Real}:
 0 0 0 0 0
 0 0.0 0.0 0.0 0
 0 0.0 0.0 0.0 0
 0 0 0.0 0.0 0
 0 0 0 0 0

```

---

<div class="post-metadata">

**Author:** ![Djamil\_Lakhdar-Hamin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djamil_lakhdar-hamin/32/19910_2.png) [@Djamil\_Lakhdar-Hamin](https://discourse.julialang.org/u/Djamil_Lakhdar-Hamin)\
**Post date:** [November 16, 2022, 10:17pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/3 "2022-11-16T22:17:24Z")

</div>

> [@empet](#):
>
> ```julia-auto
> layout = Layout(title_text="Finding Pontential Via Relaxation", title_x="x",
> width=700, height=500,
> scene=attr(xaxis_range=[0, lattice.length+5], 
> yaxis_range=[0, lattice.length+5]),
> updatemenus=updatemenus,
> sliders=sliders)
> 
> ```

So it does work if one uses a large enough potential, for instance

```julia-auto

latt=make_lattice_node(100,100,10234231234)
animate(latt,100,100)

```

---

<div class="post-metadata">

**Author:** ![Djamil\_Lakhdar-Hamin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djamil_lakhdar-hamin/32/19910_2.png) [@Djamil\_Lakhdar-Hamin](https://discourse.julialang.org/u/Djamil_Lakhdar-Hamin)\
**Post date:** [November 16, 2022, 10:17pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/4 "2022-11-16T22:17:44Z")

</div>

Is there a way to save these plotlyjs animations in gif format?

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [November 17, 2022, 5:59pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/5 "2022-11-17T17:59:45Z")

</div>

From your trace and layout, modified to:

```julia
layout = Layout(title_text="Finding Pontential Via Relaxation", title_x=0.5,
                width=700, height=500,
                scene=attr(xaxis_range=[0, lattice.length+5], 
                yaxis_range=[0, lattice.length+5]),
                )

```

define:

```julia
fig=Plot(trace, layout)

```

remove the frames, updatemenus and sliders definition, and create the gif/mp4 as follows:

```julia
using Plots
fnames=String[]
for k in 1:n_frames
    relax(lattice,lattice.length,n_grids) 
    update!(fig, attr(z=[node.potential for node in lattice.lattice_grid[2:end-1,2:end-1]]),
                 layout=attr(title_text="Iteration $k")                    
                ) 
    filename=lpad(k, 6, "0")*".png"
    push!(fnames, filename)
    savefig(fig, "tmp/"*filename, width=700, height=500, scale=1) #tmp, a folder where the frames are saved
end
anim = Plots.Animation("tmp", fnames)
Plots.buildanimation(anim, "your.gif", fps = 12, show_msg=false)
#or Plots.buildanimation(anim, "your.mp4", fps = 12, show_msg=false)  

```

---

<div class="post-metadata">

**Author:** ![Djamil\_Lakhdar-Hamin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djamil_lakhdar-hamin/32/19910_2.png) [@Djamil\_Lakhdar-Hamin](https://discourse.julialang.org/u/Djamil_Lakhdar-Hamin)\
**Post date:** [November 23, 2022, 3:58pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/6 "2022-11-23T15:58:43Z")

</div>

> [@empet](#):
>
> ```julia-auto
> layout = Layout(title_text="Finding Pontential Via Relaxation", title_x=0.5,
> width=700, height=500,
> scene=attr(xaxis_range=[0, lattice.length+5], 
> yaxis_range=[0, lattice.length+5]),
> )
> 
> ```

This unfortunately does not work, the types do not coincide. The problem seems to be when one creates a trace with PlotLYJS that that conflicts with argument types for update!. Stack trace:

```julia-auto

ERROR: TypeError: in keyword argument layout, expected Layout, got PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}}

```

when I eliminate the layout line I get:

```julia-auto
ERROR: 
MethodError: no method matching update!(::Plot{Array{GenericTrace{Dict{Symbol,Any}},1},Layout{Dict{Symbol,Any}},Array{PlotlyFrame,1}}, ::UnitRange{Int64}, ::PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}}; layout=Layout{Dict{Symbol,Any}}(Dict{Symbol,Any}(:template => Template
  data: Dict{Symbol,Array{PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}},1}}
  layout: PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}}
,:margin => Dict(:l => 50,:b => 50,:r => 50,:t => 60)), Subplots
  rows: Int64 1
  cols: Int64 1
  shared_xaxes: Bool false
  shared_yaxes: Bool false
  start_cell: String "top-left"
  subplot_titles: Missing missing
  column_widths: Missing missing
  row_heights: Missing missing
  specs: Array{Spec}((1, 1))
  insets: Missing missing
  column_titles: Missing missing
  row_titles: Missing missing
  x_title: Missing missing
  y_title: Missing missing
  grid_ref: Array{Array{PlotlyBase.SubplotRef,1}}((1, 1))
  has_secondary_y: Bool false
  horizontal_spacing: Float64 0.2
  vertical_spacing: Float64 0.3
  max_width: Float64 1.0
  _widths: Array{Float64}((1,)) [1.0]
  _heights: Array{Float64}((1,)) [1.0]
))
Closest candidates are:
  update!(::Plot, ::Union{Int64, AbstractArray{Int64,1}}) at /Users/dlakhdar/.julia/packages/PlotlyBase/4NWbR/src/api.jl:226 got unsupported keyword argument "layout"
  update!(::Plot, ::Union{Int64, AbstractArray{Int64,1}}, ::AbstractDict; layout, kwargs...) at /Users/dlakhdar/.julia/packages/PlotlyBase/4NWbR/src/api.jl:226
  update!(::Plot, ::Any; layout, kwargs...) at /Users/dlakhdar/.julia/packages/PlotlyBase/4NWbR/src/api.jl:232
  ...
Stacktrace:
 [1] update!(::Plot{Array{GenericTrace{Dict{Symbol,Any}},1},Layout{Dict{Symbol,Any}},Array{PlotlyFrame,1}}, ::PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}}; layout::Layout{Dict{Symbol,Any}}, kwargs::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at /Users/dlakhdar/.julia/packages/PlotlyBase/4NWbR/src/api.jl:232
 [2] update!(::Plot{Array{GenericTrace{Dict{Symbol,Any}},1},Layout{Dict{Symbol,Any}},Array{PlotlyFrame,1}}, ::PlotlyBase.PlotlyAttribute{Dict{Symbol,Any}}) at /Users/dlakhdar/.julia/packages/PlotlyBase/4NWbR/src/api.jl:232
 [3] top-level scope at REPL[35]

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 23, 2022, 4:22pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/7 "2022-11-23T16:22:24Z")

</div>

I don’t have any input on your actual problem, but a couple of unrelated pointers:

Unless you want your code to be unnecessarily slow, avoid abstract field types in your structs. No `Real` or `Array{Node}`. Either make the fields concrete or parametric.

This is a bit pointless

> [@Djamil\_Lakhdar-Hamin](#):
>
> ```julia
> if √number * √number==number 
> return true 
> else 
> return false
> end 
> 
> ```

The comparison already returns `true` or `false`, so just write

```julia
return (√number * √number==number) 

```

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [November 23, 2022, 5:43pm UTC](https://discourse.julialang.org/t/plotlyjs-jl-question/90403/8 "2022-11-23T17:43:20Z")

</div>

I used your functions, and separate definition of trace and layout, without understanding what you really want to animate. It would have been useful to post a minimal example that requires `update!`  
To avoid the displayed error define the basic figure as follows:

```julia
lattice = make_lattice_node(100, 100, 10234231234)
fig = Plot(topographical_map(lattice), 
           PlotlyJS.Layout(title_text="", title_x=0.5,
                width=700, height=500,
                scene=attr(xaxis_range=[0, lattice.length+5], 
                yaxis_range=[0, lattice.length+5]),
                ))

```

and by [https://github.com/sglyon/PlotlyBase.jl/blob/master/src/api.jl#L231](https://github.com/sglyon/PlotlyBase.jl/blob/master/src/api.jl#L231) `update!` should be defined as:

```julia
update!(fig, Dict(:z=>[[node.potential for node in lattice.lattice_grid[2:end-1,2:end-1]]]),
             layout=PlotlyJS.Layout(title_text="Iteration $k"))

```

In `Dict(:z =>[[.....]]) ` the outermost `[]` are necessary because the data in Plot consists in a vector of traces, and here `[]` points out to update z in the unique trace from `fig`.
