Plotly 中的插图 (Python)

Inset Maps in Plotly (Python)

我目前正尝试在 Python 中使用 Plotly Express 和 Mapbox 绘制一些地图。

我需要一张带有国家/地区的大地图,旁边是一张“缩放”到特定区域的插图(参见示例图片)INSET MAP EXAMPLE。

我设法绘制了更大的地图(即整个国家/地区),但找不到正确的方法来创建插图。

我尝试制作 2 个子图(1 行,2 列),但没有按预期工作。还尝试同时绘制两个图形(一个大一点,整个国家,一个小一点,插图),但他们两个“碰撞”(插图原来在另一个上面) .

对此有任何想法或可能的转变吗?

谢谢。

  • 已使用 dash 启用主 choropeth 的回调以更新 inset choropeth(我构造 dash HTML 的方式意味着它开始了...)
  • 为了将它们整合在一起,我使用了一些来自英国政府来源的地理映射数据
  • 刚刚修改了 callback 中 choropeth 的 center。也可以修改 zoom and/or range

获取一些地理数据

import requests
import geopandas as gpd
import pandas as pd

# https://ons-inspire.esriuk.com/arcgis/rest/services/Administrative_Boundaries/Local_Authority_Districts_December_2017_Boundaries/MapServer/3/query?where=1%3D1&outFields=*&outSR=4326&f=json
# res = requests.get("https://opendata.arcgis.com/datasets/ae90afc385c04d869bc8cf8890bd1bcd_3.geojson")
res = requests.get(
    "https://opendata.arcgis.com/datasets/69dc11c7386943b4ad8893c45648b1e1_0.geojson"
)
# use 2019 so Buckinghamshire is included
rescounties = requests.get(
    "https://opendata.arcgis.com/datasets/37363d379f4f40fa8c3f0d28eedfdd37_0.geojson"
)
gdf = pd.concat(
    [
        gpd.GeoDataFrame.from_features(res.json()["features"], crs="CRS84")
        .pipe(lambda d: d.rename(columns={c: c.lower() for c in d.columns}).drop(columns="lad20nmw"))
        .rename(columns={"lad20cd": "areaCode", "lad20nm": "areaName"}),
        gpd.GeoDataFrame.from_features(rescounties.json()["features"], crs="CRS84")
        .pipe(lambda d: d.rename(columns={c: c.lower() for c in d.columns}))
        .rename(columns={"cty19cd": "areaCode","cty20cd": "areaCode","cty19nm": "areaName","st_areashape": "shape__area",
                         "st_lengthshape": "shape__length",}),
    ]
).set_index("areaCode").assign(country=lambda d: d.index.str[0])

构建基础模型

import plotly.express as px
fig = px.choropleth_mapbox(
    gdf,
    geojson=gdf.geometry,
    locations=gdf.index,
    hover_name="areaName",
    color="country",
    color_discrete_sequence=["green", "yellow", "orange", "red", "magenta"],
    center={"lat": (gdf.total_bounds[1] + gdf.total_bounds[3])/2, "lon": (gdf.total_bounds[0] + gdf.total_bounds[2])/2},
    mapbox_style="carto-positron",
    zoom=5,
).update_layout(margin={"l":0,"r":0,"b":0,"t":0})

达世币应用程序

import json
import numpy as np
import plotly.graph_objects as go
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc

import dash_table
from dash.dependencies import Input, Output, State
from jupyter_dash import JupyterDash

ifig = go.Figure(fig.data).update_layout(fig.layout).update_layout(showlegend=False)

# Build App
app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div(
    [
        dbc.Row(
            [
                dbc.Col(
                    dcc.Graph(id="map", figure=fig, style={"height": "90vh"}),
                    width=9,
                ),
                dbc.Col(
                    dcc.Graph(id="insetmap", style={"height": "25vh"}),
                    width=3,
                ),
            ]
        ),
    ],
    style={"font-family": "Arial", "font-size": "0.9em"},
)


@app.callback(
    Output("insetmap", "figure"),
    Input("map", "selectedData"),
)
def mapSelect(selectData):
    global ifig
    if selectData and "range" in selectData.keys():
        r = selectData["range"]["mapbox"]
        ifig = ifig.update_layout(
            mapbox={
                "center": {
                    "lon": (r[0][0] + r[1][0]) / 2,
                    "lat": (r[0][1] + r[1][1]) / 2,
                }
            }
        )
        print(ifig.layout["mapbox"])
        return ifig
    return ifig


# Run app and display result inline in the notebook
app.run_server(mode="inline")

产出