# -*- coding: utf-8 -*-
"""
Created on 2016.4.25

@author: gongming.wei.shanghai@gmail.com

A simple demo to show heat flow map of dwelling time, density and path flow

"""

from bokeh.plotting import figure, show, output_file, ColumnDataSource,vplot
from bokeh.models import Range1d, HoverTool, CustomJS
import numpy as np #random
import pandas as pd
from collections import OrderedDict

# Calculate the area of polygon
def PolyArea(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))

# Construct the zone data, and lats, lons represents y an x position on the boundary in the shop
#shop_area={}
booth_area={}
guest_area={}

# booth area
booth_area['kid']={'lon':[0,5,5,0],'lat':[5,5,20,20],'name':'booth_kid','center':[1,12],'count':'N.A.'}
booth_area['men']={'lon':[10,30,30,10],'lat':[16,16,20,20],'name':'booth_men','center':[17,17.5],'count':'N.A.'}
booth_area['lady']={'lon':[40,45,45,30,30,40],'lat':[0,0,20,20,16,16],'name':'booth_lady','center':[35,17.5],'count':'N.A.'}
booth_area['new']={'lon':[13,27,27,13],'lat':[5,5,8,8],'name':'booth_new','center':[18,6],'count':'N.A.'}
booth_area['counter']={'lon':[10,30,30,10],'lat':[11,11,13,13],'name':'booth_counter','center':[16,11.5],'count':'N.A.'}
#booth_area['rest_a']={'lon':[32,34,34,32],'lat':[0,0,8,8]}
booth_area['onpromotion']={'lon':[0,10,10,5,5,0],'lat':[0,0,2,2,5,5],'name':'booth_on_promo','center':[1,0.5],'count':'N.A.'}

# guest area
guest_area['kid']={'lon':[5,10,10,5],'lat':[5,5,20,20],'name':'kid','center':[7,12],'count':50}
guest_area['men']={'lon':[10,30,30,10],'lat':[13,13,16,16],'name':'men','center':[20,14],'count':10}
guest_area['lady']={'lon':[30,40,40,30],'lat':[0,0,16,16],'name':'lady','center':[34,8],'count':70}
guest_area['new']={'lon':[10,30,30,27,27,13,13,10],'lat':[2,2,8,8,5,5,8,8],'name':'new','center':[20,3],'count':40}
guest_area['counter']={'lon':[10,30,30,10],'lat':[8,8,11,11],'name':'counter','center':[20,9],'count':25}
#guest_area['rest_a']={'lon':[28,32,32,28],'lat':[0,0,8,8]}
guest_area['onpromotion']={'lon':[5,10,10,5],'lat':[2,2,5,5],'name':'onpromotion','center':[7,4],'count':10}
guest_area['door']={'lon':[10,30,30,10],'lat':[0,0,2,2],'name':'door','center':[20,0],'count':10}

# Very important to keep the fixed order of the dict
booth_area=OrderedDict(sorted(booth_area.items()))
guest_area=OrderedDict(sorted(guest_area.items()))

# Calculate the area of each guest area. For calculation of footage per square meter
for code in guest_area:
    guest_area[code]['area']=PolyArea(guest_area[code]['lon'],guest_area[code]['lat'])

booth_xs=[booth_area[code]['lon'] for code in booth_area] # if booth_area[code]["area_id"] not in EXCLUDED]
booth_ys=[booth_area[code]['lat'] for code in booth_area] # if booth_area[code]["area_id"] not in EXCLUDED]
booth_name=[booth_area[code]['name'] for code in booth_area]

guest_xs=[guest_area[code]['lon'] for code in guest_area] # if guest_area[code]["area_id"] not in EXCLUDED]
guest_ys=[guest_area[code]['lat'] for code in guest_area] # if guest_area[code]["area_id"] not in EXCLUDED]
guest_name=[guest_area[code]['name'] for code in guest_area]

colors = ["#F1EEF6", "#D4B9DA", "#C994C7", "#DF65B0", "#DD1C77", "#980043"]

guest_colors=[]

#guest_count= np.rint(np.random.random((1,len(guest_area)))*50).astype(int) #

guest_count_per_area=([guest_area[code]['count']/guest_area[code]['area'] for code in guest_area])

tmpidx=(5*np.array(guest_count_per_area)/max(guest_count_per_area)).astype(int)

thiscolors=[colors[idx] for idx in tmpidx]

booth_nr=len(booth_area)
guest_nr=len(guest_area)

# Load test data from excel file
testdata=pd.read_csv('static\\data\\ShopData\\shop demo data.csv',sep=',',keep_date_col=True,index_col=0, parse_dates=True)
# reorder the data so that we can create the the same order as here
testdata.sort_index(axis=1, inplace=True)

timelabel=testdata.index

pcdata=np.hstack((np.zeros((len(timelabel),booth_nr)),testdata.values))  # Array of test data with zeo value filled in booth area

# Smart way to combine the two patches area
data_source = ColumnDataSource(data=dict(
    x=booth_xs+guest_xs,                                  # area borders
    y=booth_ys+guest_ys,                                  # area borders
    linecolor=["white"]*booth_nr+["#884444"]*guest_nr,    # line color
    linewidth=[2]*booth_nr+[0.5]*guest_nr,       
    fillcolor=["grey"]*booth_nr+thiscolors,               # fill color
    fillalpha=[0.5]*booth_nr+[0.7]*guest_nr,
    guestcount=["N.A."]*booth_nr+[guest_area[code]['count'] for code in guest_area],
    name=booth_name+guest_name,                           # Area name to be referred for the 2nd Chart
    rate=["N.A."]*booth_nr+guest_count_per_area,
    pcdata=pcdata.T.tolist()  # updated on Apr. 26 we have to do this transform !!!
))

data_source_2 = ColumnDataSource(data=dict(
    x=timelabel,
    y=pcdata[:,booth_nr]  #
))

TOOLS="pan,wheel_zoom,box_zoom,reset,hover,save"

pheatmap = figure(title="heatmap over location", toolbar_location="left",
           plot_width=900, plot_height=600, tools=TOOLS)

# Split patches to
pheatmap.patches('x','y',source=data_source,
          fill_color='fillcolor', fill_alpha='fillalpha',
          line_color='linecolor', line_width='linewidth')

#p.patches(booth_xs, booth_ys, fill_color="grey", fill_alpha=0.5,
#          line_color="#884444", line_width=2, line_alpha=0.3)

pheatmap.xaxis.visible = False
pheatmap.yaxis.visible = False
pheatmap.xgrid.grid_line_color = None
pheatmap.ygrid.grid_line_color = None

pheatmap.text([booth_area[code]['center'][0] for code in booth_area],[booth_area[code]['center'][1] for code in booth_area],[booth_area[code]['name'] for code in booth_area],text_color='white',text_font_size='22pt')

hover=pheatmap.select_one(HoverTool)

# define the callback to update the plot data for plinedaily
update_ts = CustomJS(args=dict(sourcedata=data_source,sourcedata2=data_source_2),
	   code="""
       // get data source from callback args
	var data = sourcedata.get('data');
      var data2=sourcedata2.get('data')
	var names        = data['name'];
      var id=names.indexof("@name")   // Get the index of current area to pick up 
      var tmpdata=data2['y']
          
      // output the data for curve plot
      var datlen=tmpdata.length
      var tmppcdata=data['pcdata']
	for (var i = 0; i <datlen; i++) {    // Retrieve the new data according to id
              tmpdata[i]=tmppcdata[id][i]
	}
       // trigger update of data source 2 for line plot updating
      data2['y'].push(tmpdata)
      sourcedata2.trigger('change');
	""")

hover.point_policy = "follow_mouse"
hover.callback = update_ts

hover.tooltips = [
    ("guest area", "@name"),
    ('guest count','@guestcount'),
    ("footfall by area", "@rate"),
    ("(xpos, ypos)", "($x, $y)"),
]

# Create plinedaily to plot the daily distribution for any area
plinedaily = figure(title="hourly footfall", toolbar_location="left",
           plot_width=900, plot_height=200, tools="reset,save")

#plinedaily.line(x= timelabel, y =pcdata[:,booth_nr],line_width=2, color='green')

plinedaily.line(x='x', y ='y',source=data_source_2, line_width=2, color='green')

plinedaily.title = " data quick view"
plinedaily.yaxis.axis_label = "traffic flow"
plinedaily.xaxis.axis_label = "hour"
plinedaily.yaxis[0].ticker.desired_num_ticks = 3
plinedaily.yaxis.axis_label_standoff = 10
plinedaily.min_border_bottom = 0
plinedaily.axis.axis_line_width=5
plinedaily.axis.axis_line_color = "gray"
#plinedaily.legend.orientation = "top_left"
#plinedaily.legend.label_text_font_size='14pt'

figout = vplot(pheatmap,plinedaily)

output_file("shop_demo_1.html", title="shop demo example")

show(figout)