Custom Mapbox Style + Animation?

Hi everyone, I came upon an issue trying to animate Scattermapbox and use a custom Mapbox Studio style delivered via URL simultaneously. Is it even possible?

It shows the map with a new style well in a static view;
Animation (slider and buttons) works correctly with default styles;
However, applying both style + animation leads to both slider and buttons stop updateing the map :thinking: :frowning:

@lomska
Custom style must be updated in each frame, which in this case has the following structure:

go.Frame(data=[...],
         traces=[0,1], #depending which fig.data[k] this frame updates
         name=f"fr{j}", #where j is a frame counter
         layout=dict()# here you insert  layout attributes  updated by this frame
        )
1 Like

Hi @empet! Thank you for your response! I’m trying to pass layout mapbox dicts into frames, but it seems that I’m doing it wrongly, since the animation still won’t start with the custom map:

go.Frame(data=[…],
traces=[0,1], #depending which fig.data[k] this frame updates
name=f"fr{j}", #where j is a frame counter
layout = dict(mapbox = dict(accesstoken = [my token], style = [link to the style]))

Token + Link work for the default view, but not for frames. Should I specify more parameters?

I also tried to retrieve the style as a JSON file and extract layers from it, but it seems that some keys do not match those from plotly go.

@lomska
Without your code I cannot express any opinion. But theoretically, I don’t think that you may involve mapbox token in each frame.

Hi @empet ! Here is an example with random values, still not moving with custom style, but OK with the default ones :thinking:

a = ['A', 'B', 'C']
b = np.arange(2010, 2021)
c = 0

df = pd.melt(pd.DataFrame(data=c, index=b, columns=a).reset_index().rename(
    columns = {'index':'year'}), id_vars = 'year', var_name = 'category').drop('value', axis=1)
df['lat'] = pd.Series(np.random.randint(34, 72, size=33))
df['lon'] = pd.Series(np.random.randint(-25, 45, size=33))
df = df.set_index(['year', 'category'])


mono_dark_1 = link to my mapbox style


###########################################################################3

index_list = df.index.levels[0].tolist()

n_frames = len(index_list)

fig = go.Figure(
    go.Scattermapbox(
    lat = df.xs(2015)['lat'],
    lon = df.xs(2015)['lon'],
    mode = 'markers',
    marker = dict(
        size = 4.5,
        color = '#ba9c30',
        opacity = 1,
    ),
        name='x1'
    )
)

frames=[]

for i in range(n_frames):
    year = index_list[i]
    frames.append(
        go.Frame(data = [
            go.Scattermapbox(
                lat = df.xs(year)['lat'],
                lon = df.xs(year)['lon']
            )],
                 name = f"fr{i}", 
                 layout = dict(mapbox = dict(style = mono_dark_1, 
                                             zoom = 2,
                                             center = {'lon': 17, 'lat': 50}))
                )
    )

fig.update(frames=frames)
    
steps = []
for i in range(n_frames):
    year = index_list[i]
    step = dict(label = year,
                method = 'animate',
                args = [
                    [f"fr{i}"],
                    dict(mode = 'immediate',
                         frame = dict(duration = 1000,
                                      redraw = True),
                         transition = dict(duration = 500)
                        )
                ]
               )
    steps.append(step)

sliders = [
    dict(
        transition = dict(duration = 0),
        x = 0.16,
        y = 0.03,
        len = 0.8,
        currentvalue = dict(font = {'family': 'Book Antiqua', 'size': 18, 'color': '#ffffff'},
                            visible = True,
                            xanchor = 'center', 
                            offset = 20),
        steps = steps,
        active = 5,
        bgcolor = '#333333',
        activebgcolor='#ff0000', 
        font = {'family': 'Book Antiqua', 'size': 10, 'color': '#ffffff'},
        ticklen = 4     
        )
]

play_buttons = [{
    'type': 'buttons',
    'showactive': False,
    'bgcolor': '#333333',
    'font': {'family': 'Book Antiqua', 'size': 14, 'color': '#e4e3bf'},
    "direction": "left",
    "pad": {"r": 10, "t": 87},
    'x': 0.15,
    'y': 0.1,
    'buttons': 
    [
        {
            'label': 'β–Ά',
            'method': 'animate',
            'args':
            [
                None,
                {
                    'frame': {'duration': 1000, 'redraw': True},
                    'transition': {'duration': 500},
                    'fromcurrent': True,
                    'mode': 'immediate',
                }
            ]
        },
        {
            'label': 'β—Ό',
            'method': 'animate',
            'args':
            [
                [None],
                {
                    'frame': {'duration': 0, 'redraw': False},
                    'transition': {'duration': 0},
                    'mode':'immediate',
                }
            ]
        }
    ]
}]

fig.update_layout(sliders=sliders,
                  updatemenus=play_buttons,
                  height = 600,
                  width = 900,
                  plot_bgcolor = '#040609', 
                  paper_bgcolor = '#040609', 
                  mapbox = dict(
                      accesstoken = mapbox_token,
                      zoom = 2,
                      center = {'lon': 17, 'lat': 50},
                      style = mono_dark_1),
                  legend = {'title': None,
                            'orientation': 'h',
                            'xanchor': 'right',
                            'yanchor': 'middle',
                            'x': 1,
                            'y': 1.05,
                            'font': {'family': 'Book Antiqua', 'size': 10, 'color': '#e4e3bf'},
                            'itemsizing': 'constant'},
                  
                 )

fig.show()

@lomska
First you have to define layout.mapbox after this definition:

fig = go.Figure(
    go.Scattermapbox(
    lat = df.xs(2015)['lat'],
    lon = df.xs(2015)['lon'],
    mode = 'markers',
    marker = dict(
        size = 4.5,
        color = '#ba9c30',
        opacity = 1,
    ),
        name='x1'
    )
)
fig.update_layout(mapbox=dict()....)

Here you pass your mapbox token, and set the custom style. If it isn’t preserved in frames, then try to set your style in each frame without setting the mapbox token again and again.
I cannot test this idea because I have no personal style for maps.

@empet
Tried it, but with the same result unfortunately :frowning:
Maybe something with the style, though I have only changed some colors and uploaded a custom font…

OK, I made this work in the end by changing the custom style. It seems that the problem is with the custom font, which I uploaded to Mapbox (though it was Book Antiqua, which is preset in Python and Plotly). So for animation to work correctly, it’s better to use one of the Mapbox fonts.