首页 > 编程问答 >在绘图破折号地图框中切换几何图层

在绘图破折号地图框中切换几何图层

时间:2024-07-31 14:39:55浏览次数:9  
标签:python plotly plotly-dash

我使用下面的帖子在plotly mapbox上绘制maki符号。

Plotly Mapbox标记未渲染(圆圈除外)

import dash
from dash import Dash, dcc, html, Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import numpy as np
import requests
import svgpath2mpl, shapely.geometry, shapely.affinity
from pathlib import Path
from zipfile import ZipFile
import pandas as pd
import geopandas as gpd
import json

# download maki icons
# https://github.com/mapbox/maki/tree/main/icons
f = Path.cwd().joinpath("maki")
if not f.is_dir():
    f.mkdir()
f = f.joinpath("maki.zip")
if not f.exists():
    r = requests.get("https://github.com/mapbox/maki/zipball/main")
    with open(f, "wb") as f:
        for chunk in r.iter_content(chunk_size=128):
            f.write(chunk)

fz = ZipFile(f)
fz.extractall(f.parent)

def to_shapely(mpl, simplify=0):
    p = shapely.geometry.MultiPolygon([shapely.geometry.Polygon(a).simplify(simplify) for a in mpl])
    p = shapely.affinity.affine_transform(p,[1, 0, 0, -1, 0, 0],)
    p = shapely.affinity.affine_transform(p,[1, 0, 0, 1, -p.centroid.x, -p.centroid.y],)
    return p

# convert SVG icons to matplolib geometries and then into shapely geometries
# keep icons in dataframe for further access...
SIMPLIFY=.1
dfi = pd.concat(
    [
        pd.read_xml(sf).assign(
            name=sf.stem,
            mpl=lambda d: d["d"].apply(
                lambda p: svgpath2mpl.parse_path(p).to_polygons()
            ),
            shapely=lambda d: d["mpl"].apply(lambda p: to_shapely(p, simplify=SIMPLIFY)),
        )
        for sf in f.parent.glob("**/*.svg")
    ]
).set_index("name")

# build a geojson layer that can be used in plotly mapbox figure layout
def marker(df, marker="marker", size=1, color="green", lat=51.379997, lon=-0.406042):
    m = df.loc[marker, "shapely"]
    if isinstance(lat, float):
        gs = gpd.GeoSeries(
            [shapely.affinity.affine_transform(m, [size, 0, 0, size, lon, lat])]
        )
    elif isinstance(lat, (list, pd.Series, np.ndarray)):
        gs = gpd.GeoSeries(
            [
                shapely.affinity.affine_transform(m, [size, 0, 0, size, lonm, latm])
                for latm, lonm in zip(lat, lon)
            ]
        )
    return {"source":json.loads(gs.to_json()), "type":"fill", "color":color}

当使用帖子中概述的方法直接绘制到地图上时,这效果很好。但我想包含一个允许用户关闭和打开这些符号的组件。 我试图将它们附加到单个层并使用它来更新布局。可以这样做吗?

us_cities = pd.read_csv(
    'https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv'
)

external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]

app = dash.Dash(__name__, external_stylesheets = external_stylesheets)

app.layout = html.Div([

    dcc.Checklist(
            id="symbol_on",
            options=[{"label": "Symbol", "value": True}],
            value=[],
            inline=True
            ),
             
    html.Div([
        dcc.Graph(id="the_graph")
    ]),

])

@app.callback(
    Output("the_graph", "figure"),
    Input('symbol_on', 'value')
)

def update_graph(symbol_on):

    fig = go.Figure()

    scatter = px.scatter_mapbox(data_frame = us_cities, 
                               lat = 'lat', 
                               lon = 'lon',
                               zoom = 0,
                               hover_data = ['State', 'lat', 'lon']
                               )

    fig.add_traces(list(scatter.select_traces()))

    fig.update_layout(
        height = 750,
        mapbox=dict(
        style='carto-positron',
        ),
    )

    star = marker(
                dfi, "star", size=.1, color="red", lon=[-70, -80, -90], lat=[30, 40, 45]
            ),

    airport = marker(
                dfi, "airport", size=.1, color="green", lon=[-70, -80, -90], lat=[30, 40, 45]
            ),

    layers = []

    for lyr in symbol_on:
        layers.append(star)
        layers.append(airport)

    fig.update_layout(mapbox={"layers": layers})

    return fig

if __name__ == '__main__':
    app.run_server(debug=True, port = 8050)

你几乎成功了!问题在于你循环的方式和 layers 列表的追加方式。你不需要循环遍历 symbol_on ,因为它已经是真/假值的列表了。

以下是修正后的代码:

import dash
from dash import Dash, dcc, html, Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import numpy as np
import requests
import svgpath2mpl, shapely.geometry, shapely.affinity
from pathlib import Path
from zipfile import ZipFile
import pandas as pd
import geopandas as gpd
import json

# ... (之前的代码保持不变)

@app.callback(
    Output("the_graph", "figure"),
    Input('symbol_on', 'value')
)

def update_graph(symbol_on):

    fig = go.Figure()

    scatter = px.scatter_mapbox(data_frame = us_cities, 
                               lat = 'lat', 
                               lon = 'lon',
                               zoom = 0,
                               hover_data = ['State', 'lat', 'lon']
                               )

    fig.add_traces(list(scatter.select_traces()))

    fig.update_layout(
        height = 750,
        mapbox=dict(
        style='carto-positron',
        ),
    )

    star = marker(
                dfi, "star", size=.1, color="red", lon=[-70, -80, -90], lat=[30, 40, 45]
            )

    airport = marker(
                dfi, "airport", size=.1, color="green", lon=[-70, -80, -90], lat=[30, 40, 45]
            )

    layers = []

    if symbol_on:  # 如果 symbol_on 列表不为空
        layers.append(star)
        layers.append(airport)

    fig.update_layout(mapbox={"layers": layers})

    return fig

# ... (之后的代码保持不变)

在这个修正后的代码中:

  1. 我们直接检查 symbol_on 是否为真 (即,是否被选中)。
  2. 如果为真,我们把 star airport 层添加到 layers 列表中。
  3. 最后,我们用更新后的 layers 列表来更新图形布局。

现在,当你勾选和取消勾选 "Symbol" 复选框时,星形和机场标记应该会相应地出现和消失。

标签:python,plotly,plotly-dash
From: 78814702

相关文章