Search code examples
pythonplotlyplotly-python

Add ff.create_quiver to go.Scattermapbox as a trace in Plotly Python


I'm trying to add wind vectors to my Plotly map. This is a simplified version of the code:

import plotly.graph_objects as go
import plotly.figure_factory as ff

    fig = go.Figure(go.Scattermapbox(
        mode = "markers",
        lon = df['lon'],
        lat = df['lat'],
        marker = {'size': 5, 'color':'black'},
    
    x, y = np.meshgrid(np.arange(0,2,.2), np.arange(0,2,.2))
    u = np.cos(x) * y
    v = np.sin(x) * y
    
    vec_field = ff.create_quiver(x, y, u, v)
    fig.add_traces(data = vec_field.data[0])
    
    fig.update_layout(
        margin={"l": 0, "r": 0, "t": 15, "b": 0},
        mapbox={
            "style": "carto-positron",
            "zoom": 5,
            "center": {
                "lon": df['lon'].mean(),
                "lat": df['lat'].mean(),
            },
        },
    )

However, the plot generated is not what I'm looking for. The map ends up overlaying the quiver plot, so I can't see the arrows at all. Is there any way to rectify this, such that the arrows are shown clearly above the map?


Solution

  • import math
    import geopandas as gpd
    import pandas as pd
    import plotly.express as px
    import shapely.geometry
    import numpy as np
    from shapely.affinity import affine_transform as T
    from shapely.affinity import rotate as R
    
    # some geometry for an arrow
    a = shapely.wkt.loads(
        "POLYGON ((-0.6227064947841563 1.890841205238906, -0.3426264166591566 2.156169330238906, -0.07960493228415656 2.129731830238906, 1.952059130215843 0.022985736488906, -0.2085619635341561 -2.182924419761094, -0.6397611822841562 -1.872877544761094, -0.6636088385341563 -1.606053326011095, 0.5862935052158434 -0.400158794761094, -2.312440869784157 -0.3993228572610942, -2.526870557284156 -0.1848931697610945, -2.517313916659156 0.2315384708639062, -2.312440869784157 0.3990052677389059, 0.5862935052158434 0.399841205238906, -0.6363314947841564 1.565763080238906, -0.6227064947841563 1.890841205238906))"
    )
    
    gdf = (
        gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
        .set_crs("EPSG:4326")
    )
    
    # generate some data that has GPS co-ordinates, direction and magnitude of wind
    df_wind = pd.concat(
        [
            pd.DataFrame(
                {
                    "lat": np.linspace(*b[[0, 2]], 20),
                    "lon": np.linspace(*b[[1, 3]], 20),
                    "d": np.random.uniform(0, math.pi *2, 20),
                    "s": np.linspace(0.3, 1.5, 20),
                }
            )
            for b in [gdf.sample(2)["geometry"].total_bounds for _ in range(5)]
        ]
    ).reset_index(drop=True)
    
    # scatter points
    t = (
        px.scatter_mapbox(df_wind, lat="lon", lon="lat")
        .update_layout(mapbox={"style": "carto-positron"})
        .data
    )
    
    # wind direction and strength
    px.choropleth_mapbox(
        df_wind,
        geojson=gpd.GeoSeries(
            df_wind.loc[:, ["lat", "lon", "d", "s"]].apply(
                lambda r: R(
                    T(a, [r["s"], 0, 0, r["s"], r["lat"], r["lon"]]),
                    r["d"],
                    origin=(r["lat"], r["lon"]),
                    use_radians=True,
                ),
                axis=1,
            )
        ).__geo_interface__,
        locations=df_wind.index,
        color="d",
    ).add_traces(t).update_layout(mapbox={"style": "carto-positron", "zoom": 1}, margin={"l":0,"r":0,"t":0,"b":0})
    

    enter image description here