Plotly - 在 Density Mapbox 顶部添加 Scatter Geo 点和轨迹
Plotly - Adding Scatter Geo points and traces on top of Density Mapbox
我正在尝试在 white-bg
密度地图框顶部添加 Scattergeo
轨迹或叠加层,以获取通用美国各州轮廓上的热图。
我使用 scattergeo
的原因是我想在密度图框的顶部绘制一个星号,而 add_scattermapbox
接受的唯一符号是一个点。如果您选择 star
符号,则没有添加任何符号。
我也知道 add_scattermapbox
或 density_scattermapbox
的 p mapbox_styles
可以接受星号,但目前我无法按网站付费试用金额用完后加载
有没有巧妙的方法在 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()
瓦片地图和图层地图不能一起使用。因此,您不能在 mapbox
上使用 geo 中的标记
横向思考,您可以将自己的 geojson 图层添加到 mapbox 图
生成几何图形。为此提供了两个选项
- 一个简单的三角形
get_geom(df["long"], df["lat"], marker=None, size=k)
- https://labs.mapbox.com/maki-icons/
get_geom(df["long"], df["lat"], marker="star", size=k)
其中 marker 是 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",
)
#
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
我正在尝试在 white-bg
密度地图框顶部添加 Scattergeo
轨迹或叠加层,以获取通用美国各州轮廓上的热图。
我使用 scattergeo
的原因是我想在密度图框的顶部绘制一个星号,而 add_scattermapbox
接受的唯一符号是一个点。如果您选择 star
符号,则没有添加任何符号。
我也知道 add_scattermapbox
或 density_scattermapbox
的 p mapbox_styles
可以接受星号,但目前我无法按网站付费试用金额用完后加载
有没有巧妙的方法在 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()
瓦片地图和图层地图不能一起使用。因此,您不能在 mapbox
上使用 geo 中的标记横向思考,您可以将自己的 geojson 图层添加到 mapbox 图
生成几何图形。为此提供了两个选项
- 一个简单的三角形
get_geom(df["long"], df["lat"], marker=None, size=k)
- https://labs.mapbox.com/maki-icons/
get_geom(df["long"], df["lat"], marker="star", size=k)
其中 marker 是 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",
)
#
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