Plotly - 在密度地图上添加散点地理坐标和轨迹

6
我正在尝试在白色背景的密度Mapbox上添加Scattergeo跟踪或叠加,以获得一个通用美国州轮廓的热力图。我使用Scattergeo的原因是我想在密度Mapbox上绘制一个星形符号,而唯一被add_scattermapbox接受的符号是点。如果选择星形符号,则不会添加符号。我也知道星形符号可用于add_scattermapbox或density_scattermapbox的p mapbox_styles,但目前我没有支付试用期后每个Web加载的能力。有没有聪明的方法在density_mapbox图上添加星形符号?
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()

add picture here

工作密度地图箱

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()

insert 2nd picture here



尝试 #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()

insert 3rd picture here

尝试 #2 - 在散点地理图上添加密度 Mapbox
在散点地理图上添加 density_mapbox 可以产生相同的地理图,但没有更多的内容。密度 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()

insert 4th picture here

1个回答

2
  • 瓦片地图和层地图不能一起使用。因此,您不能在Mapbox上使用来自geo的标记。

  • 从侧面思考,您可以将自己的GeoJSON图层添加到Mapbox绘图中

  • 生成几何图形。提供了两个选项:

    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) 其中 marker 是MAKI图标名称。注意:带有孔洞的图标可以填充,例如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://dev59.com/m2Ag5IYBdhLWcg3wlb1h
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无法读取XML”属性错误。Pandas版本1.3.2。 - Jkiefn1
我知道read_xml()是pandas的最新添加.. 1.3.2是100%最新的。出了什么错误吗?我想不到一种方法来缩放geojson与缩放比例,将进一步调查。 - Rob Raymond
我已经进行了一些重构。现在通过添加具有minzoommaxzoom的多个图层来支持缩放,其中几何尺寸已调整为大多数与缩放级别配合使用。 - Rob Raymond
Rob,你能否发布原始解决方案,而不包括缩放图层? - Jkiefn1
我已经没有它了...需要重新构建。通过设置“LAYER”,上述解决方案可以拉回到一个层。 - Rob Raymond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接