如何使用redis缓存bokeh绘图

q43xntqr  于 2021-06-09  发布在  Redis
关注(0)|答案(1)|浏览(417)

我正在使用bokeh服务器在Map上呈现一个timeseries图。随着时间序列的进行,Map的焦点会移动。
下面的代码可以工作,但每个进程都会创建一个调用,调用googleapi(gmap)来获取背景。然后,这需要时间来渲染。在时间序列连续几次快速移动焦点的点上,背景在更新之前没有时间渲染。
我一直在试图弄清楚,这些请求是否可以提前发出,如何缓存(使用redis),使用户能够查看缓存,并且已经为timeseries上的每个记号加载了所有数据。
主.py

import settings
from bokeh.plotting import figure, gmap
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider, GMapOptions, GMapPlot, Range1d, Button
from bokeh.models.widgets import DataTable, TableColumn, HTMLTemplateFormatter
from bokeh.layouts import column, row, gridplot, layout
from bokeh.io import show, export_png, curdoc

from filehandler import get_graph_data

"""
Get arguments from request
"""
try:
    args = curdoc().session_context.request.arguments
    pk = int(args.get('pk')[0])
except:
    pass
"""
get data for graph from file and initialise variables
"""

# load data into dictionary from file referenced by pk

data_dict = get_graph_data(pk)

no_of_markers = data_dict.get('markers') 
length_of_series = data_dict.get('length')
series_data = data_dict.get('data') #lat/lon position of each series at each point in time
series_names = series_data.get('series_names') #names of series
range_x_axis = data_dict.get('xaxis') #min/max lat co-ords
range_y_axis = data_dict.get('yaxis') #min/max lon co-ords

"""
Build data
"""
graph_source = ColumnDataSource(series_data)

"""
Build markers to show current location
"""
markers = ColumnDataSource(data=dict(lon=[], lat=[]))

"""
Build mapping layer
"""
def create_map_backdrop(centroid, zoom, tools):
    """
    Create the map backdrop, centered on the starting point
    Using GoogleMaps api
    """
    map_options = GMapOptions(lng=centroid[1],
                              lat=centroid[0],
                              map_type='roadmap',
                              zoom=zoom,
                              )

    return gmap(google_api_key=settings.MAP_KEY,
                map_options=map_options,
                tools=tools,
                )

# set map focus

centroid = (graph_source.data['lats'][0][0],
            graph_source.data['lons'][0][0],
            )

"""
Build Plot
"""

tools="pan, wheel_zoom, reset"
p = create_map_backdrop(centroid, 18, tools)
p.multi_line(xs='lons',
             ys='lats',
             source=graph_source,
             line_color='color',
             )
p.toolbar.logo = None
p.circle(x='lon', y='lat', source=markers)

"""
User Interactions
"""

def animate_update():
    tick = slider.value + 1
    slider.value = tick

def slider_update(attr, old, new):
    """
    Updates all of the datasources, depending on current value of slider
    """
    start = timer()
    if slider.value>series_length:
        animate()
    else:
        tick = slider.value
        i=0
        lons, lats = [], []
        marker_lons, marker_lats = [], []

        while i < no_of_markers:

            #update lines
            lons.append(series_data['lons'][i][0:tick])
            lats.append(series_data['lats'][i][0:tick])

            #update markers
            marker_lons.append(series_data['lons'][i][tick])
            marker_lats.append(series_data['lats'][i][tick])

            #update iterators
            i += 1

        #update marker display
        markers.data['lon'] = marker_lons
        markers.data['lat'] = marker_lats

        #update line display
        graph_source.data['lons'] = lons
        graph_source.data['lats'] = lats    

        #set map_focus
        map_focus_lon = series_data['lons'][tick]
        map_focus_lat = series_data['lats'][tick]

        #update map focus
        p.map_options.lng = map_focus_lon
        p.map_options.lat = map_focus_lat

slider = Slider(start=0, end=series_length, value=0, step=5)
slider.on_change('value', slider_update)
callback_id = None

def animate():
    global callback_id
    if button.label == "► Play":
        button.label = "❚❚ Pause"
        callback_id = curdoc().add_periodic_callback(animate_update, 1)

    else:
        button.label = "► Play"
        curdoc().remove_periodic_callback(callback_id)

button = Button(label="► Play", width=60)
button.on_click(animate)

"""
Display plot
"""

grid = layout([[p, data_table],
                [slider, button],
                ])

curdoc().add_root(grid)

我试过缓存绘图数据( p ),但它看起来像是在调用googleapi之前保存的。
我已经探索过直接从api缓存Map块,然后将它们作为背景图像缝合到绘图中(使用bokeh imageurl),但是我无法让imageurl识别内存中的图像。
服务器文档表明redis可以用作后端,所以我想知道这是否会加快速度,但是当我尝试启动它时 bokeh serve myapp --allow-websocket-origin=127.0.0.1:5006 --backend=redis 我明白了 --backend is not a recognised command .
有没有一种方法可以缓存完全呈现的图形(可能是图形文档本身),同时保留用户与绘图交互的能力;或者在gmap绘图渲染后缓存它,然后将其添加到绘图的其余部分?

o3imoua4

o3imoua41#

如果这是独立的bokeh内容(即,不是bokeh服务器应用程序),那么可以使用 json_items 在浏览器中用 Bokeh.embed_items . json可能存储在redis中,这可能是相关的。但博凯服务器不是这样的。在初始会话创建之后,不会有任何“整个文档”要存储或缓存,只是通过websocket协议进行的一系列增量、部分更新。e、 服务器说“这个特定的数据源已更改”,浏览器说“好的,我应该重新计算边界并重新渲染”。
也就是说,我建议做一些改变。
首先,您不应该逐个更新cds列。您不应该这样做:


# BAD

markers.data['lon'] = marker_lons
markers.data['lat'] = marker_lats

这将生成两个单独的更新事件和两个单独的重新呈现请求。除了这会导致额外的工作之外,第一次更新还保证有不匹配的旧/新坐标。相反,您应该始终更新CD .data 口述“原子”,一次性:

source.data = new_data_dict

另外,你可以试试 curdoc().hold 将更新收集到较少的事件中。

相关问题