Generates a plotly Layout

	Parameters:
	-----------
		theme : string
			Layout Theme
				solar
				pearl
				white
		title : string
			Chart Title
		xTitle : string
			X Axis Title
		yTitle : string
			Y Axis Title
		zTitle : string
			Z Axis Title
			Applicable only for 3d charts
		barmode : string
			Mode when displaying bars
				group
				stack
				overlay
		bargap : float
			Sets the gap between bars
				[0,1)
			Applicabe for bar and histogram plots
		bargroupgap : float
			Set the gap between groups
				[0,1)
			Applicabe for bar and histogram plots
		gridcolor : string
				grid color
		zerolinecolor : string
				zero line color
		margin : dict or tuple
				Dictionary (l,r,b,t) or
				Tuple containing the left,
				right, bottom and top margins
		dimensions : tuple
			Dimensions of figure
		annotations : dict or list
			Dictionary of annotations
				{x_point : text}
			or
			List of Plotly Annotations
		is3d : bool
			Indicates if the layout is for a 3D chart

		Other Kwargs
		============

		Shapes
			hline : int, list or dict
				Draws a horizontal line at the
				indicated y position(s)
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			vline : int, list or dict
				Draws a vertical line at the
				indicated x position(s)
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			hspan : (y0,y1)
				Draws a horizontal rectangle at the
				indicated (y0,y1) positions.
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			vspan : (x0,x1)
				Draws a vertical rectangle at the
				indicated (x0,x1) positions.
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			shapes : dict or list(dict)
				List of dictionaries with the
				specifications of a given shape.
				See help(cufflinks.tools.get_shape)
				for more information

		Axis Ranges
			xrange : [lower_bound,upper_bound]
				Sets the range for the x axis
			yrange : [lower_bound,upper_bound]
				Sets the range for the y axis
			zrange : [lower_bound,upper_bound]
				Sets the range for the z axis

		Explicit Layout Updates
			layout_update : dict
				The layout will be modified with all
				the explicit values stated in the
				dictionary

		Range Selector
			rangeselector : dict
				Defines a rangeselector object
				see help(cf.tools.get_range_selector) for more information
				Example:
					{'steps':['1y','2 months','5 weeks','ytd','2mtd'],
					 'axis':'xaxis', 'bgcolor' : ('blue',.3),
					 'x': 0.2 , 'y' : 0.9}

		Range Slider
			rangeslider : bool or dict
				Defines if a rangeslider is displayed
				If bool:
					True : Makes it visible
				if dict:
					Rangeslider object
				Example:
					{'bgcolor':('blue',.3),'autorange':True}

		Annotations
			fontcolor : str
				Text color for annotations
			fontsize : int
				Text size for annotations
			textangle : int
				Textt angle
			See https://plot.ly/python/reference/#layout-annotations
			for a complete list of valid parameters.

def getLayout(kind=None,theme=None,title='',xTitle='',yTitle='',zTitle='',barmode='',bargap=None,bargroupgap=None,
			  margin=None, dimensions=None, width=None, height=None,
			  annotations=None,is3d=False,**kwargs):
	"""
	Generates a plotly Layout

	Parameters:
	-----------
		theme : string
			Layout Theme
				solar
				pearl
				white
		title : string
			Chart Title
		xTitle : string
			X Axis Title
		yTitle : string
			Y Axis Title
		zTitle : string
			Z Axis Title
			Applicable only for 3d charts
		barmode : string
			Mode when displaying bars
				group
				stack
				overlay
		bargap : float
			Sets the gap between bars
				[0,1)
			Applicabe for bar and histogram plots
		bargroupgap : float
			Set the gap between groups
				[0,1)
			Applicabe for bar and histogram plots
		gridcolor : string
				grid color
		zerolinecolor : string
				zero line color
		margin : dict or tuple
				Dictionary (l,r,b,t) or
				Tuple containing the left,
				right, bottom and top margins
		dimensions : tuple
			Dimensions of figure
		annotations : dict or list
			Dictionary of annotations
				{x_point : text}
			or
			List of Plotly Annotations
		is3d : bool
			Indicates if the layout is for a 3D chart

		Other Kwargs
		============

		Shapes
			hline : int, list or dict
				Draws a horizontal line at the
				indicated y position(s)
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			vline : int, list or dict
				Draws a vertical line at the
				indicated x position(s)
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			hspan : (y0,y1)
				Draws a horizontal rectangle at the
				indicated (y0,y1) positions.
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			vspan : (x0,x1)
				Draws a vertical rectangle at the
				indicated (x0,x1) positions.
				Extra parameters can be passed in
				the form of a dictionary (see shapes)
			shapes : dict or list(dict)
				List of dictionaries with the
				specifications of a given shape.
				See help(cufflinks.tools.get_shape)
				for more information

		Axis Ranges
			xrange : [lower_bound,upper_bound]
				Sets the range for the x axis
			yrange : [lower_bound,upper_bound]
				Sets the range for the y axis
			zrange : [lower_bound,upper_bound]
				Sets the range for the z axis

		Explicit Layout Updates
			layout_update : dict
				The layout will be modified with all
				the explicit values stated in the
				dictionary

		Range Selector
			rangeselector : dict
				Defines a rangeselector object
				see help(cf.tools.get_range_selector) for more information
				Example:
					{'steps':['1y','2 months','5 weeks','ytd','2mtd'],
					 'axis':'xaxis', 'bgcolor' : ('blue',.3),
					 'x': 0.2 , 'y' : 0.9}

		Range Slider
			rangeslider : bool or dict
				Defines if a rangeslider is displayed
				If bool:
					True : Makes it visible
				if dict:
					Rangeslider object
				Example:
					{'bgcolor':('blue',.3),'autorange':True}

		Annotations
			fontcolor : str
				Text color for annotations
			fontsize : int
				Text size for annotations
			textangle : int
				Textt angle
			See https://plot.ly/python/reference/#layout-annotations
			for a complete list of valid parameters.
	"""


	for key in list(kwargs.keys()):
		if key not in __LAYOUT_KWARGS:
			raise Exception("Invalid keyword : '{0}'".format(key))

	if not theme:
		theme = auth.get_config_file()['theme']

	theme_data = getTheme(theme)
	layout=theme_data['layout']
	layout['xaxis'].update({'title':xTitle})
	layout['yaxis'].update({'title':yTitle})

	fontfamily=kwargs.pop('fontfamily',None)
	if fontfamily:
		deep_update(layout,{'font':{'family':fontfamily}})


	if barmode:
		layout.update({'barmode':barmode})
	if bargroupgap:
		layout.update({'bargroupgap':bargroupgap})
	if bargap:
		layout.update(bargap=bargap)
	if title:
		layout.update({'title':title})
	if annotations:
		layout.update({'annotations':annotations})


	def update_axis(layout,axis='xy',**vals):
		for _x in axis:
			for k,v in list(vals.items()):
				if v==None:
					vals.pop(k)
			for k in layout:
				if '{0}{1}'.format(_x,'axis') in k:
					layout[k].update(**vals)
		return layout

	axis_kwargs=check_kwargs(kwargs,__LAYOUT_AXIS,{},True)
	xaxis_kwargs=kwargs_from_keyword(kwargs,{},'xaxis',True)
	yaxis_kwargs=kwargs_from_keyword(kwargs,{},'yaxis',True)

	for _x,_vals in (('xy',axis_kwargs),('x',xaxis_kwargs),('y',yaxis_kwargs)):
		layout=update_axis(layout,_x,**_vals)

	if margin:
		if isinstance(margin,dict):
			margin=margin
		else:
			margin=dict(list(zip(('l','r','b','t'),margin)))
		layout.update(margin=margin)

	if dimensions:
		layout.update(width=dimensions[0])
		layout.update(height=dimensions[1])

	if height:
		layout.update(height=height)
	if width:
		layout.update(width=width)
	if is3d:
		if '3d' in theme_data:
			layout=deep_update(layout,theme_data['3d'])
		zaxis=layout['xaxis'].copy()
		zaxis.update(title=zTitle)
		scene=dict(xaxis=layout['xaxis'].copy(),yaxis=layout['yaxis'].copy(),zaxis=zaxis)
		layout.update(scene=scene)
		del layout['xaxis']
		del layout['yaxis']

	## Axis Range
	for r in ['x','y','z']:
		if '{0}range'.format(r) in kwargs:
			if is3d:
				layout['scene']['{0}axis'.format(r)].update(range=kwargs['{0}range'.format(r)])
			else:
				layout['{0}axis'.format(r)].update(range=kwargs['{0}range'.format(r)])

	# Need to update this for an add_axis approach.
	if kind in ('candlestick','ohlc','candle'):
		layout['yaxis2']=layout['yaxis'].copy()
		layout['yaxis'].update(showticklabels=False)

	## Kwargs

	if 'legend' in kwargs:
		if type(kwargs['legend'])==bool:
			layout['showlegend']=kwargs['legend']
		elif type(kwargs['legend'])==str:
			if kwargs['legend']=='top':
				layout['legend'].update(orientation='h',yanchor='bottom',x=.3,y=.95)
			elif kwargs['legend']=='bottom':
				layout['legend'].update(orientation='h',yanchor='bottom',x=.3,y=-0.5)
			layout['showlegend']=True
		else:
			layout['legend']=kwargs['legend']
			layout['showlegend']=True

	if 'showlegend' in kwargs:
		layout['showlegend']=kwargs['showlegend']

	# Logarithmic Axis
	for _ in ['x','y','z']:
		if 'log{0}'.format(_) in kwargs:
			if is3d:
				if kwargs['log{0}'.format(_)]:
					layout['scene']['{0}axis'.format(_)]['type']='log'
			else:
				if kwargs['log{0}'.format(_)]:
					layout['{0}axis'.format(_)]['type']='log'

	# Shapes

	if any(k in kwargs for k in ['vline','hline','shapes','hspan','vspan']):
		shapes=[]

		def get_shapes(xline):

			orientation=xline[0]
			xline=kwargs[xline]
			if isinstance(xline,list):
				for x_i in xline:
					if isinstance(x_i,dict):
						x_i['kind']='line'
						shapes.append(get_shape(**x_i))
					else:
						if orientation=='h':
							shapes.append(get_shape(kind='line',y=x_i))
						else:
							shapes.append(get_shape(kind='line',x=x_i))
			elif isinstance(xline,dict):
				shapes.append(get_shape(**xline))
			else:
				if orientation=='h':
					shapes.append(get_shape(kind='line',y=xline))
				else:
					shapes.append(get_shape(kind='line',x=xline))

		def get_span(xspan):
			orientation=xspan[0]
			xspan=kwargs[xspan]
			if isinstance(xspan,list):
				for x_i in xspan:
					if isinstance(x_i,dict):
						x_i['kind']='rect'
						shapes.append(get_shape(**x_i))
					else:
						v0,v1=x_i
						if orientation=='h':
							shapes.append(get_shape(kind='rect',y0=v0,y1=v1,fill=True,opacity=.5))
						else:
							shapes.append(get_shape(kind='rect',x0=v0,x1=v1,fill=True,opacity=.5))
			elif isinstance(xspan,dict):
				xspan['kind']='rect'
				shapes.append(get_shape(**xspan))
			elif isinstance(xspan,tuple):
				v0,v1=xspan
				if orientation=='h':
					shapes.append(get_shape(kind='rect',y0=v0,y1=v1,fill=True,opacity=.5))
				else:
					shapes.append(get_shape(kind='rect',x0=v0,x1=v1,fill=True,opacity=.5))
			else:
				raise Exception('Invalid value for {0}span: {1}'.format(orientation,xspan))

		if 'hline' in kwargs:
			get_shapes('hline')
		if 'vline' in kwargs:
			get_shapes('vline')
		if 'hspan' in kwargs:
			get_span('hspan')
		if 'vspan' in kwargs:
			get_span('vspan')
		if 'shapes' in kwargs:
			shapes_=kwargs['shapes']
			if isinstance(shapes_,list):
				for i in shapes_:
					shp=i if 'type' in i else get_shape(**i)
					shapes.append(shp)
			elif isinstance(shapes_,dict):
					shp=shapes_ if 'type' in shapes_ else get_shape(**shapes_)
					shapes.append(shp)
			else:
				raise Exception("Shapes need to be either a dict or list of dicts")


		layout['shapes']=shapes

	# Maps
	if kind in ('choropleth','scattergeo'):
		kw=check_kwargs(kwargs,__GEO_KWARGS)
		defaults={'projection':{'type':'equirectangular'},'showframe':False,'showcoastlines':False}
		for k,v in list(defaults.items()):
			if k not in kw:
				kw[k]=v
		kw_=kwargs_from_keyword(kw,{},'projection')
		deep_update(kw,kw_)
		layout['geo']=kw
		del layout['xaxis']
		del layout['yaxis']
		if not margin:
			layout['margin']={'autoexpand':True}

	# Range Selector
	if 'rangeselector' in kwargs:
		rs=kwargs['rangeselector']
		if 'axis' in rs:
			axis=rs['axis']
			del rs['axis']
		else:
			axis='xaxis'
		layout[axis]['rangeselector']=get_range_selector(**rs)

	# Range Slider
	if 'rangeslider' in kwargs:
		if type(kwargs['rangeslider'])==bool:
			if kwargs['rangeslider']:
				layout['xaxis']['rangeslider']=dict(visible=kwargs['rangeslider'])
			else:
				layout['xaxis']['rangeslider']=dict(visible=False)
				# layout['yaxis1'].update(domain=(0,0))
		else:
			layout['xaxis']['rangeslider']=kwargs['rangeslider']
	else:
		if kind in ('ohlc','candle','candlestick'):
			layout['xaxis']['rangeslider']=dict(visible=False)
			# layout['yaxis1'].update(domain=(0,0))



	# Explicit Updates

	if 'layout_update' in kwargs:
		layout=deep_update(layout,kwargs['layout_update'])

	return layout