如何在Bokeh中为Tile Provider使用额外坐标轴(extra_x_ranges/extra_y_ranges)以直接显示EPSG:4326坐标的地图
刚好遇到过类似的问题,我来帮你梳理清楚问题所在,以及可行的解决方案:
问题分析:为什么你的原始代码没显示地图?
你尝试用额外坐标轴兼容EPSG:4326和EPSG:3857的思路方向没错,但忽略了一个核心点:Web墨卡托(EPSG:3857)是投影坐标,和经纬度(EPSG:4326)之间是非线性的映射关系,不是简单的线性坐标轴能模拟的。
你的代码里用LinearAxis创建额外的3857坐标轴,本质上是把3857坐标当成了线性数值,这和瓦片服务要求的投影坐标系不匹配,所以瓦片无法正确渲染。另外,Bokeh的Tile Provider必须在EPSG:3857的坐标空间下才能正常显示,这是所有主流在线瓦片服务的标准要求。
可行解决方案
不需要手动转换你的经纬度数据,我们可以让Bokeh自动处理坐标转换,同时在图表上显示经纬度刻度。下面是两种实用的方法:
方法一:用GeoAxis显示经纬度刻度,底层用Web墨卡托坐标
这种方法的核心是让figure的底层坐标为EPSG:3857,同时替换默认坐标轴为GeoAxis,让它显示经纬度刻度。你绘制数据时只需要传入经纬度,Bokeh会自动帮你转换为3857坐标:
from bokeh.plotting import figure, show from bokeh.tile_providers import CARTODBPOSITRON, get_provider from bokeh.models import GeoAxis, MercatorTickFormatter, MercatorTicker # 获取瓦片服务 tile_provider = get_provider(CARTODBPOSITRON) # 创建figure,设置底层为Web墨卡托坐标范围(覆盖全球) p = figure( x_range=(-20026376.39, 20026376.39), y_range=(-20048966.10, 20048966.10), x_axis_type="mercator", y_axis_type="mercator" ) # 添加瓦片背景 p.add_tile(tile_provider) # 替换坐标轴为显示经纬度的GeoAxis p.xaxis = GeoAxis(ticker=MercatorTicker(), formatter=MercatorTickFormatter()) p.yaxis = GeoAxis(ticker=MercatorTicker(), formatter=MercatorTickFormatter()) # 直接用经纬度绘制数据,Bokeh自动转换坐标 # 示例:绘制北京的坐标(116.4, 39.9) p.circle(x=116.4, y=39.9, size=12, color="crimson", transform=p.xaxis_transform) show(p)
方法二:用GeoJSONDataSource加载经纬度数据
如果你的数据是GeoJSON格式(EPSG:4326),可以直接用GeoJSONDataSource,它会自动处理坐标转换,非常适合批量地理数据:
from bokeh.plotting import figure, show from bokeh.tile_providers import CARTODBPOSITRON, get_provider from bokeh.models import GeoJSONDataSource, GeoAxis, MercatorTickFormatter, MercatorTicker tile_provider = get_provider(CARTODBPOSITRON) # 创建Web墨卡托坐标的figure p = figure( x_range=(-20026376.39, 20026376.39), y_range=(-20048966.10, 20048966.10), x_axis_type="mercator", y_axis_type="mercator" ) p.add_tile(tile_provider) # 示例EPSG:4326的GeoJSON数据 geo_data = { "type": "FeatureCollection", "features": [ {"type": "Feature", "geometry": {"type": "Point", "coordinates": [116.4, 39.9]}}, {"type": "Feature", "geometry": {"type": "Point", "coordinates": [121.5, 31.2]}} ] } # 加载GeoJSON数据,自动转换坐标 source = GeoJSONDataSource(geojson=geo_data) p.circle(x='x', y='y', size=12, color="navy", source=source) # 添加经纬度刻度坐标轴 p.xaxis = GeoAxis(ticker=MercatorTicker(), formatter=MercatorTickFormatter()) p.yaxis = GeoAxis(ticker=MercatorTicker(), formatter=MercatorTickFormatter()) show(p)
这两种方法都不需要你手动转换数据坐标,既满足了显示经纬度刻度的需求,又能正常加载瓦片地图背景。
内容的提问来源于stack exchange,提问作者CodePrinz
相关产品推荐
相关产品推荐

