Bokeh Widget修改颜色时带瓦片提供商底图的图表变形问题
问题根源
变形问题核心是坐标投影不匹配:
- 你代码中使用的州边界、城市点位坐标都是WGS84坐标系的经纬度数值
- Bokeh瓦片底图默认使用Web墨卡托(EPSG:3857)平面投影坐标(单位为米)
初始加载时Bokeh还未完成投影匹配的初始化计算,修改颜色触发全量重绘时,会直接用原始经纬度数值匹配瓦片的米级投影坐标,就会出现变形;手动拖动地图时会触发投影自动校准逻辑,之后重绘就不会出问题。
修复方案
- 新增WGS84转Web墨卡托的坐标转换函数,把所有经纬度坐标转为适配瓦片的平面坐标
- 初始化figure时明确指定坐标轴类型为墨卡托
- 所有绘图逻辑使用转换后的坐标
转换函数参考:
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
相关产品推荐
相关产品推荐

