如何为Holoviews栅格分类数据可视化配置外部图例?
Holoviz hvplot绘制分类栅格:自定义色图、匹配图例、外部图例及缺失类别处理
最优解决方案代码
以下代码直接满足你的全部需求,通过xarray分类数据类型实现稳健的类别-颜色映射,自动生成正确图例并支持外部放置:
import holoviews as hv import hvplot.xarray import numpy as np import xarray as xr # 定义土地覆盖类别与对应十六进制色码 color_key = { "No Data": "#000000", "Saturated / Defective": "#ff0000", "Dark Area Pixels": "#2f2f2f", "Cloud Shadows": "#643200", "Vegetation": "#00a000", "Bare Soils": "#ffe65a", "water": "#0000ff", "Clouds low probability / Unclassified": "#808080", "Clouds medium probability": "#c0c0c0", "Clouds high probability": "#ffffff", "Cirrus": "#64c8ff", "Snow / Ice": "#ff96ff", } # 生成模拟数据(故意排除类别1,模拟数据缺失部分类别的场景) nx = 40 ny = 70 xcoords = [37 + 0.1 * i for i in range(nx)] ycoords = [5 + 0.2 * i for i in range(ny)] data = np.random.choice([0,2,3,4,5,6,7,8,9,10,11], size=nx*ny).reshape(nx, ny) da = xr.DataArray( data, dims=["x", "y"], coords={"x": xcoords, "y": ycoords}, ) # 将数值型数据转换为分类类型,绑定所有预定义类别 value_to_class = {i: cls for i, cls in enumerate(color_key.keys())} da_class = da.map_blocks(lambda x: np.vectorize(value_to_class.get)(x)).astype('category') da_class = da_class.cat.set_categories(list(color_key.keys())) # 可视化配置 plot = da_class.hvplot( cmap=color_key, # 直接传入类别-颜色映射字典 rasterize=True, # 大栅格数据推荐开启,提升渲染性能 legend='right', # 图例置于绘图区域右侧(可选:left/top/bottom/top_right等) legend_opts={ 'title': '土地覆盖类别', 'label_width': 220, # 调整标签宽度避免文本换行 'padding': 10, }, frame_width=550, frame_height=350 ) # 渲染绘图 hv.render(plot)
关键功能实现说明
自定义分类色图
直接将color_key字典传入cmap参数,hvplot会自动将每个类别名称与对应十六进制颜色绑定,无需手动转换为颜色元组。带类别标签的图例
通过xarray的分类数据类型(astype('category')+cat.set_categories()),强制保留所有预定义类别。即使数据中缺失部分类别,图例依然会完整显示所有条目,颜色与标签严格匹配。处理缺失类别的场景
传统直接传入颜色元组的方式会因数据缺失类别导致颜色索引错位,而分类数据类型通过类别名称而非数值索引匹配颜色,彻底解决了这个问题。外部图例设置
通过legend参数指定图例位置(如'right'、'top_right')即可将图例移至绘图区域外部,同时可通过legend_opts调整图例标题、标签宽度等样式,优化视觉效果。
基于原代码的优化方案(保留NdOverlay方式)
如果你想沿用原有的NdOverlay图例实现,只需关闭原栅格图的色条并设置图例位置:
# 关闭原栅格图的色条,避免重复显示 raster_plot = da.hvplot().opts(cmap=tuple(color_key.values()), colorbar=False) # 构建自定义图例并设置外部位置 legend = hv.NdOverlay( {k: hv.Points([0,0], label=k).opts(color=v, size=0, apply_ranges=False) for k,v in color_key.items()}, "Classification" ).opts(legend_position='right', legend_opts={'title': '类别'}) # 组合绘图 final_plot = raster_plot * legend hv.render(final_plot)
此方式需手动确保数值与颜色索引严格对应,适用于简单场景,但稳健性不如分类数据方案。
内容的提问来源于stack exchange,提问作者sir_olf
相关产品推荐
相关产品推荐

