From 42578702ba3a1164b3e557586290772cfadfb95c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 10 Jul 2017 18:43:16 +0200 Subject: [PATCH 1/5] color scheme exploration --- rowers/.#views.py | 1 + rowers/interactiveplots.py | 4 ++-- rowers/views.py | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 rowers/.#views.py diff --git a/rowers/.#views.py b/rowers/.#views.py new file mode 100644 index 00000000..b090c519 --- /dev/null +++ b/rowers/.#views.py @@ -0,0 +1 @@ +E408191@CZ27LT9RCGN72.22500:1499438870 \ No newline at end of file diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 47d446cd..7c9b1e26 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -70,7 +70,7 @@ watermarkh = 35 watermarkanchor = 'bottom_right' def errorbar(fig, x, y, source=ColumnDataSource(), - xerr=False, yerr=False, color='red', + xerr=False, yerr=False, color='black', point_kwargs={}, error_kwargs={}): fig.circle(x, y, source=source, name='data',color=color, @@ -1292,7 +1292,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', xerr=ploterrorbars, yerr=ploterrorbars, point_kwargs={ - 'line_color':None, + 'line_color':"black", 'size':"groupsize", 'fill_color':"color", }, diff --git a/rowers/views.py b/rowers/views.py index c0e1d69f..9bbf5ae4 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3606,6 +3606,8 @@ def multiflex_view(request,userid=0, groupcols *= 100. rgb = [colorsys.hsv_to_rgb(float(x/100.), 1.0, 1.0) for x in groupcols] + rgb = [colorsys.hsv_to_rgb(0.6-0.3*float(x/100.), 0.4+0.4*float(x/100.), 1.0) for x in groupcols] +# rgb = [colorsys.hsv_to_rgb(0.1+0.8*float(x/100.), 0.5, 0.5-0.2*float(x/100.)) for x in groupcols] RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] df['color'] = colors From 5105834873e0aecfb4d1a3a6d25d2652c7b5de58 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 10 Jul 2017 18:43:52 +0200 Subject: [PATCH 2/5] more color scheme --- rowers/.#views.py | 1 - rowers/views.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 rowers/.#views.py diff --git a/rowers/.#views.py b/rowers/.#views.py deleted file mode 100644 index b090c519..00000000 --- a/rowers/.#views.py +++ /dev/null @@ -1 +0,0 @@ -E408191@CZ27LT9RCGN72.22500:1499438870 \ No newline at end of file diff --git a/rowers/views.py b/rowers/views.py index 9bbf5ae4..93b5bb77 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3606,7 +3606,7 @@ def multiflex_view(request,userid=0, groupcols *= 100. rgb = [colorsys.hsv_to_rgb(float(x/100.), 1.0, 1.0) for x in groupcols] - rgb = [colorsys.hsv_to_rgb(0.6-0.3*float(x/100.), 0.4+0.4*float(x/100.), 1.0) for x in groupcols] + rgb = [colorsys.hsv_to_rgb(0.5-0.3*float(x/100.), 0.4+0.4*float(x/100.), 1.0) for x in groupcols] # rgb = [colorsys.hsv_to_rgb(0.1+0.8*float(x/100.), 0.5, 0.5-0.2*float(x/100.)) for x in groupcols] RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] From 447c833fa7517e2f4dd9947e15d851b98cb357eb Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 11 Jul 2017 11:13:17 +0200 Subject: [PATCH 3/5] blue single hue color scheme --- rowers/interactiveplots.py | 25 +++++++++++++++++++------ rowers/views.py | 8 +++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 7c9b1e26..fe428a73 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -1,3 +1,4 @@ +import colorsys from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage from rowingdata import rower as rrower from rowingdata import main as rmain @@ -73,8 +74,6 @@ def errorbar(fig, x, y, source=ColumnDataSource(), xerr=False, yerr=False, color='black', point_kwargs={}, error_kwargs={}): - fig.circle(x, y, source=source, name='data',color=color, - **point_kwargs) xvalues = source.data[x] yvalues = source.data[y] @@ -96,7 +95,12 @@ def errorbar(fig, x, y, source=ColumnDataSource(), for px, py, err, color in zip(xvalues, yvalues, xerrvalues, colorvalues): x_err_x.append((px - err, px + err)) x_err_y.append((py, py)) - err_color.append(color) + (r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5)) + h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) + v = v*0.8 + r, g, b = colorsys.hsv_to_rgb(h, s, v) + color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b)) + err_color.append(color2) fig.multi_line(x_err_x, x_err_y, color=err_color, name='xerr', @@ -113,13 +117,22 @@ def errorbar(fig, x, y, source=ColumnDataSource(), for px, py, err, color in zip(xvalues, yvalues, yerrvalues, colorvalues): y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) - err_color.append(color) + (r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5)) + h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.) + v = v*0.8 + r, g, b = colorsys.hsv_to_rgb(h, s, v) + color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b)) + err_color.append(color2) fig.multi_line(y_err_x, y_err_y, color=err_color, name='yerr',**error_kwargs) except TypeError: pass - + + fig.circle(x, y, source=source, name='data',color=color, + **point_kwargs) + + def tailwind(bearing,vwind,winddir): """ Calculates head-on head/tailwind in direction of rowing @@ -1292,7 +1305,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', xerr=ploterrorbars, yerr=ploterrorbars, point_kwargs={ - 'line_color':"black", + 'line_color':"#969696", 'size':"groupsize", 'fill_color':"color", }, diff --git a/rowers/views.py b/rowers/views.py index 93b5bb77..6527134a 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3604,10 +3604,12 @@ def multiflex_view(request,userid=0, if aantal == 1: groupcols = np.array([1.]) + # rgb = [colorsys.hsv_to_rgb(float(x/100.), 1.0, 1.0) for x in groupcols] + rgb = [colorsys.hsv_to_rgb((207-4*x)/360., + 0.06+0.89*x, + 1-0.38*x) for x in groupcols] + groupcols *= 100. - rgb = [colorsys.hsv_to_rgb(float(x/100.), 1.0, 1.0) for x in groupcols] - rgb = [colorsys.hsv_to_rgb(0.5-0.3*float(x/100.), 0.4+0.4*float(x/100.), 1.0) for x in groupcols] -# rgb = [colorsys.hsv_to_rgb(0.1+0.8*float(x/100.), 0.5, 0.5-0.2*float(x/100.)) for x in groupcols] RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] df['color'] = colors From ab8627e311468c7115bd00bbe2ccd06b264fd76e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 11 Jul 2017 14:11:15 +0200 Subject: [PATCH 4/5] legend --- rowers/interactiveplots.py | 41 +++++++++++++++++++++++++++++++------- rowers/utils.py | 11 ++++++++++ rowers/views.py | 31 +++++++++++++++++----------- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index fe428a73..0a4b2abc 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -11,7 +11,7 @@ from django.utils import timezone from bokeh.palettes import Dark2_8 as palette import itertools from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc -from bokeh.models import CustomJS,Slider, TextInput +from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation from bokeh.charts import Histogram,HeatMap,Area,BoxPlot from bokeh.resources import CDN,INLINE from bokeh.embed import components @@ -1189,7 +1189,7 @@ def interactive_chart(id=0,promember=0): def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', ploterrorbars=False, - title=None,binsize=1): + title=None,binsize=1,colorlegend=[]): if datadf.empty: return ['','

No non-zero data in selection

'] @@ -1259,18 +1259,17 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', datadf, ) - TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize' if groupby != 'date': hover = HoverTool(names=['data'], tooltips = [ - (groupby,'@groupval{1.1}') + (groupby,'@groupval{1.1}'), ]) else: hover = HoverTool(names=['data'], tooltips = [ - (groupby,'@groupval') + (groupby,'@groupval'), ]) hover.mode = 'mouse' @@ -1281,7 +1280,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", - toolbar_sticky=False) + toolbar_sticky=False) #,plot_width=500,plot_height=500) # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} @@ -1300,7 +1299,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', x_range_name = "watermark", y_range_name = "watermark", ) - + errorbar(plot,xparam,yparam,source=source, xerr=ploterrorbars, yerr=ploterrorbars, @@ -1311,6 +1310,34 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', }, ) + + + for nr, gvalue, color in colorlegend: + box = BoxAnnotation(bottom=500-20*nr,left=550,top=520-20*nr, + right=570, + bottom_units='screen', + top_units='screen', + left_units='screen', + right_units='screen', + fill_color=color) + legendlabel = Label(x=571,y=503-20*nr,x_units='screen', + y_units='screen', + text = "{gvalue:3.0f}".format(gvalue=gvalue), + background_fill_alpha=1.0, + text_color='black', + text_font_size=value("0.7em")) + plot.add_layout(box) + plot.add_layout(legendlabel) + + if colorlegend: + legendlabel = Label(x=372,y=300,x_units='screen', + y_units='screen', + text = 'group legend', + text_color='black', + text_font_size=value("0.7em"), + angle=90, + angle_units='deg') + if xparam == 'workoutid': plot.xaxis.axis_label = 'Workout' else: diff --git a/rowers/utils.py b/rowers/utils.py index 526dd327..51339a53 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -1,9 +1,20 @@ import math import numpy as np import pandas as pd +import colorsys lbstoN = 4.44822 +def range_to_color_hex(groupcols): + rgb = [colorsys.hsv_to_rgb((207-4*x)/360., + 0.06+0.89*x, + 1-0.38*x) for x in groupcols] + + RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] + colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + + return colors + def str2bool(v): return v.lower() in ("yes", "true", "t", "1") diff --git a/rowers/views.py b/rowers/views.py index 6527134a..c4f274bd 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -272,7 +272,7 @@ def splitstdata(lijst): from utils import ( geo_distance,serialize_list,deserialize_list,uniqify, - str2bool + str2bool,range_to_color_hex ) import datautils @@ -3570,9 +3570,12 @@ def multiflex_view(request,userid=0, yerror = groups.std()[yparam] groupsize = groups.count()[xparam] - #groupsize = 15.*np.log10(1+99.*groupsize/float(max(groupsize))) - groupsize = 30.*np.sqrt(groupsize/float(max(groupsize))) - + if len(groupsize) == 0: + messages.error(request,'No data in selection') + url = reverse(user_multiflex_select) + return HttpResponseRedirect(url) + else: + groupsize = 30.*np.sqrt(groupsize/float(groupsize.max())) df = pd.DataFrame({ xparam:xvalues, @@ -3604,16 +3607,19 @@ def multiflex_view(request,userid=0, if aantal == 1: groupcols = np.array([1.]) - # rgb = [colorsys.hsv_to_rgb(float(x/100.), 1.0, 1.0) for x in groupcols] - rgb = [colorsys.hsv_to_rgb((207-4*x)/360., - 0.06+0.89*x, - 1-0.38*x) for x in groupcols] - groupcols *= 100. - RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] - colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + colors = range_to_color_hex(groupcols) + df['color'] = colors + clegendx = np.arange(0,1.2,.2) + legcolors = range_to_color_hex(clegendx) + clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min()) + + + colorlegend = zip(range(6),clegendy,legcolors) + + if userid == 0: extratitle = '' else: @@ -3626,7 +3632,8 @@ def multiflex_view(request,userid=0, groupby, extratitle=extratitle, ploterrorbars=ploterrorbars, - binsize=binsize) + binsize=binsize, + colorlegend=colorlegend) From 7b99fe8cb105db6c011e130cd7f3ff821d828f5e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 11 Jul 2017 15:48:19 +0200 Subject: [PATCH 5/5] palette picker multiflex --- rowers/forms.py | 10 ++++++- rowers/interactiveplots.py | 5 +++- rowers/utils.py | 53 +++++++++++++++++++++++++++++++++----- rowers/views.py | 15 ++++++++--- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/rowers/forms.py b/rowers/forms.py index 412087b1..4e199518 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -327,6 +327,11 @@ formaxlabelsmultiflex.pop('distance') formaxlabelsmultiflex['workoutid'] = 'Workout' parchoicesmultiflex = list(sorted(formaxlabelsmultiflex.items(), key = lambda x:x[1])) +from utils import palettes +#palettechoices = { key:key for key, value in palettes.iteritems() } + +palettechoices = tuple((p,p) for p in palettes.keys()) + class MultiFlexChoiceForm(forms.Form): xparam = forms.ChoiceField(choices=parchoicesmultiflex, @@ -352,7 +357,10 @@ class MultiFlexChoiceForm(forms.Form): includereststrokes = forms.BooleanField(initial=False, required=False, label='Include Rest Strokes') - + palette = forms.ChoiceField(choices=palettechoices, + label = 'Color Scheme', + initial='monochrome_blue') + class ChartParamChoiceForm(forms.Form): plotchoices = ( ('line','Line Plot'), diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 0a4b2abc..b6f19b46 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -1307,6 +1307,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', 'line_color':"#969696", 'size':"groupsize", 'fill_color':"color", + 'fill_alpha':1.0, }, ) @@ -1319,7 +1320,9 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', top_units='screen', left_units='screen', right_units='screen', - fill_color=color) + fill_color=color, + fill_alpha=1.0, + line_color=color) legendlabel = Label(x=571,y=503-20*nr,x_units='screen', y_units='screen', text = "{gvalue:3.0f}".format(gvalue=gvalue), diff --git a/rowers/utils.py b/rowers/utils.py index 51339a53..c6052de6 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -5,15 +5,54 @@ import colorsys lbstoN = 4.44822 -def range_to_color_hex(groupcols): - rgb = [colorsys.hsv_to_rgb((207-4*x)/360., - 0.06+0.89*x, - 1-0.38*x) for x in groupcols] +def trcolors(r1,g1,b1,r2,g2,b2): + r1 = r1/255. + r2 = r2/255. + g1 = g1/255. + g2 = g2/255. + b2 = b2/255. + b1 = b1/255. + h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1) + h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2) - RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] - colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + + return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1) - return colors +palettes = { + 'monochrome_blue':(207,-4,0.06,0.89,1.0,-0.38), + 'gold_sunset':(47,-31,.26,-0.12,0.94,-0.5), + 'blue_red':(207,-200,.85,0,.74,-.24), + 'blue_green':(207,-120,.85,0,.75,.25), + 'cyan_green':(192,-50,.08,.65,.98,-.34), + 'cyan_purple':trcolors(237,248,251,136,65,157), + 'green_blue':trcolors(240,249,232,8,104,172), + 'orange_red':trcolors(254,240,217,179,0,0), + 'cyan_blue':trcolors(241,238,246,4,90,141), + 'cyan_green':trcolors(246,239,247,1,108,89), + 'cyan_magenta':trcolors(241,238,246,152,0,67), + 'beige_magenta':trcolors(254,235,226,122,1,119), + 'yellow_green':trcolors(255,255,204,0,104,55), + 'yellow_blue':trcolors(255,255,205,37,52,148), + 'autumn':trcolors(255,255,212,153,52,4), + 'yellow_red':trcolors(255,255,178,189,0,39) +} + + +def range_to_color_hex(groupcols,palette='monochrome_blue'): + + try: + plt = palettes[palette] + except KeyErro: + plt = palettes['monochrome_blue'] + + rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360., + plt[2]+plt[3]*x, + plt[4]+plt[5]*x) for x in groupcols] + + RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb] + colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB] + + return colors def str2bool(v): return v.lower() in ("yes", "true", "t", "1") diff --git a/rowers/views.py b/rowers/views.py index c4f274bd..2e600225 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3435,6 +3435,11 @@ def multiflex_view(request,userid=0, ploterrorbars = options['ploterrorbars'] except KeyError: ploterrorbars = False + + try: + palette = options['palette'] + except KeyError: + palette = 'blue_green' workstrokesonly = not includereststrokes @@ -3454,7 +3459,9 @@ def multiflex_view(request,userid=0, request.session['ploterrorbars'] = ploterrorbars request.session['includereststrokes'] = includereststrokes workstrokesonly = not includereststrokes - + palette = chartform.cleaned_data['palette'] + request.session['palette'] = palette + groupby = chartform.cleaned_data['groupby'] binsize = chartform.cleaned_data['binsize'] if binsize <= 0: @@ -3482,6 +3489,8 @@ def multiflex_view(request,userid=0, request.session['ploterrorbars'] = ploterrorbars request.session['includereststrokes'] = includereststrokes workstrokesonly = not includereststrokes + palette = chartform.cleaned_data['palette'] + request.session['palette'] = palette groupby = chartform.cleaned_data['groupby'] binsize = chartform.cleaned_data['binsize'] @@ -3608,12 +3617,12 @@ def multiflex_view(request,userid=0, groupcols = np.array([1.]) - colors = range_to_color_hex(groupcols) + colors = range_to_color_hex(groupcols,palette=palette) df['color'] = colors clegendx = np.arange(0,1.2,.2) - legcolors = range_to_color_hex(clegendx) + legcolors = range_to_color_hex(clegendx,palette=palette) clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())