Search code examples
pythonbokeh

Adding Bokeh slider makes dashboard blank


I'm trying to add a slider to filter the cities that show up on a map of the world. When i just add all cities with the code below it works fine.

import pandas as pd
import numpy as np

import geopandas
import geoplot
import json

from bokeh.io import output_notebook, show, output_file
from bokeh.models import  (NumeralTickFormatter, Select, CDSView, ColorBar, ColumnDataSource,
                          CustomJS, CustomJSFilter,
                          GeoJSONDataSource, HoverTool,
                          LinearColorMapper, Slider)
from bokeh.io.doc import curdoc
from bokeh.layouts import column, row, widgetbox
from bokeh.palettes import brewer
from bokeh.plotting import figure, Figure


world = geopandas.read_file(
    geopandas.datasets.get_path('naturalearth_lowres')
)
bokeh_ready = json.dumps(json.loads(world.to_json()))

geosource = GeoJSONDataSource(geojson = world.to_json())

cities = pd.read_csv('/Users/620751/Documents/cities.csv')


geocities = GeoJSONDataSource(geojson = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm','Leg_Arrv_Dt_Tm',
       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry']].to_json())

cities_input = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
                       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm', 'Leg_Arrv_Dt_Tm',
                       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
                       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry', 'duration_hours']]


p1 = Figure(title = 'Flights', 
           plot_height = 720 ,
           plot_width = 1200, 
           toolbar_location = 'below',
           tools = 'pan, wheel_zoom, box_zoom, reset')

p1.xgrid.grid_line_color = None
p1.ygrid.grid_line_color = None# Add patch renderer to figure.

countries = p1.patches('xs','ys', source = geosource,
                   fill_color = None,
                   line_color = 'gray', 
                   line_width = 0.25, 
                   fill_alpha = 1)# Create hover tool

p1.add_tools(HoverTool(renderers = [countries],
                      tooltips = [('Country','@name')]))


cities_input = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm','Leg_Arrv_Dt_Tm',
       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry','duration_hours']]





city_source = ColumnDataSource(cities.drop('geometry', axis=1))
city_pt = p1.circle('dest_long', 'dest_lat', source = newsource,
                       color='red')

p1.add_tools(HoverTool(renderers = [newsource],line_policy='next',
                          tooltips = [('City','@MAILG_CTY_NM'),
                                     ('Departure Time','@Leg_Dep_Dt_Tm'),
                                     ('Seats','@seats'),
                                     ('Flight Time','@duration_hours' + ' hours'),
                                     ('weather', '@weather_desc' + ' low:' + '@low_temp' + ' high:' + '@high_temp' + ' rain:' + '@rain')]))


layout = column(p1)
curdoc().add_root(layout)

When I try to add a slider so I can filter what cities show on the map(based on flight time from base city using column 'duration_hours' I tried to change my code to this:

import pandas as pd
import numpy as np

import geopandas
import geoplot
import json

from bokeh.io import output_notebook, show, output_file
from bokeh.models import  (NumeralTickFormatter, Select, CDSView, ColorBar, ColumnDataSource,
                          CustomJS, CustomJSFilter,
                          GeoJSONDataSource, HoverTool,
                          LinearColorMapper, Slider)
from bokeh.io.doc import curdoc
from bokeh.layouts import column, row, widgetbox
from bokeh.palettes import brewer
from bokeh.plotting import figure, Figure


world = geopandas.read_file(
    geopandas.datasets.get_path('naturalearth_lowres')
)
bokeh_ready = json.dumps(json.loads(world.to_json()))

geosource = GeoJSONDataSource(geojson = world.to_json())

cities = pd.read_csv('/Users/620751/Documents/cities.csv')

#cities = geopandas.GeoDataFrame(lat_long1, geometry=geopandas.points_from_xy(lat_long.dest_long, lat_long.dest_lat))
cities['Leg_Arrv_Dt_Tm'] = [str(a) for a in cities['Leg_Arrv_Dt_Tm']]
cities['Leg_Dep_Dt_Tm'] = [str(a) for a in cities['Leg_Dep_Dt_Tm']]
cities['Leg_Dep_Dt'] = [str(a) for a in cities['Leg_Dep_Dt']]
cities.Leg_Dep_Tm = [str(a) for a in cities['Leg_Dep_Tm']]
geocities = GeoJSONDataSource(geojson = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm','Leg_Arrv_Dt_Tm',
       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry']].to_json())

cities_input = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
                       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm', 'Leg_Arrv_Dt_Tm',
                       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
                       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry', 'duration_hours']]


p1 = Figure(title = 'Flights', 
           plot_height = 720 ,
           plot_width = 1200, 
           toolbar_location = 'below',
           tools = 'pan, wheel_zoom, box_zoom, reset')

p1.xgrid.grid_line_color = None
p1.ygrid.grid_line_color = None# Add patch renderer to figure.

countries = p1.patches('xs','ys', source = geosource,
                   fill_color = None,
                   line_color = 'gray', 
                   line_width = 0.25, 
                   fill_alpha = 1)# Create hover tool

p1.add_tools(HoverTool(renderers = [countries],
                      tooltips = [('Country','@name')]))

cities_input = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
       'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm','Leg_Arrv_Dt_Tm',
       'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
       'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry','duration_hours']]

def json_data(duration):
    dur = duration
    df_dur = cities_input[cities_input['duration_hours'] <= dur]
    dur_json = json.loads(df_dur.to_json())
    json_data = json.dumps(dur_json)
    return json_data

newsource = GeoJSONDataSource(geojson = json_data(24))

def update_plot(attr, old, new):
    yr = slider.value
    new_data = json_data(yr)
    newsource.geojson = new_data


slider = Slider(title='Duration', start=0, end = int(np.round(cities.duration_hours.max(),0)), step=1, value=12)


city_pt = p1.circle('dest_long', 'dest_lat', source = newsource,
                       color='red')

p1.add_tools(HoverTool(renderers = [newsource],line_policy='next',
                          tooltips = [('City','@MAILG_CTY_NM'),
                                     ('Departure Time','@Leg_Dep_Dt_Tm'),
                                     ('Seats','@seats'),
                                     ('Flight Time','@duration_hours' + ' hours'),
                                     ('weather', '@weather_desc' + ' low:' + '@low_temp' + ' high:' + '@high_temp' + ' rain:' + '@rain')]))

slider.on_change('value', update_plot)

layout = column(p1,widgetbox(slider))
curdoc().add_root(layout)

Instead of a map with all the cities shown as red dots I get a blank screen. I'm not sure what dumb mistake I am making but I hope someone might?


Solution

  • Welp, I had to rewrite a bit of it but I got it working correctly. Credit to towardsdatascience where I stole the code template from.

    import pandas as pd
    import numpy as np
    
    import geopandas
    import geoplot
    import json
    
    from bokeh.io import output_notebook, show, output_file
    from bokeh.models import  (NumeralTickFormatter, Select, CDSView, ColorBar, ColumnDataSource,
                              CustomJS, CustomJSFilter,
                              GeoJSONDataSource, HoverTool,
                              LinearColorMapper, Slider)
    from bokeh.io.doc import curdoc
    from bokeh.layouts import column, row, widgetbox
    from bokeh.palettes import brewer
    from bokeh.plotting import figure, Figure
    from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
    
    world = geopandas.read_file(
        geopandas.datasets.get_path('naturalearth_lowres')
    )
    bokeh_ready = json.dumps(json.loads(world.to_json()))
    
    geosource = GeoJSONDataSource(geojson = world.to_json())
    
    cities = pd.read_csv('/Users/620751/Documents/cities.csv')
    
    #cities = geopandas.GeoDataFrame(lat_long1, geometry=geopandas.points_from_xy(lat_long.dest_long, lat_long.dest_lat))
    cities['Leg_Arrv_Dt_Tm'] = [str(a) for a in cities['Leg_Arrv_Dt_Tm']]
    cities['Leg_Dep_Dt_Tm'] = [str(a) for a in cities['Leg_Dep_Dt_Tm']]
    cities['Leg_Dep_Dt'] = [str(a) for a in cities['Leg_Dep_Dt']]
    cities.Leg_Dep_Tm = [str(a) for a in cities['Leg_Dep_Tm']]
    geocities = GeoJSONDataSource(geojson = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
           'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm','Leg_Arrv_Dt_Tm',
           'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
           'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry']].to_json())
    
    cities_input = cities[['Leg_Orig', 'Leg_Dest', 'MAILG_CTY_NM', 'MAILG_ST_CTRY_NM',
                           'Leg_Dep_Dt', 'Opr_Car_Flt_Nbr', 'Leg_Dep_Tm', 'Leg_Arrv_Dt_Tm',
                           'Opr_Car_Cd', 'Leg_Dep_Dt_Tm', 'd1_avail', 'cp_avail', 'mn_avail',
                           'ot_avail', 'total_seats', 'dest_lat', 'dest_long', 'geometry', 'duration_hours']]
    
    city_source = ColumnDataSource(cities.drop('geometry', axis=1))
    
    p1 = Figure(title='Flights',
                plot_height=936,
                plot_width=1560,
                toolbar_location='below',
                tools='pan, wheel_zoom, box_zoom, reset')
    
    p1.xgrid.grid_line_color = None
    
    p1.ygrid.grid_line_color = None  # Add patch renderer to figure.
    
    p1.patches('xs', 'ys', source=geosource,
               fill_color=None,
               line_color='gray',
               line_width=0.25,
               fill_alpha=1)  # Create hover tool
    
    slider_duration = Slider(title='Duration', start=0, end=int(np.round(cities.duration_hours.max(), 0)), step=1, value=12)
    
    callback = CustomJS(args = dict(source=city_source), 
                        code = """source.change.emit();""")
    slider_duration.js_on_change('value', callback)
    
    duration_filter = CustomJSFilter(args = dict(slider = slider_duration, 
                                               source = city_source), 
                                   code = """
    var indices = [];
    // iterate through rows of data source and see if each satisfies some constraint
    for (var i = 0; i < source.get_length(); i++){
     if (source.data['duration_hours'][i] <= slider.value){
     indices.push(true);
     } else {
     indices.push(false);
     }
    }
    return indices;
    """)
    
    #SEAT FILTER START
    
    slider_seats = Slider(title='Seats', start=0, end=int(cities.total_seats.max()), step=1, value=10)
    
    callback_seats = CustomJS(args = dict(source=city_source), 
                        code = """source.change.emit();""")
    slider_seats.js_on_change('value', callback_seats)
    
    seats_filter = CustomJSFilter(args = dict(slider = slider_seats, 
                                               source = city_source), 
                                   code = """
    var indices = [];
    // iterate through rows of data source and see if each satisfies some constraint
    for (var i = 0; i < source.get_length(); i++){
     if (source.data['total_seats'][i] >= slider.value){
     indices.push(true);
     } else {
     indices.push(false);
     }
    }
    return indices;
    """)
    
    #SEAT FILTER END
    
    columns =  [TableColumn(field=Ci, title=Ci) for Ci in ['Leg_Orig',  'Leg_Dest',  'MAILG_CTY_NM',  'Opr_Car_Flt_Nbr', 'Leg_Arrv_Dt_Tm',  'd1_avail',  'cp_avail',  'mn_avail',  'total_seats', 'weather_desc',  'duration_hours']]
    
    
    
    
    
    view = CDSView(source = city_source, filters = [duration_filter, seats_filter])
    
    sites = p1.circle('dest_long', 'dest_lat', source = city_source, color = 'red', 
                     size = 5, alpha = 0.3, view = view)
    
    data_table = DataTable(source=city_source, columns=columns, width=1560, height=280, view= view)
    
    p1.add_tools(HoverTool(renderers = [sites],line_policy='next',
                              tooltips = [('City','@MAILG_CTY_NM'),
                                         ('Departure Time','@Leg_Dep_Dt_Tm'),
                                         ('Seats','@seats'),
                                         ('Flight Time','@duration_hours' + ' hours'),
                                         ('weather', '@weather_desc' + ' low:' + '@low_temp' + ' high:' + '@high_temp' + ' rain:' + '@rain')]))
    
    
    
    #city_pt = p1.circle('dest_long', 'dest_lat', source = newsource,
    #                       color='red')
    
    layout = column(p1, slider_duration, slider_seats,data_table)
    curdoc().add_root(layout)