Search code examples
plotly-pythonpython-polarsplotly-expressplotly.graph-objects

How to overlap “map A made by plotly.express with animation” on top of “map B made by plotly.graph_objects” and maintain the animation on map A?


As shown below, map A is created using plotly.express and map B using plotly.graph_objects.'

I’d like to ask to align and overlap map A on top of map B while keeping the animation on map A.

  1. map A (left) animates two typhoon paths near Japan, and map B (right) shows 3 solar farms in Japan: enter image description here

  2. code to create the dataframe:

import polars as pl
import plotly.express as px
import plotly.graph_objects as go

# create df for solar farms
data_solar_farms = {
    "name"          : ["Setouchi Kirei", "Eurus Rokkasho", "Tomatoh Abira"],
    "capacity_mw"   : [235, 148, 111],
    "latitude"      : [34.6, 40.9, 42.7],
    "longitude"     : [134.1, 141.3, 141.7],
    "state"         : ["Okayama", "Aomori", "Hokkaido"],
    "city"          : ["Setouchi", "Rokkasho", "Abira"],
    "year_operation": [2018, 2015, 2015],
}
df_solar_farms = pl.DataFrame(data_solar_farms)

# create df for typhoon paths
data_typhoon = {
    'name'            : ['Lan',  'Lan',  'Lan',  'Guc',  'Guc',  'Guc'],
    'idx'             : [230701, 230702, 230703, 220301, 220302, 220303],
    'latitude'        : [31.1,   36.6,   41.0,   31.8,   36.6,   37.8],
    'longitude'       : [137.3,  133.0,  135.8,  128.4,  135.0,  142.2],
    'date_time'       : [
        '2022-08-07 00:00:00',
        '2022-08-07 06:00:00',
        '2022-08-07 12:00:00',
        '2023-06-06 18:00:00',
        '2023-06-07 00:00:00',
        '2023-06-07 06:00:00',
    ],
    'grade'           : [1, 3, 2, 4, 6, 5]
}
df_typhoon = pl.DataFrame(data_typhoon)
  1. code for map A:
fig_typhoon = px.line_mapbox(
    df_typhoon,
    lat='latitude',
    lon='longitude',
    color='name',
    animation_frame='name',
)

fig_typhoon.update_layout(
    mapbox_style="carto-darkmatter",
    mapbox_center_lat=36,
    mapbox_center_lon=138,
    mapbox_zoom=3,
)

fig_typhoon.show()
  1. code for map B:
fig_solar_farms = go.Figure(
    go.Scattermapbox(
        lat = df_solar_farms['latitude'],
        lon = df_solar_farms['longitude'],
        mode = 'markers',
        marker = go.scattermapbox.Marker(
            size=df_solar_farms['capacity_mw']/10
        ),
    )
)

fig_solar_farms.update_layout(
    mapbox_style="carto-darkmatter",
    mapbox=dict(
        center=go.layout.mapbox.Center(
            lat=36,
            lon=138
        ),
        zoom=3
    ),
)

fig_solar_farms.show()

I've tried the following:

fig_merged = go.Figure()
fig_merged.add_traces(fig_solar_farms, fig_typhoon)
fig_merged.show()

...but got the following error:

ValueError: 
    Invalid element(s) received for the 'data' property of 
        Invalid elements include: [Figure({
    'data': [{'lat': array([34.6, 40.9, 42.7]),
              'lon': array([134.1, 141.3, 141.7]),
              'marker': {'size': array([23.5, 14.8, 11.1])},
              'mode': 'markers',
              'type': 'scattermapbox'}],
    'layout': {'mapbox': {'center': {'lat': 36, 'lon': 138}, 'style': 'carto-darkmatter', 'zoom': 3}, 'template': '...'}
})]

    The 'data' property is a tuple of trace instances
    that may be specified as:
      - A list or tuple of trace instances
        (e.g. [Scatter(...), Bar(...)])
      - A single trace instance
        (e.g. Scatter(...), Bar(...), etc.)
      - A list or tuple of dicts of string/value properties where:
        - The 'type' property specifies the trace type
            One of: ['bar', 'barpolar', 'box', 'candlestick',
                     'carpet', 'choropleth', 'choroplethmapbox',
                     'cone', 'contour', 'contourcarpet',
                     'densitymapbox', 'funnel', 'funnelarea',
                     'heatmap', 'heatmapgl', 'histogram',
                     'histogram2d', 'histogram2dcontour', 'icicle',
                     'image', 'indicator', 'isosurface', 'mesh3d',
                     'ohlc', 'parcats', 'parcoords', 'pie',
                     'pointcloud', 'sankey', 'scatter',
                     'scatter3d', 'scattercarpet', 'scattergeo',
                     'scattergl', 'scattermapbox', 'scatterpolar',
                     'scatterpolargl', 'scattersmith',
                     'scatterternary', 'splom', 'streamtube',
                     'sunburst', 'surface', 'table', 'treemap',
                     'violin', 'volume', 'waterfall']

        - All remaining properties are passed to the constructor of
          the specified trace type

        (e.g. [{'type': 'scatter', ...}, {'type': 'bar, ...}])

Solution

  • In general, the simplest is to use the figure returned by plotly express as the main figure, then add trace(s) to it, then fine tune the layout :

    fig = px.line_mapbox(
        df_typhoon,
        lat='latitude',
        lon='longitude',
        color='name',
        animation_frame='name'
    )
    
    fig.add_trace(
        go.Scattermapbox(
            lat = df_solar_farms['latitude'],
            lon = df_solar_farms['longitude'],
            mode = 'markers',
            marker = go.scattermapbox.Marker(
                size=df_solar_farms['capacity_mw']/10
            )
        )
    )
    
    fig.update_layout(
        mapbox_style="carto-darkmatter",
        mapbox_center_lat=36,
        mapbox_center_lon=138,
        mapbox_zoom=3
    )
    
    fig.show()
    

    screenshot