Plotly 图形对象中线条的离散色标 (Python)
Discrete colour scale for lines in Plotly graph objects (Python)
我正在将 plotly.graph_objects.Scattermapbox
中的线映射到 fig.add_trace
。理想情况下,我想更改线条的颜色,使其对应于另一列中的属性。例如:
Line
Attribute
LINE01
Highway
LINE02
River
LINE03
Highway
我不知道该怎么做。当我创建我的情节时,每一行都显示为图例条目(LINE01
显示为 trace 0
,等等)这是我的代码:
import plotly.graph_objects as go
fig = go.Figure()
for i in range(len(df)):
fig.add_trace(go.Scattermapbox(
mode = "lines",
lon = [df['lon.start'][i], df['lon.end'][i]],
lat = [df['lat.start'][i], df['lat.end'][i]],
line = dict(width = 3)
)
)
如何更改它,使我的图例按 Attribute
列分组以创建离散色标?
- 你的问题没有道路和河流的样本数据。从英国政府来源获取英国河流和道路
- 有两种方法可以将线层添加到绘图图形
- 作为地图框图层。这样做的好处是您可以利用 plotly 和 geopandas geojson 功能更简单地使用来自第三方来源的参考映射信息
- as traces - 您一直在使用的方法。您已经使用了这些行的开始和结束,这意味着您丢失了
之间的所有坐标
- 我已经使用这个答案 How to plot visualize a Linestring over a map with Python? 关于如何在 mapbox 图上生成线条。
- 使用了数据的子集,只是因为图形生成时间对全套河流和道路很重要
来源示例河流和道路数据
import urllib
from pathlib import Path
from zipfile import ZipFile
import geopandas as gpd
import pandas as pd
# get some river and road geometry....
src = [
{
"name": "rivers",
"suffix": ".shp",
"color": "blue",
"width": 1.5,
"url": "https://environment.data.gov.uk/UserDownloads/interactive/023ce3a412b84aca949cad6dcf6c5338191808/EA_StatutoryMainRiverMap_SHP_Full.zip",
},
{
"name": "roads",
"suffix": ".shp",
"color": "red",
"width": 3,
"url": "https://maps.dft.gov.uk/major-road-network-shapefile/Major_Road_Network_2018_Open_Roads.zip",
},
]
data = {}
for s in src:
f = Path.cwd().joinpath(urllib.parse.urlparse(s["url"]).path.split("/")[-1])
if not f.exists():
r = requests.get(s["url"],stream=True,)
with open(f, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
fz = ZipFile(f)
fz.extractall(f.parent.joinpath(f.stem))
data[s["name"]] = gpd.read_file(
f.parent.joinpath(f.stem).joinpath(
[
f.filename
for f in fz.infolist()
if Path(f.filename).suffix == s["suffix"]
][0]
)
).assign(source_name=s["name"])
gdf = pd.concat(data.values()).to_crs("EPSG:4326")
使用 mapbox 图层
import plotly.graph_objects as go
import json
# let's work with longer rivers and smaller numer of random roads
gdf2 = gdf.loc[gdf["length_km"].gt(50).fillna(False) | gdf["roadNumber"].isin(gdf["roadNumber"].fillna("").sample(50).unique()) ]
fig = go.Figure(go.Scattermapbox())
# use geopandas and plotly geojson layer capabilities, keep full definition of line strings
fig.update_layout(
margin={"l": 0, "r": 0, "t": 0, "b": 0},
mapbox={
"style": "carto-positron",
"zoom": 4,
"center": {
"lon": gdf.total_bounds[[0, 2]].mean(),
"lat": gdf.total_bounds[[1, 3]].mean(),
},
"layers": [
{
"source": json.loads(gdf2.loc[gdf2["source_name"].eq(s["name"])].geometry.to_json()),
"below": "traces",
"type": "line",
"color": s["color"],
"line": {"width": s["width"]},
}
for s in src
],
},
)
使用 mapbox 线
import numpy as np
# plotly takes array delimited with None between lines. Use numpy padding and shaping to generate this array
# from pair of features
def line_array(df, cols):
return np.pad(
df.loc[:, cols].values, [(0, 0), (0, 1)], constant_values=None
).reshape(1, (len(df) * 3))[0]
# map to question columns.... looses all detail of a linestring
gdf3 = gdf2.join(
gdf2.geometry.bounds.rename(
columns={
"minx": "lon.start",
"miny": "lat.start",
"maxx": "lon.end",
"maxy": "lat.end",
}
)
)
fig = go.Figure(
[
go.Scattermapbox(
name=g[0],
lat=line_array(g[1], ["lat.start", "lat.end"]),
lon=line_array(g[1], ["lon.start", "lon.end"]),
mode="lines",
)
for g in gdf3.groupby("source_name")
]
)
fig.update_layout(
margin={"l": 0, "r": 0, "t": 15, "b": 0},
mapbox={
"style": "carto-positron",
"zoom": 4,
"center": {
"lon": gdf3.loc[:, ["lon.start", "lon.end"]].mean().mean(),
"lat": gdf3.loc[:, ["lat.start", "lat.end"]].mean().mean(),
},
},
)
我正在将 plotly.graph_objects.Scattermapbox
中的线映射到 fig.add_trace
。理想情况下,我想更改线条的颜色,使其对应于另一列中的属性。例如:
Line | Attribute |
---|---|
LINE01 | Highway |
LINE02 | River |
LINE03 | Highway |
我不知道该怎么做。当我创建我的情节时,每一行都显示为图例条目(LINE01
显示为 trace 0
,等等)这是我的代码:
import plotly.graph_objects as go
fig = go.Figure()
for i in range(len(df)):
fig.add_trace(go.Scattermapbox(
mode = "lines",
lon = [df['lon.start'][i], df['lon.end'][i]],
lat = [df['lat.start'][i], df['lat.end'][i]],
line = dict(width = 3)
)
)
如何更改它,使我的图例按 Attribute
列分组以创建离散色标?
- 你的问题没有道路和河流的样本数据。从英国政府来源获取英国河流和道路
- 有两种方法可以将线层添加到绘图图形
- 作为地图框图层。这样做的好处是您可以利用 plotly 和 geopandas geojson 功能更简单地使用来自第三方来源的参考映射信息
- as traces - 您一直在使用的方法。您已经使用了这些行的开始和结束,这意味着您丢失了 之间的所有坐标
- 我已经使用这个答案 How to plot visualize a Linestring over a map with Python? 关于如何在 mapbox 图上生成线条。
- 使用了数据的子集,只是因为图形生成时间对全套河流和道路很重要
来源示例河流和道路数据
import urllib
from pathlib import Path
from zipfile import ZipFile
import geopandas as gpd
import pandas as pd
# get some river and road geometry....
src = [
{
"name": "rivers",
"suffix": ".shp",
"color": "blue",
"width": 1.5,
"url": "https://environment.data.gov.uk/UserDownloads/interactive/023ce3a412b84aca949cad6dcf6c5338191808/EA_StatutoryMainRiverMap_SHP_Full.zip",
},
{
"name": "roads",
"suffix": ".shp",
"color": "red",
"width": 3,
"url": "https://maps.dft.gov.uk/major-road-network-shapefile/Major_Road_Network_2018_Open_Roads.zip",
},
]
data = {}
for s in src:
f = Path.cwd().joinpath(urllib.parse.urlparse(s["url"]).path.split("/")[-1])
if not f.exists():
r = requests.get(s["url"],stream=True,)
with open(f, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
fz = ZipFile(f)
fz.extractall(f.parent.joinpath(f.stem))
data[s["name"]] = gpd.read_file(
f.parent.joinpath(f.stem).joinpath(
[
f.filename
for f in fz.infolist()
if Path(f.filename).suffix == s["suffix"]
][0]
)
).assign(source_name=s["name"])
gdf = pd.concat(data.values()).to_crs("EPSG:4326")
使用 mapbox 图层
import plotly.graph_objects as go
import json
# let's work with longer rivers and smaller numer of random roads
gdf2 = gdf.loc[gdf["length_km"].gt(50).fillna(False) | gdf["roadNumber"].isin(gdf["roadNumber"].fillna("").sample(50).unique()) ]
fig = go.Figure(go.Scattermapbox())
# use geopandas and plotly geojson layer capabilities, keep full definition of line strings
fig.update_layout(
margin={"l": 0, "r": 0, "t": 0, "b": 0},
mapbox={
"style": "carto-positron",
"zoom": 4,
"center": {
"lon": gdf.total_bounds[[0, 2]].mean(),
"lat": gdf.total_bounds[[1, 3]].mean(),
},
"layers": [
{
"source": json.loads(gdf2.loc[gdf2["source_name"].eq(s["name"])].geometry.to_json()),
"below": "traces",
"type": "line",
"color": s["color"],
"line": {"width": s["width"]},
}
for s in src
],
},
)
使用 mapbox 线
import numpy as np
# plotly takes array delimited with None between lines. Use numpy padding and shaping to generate this array
# from pair of features
def line_array(df, cols):
return np.pad(
df.loc[:, cols].values, [(0, 0), (0, 1)], constant_values=None
).reshape(1, (len(df) * 3))[0]
# map to question columns.... looses all detail of a linestring
gdf3 = gdf2.join(
gdf2.geometry.bounds.rename(
columns={
"minx": "lon.start",
"miny": "lat.start",
"maxx": "lon.end",
"maxy": "lat.end",
}
)
)
fig = go.Figure(
[
go.Scattermapbox(
name=g[0],
lat=line_array(g[1], ["lat.start", "lat.end"]),
lon=line_array(g[1], ["lon.start", "lon.end"]),
mode="lines",
)
for g in gdf3.groupby("source_name")
]
)
fig.update_layout(
margin={"l": 0, "r": 0, "t": 15, "b": 0},
mapbox={
"style": "carto-positron",
"zoom": 4,
"center": {
"lon": gdf3.loc[:, ["lon.start", "lon.end"]].mean().mean(),
"lat": gdf3.loc[:, ["lat.start", "lat.end"]].mean().mean(),
},
},
)