Python:如何更新bokeh中的数据选择?

时间:2019-05-18 10:29:31

标签: python bokeh

我刚使用bokeh

这就是我在做什么。从osmnx中获得海地学校和医院的数据。

我无需编写所有代码即可获得以下信息

data1=dict(
    x=list(schools['x'].values),
    y=list(schools['y'].values)
)

data2=dict(
    x=list(hospitals['x'].values),
    y=list(hospitals['y'].values)
)

building = 'Schools'

buildings = {
    'Schools': {
    'title': 'Schools',
    'data': data1,
    'color': 'black'
    },

    'Hospitals': {
    'title': 'Hospitals',
    'data': data2,
    'color': 'red'
    }
}

building_select = Select(value=building, title='Building', options=sorted(buildings.keys()))

我想通过选择它来更改学校和医院之间的可视化。我定义了更改要获取的数据和颜色的函数。

def returnInfo(building):
    dataPoints = buildings[building]['data']
    color = buildings[building]['color']
    return dataPoints, color

dataPoints, color = returnInfo(building)

我定义了函数make_plot

def make_plot(dataPoints, title, color):

    TOOLS = "pan, wheel_zoom, reset,save"

    p = figure(plot_width=800,
           tools=TOOLS,
           x_axis_location=None, 
           y_axis_location=None)

# Add points on top (as black points)
    buildings = p.circle('x', 'y', size=4, source=data1, color=color)


    hover_buildings = HoverTool(renderers = [buildings], point_policy="follow_mouse", tooltips = [("(Long, Lat)", "($x, $y)")])

    p.add_tools(hover_buildings)

    return p

plot = make_plot(dataPoints,“ +建筑物[building] ['title'],颜色的数据)

然后我更新

def update_plot(attrname, old, new):
    building = building_select.value
    p.title.text = "Data for " + buildings[building]['title']
    src = buildings[building]['data']
    dataPoints, color = returnInfo(building)
    dataPoints.update

building_select.on_change('value', update_plot)

controls = column(building_select)
curdoc().add_root(row(plot, controls))

,但它不起作用:即,即使有光标,我也无法将点从学校更改为医院。更新部分的错误在哪里?

2 个答案:

答案 0 :(得分:5)

作为第一个解决方案,我建议使用legend.click_plolicy = 'hide'来切换建筑物在地图上的可见性(Bokeh v1.1.0)

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.tile_providers import CARTODBPOSITRON_RETINA
import osmnx as ox

amenities = ['hospital', 'school']
for i, amenity in enumerate(amenities):
    buildings = ox.pois_from_address("Port-au-Prince, Haiti", amenities = [amenity], distance = 3500)[['geometry', 'name', 'element_type']]
    for item in ['way', 'relation']:
        buildings.loc[buildings.element_type == item, 'geometry'] = buildings[buildings.element_type == item]['geometry'].map(lambda x: x.centroid)
        buildings.name.fillna('Hospital' if i == 0 else 'School', inplace = True)
        amenities[i] = buildings.to_crs(epsg = 3857)

p = figure(title = "Port-au-Prince, Haiti", tools = "pan,wheel_zoom,hover,reset", x_range = (-8057000, -8048500), y_range = (2098000, 2106000), 
           tooltips = [('Name', '@name'), ("(Long, Lat)", "($x, $y)")], x_axis_location = None, y_axis_location = None, active_scroll = 'wheel_zoom')
p.add_tile(CARTODBPOSITRON_RETINA)
p.grid.grid_line_color = None

for i, b in enumerate(amenities):
    source = ColumnDataSource(data = dict(x = b.geometry.x, y = b.geometry.y, name = b.name.values))
    p.circle('x', 'y', color = 'red' if i == 0 else 'blue', source = source, legend = 'Hospital' if i == 0 else 'School')

p.legend.click_policy = 'hide' 

show(p)

enter image description here

如果您想要Select小部件,那么这是另一种选择(Bokeh v1.1.0):

from bokeh.models import ColumnDataSource, Column, Select, CustomJS
from bokeh.plotting import figure, show
from bokeh.tile_providers import CARTODBPOSITRON_RETINA
import osmnx as ox

amenities = ['hospital', 'school']
for i, amenity in enumerate(amenities):
    buildings = ox.pois_from_address("Port-au-Prince, Haiti", amenities = [amenity], distance = 3500)[['geometry', 'name', 'element_type']]
    for item in ['way', 'relation']:
        buildings.loc[buildings.element_type == item, 'geometry'] = buildings[buildings.element_type == item]['geometry'].map(lambda x: x.centroid)
        buildings.name.fillna('Hospital' if i == 0 else 'School', inplace = True)
        buildings = buildings.to_crs(epsg = 3857)      
    amenities[i] = dict(x = list(buildings.geometry.x), y = list(buildings.geometry.y), name = list(buildings.name.values), color = (['red'] if i == 0 else ['blue']) * len(buildings.name.values))

source = ColumnDataSource(amenities[0])
p = figure(title = "Hospitals", tools = "pan,wheel_zoom,hover,reset", x_range = (-8057000, -8048500), y_range = (2098000, 2106000), 
           tooltips = [('Name', '@name'), ("(Long, Lat)", "($x, $y)")], x_axis_location = None, y_axis_location = None, active_scroll = 'wheel_zoom')
p.add_tile(CARTODBPOSITRON_RETINA)
p.circle(x = 'x', y = 'y', color = 'color', source = source)
p.grid.grid_line_color = None

code = '''  source.data = (cb_obj.value == 'Hospitals' ? data[0] : data[1]); p.title.text =  cb_obj.value; '''
select = Select(options = ['Hospitals', 'Schools'], callback = CustomJS(args=dict(p = p, source = source, data = amenities), code = code))

show(Column(p, select))

enter image description here

如果您需要对此代码进行解释,请告诉我。

答案 1 :(得分:0)

以下是使代码正常工作所需的更改:

在您的make_plot方法中,由于要在选择更改时更新图的标题,因此替换

p = figure(plot_width=800,
           tools=TOOLS,
           x_axis_location=None, 
           y_axis_location=None)

p = figure(plot_width=800,
                tools=TOOLS,
                title=title,
                x_axis_location=None, 
                y_axis_location=None)

此外,由于您要更新建筑物的数据和颜色,因此在方法中也返回buildings,因此完整的方法现在看起来像:

def make_plot(dataPoints, title, color):

            TOOLS = "pan, wheel_zoom, reset,save"

            p = figure(plot_width=800,
                tools=TOOLS,
                title=title,
                x_axis_location=None, 
                y_axis_location=None)

        # Add points on top (as black points)
            buildings = p.circle('x', 'y', size=4, source=data1, color=color)


            hover_buildings = HoverTool(renderers = [buildings], point_policy="follow_mouse", tooltips = [("(Long, Lat)", "($x, $y)")])

            p.add_tools(hover_buildings)

            return p, buildings

下一步,而不是调用

plot = make_plot(dataPoints, "Data for " + buildings[building]['title'], color)

您还需要在变量中获取返回的建筑物,以便可以直接对其进行更新。所以现在您的通话看起来像

plot, b = make_plot(dataPoints, "Data for " + buildings[building]['title'], color)

最后,更改您的update_plot方法,使其看起来像这样:

def update_plot(attrname, old, new):
            building = building_select.value
            plot.title.text = "Data for " + buildings[building]['title']
            src = buildings[building]['data']
            dataPoints, color = returnInfo(building)
            b.data_source.data = dataPoints
            b.glyph.fill_color = color

有了这些更改,它将可以按预期工作。见所附结果。 使用的示例数据是:

data1=dict(
        x=[1,2,3],
        y=[2,1,3]
    )

data2=dict(
        x=[1,2,3],
        y=[1,3,2]
    )

enter image description here