Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: how to update data selection in bokeh?

Tags:

python

bokeh

I new in using bokeh.

This is what I am doing. From osmnx I get data of schools and hospitals in Haiti.

Without writing all the code I arrive to get the following

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()))

I would like to change the visualisation between schools and hospitals by selecting it. I define the function that change the data to take and the color.

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

dataPoints, color = returnInfo(building)

I define the function 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, "Data for " + buildings[building]['title'], color)

then I update

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))

but it does not work: i.e. I am not able to change the points from schools to hospitals even if I have the cursor. Where is the error in the update section?

like image 527
emax Avatar asked May 18 '19 10:05

emax


1 Answers

As first solution I suggest to use legend.click_plolicy = 'hide' to toggle visibility of your buildings on the map (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

And if you want the Select widget then here is another alternative (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

Let me know if you need any explanation on this code.

like image 177
Tony Avatar answered Sep 22 '22 02:09

Tony