You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bokeh Widget修改颜色时带瓦片提供商底图的图表变形问题

问题根源

变形问题核心是坐标投影不匹配:

  • 你代码中使用的州边界、城市点位坐标都是WGS84坐标系的经纬度数值
  • Bokeh瓦片底图默认使用Web墨卡托(EPSG:3857)平面投影坐标(单位为米)

初始加载时Bokeh还未完成投影匹配的初始化计算,修改颜色触发全量重绘时,会直接用原始经纬度数值匹配瓦片的米级投影坐标,就会出现变形;手动拖动地图时会触发投影自动校准逻辑,之后重绘就不会出问题。

修复方案
  1. 新增WGS84转Web墨卡托的坐标转换函数,把所有经纬度坐标转为适配瓦片的平面坐标
  2. 初始化figure时明确指定坐标轴类型为墨卡托
  3. 所有绘图逻辑使用转换后的坐标

转换函数参考:

import math
def wgs84_to_web_mercator(lon, lat):
    k = 6378137
    x = lon * (k * math.pi/180.0)
    y = math.log(math.tan((90 + lat) * math.pi/360.0)) * k
    return x, y

修复后的完整可运行代码:

import math
from bokeh.layouts import grid, column
from bokeh.models.widgets.inputs import ColorPicker
from bokeh.sampledata import us_states
from bokeh.plotting import figure, output_file, show
from bokeh.tile_providers import get_provider, CARTODBPOSITRON

def wgs84_to_web_mercator(lon, lat):
    k = 6378137
    x = lon * (k * math.pi/180.0)
    y = math.log(math.tan((90 + lat) * math.pi/360.0)) * k
    return x, y

us_states = us_states.data.copy()
del us_states["HI"]
del us_states["AK"]

# 转换州边界坐标
state_xs = []
state_ys = []
for code in us_states:
    lons = us_states[code]["lons"]
    lats = us_states[code]["lats"]
    xs, ys = [], []
    for lon, lat in zip(lons, lats):
        x, y = wgs84_to_web_mercator(lon, lat)
        xs.append(x)
        ys.append(y)
    state_xs.append(xs)
    state_ys.append(ys)

# 转换城市点位坐标
x = [-97.7431, -96.79, -97.33, -95.36, -98.49]
y = [30.26, 32.77, 32.75, 29.76, 29.42] 
merc_x, merc_y = [], []
for lon, lat in zip(x, y):
    mx, my = wgs84_to_web_mercator(lon, lat)
    merc_x.append(mx)
    merc_y.append(my)

# 初始化图时明确指定墨卡托坐标轴
p = figure(title="Plotting Points Example: The 5 Largest Cities in Texas", 
           toolbar_location="left", plot_width=1100, plot_height=700,
           x_axis_type="mercator", y_axis_type="mercator")

p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None

# 用转换后的坐标画州边界
p.patches(state_xs, state_ys, fill_alpha=0.0,
    line_color="#884444", line_width=1.5)

# 添加瓦片底图
tile_provider = get_provider(CARTODBPOSITRON)
p.add_tile(tile_provider)
# 用转换后的坐标画点位
points = p.circle(merc_x, merc_y, size=8, color='navy', alpha=1)

picker = ColorPicker(title=f"Point Color", color="navy")
picker.js_link("color", points.glyph, "fill_color")

output_file("texas.html")
l = grid([p, picker], ncols=2, sizing_mode="fixed")
show(l)
调试排查思路

你可以按照这个流程复现排查过程,方便后续处理类似问题:

  • 打开浏览器开发者工具控制台,分别在初始加载、拖动地图后打印Bokeh figure对象的x_range、y_range属性,能看到初始坐标范围是-180180/-9090的经纬度区间,拖动后变成千万级的米级数值,直接就能定位是坐标范围不匹配问题
  • 查Bokeh官方文档的瓦片图层说明,确认瓦片默认使用Web墨卡托投影,要求输入坐标必须提前转换为对应投影
  • 写坐标转换函数转换所有坐标后测试,验证变形问题消失

内容的提问来源于stack exchange,提问作者The dude

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:48:02