Dropdown menu in Plotly Scattermapbox not Updating Points Correctly


#1

I am trying to create a plotly graph using Scattermapbox with a dropdown box. The aim is for the dropdown to control the colorscale and labels associated with the scatter points. The data plots OK initially. When I select the second value in the dropdown the colors and labels change, but when I reselect the first value in the menu, which has the same properties as the initial plot, all but the first value has a non-grey colour.

Any ideas why this might be happening? I have included a simplified example below:

import plotly as py
import plotly.graph_objs as go
import copy


lat = [20, 30, 40]
lon = [0, 10, 30]

var1=[1, 2, -1]
var2=[10, 20, 30]

var1Str = [str(x) for x in var1]
var2Str = [str(x) for x in var2]
#%% plot points on a map

MAPBOX_TOKEN= USER TO INSERT TOKEN HERE

markerTemplate=dict(
            size=9,
            color=var1,
            colorbar=dict(
                titleside = 'right',
            ),
            colorscale='Viridis')

marker1=copy.deepcopy(markerTemplate)
marker1['color']=var1
marker1['cmin']=-1
marker1['cmax']=2

marker2=copy.deepcopy(markerTemplate)
marker2['color']=var2
marker2['cmin']=10
marker2['cmax']=30

data = [
    go.Scattermapbox(
        lat=lat,
        lon=lon,
        mode='markers',
        marker=marker1,
        text=var1Str,
    )
]

layout = go.Layout(
    mapbox=dict(
        accesstoken=MAPBOX_TOKEN,
        bearing=0,
        center=dict(
            lat=15,
            lon=0
        ),
        zoom=0.75
    ),
)

updatemenus=list([
    dict(
        buttons=list([   
            dict(
                args=[{'marker': marker1}, {'text': var1Str}],
                label='var1',
                method='update'
            ),
            dict(
                args=[{'marker': marker2},{'text': var2Str}],
                label='var2',
                method='update'
            ),
        ]),
        direction = 'down',
        pad = {'r': 10, 't': 10},
        showactive = False,
        x = 0.1,
        xanchor = 'left',
        y = 1.1,
        yanchor = 'top' 
    ),
])

layout['updatemenus'] = updatemenus

fig = dict(data=data, layout=layout)
py.offline.plot(fig, filename='ScatterMapBox_TEST.html')

#2

Hi @TheDza,

I see what you mean. I’m not sure exactly why you’re seeing this, but I was able to fix it wrapping the marker updates in a single element list

updatemenus=list([
    dict(
        buttons=list([   
            dict(
                args=[{'marker': [marker1]}, {'text': var1Str}],
                label='var1',
                method='update'
            ),
            dict(
                args=[{'marker': [marker2]},{'text': var2Str}],
                label='var2',
                method='update'
            ),
        ]),
        ...

The style (trace) arguments passed to restyle/update can update multiple traces using an array in some cases. So if you’re working with a single trace, it’s usually a good idea to wrap the argument in a single element list, which means that this argument should be applied, in full, to each trace in the figure.

Hope that helps!
-Jon


#3

Thanks @jmmease that’s great. As an extension of this, I tried to also update the lat and lon arguments using

lat1 = [20, 30, 40]
lon1 = [0, 10, 30]

lat2 = [-20, -30, -10]
lon2 = [10, 0, 20] 

and updating using the following code

buttons=list([   
    dict(
        args=[{'marker': [marker1]}, {'lat': [lat1]}, {'lon': [lon1]}],
        label='var1',
        method='update'
    ),
    dict(
        args=[{'marker': [marker2]}, {'lat': [lat1]}, {'lon': [lon1]}],
        label='var2',
        method='update'
    ),
]),

but it fails to redraw. to go further again it would be nice to be able to actually change the number of points using something like this

lat1 = [20, 30, 40]
lon1 = [0, 10, 30]

lat2 = [-20, -30]
lon2 = [10, 0]

Is this within the functionality of this system or should I move towards using Dash?


#4

Hi @TheDza,

I haven’t tested this, but I think there’s a problem with your args specifications. It think you want a list of a single dict that includes marker, lat, and lon. Rather than three separate dicts

args=[{'marker': [marker1], 'lat': [lat1], 'lon': [lon1]}]

That said, if you would like to keep adding more and more custom interactivity you’ll have a lot more options using either FigureWidget (in the Jupyter Notebook) or Dash (for standalone web app).

Hope that helps!
-Jon


#5

Perfect @jmmease. That’s exactly what I was looking for. Thanks for your help.

Final version of the example code uses:

lat1 = [20, 30, 40]
lon1 = [0, 10, 30]

lat2 = [-20, -30]
lon2 = [10, 0]

and

buttons=list([   
    dict(
        args=[{'marker': [marker1], 'lat': [lat1], 'lon': [lon1]}],
        label='var1',
        method='update'
    ),
    dict(
        args=[{'marker': [marker2], 'lat': [lat2], 'lon': [lon2]}],
        label='var2',
        method='update'
    ),
]),