kms
kms

Reputation: 2024

Plotly mapbox : Get the geometry of current view / zoom level

I am rendering a scattermapbox using plotly in my Flask / Dash Application. I have set a default zoom level and lat,long coords. As the user pans the map via the plotly interface and changes the zoom level, I'd like to update the map with points and data layer.

Here's some code for reference:

import pandas as pd
import geopandas as gpd

# import mapbox
import requests
import plotly.graph_objects as go

# Update with host url
df_geo = gpd.GeoDataFrame.from_features(
    requests.get(
        "https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_050_00_20m.json"
    ).json()
)

import plotly.graph_objects as go

fig = go.Figure(
    go.Choroplethmapbox(
        geojson=df_geo.set_index("GEO_ID")["geometry"].__geo_interface__,
        locations=df_geo["GEO_ID"],
        z=df_geo["CENSUSAREA"],
        autocolorscale=False,
        colorscale="Viridis",
        zmin=df_geo["CENSUSAREA"].min(),
        zmax=df_geo["CENSUSAREA"].quantile(0.95),
        marker_line_width=0,
        colorbar={"orientation": "h", "x": 0.5, "yanchor": "middle", "y": 0.1},
    )
)


fig.update_layout(
    mapbox_style="carto-positron",
    # mapbox_accesstoken=token,
    mapbox_zoom=3,
    mapbox_center={"lat": 37.0902, "lon": -95.7129},
    margin={"r": 0, "t": 0, "l": 0, "b": 0},
)

fig.show()

My question is, how do I obtain the geometries / bbox or lat,longs coords of the current map view?

Links to docs for reference:

https://plotly.com/python/reference/#scattermapbox

https://dash.plotly.com/dash-core-components/graph

Upvotes: 0

Views: 2824

Answers (1)

Rob Raymond
Rob Raymond

Reputation: 31226

  • relayoutData on a mapbox does return center and coordinates
  • have demonstrated by inserting this into trace using client side callback. This is working, however to improve efficiency I really wants to just update the figure rather than re-render it.
import pandas as pd
import geopandas as gpd
import requests
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash
from dash.dependencies import Input, Output, State
import json

# Update with host url
df_geo = gpd.GeoDataFrame.from_features(
    requests.get(
        "https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_050_00_20m.json"
    ).json()
)

fig = go.Figure(
    [
        go.Choroplethmapbox(
            geojson=df_geo.set_index("GEO_ID")["geometry"].__geo_interface__,
            locations=df_geo["GEO_ID"],
            z=df_geo["CENSUSAREA"],
            autocolorscale=False,
            colorscale="Viridis",
            zmin=df_geo["CENSUSAREA"].min(),
            zmax=df_geo["CENSUSAREA"].quantile(0.95),
            marker_line_width=0,
            name="choropleth"
            # colorbar={"orientation": "h", "x": 0.5, "yanchor": "middle", "y": 0.1},
        ),
        go.Scattermapbox(
            name="scatter", marker={"size": 30, "color": "red", "opacity": 1}
        ),
    ]
)

fig.update_layout(
    mapbox_style="carto-positron",
    # mapbox_accesstoken=token,
    mapbox_zoom=3,
    mapbox_center={"lat": 37.0902, "lon": -95.7129},
    margin={"r": 0, "t": 0, "l": 0, "b": 0},
    datarevision=0,
    height=300,
    width=600,
    autosize=False,
)

# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
    [
        dash.dcc.Checklist(
            options=[{"label":"refesh", "value":"yes"}],
            id="refresh",
        ),
        dash.dcc.Graph(id="mapbox_fig", figure=fig),
        dash.html.Div(
            id="debug_container",
        ),
        dash.dcc.Store(
            id="points-store",
            data={
                "lat": [],
                "lon": [],
            },
        ),
    ]
)


@app.callback(
    Output("points-store", "data"),
    Output("debug_container", "children"),
    Input("mapbox_fig", "relayoutData"),
    Input("refresh","value")
)
def mapbox_cb(mapbox_cfg, refresh):
    try:
        refresh = refresh[0]=="yes"
    except Exception:
        refresh = False
    if mapbox_cfg and "mapbox.zoom" in mapbox_cfg.keys() and refresh:
        bbox = np.array(mapbox_cfg["mapbox._derived"]["coordinates"])
        # bbox = bbox * .8
        data = {
            "lon": bbox[:, 0].tolist() + [mapbox_cfg["mapbox.center"]["lon"]],
            "lat": bbox[:, 1].tolist() + [mapbox_cfg["mapbox.center"]["lat"]],
        }

        return data, [
            dash.html.Pre(json.dumps(mapbox_cfg, indent=2)),
            dash.html.Pre(json.dumps(data, indent=2)),
        ]
    else:
        raise dash.exceptions.PreventUpdate


app.clientside_callback(
    """
    function(data, fig) {
        fig.data[1]['lat'] = data['lat'];
        fig.data[1]['lon'] = data['lon'];
        fig.layout.datarevision = fig.layout.datarevision + 1;
        /* return fig; */
        return JSON.parse(JSON.stringify(fig)); 
    }
    """,
    Output("mapbox_fig", "figure"),
    Input("points-store", "data"),
    State("mapbox_fig", "figure"),
)

app.run_server(mode="inline")

Upvotes: 3

Related Questions