import geopandas as gpd # for reading and manupulating shapefile
import matplotlib.pyplot as plt # for making figure
import seaborn as sns # for making distplot

from colouringmap import theme_mapping as tm # a function named leveling_vector in tm will be used
from colouringmap import breaking_levels as bk # a function named get_levels in bk will be used

# magic line for matlotlib figure to be shown inline in jupyter cell
%matplotlib inline 

"""
Explanation: Breaking a variable to levels
The scenario for this tutorial is that, you have a series of a variable, such as the population density of different cities. And, you need to classify them into different groups according to this variable, e.g. the very high, medium high, medium, medium low, very low population density, etc.
In some cases, you already have a GeoDataFrame/DataFrame, in other cases, you just have a list that contain the numbers. So, the following cover two major functions:

tm.leveling_vector, which take a dataframe and a column name for the classifying; and
bk.get_levels, which take a list.

The two functions takes a break_method for the breaking methods, such as quantile(default), head_tail_break, natural_break, equal_interval (and manual).
They take a break_N parameter, for specifying the number of groups.
And they also take a break_cuts. 
First, import things that is needed.
End of explanation
"""


grid_res = gpd.read_file('data/community_results.shp')
grid_res.head()

"""
Explanation: read a demo file, and take a look
End of explanation
"""


sns.distplot(grid_res['usercount'], kde=False)

"""
Explanation: take a look at the data distribution. using seaborn distplot.
End of explanation
"""


ax = sns.distplot(grid_res['usercount'], kde=False)
#ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')


"""
Explanation: the above plot showed that the data is potentially an exponential distribution.
so lets try to make the yscale log.
End of explanation
"""


level_list, cuts = tm.leveling_vector(grid_res, 'usercount') #, break_method='quantile') #default method is quantile

"""
Explanation: using different break method:

quantile
head_tail_break
natural_break
equal_interval

the following is the most simple way of converting the column of a gdf to levels
End of explanation
"""


grid_res['user_level'] = level_list

grid_res.head()

"""
Explanation: Normally, the level_list is used to be assign to the gdf. This is what I did in other functions of mapping.
End of explanation
"""


cuts

ax = sns.distplot(grid_res['usercount'], kde=False)
#ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
for c in cuts:
    ax.axvline(x=c)

lev = list(set(level_list))
count = [ level_list.count(l) for l in lev ]
print lev
print count

"""
Explanation: cuts contain the breaking values, and the min/max at the both end of the list.
End of explanation
"""


level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='head_tail_break')
print cuts

ax = sns.distplot(grid_res['usercount'], kde=False)
#ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
for c in cuts:
    ax.axvline(x=c)

lev = list(set(level_list))
count = [ level_list.count(l) for l in lev ]
print lev
print count

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='natural_break')
print cuts

ax = sns.distplot(grid_res['usercount'], kde=False)
#ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
for c in cuts:
    ax.axvline(x=c)

lev = list(set(level_list))
count = [ level_list.count(l) for l in lev ]
print lev
print count

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='equal_interval')
print cuts

ax = sns.distplot(grid_res['usercount'], kde=False)
#ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
for c in cuts:
    ax.axvline(x=c)

lev = list(set(level_list))
count = [ level_list.count(l) for l in lev ]
print lev
print count

"""
Explanation: quantile has a similar count for each level.
Lets try some other break method.
End of explanation
"""


level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='head_tail_break', break_N=3)
print cuts

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='head_tail_break', break_N=5)
print cuts

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='head_tail_break', break_N=7)
print cuts

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='head_tail_break', break_N=9)
print cuts

"""
Explanation: specifying the number of level
The number of level is set to the parameter break_N, which is default to 5.
After setting the break_N to N, the number of cuts become N+1, because it contain both the largest and the smallest values.
End of explanation
"""


level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='quantile', break_cuts=[0.,.25,.5,.75,1.])
print cuts

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='quantile', break_cuts=[0.,0.1,.5,.99,1.])
print cuts

level_list, cuts = tm.leveling_vector(grid_res, 'usercount', break_method='manual', break_cuts=[0.0, 120, 490, 1200, 2200, 4506.0])
print cuts

"""
Explanation: note that what head_tail_break do for increased number of levels. 
specifying cuts manually
There are two ways of using the cuts. This will return a cut list, and a level_list that is in the same length and same sequence with the input vector. 

using quantile as method, and the cuts are some float numbers betweent 0-1. 
using manual as method, and the cuts are some user defined cuts. 

NOTE that the cut list has to include the minimum and maximum values.
End of explanation
"""


a_list = grid_res['usercount'].tolist()

"""
Explanation: breaking a list instead of a column of a dataframe
Let say you have a list, instead of a dataframe/geodataframe.
End of explanation
"""


level_list, cuts = bk.get_levels(a_list, method='head_tail_break', N=5)

print cuts

len(level_list)==len(a_list)

"""
Explanation: And you want to get the break levels, another function is also provided (the function that is called by tm.leveling_vector).
End of explanation
"""


