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.