【问题标题】:How to create a symbol/button on a Plotly choropleth map如何在 Plotly choropleth 地图上创建符号/按钮
【发布时间】:2022-01-01 11:23:08
【问题描述】:

我想在 Plotly choropleth 地图上创建按钮,就像地图 https://resources-covid19canada.hub.arcgis.com/ 上的按钮(我的三个红色箭头指向)一样。单击按钮时,会显示一个图例窗口。

您的建议将不胜感激。

感谢您的帮助!

【问题讨论】:

    标签: button plotly choropleth


    【解决方案1】:
    • 您可以使用 plotly 实现您显示的图形类型。在 ma​​pbox 图上使用 symbols 存在多个问题。因此,如果您想着色、调整大小和使用不同的形状,则需要使用 geojson 图层
    • 以类似方式创建图例实际上是不可能的。显然你有 colorbar 图例。标记的大小和形状实际上取决于视觉方面,没有图例
    • 完整代码在最后
    px.choropleth_mapbox(
        df.loc[df["SummaryDate"].eq(df["SummaryDate"].max())].merge(df_t, on="Abbreviation"),
        geojson=gdf_can.geometry,
        locations="Abbreviation",
        color="DailyTotals",
        hover_data={"Province":True, "SummaryDate":True, "Change":":.2%"},
        color_continuous_scale="BuPu"
    ).update_layout(
        mapbox={
            "style": "carto-positron",
            "zoom": 2,
            "center": {
                "lon": sum(gdf_can.total_bounds[[0, 2]]) / 2,
                "lat": sum(gdf_can.total_bounds[[1, 3]]) / 2,
            },
            "layers": px_marker_mapbox(
                df_t.join(gdf_can),
                color_discrete_map={
                    "solid/arrow-up": "yellow",
                    "solid/arrow-down": "silver",
                },
            ),
        },
        margin={"l": 0, "r": 0, "t": 0, "b": 0},
    )
    

    设置代码

    import geopandas as gpd
    import pandas as pd
    import requests
    import plotly.express as px
    import shapely.geometry
    import svgpath2mpl
    import numpy as np
    
    # create shapely multi-polygon from maki or font-awesome SVG path
    def marker(name="star", source="fa"):
        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],
            )
            scale = 1 if source == "maki" else 10 ** -2
            p = shapely.affinity.affine_transform(
                p,
                [1, 0, 0, 1, -p.centroid.x, -p.centroid.y],
            )
            return shapely.affinity.affine_transform(
                p,
                [scale, 0, 0, scale, -p.centroid.x, -p.centroid.y],
            )
    
        if source == "maki":
            url = f"https://raw.githubusercontent.com/mapbox/maki/main/icons/{name}.svg"
        elif source == "fa":
            url = f"https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/{name}.svg"
        svgpath = pd.read_xml(requests.get(url).text).loc[0, "d"]
        return to_shapely(svgpath2mpl.parse_path(svgpath).to_polygons())
    
    # create mapbox layers for markers.  icon defines layer and color
    def px_marker_mapbox(
        df,
        icon="icon",
        size="size",
        lat="lat",
        lon="lon",
        color_discrete_map=None,
        color_discrete_sequence=px.colors.qualitative.Plotly,
    ):
        layers = []
        for i, g in enumerate(df.groupby(icon)):
            m = marker(g[0])
            geoms = [
                shapely.affinity.affine_transform(
                    m, [r[1][size], 0, 0, r[1][size], r[1][lon], r[1][lat]]
                )
                for r in g[1].iterrows()
            ]
            if color_discrete_map and g[0] in color_discrete_map.keys():
                color = color_discrete_map[g[0]]
            else:
                color = color_discrete_sequence[i % len(color_discrete_sequence)]
            layers.append(
                {
                    "source": gpd.GeoSeries(geoms).__geo_interface__,
                    "type": "fill",
                    "color": color,
                }
            )
    
        return layers
    
    
    # fmt: off
    gdf_can = gpd.GeoDataFrame.from_features(requests.get("https://data.opendatasoft.com/explore/dataset/georef-canada-province@public/download/?format=geojson&timezone=Europe/London&lang=en").json())
    gdf_can["lat"] = gdf_can["geo_point_2d"].apply(lambda l: l[0])
    gdf_can["lon"] = gdf_can["geo_point_2d"].apply(lambda l: l[1])
    
    # two different province codes used by COVID and geometry, get map...
    df_prov = pd.read_html("https://www150.statcan.gc.ca/n1/pub/92-195-x/2011001/geo/prov/tbl/tbl8-eng.htm")[0].drop(13)
    df_prov = df_prov.rename(
        columns={
            "Internationally approved alpha code (Source: Canada Post)": "Abbreviation",
            "Standard geographical classification (SGC) code": "prov_code",
        }
    )
    
    gdf_can = gdf_can.merge(df_prov.loc[:, ["prov_code", "Abbreviation"]], on="prov_code").set_index("Abbreviation")
    
    # get COVID daily data...
    df = pd.json_normalize(requests.get("https://opendata.arcgis.com/datasets/3afa9ce11b8842cb889714611e6f3076_0.geojson").json()["features"])
    df = df.rename(columns={c:c.split(".")[1] for c in df.columns if len(c.split("."))==2})
    df["SummaryDate"] = pd.to_datetime(df["SummaryDate"].str[0:10]) if df["SummaryDate"].dtype=="O" else df["SummaryDate"]
    df = df.loc[df["SummaryDate"].ge(df["SummaryDate"].max()-pd.Timedelta(days=7)) & df["Abbreviation"].ne("CA")]
    # fmt: on
    
    # rollup changes data...
    df_t = df.groupby("Abbreviation")["DailyTotals"].apply(
        lambda s: s.pct_change(periods=7).dropna()
    ).to_frame().rename(columns={"DailyTotals":"Change"}).assign(
        icon=lambda d: np.select(
            [d["Change"] < -0.1, d["Change"] > 0.1],
            ["solid/arrow-down", "solid/arrow-up"],
            "solid/arrows-alt-h",
        ),
        size=lambda d: d["Change"].abs()
    ).droplevel(1)
    

    【讨论】:

      猜你喜欢
      • 2020-11-09
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 2022-06-29
      • 2021-07-11
      • 1970-01-01
      • 2022-09-29
      相关资源
      最近更新 更多