Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflow2 #8

Merged
merged 4 commits into from Sep 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
226 changes: 174 additions & 52 deletions main.py
Expand Up @@ -9,9 +9,10 @@
from datetime import datetime
from bokeh.io import curdoc
from bokeh.layouts import layout,gridplot, column, row
from bokeh.models import Button, Toggle, CategoricalColorMapper, ColumnDataSource, HoverTool, Label, SingleIntervalTicker, Slider, Spacer, GlyphRenderer, DatetimeTickFormatter, DateRangeSlider, DataRange1d, Range1d, DateSlider, LinearColorMapper, Div
from bokeh.models import Button, Toggle, CategoricalColorMapper, ColumnDataSource, HoverTool, Label, SingleIntervalTicker, Slider, Spacer, GlyphRenderer, DatetimeTickFormatter, DateRangeSlider, DataRange1d, Range1d, DateSlider, LinearColorMapper, Div, CustomJS
from bokeh.palettes import Inferno256, Magma256, Turbo256, Plasma256, Cividis256, Viridis256, OrRd
from bokeh.plotting import figure
from bokeh.events import DocumentReady

from .data import process_data, process_data_counties

Expand Down Expand Up @@ -94,6 +95,16 @@

TEXT_NOTES ='<strong>Important:</strong> use the mouse for the initial selection and the cursors for fine tuning. The plot takes ~ 1s to update after each date selection.'

# layout related variables
MIN_HORIZONTAL_WIDTH = 1400

window_size_data_source = ColumnDataSource( data = { 'width' :[0] , 'height': [0] } )

# by default we assume the layout is horizontal
current_horizontal = True

# functions

def make_age_labels ( nr_labels ):

labels = []
Expand Down Expand Up @@ -314,7 +325,7 @@ def reverse_palette ( original_palette ):

return palette

# callbacks
#### callbacks ####

# for the toggle button action
def update_state(new):
Expand Down Expand Up @@ -395,6 +406,73 @@ def update_map(attr, old, new):
# we refresh the tooltips using the Colormap column as the list
plot_map.hover.tooltips = [ ('County', '@NAME_2'), ('Incidence', '@Colormap'), ]

# after document load
def on_document_ready(evt):
# here we change some property on the fake_toggle widget
print('document is ready, refreshing fake widget')

# this forces a change on the fake slider, which then invokes the JS callback
fake_slider.value = ( date_i, date_i )
fake_slider.value = ( date_i, date_f )

# this callbacks takes action on the server side upon dimensions change
def on_dimensions_change(attr, old, new):

width = new['width'][0]
height = new['height'][0]

global current_horizontal

print('current dimensions', width, height)

if width >= height and width > MIN_HORIZONTAL_WIDTH:
print('orientation is horizontal')
horizontal = True
else:
print('orientation is vertical')
horizontal = False

if (current_horizontal != horizontal):
print ('redoing layouts')

curdoc().clear()

curdoc().add_root(controls1)

if horizontal:
curdoc().add_root(layout1_h)
else:
curdoc().add_root(layout1_v)

curdoc().add_root(controls2)

if horizontal:
curdoc().add_root(layout2_h)
curdoc().add_root(layout3_h)
else:
curdoc().add_root(layout2_v)
curdoc().add_root(layout3_v)

# store the horizontalness state
current_horizontal = horizontal

dimensions_callback = CustomJS( args=dict(ds=window_size_data_source), code="""
var width, height;
var new_data = {};
height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
console.log("Javascript callback", height, width);

// needs to be a list, otherwise we have a server side error
new_data['height'] = [height];
new_data['width' ] = [width];
ds.data = new_data;
ds.change.emit();

""")

#### end of call backs ####

def get_y_limits ( source, date_i, date_f ):

# calculate indexes in the y data
Expand All @@ -414,11 +492,86 @@ def get_y_limits ( source, date_i, date_f ):
# return the minimum of the minimuns for the interval, same for maximum
return min(y_min_list), max(y_max_list)

def make_layouts( ):

control_spacer = Spacer(width=10, height=10, width_policy='auto', height_policy='fixed')

# use this line for debugging with the fake slider
controls1 = row (date_slider1, control_spacer, fake_slider, clines_switch, name="section1_controls" )

fake_slider.visible = False

# first

grid_h = gridplot([
[ plot1, plot3, plot5, plot7 ],
[ plot2, plot4, plot6, plot8 ] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT, toolbar_location=None, sizing_mode='scale_width')

grid_v = gridplot([
[ plot1, plot3 ],
[ plot5, plot7 ],
[ plot2, plot4 ],
[ plot6, plot8 ] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT, toolbar_location=None, sizing_mode='scale_width')

# second

controls2 = row (date_slider2, name="section2_controls" )

grid2_h = gridplot ([
[plot9, plot11],
[plot10, plot12] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT2, toolbar_location=None, sizing_mode='scale_width')

grid2_v = gridplot ([
[plot9 ],
[plot10],
[plot11],
[plot12] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT2, toolbar_location=None, sizing_mode='scale_width')

# third

# we create plot identical to plot1 (incidence), using the existing data source
plot1_copy = make_plot ('incidence', PLOT1_TITLE, days, 'datetime')
plot1_copy.line('x','y', source=source_plot1, line_width=PLOT_LINE_WIDTH, line_alpha=PLOT_LINE_ALPHA, line_color=PLOT_LINE_COLOR, )
set_plot_details(plot1_copy, 'Date', 'Count', '@x{%F}', '@y{0.00}', 'vline', False, False)
set_plot_date_details(plot1_copy, source_plot1)

# but we change the range
# we can't do this on a directy copy of plot1, because it is shallow
plot1_copy.x_range.start = pd.to_datetime(map_date_i)
plot1_copy.x_range.end = pd.to_datetime(map_date_f)

notes = Div(text=TEXT_NOTES, width=TEXT_WIDTH)

# now the layout

slider_spacer = Spacer(width=30, height=50, width_policy='auto', height_policy='fixed')

layout1_h = layout(grid_h, name='section1', sizing_mode='scale_width')
layout2_h = layout(grid2_h, name='section2', sizing_mode='scale_width')

column_section3_map = column(plot_map)
column_section3_others = column( [plot1_copy, row( [slider_spacer, date_slider_map] ), row( [ slider_spacer, notes] ) ] )

row_section3 = row ( column_section3_map , column_section3_others )
layout3_h = layout( row_section3, name='section3')

layout1_v = layout(grid_v, name='section1', sizing_mode='scale_width')
layout2_v = layout(grid2_v, name='section2', sizing_mode='scale_width')

column_section3_map = column( [plot_map, row( [slider_spacer, date_slider_map] ), row( [ slider_spacer, notes] ), plot1_copy ] )

layout3_v = layout( column_section3_map, name='section3')

return layout1_h, layout2_h, layout3_h, layout1_v, layout2_v, layout3_v, controls1, controls2

# main

curdoc().title = PAGE_TITLE


# fetch data from files

# regular plots data
Expand Down Expand Up @@ -654,63 +807,32 @@ def get_y_limits ( source, date_i, date_f ):

#### Plot layout section ###

# the layout name is added here then invoked from the HTML template
# all roots added here must be invoked on the HTML
## handling different layout orientations

# section 1
# register on_document_ready callback
curdoc().on_event(DocumentReady, on_document_ready)

control_spacer = Spacer(width=10, height=10, width_policy='auto', height_policy='fixed')
fake_slider = DateRangeSlider(title="Fake Range: ", start=date_i, end=date_f, value=( date_i, date_f ), step=1)
fake_slider.js_on_change('value', dimensions_callback)

controls1 = row (date_slider1, control_spacer, clines_switch, name="section1_controls" )
curdoc().add_root(controls1)
# register callback to be called upon JS callback executions
window_size_data_source.on_change('data', on_dimensions_change)

grid = gridplot([
[ plot1, plot3, plot5, plot7 ],
[ plot2, plot4, plot6, plot8 ] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT, toolbar_location=None, sizing_mode='scale_width')
# the layout name is added here then invoked from the HTML template
# all roots added here must be invoked on the HTML

layout1 = layout( grid, name='section1', sizing_mode='scale_width')
layout1_h, layout2_h, layout3_h, layout1_v, layout2_v, layout3_v, controls1, controls2 = make_layouts()

curdoc().add_root(layout1)
# by default layouts are created assuming we have enough width for the ideal visualization mode
# that is, we start with horizontal layouts

# section 2
# section 1
curdoc().add_root(controls1)
curdoc().add_root(layout1_h)

controls2 = row (date_slider2, name="section2_controls" )
# section 2
curdoc().add_root(controls2)

grid2 = gridplot ([
[plot9, plot11],
[plot10, plot12] ],
plot_width=PLOT_WIDTH, plot_height=PLOT_HEIGHT2, toolbar_location=None, sizing_mode='scale_width')

layout2 = layout( grid2, name='section2', sizing_mode='scale_width')

curdoc().add_root(layout2)
curdoc().add_root(layout2_h)

# section 3

# we create plot identical to plot1 (incidence), using the existing data source
plot1_copy = make_plot ('incidence', PLOT1_TITLE, days, 'datetime')
plot1_copy.line('x','y', source=source_plot1, line_width=PLOT_LINE_WIDTH, line_alpha=PLOT_LINE_ALPHA, line_color=PLOT_LINE_COLOR, )
set_plot_details(plot1_copy, 'Date', 'Count', '@x{%F}', '@y{0.00}', 'vline', False, False)
set_plot_date_details(plot1_copy, source_plot1)

# but we change the range
# we can't do this on a directy copy of plot1, because it is shallow
plot1_copy.x_range.start = pd.to_datetime(map_date_i)
plot1_copy.x_range.end = pd.to_datetime(map_date_f)

notes = Div(text=TEXT_NOTES, width=TEXT_WIDTH)

# now the layout

slider_spacer = Spacer(width=30, height=50, width_policy='auto', height_policy='fixed')

column_section3_map = column(plot_map)
column_section3_others = column( [plot1_copy, row( [slider_spacer, date_slider_map] ), row( [ slider_spacer, notes] ) ] )

row_section3 = row ( column_section3_map , column_section3_others )

layout3 = layout( row_section3, name='section3')

curdoc().add_root(layout3)
curdoc().add_root(layout3_h)