【问题标题】:Plotly - Adding Scatter Geo points and traces on top of Density MapboxPlotly - 在 Density Mapbox 顶部添加 Scatter Geo 点和轨迹
【发布时间】:2021-10-16 02:25:38
【问题描述】:

我正在尝试在 white-bg 密度地图框顶部添加 Scattergeo 跟踪或叠加层,以获取通用美国各州轮廓的热图。

我使用scattergeo 的原因是我想在密度图框顶部绘制一个星形符号,而通过add_scattermapbox 接受的唯一符号是一个点。如果选择star符号,则没有添加符号。

我还知道,对于 p mapbox_stylesadd_scattermapboxdensity_scattermapbox,星号是可以接受的,但目前我无法在试用金额用完后按 Web 加载付费。

有没有一种巧妙的方法可以在density_mapbox 图上添加星号?

工作 ScatterGeo

fig = go.Figure(go.Scattergeo())

fig.add_scattergeo(lat = [30, 40]
                      ,lon = [-90, -80]
                      ,hoverinfo = 'none'
                      ,marker_size = 10
                      ,marker_color = 'rgb(65, 105, 225)' # blue
                      ,marker_symbol = 'star'
                      ,showlegend = False
                     )

fig.update_geos(
    visible=False, resolution=110, scope="usa",
    showcountries=True, countrycolor="Black",
    showsubunits=True, subunitcolor="Black"
)

fig.show()

工作密度地图盒

d = {'Location': ['Point A', 'Point B'], 'lat': [30, 40], 'long': [-90, -80], 'z': [100,200]}

df = pd.DataFrame(data=d)

fig = px.density_mapbox(df
                        ,lat='lat'
                        ,lon='long'
                        ,z='z'
                        ,hover_name='Location'
                        ,center=dict(lat=38.5, lon=-96)
                        ,range_color = [0, 200]
                        ,zoom=2
                        ,radius=50
                        ,opacity=.5
                        ,mapbox_style='open-street-map')

fig.add_scattermapbox(lat = [30, 40]
                      ,lon = [-90, -80]
                      ,hoverinfo = 'none'
                      ,marker_size = 6
                      ,marker_color = 'rgb(0, 0, 0)'
#                       ,marker_symbol = 'star'
                      ,showlegend = False
                     )

fig.show()



尝试 #1 - 只需设置 marker_symbol = 'star'

取消对marker_symbol = 'star' 的注释,这将适用于mapbox 的高级样式,完全删除了散点。

d = {'Location': ['Point A', 'Point B'], 'lat': [30, 40], 'long': [-90, -80], 'z': [100,200]}

df = pd.DataFrame(data=d)

fig = px.density_mapbox(df
                        ,lat='lat'
                        ,lon='long'
                        ,z='z'
                        ,hover_name='Location'
                        ,center=dict(lat=38.5, lon=-96)
                        ,range_color = [0, 200]
                        ,zoom=2
                        ,radius=50
                        ,opacity=.5
                        ,mapbox_style='open-street-map')

fig.add_scattermapbox(lat = [30, 40]
                      ,lon = [-90, -80]
                      ,hoverinfo = 'none'
                      ,marker_size = 6
                      ,marker_color = 'rgb(0, 0, 0)'
                      ,marker_symbol = 'star'
                      ,showlegend = False
                     )

fig.show()

尝试 #2 - 在散点图顶部添加密度地图框

scattergeo 之上添加density_mapbox 会产生相同的地理图,但仅此而已。有密度图框图例,但没有热图。

d = {'Location': ['Point A', 'Point B'], 'lat': [30, 40], 'long': [-90, -80], 'z': [100,200]}

df = pd.DataFrame(data=d)

fig = go.Figure(go.Scattergeo())

fig.add_scattergeo(lat = [30, 40]
                      ,lon = [-90, -80]
                      ,hoverinfo = 'none'
                      ,marker_size = 10
                      ,marker_color = 'rgb(65, 105, 225)' # blue
                      ,marker_symbol = 'star'
                      ,showlegend = False
                     )

fig.add_densitymapbox(lat=df['lat'],
                     lon=df['long'],
                      z=df['z'],
                      radius=50,
                      opacity=.5
                     )

fig.update_geos(
    visible=False, resolution=110, scope="usa",
    showcountries=True, countrycolor="Black",
    showsubunits=True, subunitcolor="Black"
)

fig.show()

【问题讨论】:

    标签: python plotly mapbox


    【解决方案1】:
    • 平铺地图和图层地图不能一起使用。因此,您不能在 ma​​pbox

      上使用来自 geo 的标记
    • 横向思考,您可以将自己的 geojson 图层添加到 ma​​pbox

    • 生成几何。为此提供了两个选项

      1. 一个简单的三角形
        • get_geom(df["long"], df["lat"], marker=None, size=k)
      2. https://labs.mapbox.com/maki-icons/
        • get_geom(df["long"], df["lat"], marker="star", size=k) 其中 ma​​rker 是 MAKI 图标名称。 NB 有孔的图标可以填写——例如caution
    • 向 mapbox 图形布局添加图层。这被参数化以生成多个图层以支持不同的缩放级别。更多层,更多开销。

    import geopandas as gpd
    import pandas as pd
    import shapely.geometry
    import math
    import json
    import plotly.express as px
    import svgpath2mpl
    import requests
    import numpy as np
    
    d = {
        "Location": ["Point A", "Point B"],
        "lat": [30, 40],
        "long": [-90, -80],
        "z": [100, 200],
    }
    df = pd.DataFrame(data=d)
    
    fig = px.density_mapbox(
        df,
        lat="lat",
        lon="long",
        z="z",
        hover_name="Location",
        center=dict(lat=38.5, lon=-96),
        range_color=[0, 200],
        zoom=2,
        radius=50,
        opacity=0.5,
        mapbox_style="open-street-map",
    )
    
    # https://stackoverflow.com/questions/23411688/drawing-polygon-with-n-number-of-sides-in-python-3-2
    def polygon(sides, radius=1, rotation=0, translation=None):
        one_segment = math.pi * 2 / sides
    
        points = [(math.sin(one_segment * i + rotation) * radius,
                   math.cos(one_segment * i + rotation) * radius,)
                  for i in range(sides)]
    
        if translation:
            points = [[sum(pair) for pair in zip(point, translation)] for point in points]
    
        return shapely.geometry.Polygon(points)
    
    def makimarker(makiname="star", geo=(0, 0), size=0.1):
        url = f"https://raw.githubusercontent.com/mapbox/maki/main/icons/{makiname}.svg"
        svgpath = pd.read_xml(requests.get(url).text).loc[0, "d"]
        p = svgpath2mpl.parse_path(svgpath).to_polygons()
        # need centroid to adjust marked to be centred on geo location
        c = shapely.affinity.scale(
            shapely.geometry.Polygon(p[0]), xfact=size, yfact=size
        ).centroid
        # centre and place marker
        marker = shapely.geometry.Polygon(
            [[sum(triple) for triple in zip(point, geo, (-c.x, -c.y))] for point in p[0]]
        )
        # finally size geometry
        return shapely.affinity.scale(marker, xfact=size, yfact=size)
    
    
    def get_geom(long_a: list, lat_a: list, marker=None, size=0.15) -> list:
        if marker:
            geo = [
                makimarker(marker, geo=(long, lat), size=size)
                for long, lat in zip(long_a, lat_a)
            ]
        else:
            geo = [
                polygon(3, translation=(long, lat), radius=size*10)
                for long, lat in zip(long_a, lat_a)
            ]
        return json.loads(gpd.GeoDataFrame(geometry=geo).to_json())
    
    # basing math on this https://wiki.openstreetmap.org/wiki/Zoom_levels
    # dict is keyed by size with min/max zoom levels covered by this size
    MINZOOM=.1
    MAXZOOM=18
    LAYERS=7
    zoom = 512**np.linspace(math.log(MINZOOM,512), math.log(MAXZOOM, 512), LAYERS)
    zoom = {
        (200/(2**(np.percentile(zoom[i:i+2],25)+9))): {"minzoom":zoom[i], "maxzoom":zoom[i+1], "name":i}
        for i in range(LAYERS-1)
    }
    
    # add a layers to density plot that are the markers
    fig.update_layout(
        mapbox={
            "layers": [
                {
                    "source": get_geom(df["long"], df["lat"], marker="star", size=k),
                    "type": "fill",
                    "color": "blue",
                    **zoom[k],
                }
                for k in zoom.keys()
            ]
        },
        margin={"t": 0, "b": 0, "l": 0, "r": 0},
    )
    fig
    

    【讨论】:

    • 第一个解决方案是一个很好的解决方法,但理想情况下,我希望在放大和缩小时缩放符号的大小。所以不太符合需求。我确信第二个解决方案会很好用,但无论出于何种原因,我都会收到“pandas can't read_xml”属性错误。熊猫版本 1.3.2。
    • 我知道 read_xml() 是 pandas 的最新成员。1.3.2 是 100% 迄今为止的。有什么错误?我想不出用缩放来缩放 geojson 的方法,将进行更多调查
    • 我做了一些重构。现在通过添加具有 minzoommaxzoom 的多个图层来支持缩放到一定程度,其中几何尺寸已调整为大部分使用缩放级别
    • Rob,您是否也可以发布原始解决方案,而无需缩放图层?
    • 我没有它了....需要重建它。上面的解决方案可以通过设置LAYER 拉回一层
    猜你喜欢
    • 1970-01-01
    • 2016-12-01
    • 2021-10-01
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多