From b773e5fdc58be03750073cfe50fcb56716a45a7d Mon Sep 17 00:00:00 2001 From: monty Date: Sat, 14 Dec 2024 09:52:58 -0700 Subject: [PATCH] file name changes --- examples/plotly_geo.jl | 9 +- src/NMFkMap.jl | 213 ------------------------------------- src/NMFkPlotMap.jl | 236 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 227 insertions(+), 231 deletions(-) delete mode 100644 src/NMFkMap.jl diff --git a/examples/plotly_geo.jl b/examples/plotly_geo.jl index 5eb3204..e859b06 100644 --- a/examples/plotly_geo.jl +++ b/examples/plotly_geo.jl @@ -1,4 +1,5 @@ import PlotlyJS +import PlotlyBase mapbox_token = "pk.eyJ1IjoibW9udHl2IiwiYSI6ImNsMDhvNTJwMzA1OHgzY256N2c2aDdzdXoifQ.cGUz0Wuc3rYRqGNwm9v5iQ" @@ -10,8 +11,8 @@ PlotlyJS.plot( colorscale = "Portland", ), PlotlyJS.Layout( - margin = PlotlyBase.attr(r=0, t=0, b=0, l=0), - mapbox = PlotlyBase.attr(accesstoken=mapbox_token, style="mapbox://styles/mapbox/satellite-streets-v12") + margin = PlotlyJS.attr(r=0, t=0, b=0, l=0), + mapbox = PlotlyJS.attr(accesstoken=mapbox_token, style="mapbox://styles/mapbox/satellite-streets-v12") ) ) @@ -19,7 +20,7 @@ df = CSV.read(download("https://raw.githubusercontent.com/plotly/datasets/master PlotlyJS.plot( PlotlyJS.densitymapbox(df, lat =:Latitude, lon = :Longitude, z = :Magnitude, zoom = 6), PlotlyJS.Layout( - margin = PlotlyBase.attr(r=0, t=0, b=0, l=0), - mapbox = PlotlyBase.attr(accesstoken=mapbox_token, style="mapbox://styles/mapbox/satellite-streets-v12") + margin = PlotlyJS.attr(r=0, t=0, b=0, l=0), + mapbox = PlotlyJS.attr(accesstoken=mapbox_token, style="mapbox://styles/mapbox/satellite-streets-v12") ) ) diff --git a/src/NMFkMap.jl b/src/NMFkMap.jl deleted file mode 100644 index 9bc32b4..0000000 --- a/src/NMFkMap.jl +++ /dev/null @@ -1,213 +0,0 @@ -import VegaLite -import VegaDatasets -import DataFrames -import Mads -import PlotlyJS - -function plotmap(W::AbstractMatrix, H::AbstractMatrix, fips::AbstractVector, dim::Integer=1; casefilename::AbstractString="", figuredir::AbstractString=".", moviedir::AbstractString=".", dates=nothing, plotseries::Bool=true, plotpeaks::Bool=false, plottransients::Bool=false, quiet::Bool=false, movie::Bool=false, hsize::Measures.AbsoluteLength=12Compose.inch, vsize::Measures.AbsoluteLength=3Compose.inch, dpi::Integer=150, name::AbstractString="Wave peak", cleanup::Bool=true, vspeed::Number=1.0, kw...) - @assert size(W, 2) == size(H, 1) - Wa, _, _ = normalizematrix_col!(W) - Ha, _, _ = normalizematrix_row!(H) - recursivemkdir(figuredir; filename=false) - if dim == 1 - odim = 2 - Ma = Wa - S = W - Fa = Ha - else - odim = 1 - Ma = Ha - S = H - Fa = Wa - end - signalorderassignments, signalpeakindex = NMFk.signalorderassignments(Ma, odim) - nt = dim == 1 ? (Colon(),signalorderassignments) : (signalorderassignments,Colon()) - if !isnothing(dates) - @assert length(dates) == size(Ma, 1) - ndates = dates[signalpeakindex] - else - ndates = dates - end - if plotseries - fn = casefilename == "" ? "" : joinpathcheck(figuredir, casefilename * "-waves.png") - Mads.plotseries(S[nt...] ./ maximum(S), fn; xaxis=dates, names=["$name $(ndates[i])" for i in signalorderassignments]) - if movie && casefilename != "" - c = Mads.plotseries(S[nt...] ./ maximum(S); xaxis=dates, names=["S$i $(ndates[k])" for (i,k) in enumerate(signalorderassignments)], code=true, quiet=true) - progressbar = NMFk.make_progressbar_2d(c) - for i = eachindex(dates) - p = progressbar(i, true, 1, dates[1]) - Gadfly.draw(Gadfly.PNG(joinpathcheck(moviedir, casefilename * "-progressbar-$(lpad(i, 6, '0')).png"), hsize, vsize, dpi=dpi), p) - !quiet && (Mads.display(p; gw=hsize, gh=vsize)) - end - makemovie(; moviedir=moviedir, prefix=casefilename * "-progressbar", keyword="", numberofdigits=6, cleanup=cleanup, vspeed=vspeed) - end - end - if plotpeaks - NMFk.plotmap(Fa, fips, dim, signalorderassignments; dates=ndates, figuredir=figuredir, casefilename=casefilename, quiet=quiet, kw...) - end - if plottransients - for (i, k) in enumerate(signalorderassignments) - Xe = dim == 1 ? W[:,k:k] * H[k:k,:] : permutedims(W[:,k:k] * H[k:k,:]) - # p = signalpeakindex[k] - # NMFk.plotmap(Xe[p:p,:], fips; dates=[ndates[k]], figuredir=moviedir, casefilename=casefilename * "-signal-$(i)", datetext="S$(i) ", movie=movie, quiet=!movie, kw...) - NMFk.plotmap(Xe, fips; dates=dates, figuredir=moviedir, casefilename=casefilename * "-signal-$(i)", datetext="S$(i) ", movie=movie, quiet=!movie, cleanup=cleanup, vspeed=vspeed, kw...) - end - end -end - -function plotmap(X::AbstractMatrix, fips::AbstractVector, dim::Integer=1, signalorderassignments::AbstractVector=1:size(X, dim); signalid::AbstractVector=1:size(X, dim), us10m=VegaDatasets.dataset("us-10m"), goodcounties::AbstractVector=trues(length(fips)), dates=nothing, casefilename::AbstractString="", figuredir::AbstractString=".", title::Bool=false, datetext::AbstractString="", titletext::AbstractString="", leadingzeros::Integer=1 + convert(Int64, ceil(log10(length(signalorderassignments)))), scheme::AbstractString="redyellowgreen", zmin::Number=0, zmax::Number=1, zformat="f", quiet::Bool=false, movie::Bool=false, cleanup::Bool=true, vspeed::Number=1.0) - odim = dim == 1 ? 2 : 1 - @assert size(X, odim) == length(fips[goodcounties]) - @assert length(signalorderassignments) == length(signalid) - if !isnothing(dates) - @assert size(X, dim) == length(dates) - end - recursivemkdir(figuredir; filename=false) - df = DataFrames.DataFrame(FIPS=[fips[goodcounties]; fips[.!goodcounties]]) - for (i, k) in enumerate(signalorderassignments) - nt = ntuple(j->(j == dim ? k : Colon()), ndims(X)) - df[!, :Z] = [vec(X[nt...]); zeros(sum(.!goodcounties))] - signalidtext = eltype(signalid) <: Integer ? lpad(signalid[i], leadingzeros, '0') : signalid[i] - if title || (!isnothing(dates) && titletext != "") - ttitle = "$(titletext) $(signalidtext)" - if !isnothing(dates) - ttitle *= ": $(datetext): $(dates[k])" - end - ltitle = "" - else - ttitle = nothing - if !isnothing(dates) - ltitle = datetext .* "$(dates[k])" - else - ltitle = "$(titletext) $(signalidtext)" - end - end - p = VegaLite.@vlplot( - title=ttitle, - :geoshape, - width=500, height=300, - data={ - values=us10m, - format={ - type=:topojson, - feature=:counties - } - }, - transform=[{ - lookup=:id, - from={ - data=df, - key=:FIPS, - fields=["Z"] - } - }], - projection={type=:albersUsa}, - color={title=ltitle, field="Z", type="quantitative", scale={scheme=scheme, clamp=true, reverse=true, domain=[zmin, zmax]}, legend={format=zformat}} - ) - !quiet && (display(p); println()) - if casefilename != "" - VegaLite.save(joinpathcheck("$(figuredir)", "$(casefilename)-$(signalidtext).png"), p) - end - end - if casefilename != "" && movie - makemovie(; moviedir=figuredir, prefix=casefilename, keyword="", numberofdigits=leadingzeros, cleanup=cleanup, vspeed=vspeed) - end -end - -function plotmap(X::AbstractVector, fips::AbstractVector; us10m=VegaDatasets.dataset("us-10m"), goodcounties::AbstractVector=trues(length(fips)), casefilename::AbstractString="", figuredir::AbstractString=".", title::AbstractString="", quiet::Bool=false, scheme::AbstractString="category10", zmin::Number=0, zmax::Number=1) - recursivemkdir(figuredir; filename=false) - @assert length(X) == length(fips) - nc = length(unique(sort(X))) + 1 - df = DataFrames.DataFrame(FIPS=[fips[goodcounties]; fips[.!goodcounties]], Z=[X; zeros(sum(.!goodcounties))]) - p = VegaLite.@vlplot( - :geoshape, - width=500, height=300, - data={ - values=us10m, - format={ - type=:topojson, - feature=:counties - } - }, - transform=[{ - lookup=:id, - from={ - data=df, - key=:FIPS, - fields=["Z"] - } - }], - projection={type=:albersUsa}, - color={title=title, field="Z", type="ordinal", scale={scheme=vec("#" .* Colors.hex.(parse.(Colors.Colorant, NMFk.colors), :RGB))[1:nc], reverse=true, domainMax=zmax, domainMin=zmin}} - ) - !quiet && (display(p); println()) - if casefilename != "" - VegaLite.save(joinpathcheck("$(figuredir)", "$(casefilename).png"), p) - end -end - -function plotmap(x::AbstractVector{T1}, y::AbstractVector{T1}, c::AbstractVector{T2}; figuredir::AbstractString=".", filename::AbstractString="", format::AbstractString=splitext(filename)[end][2:end], title::AbstractString="", text::AbstractVector=repeat([""], length(x)), scope::AbstractString="usa", projection_type::AbstractString="albers usa", size::Number=5, showland::Bool=true, kw...) where {T1 <: Real, T2 <: Real} - @assert length(x) == length(y) - @assert length(x) == length(text) - trace = PlotlyJS.scattergeo(; - locationmode="USA-states", - lon=x, - lat=y, - hoverinfo="text", - text=text, - marker=PlotlyJS.attr(; size=size, color=c, colorscale=NMFk.colorscale(:rainbow), colorbar=PlotlyJS.attr(; thickness=20, len=0.5, width=100), line_width=0, line_color="black")) - geo = PlotlyJS.attr(; - scope=scope, - projection_type=projection_type, - showland=showland, - landcolor="rgb(217, 217, 217)", - subunitwidth=1, - countrywidth=1, - subunitcolor="rgb(255,255,255)", - countrycolor="rgb(255,255,255)") - layout = PlotlyJS.Layout(; title=title, showlegend=false, geo=geo) - p = PlotlyJS.plot(trace, layout) - if filename != "" - fn = joinpathcheck(figuredir, filename) - recursivemkdir(fn) - PlotlyJS.savefig(p, fn; format=format, kw...) - end - return p -end - -function plotmap(x::AbstractVector{T1}, y::AbstractVector{T1}, c::AbstractVector{T2}; figuredir::AbstractString=".", filename::AbstractString="", format::AbstractString=splitext(filename)[end][2:end], title::AbstractString="", text::AbstractVector=repeat([""], length(x)), scope::AbstractString="usa", projection_type::AbstractString="albers usa", size::Number=5, showland::Bool=true, kw...) where {T1 <: Real, T2 <: Union{Integer,AbstractString,AbstractChar}} - @assert length(x) == length(y) - @assert length(x) == length(text) - traces = [] - for (j, i) in enumerate(unique(sort(c))) - iz = c .== i - jj = j % length(NMFk.colors) - k = jj == 0 ? length(NMFk.colors) : jj - trace = PlotlyJS.scattergeo(; - locationmode="USA-states", - lon=x[iz], - lat=y[iz], - hoverinfo="text", - text=text[iz], - name="$i $(sum(iz))", - marker=PlotlyJS.attr(; size=size, color=NMFk.colors[k])) - push!(traces, trace) - end - geo = PlotlyJS.attr( - scope=scope, - projection_type=projection_type, - showland=showland, - landcolor="rgb(217, 217, 217)", - subunitwidth=1, - countrywidth=1, - subunitcolor="rgb(255,255,255)", - countrycolor="rgb(255,255,255)") - layout = PlotlyJS.Layout(; title=title, geo=geo) - p = PlotlyJS.plot(convert(Vector{typeof(traces[1])}, traces), layout) - if filename != "" - fn = joinpathcheck(figuredir, filename) - recursivemkdir(fn) - PlotlyJS.savefig(p, fn; format=format, kw...) - end - return p -end \ No newline at end of file diff --git a/src/NMFkPlotMap.jl b/src/NMFkPlotMap.jl index 17e8d83..6c70617 100644 --- a/src/NMFkPlotMap.jl +++ b/src/NMFkPlotMap.jl @@ -1,19 +1,227 @@ +import VegaLite +import VegaDatasets +import DataFrames +import Mads import PlotlyJS -function geomap(filename::AbstractString, x::AbstractVector, y::AbstractVector, s::AbstractVector=ones(length(x)) * 10; figuredir::AbstractString=".", title::AbstractString="") - layout = PlotlyJS.Layout(; title=title, showlegend=false, geo=geo) - trace = PlotlyJS.scattergeo(; - locationmode="USA-states", - lat=x, - lon=y, - hoverinfo="text", - text=["loc" for x = eachindex(x)], - marker_size=s, - marker_line_color="black", - marker_line_width=2) +mapbox_token = "pk.eyJ1IjoibW9udHl2IiwiYSI6ImNsMDhvNTJwMzA1OHgzY256N2c2aDdzdXoifQ.cGUz0Wuc3rYRqGNwm9v5iQ" + +# Plot a county based (FIPS) heatmap +function plotmap(W::AbstractMatrix, H::AbstractMatrix, fips::AbstractVector, dim::Integer=1; casefilename::AbstractString="", figuredir::AbstractString=".", moviedir::AbstractString=".", dates=nothing, plotseries::Bool=true, plotpeaks::Bool=false, plottransients::Bool=false, quiet::Bool=false, movie::Bool=false, hsize::Measures.AbsoluteLength=12Compose.inch, vsize::Measures.AbsoluteLength=3Compose.inch, dpi::Integer=150, name::AbstractString="Wave peak", cleanup::Bool=true, vspeed::Number=1.0, kw...) + @assert size(W, 2) == size(H, 1) + Wa, _, _ = normalizematrix_col!(W) + Ha, _, _ = normalizematrix_row!(H) + recursivemkdir(figuredir; filename=false) + if dim == 1 + odim = 2 + Ma = Wa + S = W + Fa = Ha + else + odim = 1 + Ma = Ha + S = H + Fa = Wa + end + signalorderassignments, signalpeakindex = NMFk.signalorderassignments(Ma, odim) + nt = dim == 1 ? (Colon(),signalorderassignments) : (signalorderassignments,Colon()) + if !isnothing(dates) + @assert length(dates) == size(Ma, 1) + ndates = dates[signalpeakindex] + else + ndates = dates + end + if plotseries + fn = casefilename == "" ? "" : joinpathcheck(figuredir, casefilename * "-waves.png") + Mads.plotseries(S[nt...] ./ maximum(S), fn; xaxis=dates, names=["$name $(ndates[i])" for i in signalorderassignments]) + if movie && casefilename != "" + c = Mads.plotseries(S[nt...] ./ maximum(S); xaxis=dates, names=["S$i $(ndates[k])" for (i,k) in enumerate(signalorderassignments)], code=true, quiet=true) + progressbar = NMFk.make_progressbar_2d(c) + for i = eachindex(dates) + p = progressbar(i, true, 1, dates[1]) + Gadfly.draw(Gadfly.PNG(joinpathcheck(moviedir, casefilename * "-progressbar-$(lpad(i, 6, '0')).png"), hsize, vsize, dpi=dpi), p) + !quiet && (Mads.display(p; gw=hsize, gh=vsize)) + end + makemovie(; moviedir=moviedir, prefix=casefilename * "-progressbar", keyword="", numberofdigits=6, cleanup=cleanup, vspeed=vspeed) + end + end + if plotpeaks + NMFk.plotmap(Fa, fips, dim, signalorderassignments; dates=ndates, figuredir=figuredir, casefilename=casefilename, quiet=quiet, kw...) + end + if plottransients + for (i, k) in enumerate(signalorderassignments) + Xe = dim == 1 ? W[:,k:k] * H[k:k,:] : permutedims(W[:,k:k] * H[k:k,:]) + # p = signalpeakindex[k] + # NMFk.plotmap(Xe[p:p,:], fips; dates=[ndates[k]], figuredir=moviedir, casefilename=casefilename * "-signal-$(i)", datetext="S$(i) ", movie=movie, quiet=!movie, kw...) + NMFk.plotmap(Xe, fips; dates=dates, figuredir=moviedir, casefilename=casefilename * "-signal-$(i)", datetext="S$(i) ", movie=movie, quiet=!movie, cleanup=cleanup, vspeed=vspeed, kw...) + end + end +end + +# Plot a county based (FIPS) heatmap +function plotmap(X::AbstractMatrix, fips::AbstractVector, dim::Integer=1, signalorderassignments::AbstractVector=1:size(X, dim); signalid::AbstractVector=1:size(X, dim), us10m=VegaDatasets.dataset("us-10m"), goodcounties::AbstractVector=trues(length(fips)), dates=nothing, casefilename::AbstractString="", figuredir::AbstractString=".", title::Bool=false, datetext::AbstractString="", titletext::AbstractString="", leadingzeros::Integer=1 + convert(Int64, ceil(log10(length(signalorderassignments)))), scheme::AbstractString="redyellowgreen", zmin::Number=0, zmax::Number=1, zformat="f", quiet::Bool=false, movie::Bool=false, cleanup::Bool=true, vspeed::Number=1.0) + odim = dim == 1 ? 2 : 1 + @assert size(X, odim) == length(fips[goodcounties]) + @assert length(signalorderassignments) == length(signalid) + if !isnothing(dates) + @assert size(X, dim) == length(dates) + end + recursivemkdir(figuredir; filename=false) + df = DataFrames.DataFrame(FIPS=[fips[goodcounties]; fips[.!goodcounties]]) + for (i, k) in enumerate(signalorderassignments) + nt = ntuple(j->(j == dim ? k : Colon()), ndims(X)) + df[!, :Z] = [vec(X[nt...]); zeros(sum(.!goodcounties))] + signalidtext = eltype(signalid) <: Integer ? lpad(signalid[i], leadingzeros, '0') : signalid[i] + if title || (!isnothing(dates) && titletext != "") + ttitle = "$(titletext) $(signalidtext)" + if !isnothing(dates) + ttitle *= ": $(datetext): $(dates[k])" + end + ltitle = "" + else + ttitle = nothing + if !isnothing(dates) + ltitle = datetext .* "$(dates[k])" + else + ltitle = "$(titletext) $(signalidtext)" + end + end + p = VegaLite.@vlplot( + title=ttitle, + :geoshape, + width=500, height=300, + data={ + values=us10m, + format={ + type=:topojson, + feature=:counties + } + }, + transform=[{ + lookup=:id, + from={ + data=df, + key=:FIPS, + fields=["Z"] + } + }], + projection={type=:albersUsa}, + color={title=ltitle, field="Z", type="quantitative", scale={scheme=scheme, clamp=true, reverse=true, domain=[zmin, zmax]}, legend={format=zformat}} + ) + !quiet && (display(p); println()) + if casefilename != "" + VegaLite.save(joinpathcheck("$(figuredir)", "$(casefilename)-$(signalidtext).png"), p) + end + end + if casefilename != "" && movie + makemovie(; moviedir=figuredir, prefix=casefilename, keyword="", numberofdigits=leadingzeros, cleanup=cleanup, vspeed=vspeed) + end +end + +# Plot a county based (FIPS) heatmap +function plotmap(X::AbstractVector, fips::AbstractVector; us10m=VegaDatasets.dataset("us-10m"), goodcounties::AbstractVector=trues(length(fips)), casefilename::AbstractString="", figuredir::AbstractString=".", title::AbstractString="", quiet::Bool=false, scheme::AbstractString="category10", zmin::Number=0, zmax::Number=1) + recursivemkdir(figuredir; filename=false) + @assert length(X) == length(fips) + nc = length(unique(sort(X))) + 1 + df = DataFrames.DataFrame(FIPS=[fips[goodcounties]; fips[.!goodcounties]], Z=[X; zeros(sum(.!goodcounties))]) + p = VegaLite.@vlplot( + :geoshape, + width=500, height=300, + data={ + values=us10m, + format={ + type=:topojson, + feature=:counties + } + }, + transform=[{ + lookup=:id, + from={ + data=df, + key=:FIPS, + fields=["Z"] + } + }], + projection={type=:albersUsa}, + color={title=title, field="Z", type="ordinal", scale={scheme=vec("#" .* Colors.hex.(parse.(Colors.Colorant, NMFk.colors), :RGB))[1:nc], reverse=true, domainMax=zmax, domainMin=zmin}} + ) + !quiet && (display(p); println()) + if casefilename != "" + VegaLite.save(joinpathcheck("$(figuredir)", "$(casefilename).png"), p) + end +end + +function plotmap(df::DataFrames.DataFrame; kw...) + plotmap(df.Lon, df.Lat, df[!, 3]; kw...) +end + +# Plot a scatter map (continuous color scale) +function plotmap(x::AbstractVector{T1}, y::AbstractVector{T1}, c::AbstractVector{T2}; figuredir::AbstractString=".", filename::AbstractString="", format::AbstractString=splitext(filename)[end][2:end], title::AbstractString="", text::AbstractVector=repeat([""], length(x)), scope::AbstractString="usa", projection_type::AbstractString="albers usa", size::Number=5, showland::Bool=true, kw...) where {T1 <: Real, T2 <: Real} + @assert length(x) == length(y) + @assert length(x) == length(text) + trace = PlotlyJS.scattermapbox(; + locationmode="USA-states", + lon=x, + lat=y, + hoverinfo="text", + text=text, + marker=PlotlyJS.attr(; size=size, color=c, colorscale=NMFk.colorscale(:rainbow), colorbar=PlotlyJS.attr(; thickness=20, len=0.5, width=100), line_width=0, line_color="black")) + geo = PlotlyJS.attr(; + scope=scope, + projection_type=projection_type, + showland=showland, + landcolor="rgb(217, 217, 217)", + subunitwidth=1, + countrywidth=1, + subunitcolor="rgb(255,255,255)", + countrycolor="rgb(255,255,255)") + layout = PlotlyJS.Layout(; + margin = PlotlyJS.attr(r=0, t=0, b=0, l=0), + mapbox = PlotlyJS.attr(accesstoken=mapbox_token, style="mapbox://styles/mapbox/satellite-streets-v12"), + title=title, showlegend=false, geo=geo) p = PlotlyJS.plot(trace, layout) - j = joinpathcheck(figuredir, filename) - recursivemkdir(j) - PlotlyJS.savefig(p, j; format="html") + if filename != "" + fn = joinpathcheck(figuredir, filename) + recursivemkdir(fn) + PlotlyJS.savefig(p, fn; format=format, kw...) + end + return p +end + +# Plot a scatter map (categorical) +function plotmap(x::AbstractVector{T1}, y::AbstractVector{T1}, c::AbstractVector{T2}; figuredir::AbstractString=".", filename::AbstractString="", format::AbstractString=splitext(filename)[end][2:end], title::AbstractString="", text::AbstractVector=repeat([""], length(x)), scope::AbstractString="usa", projection_type::AbstractString="albers usa", size::Number=5, showland::Bool=true, kw...) where {T1 <: Real, T2 <: Union{Integer,AbstractString,AbstractChar}} + @assert length(x) == length(y) + @assert length(x) == length(text) + traces = [] + for (j, i) in enumerate(unique(sort(c))) + iz = c .== i + jj = j % length(NMFk.colors) + k = jj == 0 ? length(NMFk.colors) : jj + trace = PlotlyJS.scattergeo(; + locationmode="USA-states", + lon=x[iz], + lat=y[iz], + hoverinfo="text", + text=text[iz], + name="$i $(sum(iz))", + marker=PlotlyJS.attr(; size=size, color=NMFk.colors[k])) + push!(traces, trace) + end + geo = PlotlyJS.attr( + scope=scope, + projection_type=projection_type, + showland=showland, + landcolor="rgb(217, 217, 217)", + subunitwidth=1, + countrywidth=1, + subunitcolor="rgb(255,255,255)", + countrycolor="rgb(255,255,255)") + layout = PlotlyJS.Layout(; title=title, geo=geo) + p = PlotlyJS.plot(convert(Vector{typeof(traces[1])}, traces), layout) + if filename != "" + fn = joinpathcheck(figuredir, filename) + recursivemkdir(fn) + PlotlyJS.savefig(p, fn; format=format, kw...) + end return p end \ No newline at end of file