'''
Path:                   magpy.mpplot
Part of package:        stream (plot)
Type:                   Library of matplotlib plotting functions

PURPOSE:
        This script provides multiple functions for plotting a stream as well
        as analysing various properties of a stream.
        All plots are done with python's matplotlib package.

CONTAINS:
    (MAIN...)
        plot:           (Func) Will plot variables from a single stream.
        plotStreams:    (Func) Plots multiple variables from multiple streams.
        ploteasy:       (Func) Quick & easy plotting function that plots all data.
    (EXTENDED...)
        plotFlag:       (Func) Enables flagging in plot
        plotEMD:        (Func) Plots Empirical Mode Decomposition from magpy.opt.emd
        plotNormStreams:(Func) Plot normalised streams
        plotPS:         (Func) Plots the power spectrum of a given key.
        plotSatMag:     (Func) Useful tool for plotting magnetic and satellite data.
        plotSpectrogram:(Func) Plots spectrogram of a given key.
        plotStereoplot: (Func) Plots stereoplot of inc and dec values.
        obspySpectrogram:(Func) Spectrogram plotting function taken from ObsPy.
    (HELPER/INTERNAL FUNCTIONS...)
        _plot:          (Func) ... internal function to funnel plot information
                        into a matplotlib plot object.
        _confinex:      (Func) ... utility function of _plot.
        maskNAN:        (Func) ... utility function of _plot.
        nan_helper:     (Func) ... utility function of _plot.
        denormalize:    (Func) ... utility function of _plot.

DEPENDENCIES:
        magpy.stream
        magpy.opt.emd
        matplotlib

CALLED BY:
        External data plotting and analysis scripts only.
'''
from __future__ import print_function
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import division

from magpy.stream import *

import logging
logger = logging.getLogger(__name__)
'''
try:
    import matplotlib
    if not os.isatty(sys.stdout.fileno()):   # checks if stdout is connected to a terminal (if not, cron is starting the job)
        print "No terminal connected - assuming cron job and using Agg for matplotlib"
        matplotlib.use('Agg') # For using cron
except:
    print "Prob with matplotlib"

try:
    version = matplotlib.__version__.replace('svn', '')
    try:
        version = map(int, version.replace("rc","").split("."))
        MATPLOTLIB_VERSION = version
    except:
        version = version.strip("rc")
        MATPLOTLIB_VERSION = version
    print "Loaded Matplotlib - Version %s" % str(MATPLOTLIB_VERSION)
    import matplotlib.pyplot as plt
    from matplotlib.colors import Normalize
    from matplotlib import mlab
    from matplotlib.dates import date2num, num2date
    import matplotlib.cm as cm
    from pylab import *
    from datetime import datetime, timedelta
except ImportError as e:
    logger.error("plot package: Matplotlib import error. If missing, please install to proceed.")
    logger.error("Error string: %s" % e)
    raise Exception("CRITICAL IMPORT ERROR FOR PLOT PACKAGE: Matplotlib import error.")
'''

# TODO:
# - Move all plotting functions over from stream.
#       STILL TO FIX:
#       spectrogram()
#               TODO: ORIGINAL FUNCTION HAS ERRORS.
#               renamed to plotSpectrogram
#               changed variable title to plottitle
#       obspyspectrogram()
#               renamed to obspySpectrogram.
#       DONE:
#       plot() + plotStreams()
#       powerspectrum()
#               renamed to plotPS
#               changed variable title to plottitle
#       stereoplot()
#               renamed to plotStereoplot
#
# KNOWN BUGS:
# - None? :)

colorlist =  ['b','g','m','c','y','k','b','g','m','c','y','k']
symbollist = ['-','-','-','-','-','-','-','-','-','-','-','-']
gridcolor = '#316931'
labelcolor = '0.2'

try:
    defaultcolormap=plt.get_cmap('plasma')
except:
    defaultcolormap=cm.Accent


def ploteasy(stream):
    '''
    DEFINITION:
        Plots all data in stream. That's it.
        This function has no formatting options whatsoever.
        Very useful for quick & easy data evaluation.

    PARAMETERS:
    Variables:
        - stream:       (DataStream object) Stream to plot

    RETURNS:
        - plot:         (Pyplot plot) Returns plot as plt.show()

    EXAMPLE:
        >>> ploteasy(somedata)
    '''

    keys = stream._get_key_headers(numerical=True)
    if len(keys) > 9:
        keys = keys[:8]
    try:
        sensorid = stream.header['SensorID']
    except:
        sensorid = ''
    try:
        datadate = datetime.strftime(num2date(stream[0].time),'%Y-%m-%d')
    except:
        datadate = datetime.strftime(num2date(stream.ndarray[0][0]),'%Y-%m-%d')

    plottitle = "%s (%s)" % (sensorid,datadate)
    logger.info("Plotting keys:", keys)
    plot_new(stream, keys,
                confinex = True,
                plottitle = plottitle)

#####################################################################
#                                                                   #
#       MAIN PLOTTING FUNCTIONS                                     #
#       (for plotting geomagnetic data)                             #
#                                                                   #
#####################################################################

def plot_new(stream,variables=[],specialdict={},errorbars=False,padding=0,noshow=False,
        annotate=False,stormphases=False,colorlist=colorlist,symbollist=symbollist,
        t_stormphases=None,includeid=False,function=None,plottype='discontinuous',resolution=None,
        **kwargs):

        plot(stream,variables=variables,specialdict=specialdict,errorbars=errorbars,padding=padding,
                        noshow=noshow,annotate=annotate,stormphases=stormphases,colorlist=colorlist,
                        symbollist=symbollist,t_stormphases=t_stormphases,includeid=includeid,
                        function=function,plottype=plottype,flagontop=flagontop,resolution=resolution, **kwargs)


def plot(stream,variables=[],specialdict={},errorbars=False,padding=0,noshow=False,
        annotate=False,stormphases=False,colorlist=colorlist,symbollist=symbollist,
        t_stormphases=None,includeid=False,flagontop=False,function=None,plottype='discontinuous',resolution=None,
        **kwargs):
    '''
    DEFINITION:
        This function creates a graph from a single stream.

    PARAMETERS:
    Variables:
        - stream:       (DataStream object) Stream to plot
        - variables:    (list) List of variables to plot.
    Kwargs:
        - annotate:     (bool/list=False) If True, will annotate plot.
        - bartrange:    (float) Variable for plotting of bars.
        - bgcolor:      (color='white') Background colour of plot.
        - colorlist:    (list(colors)) List of colours to plot with.
                        Default = ['b','g','m','c','y','k','b','g','m','c','y','k']
        - confinex:     (bool=False) x-axis will be confined to smaller t-values if True.
        - errorbars:    (bool/list=False) If True, will plot corresponding errorbars:
                        [ [False], [True], [False, False] ]
        - fill:         (list = []) List of keys for which the plot uses fill_between
        - fmt:          (str) Format of outfile.
        - fullday:      (bool=False) Will plot fullday if True.
        - function:     (func) [0] is a dictionary containing keys (e.g. fx),
                        [1] the startvalue, [2] the endvalue
                        Plot the content of function within the plot.
                        CAUTION: Not yet implemented.
        - grid:         (bool=True) If True, will plot grid.
        - gridcolor:    (color='#316931') Colour of grid.
        - includeid:    (bool) If True, sensor IDs will be extracted from header data and
                        plotted alongside corresponding data. Default=False
        - labelcolor:   (color='0.2') Colour of labels.
        - outfile:      (str) Path of file to plot figure to.
        - padding:      (float/list) Float or list of padding for each variable.
        - plottitle:    (str) Title to put at top of plot.
        - plottype:     (NumPy str='discontinuous') Can also be 'continuous'.
        - savedpi:      (float=80) Determines dpi of outfile.
        - specialdict:  (dictionary) contains special information for specific plots.
                        key corresponds to the column
                        input is a list with the following parameters
                        {'x':[ymin,ymax]}
        - stormphases:  (bool/list) If True, will plot shaded and annotated storm phases.
                        NOTE: Also requires variable t_stormphases.
        - symbollist:   (list) List of symbols to plot with. Default= '-' for all.
        - t_stormphases:(dict) Dictionary (2 <= len(dict) <= 4) containing datetime objects.
                        dict('ssc') = time of SSC
                        dict('mphase') = time of start of main phase / end of SSC
                        dict('rphase') = time of start of recovery phase / end of main phase
                        dict('stormend') = end of recovery phase

    RETURNS:
        - plot:         (Pyplot plot) Returns plot as plt.show or savedfile
                        if outfile is specified.

    EXAMPLE:
        >>>

    APPLICATION:

    '''

    # Test whether columns really contain data or whether only nans are present:
    stream = stream._remove_nancolumns()
    availablekeys = stream._get_key_headers(numerical=True)
    logger = logging.getLogger(__name__+".plot")

    # if no variables are given, use all available:
    if len(variables) < 1:
        variables = availablekeys
    else:
        variables = [var for var in variables if var in availablekeys]
    if len(variables) > 9:
        logger.info("More than 9 variables available - plotting only the first nine!")
        logger.info("Available: "+ str(variables))
        variables = variables[:9]
        logger.info("Plotting: "+ str(variables))
    else:
        logger.info("Plotting: "+ str(variables))

    # Check lists for variables have correct length:
    num_of_var = len(variables)
    if num_of_var > 9:
        logger.error("Can't plot more than 9 variables, sorry.")
        raise Exception("Can't plot more than 9 variables!")

    if len(symbollist) < num_of_var:
        logger.error("Length of symbol list does not match number of variables.")
        raise Exception("Length of symbol list does not match number of variables.")
    if len(colorlist) < num_of_var:
        logger.error("Length of color list does not match number of variables.")
        raise Exception("Length of color list does not match number of variables.")

    plot_dict = []
    count = 0

    # The follow four variables can be given in two ways:
    #    bool: annotate = True --> all plots will be annotated
    #    list: annotate = [False, True, False] --> only second plot will be annotated
    # These corrections allow for simple variable definition during use.
    if type(errorbars) == list:
        errorbars = [errorbars]
    else:
        errorbars = errorbars

    if type(stormphases) == list:
        stormphases = [stormphases]
    else:
        stormphases = stormphases

    if type(annotate) == list:
        annotate = [annotate]
    else:
        annotate = annotate

    if type(padding) == list:
        padding = [padding]
    else:
        padding = padding

    plotStreams([stream], [ variables ], specialdict=[specialdict],noshow=noshow,
        errorbars=errorbars,padding=padding,annotate=annotate,flagontop=flagontop,stormphases=stormphases,
        colorlist=colorlist,symbollist=symbollist,t_stormphases=t_stormphases,
        includeid=includeid,function=function,plottype=plottype,resolution=resolution,**kwargs)


def plotStreams(streamlist,variables,padding=None,specialdict={},errorbars=None,
        colorlist=colorlist,symbollist=symbollist,annotate=None,stormphases=None,
        t_stormphases={},includeid=False,function=None,plottype='discontinuous',
        noshow=False,labels=False,flagontop=False,resolution=None,debug=False,**kwargs):
    '''
    DEFINITION:
        This function plots multiple streams in one plot for easy comparison.

    PARAMETERS:
    Variables:
        - streamlist:   (list) A list containing the streams to be plotted
                        in a list, e.g.:
                        [ stream1, stream2, etc...]
                        [ fge, pos1, env1 ]
        - variables:    (list(list)) List containing the variables to be plotted
                        from each stream, e.g:
                        [ ['x'], ['f'], ['t1', 't2'] ]
    Args:
        LISTED VARIABLES:
        (NOTE: All listed variables must correspond in size to the variable list.)
        - annotate:     (bool/list(bool)) If True, will annotate plot with flags, e.g.:
                        [ [True], [True], [False, False] ]
                        (Enter annotate = True for all plots to be annotated.)
        - errorbars:    (bool/list(bool)) If True, will plot corresponding errorbars:
                        [ [False], [True], [False, False] ]
                        (Enter errorbars = True to plot error bars on all plots.)
        - labels:       [ (str) ] List of labels for each stream and variable, e.g.:
                        [ ['FGE'], ['POS-1'], ['ENV-T1', 'ENV-T2'] ]
        - padding:      (float/list(list)) List of lists containing paddings for each
                        respective variable, e.g:
                        [ [5], [5], [0.1, 0.2] ]
                        (Enter padding = 5 for all plots to use 5 as padding.)
        - stormphases:  (bool/list(bool)) If True, will plot shaded and annotated storm phases.
                        (Enter stormphases = True to plot storm on all plots.)
                        NOTE: Also requires variable t_stormphases.
        - specialdict:  (list(dict)) Same as plot variable, e.g:
                        [ {'z': [100,150]}, {}, {'t1':[7,8]} ]
        NORMAL VARIABLES:
        - bartrange:    (float) Variable for plotting of bars.
        - bgcolor:      (color='white') Background colour of plot.
        - colorlist:    (list(colors)) List of colours to plot with.
                        Default = ['b','g','m','c','y','k','b','g','m','c','y','k']
        - confinex:     (bool=False) x-axis will be confined to smaller t-values if True.
        - fmt:          (str) Format of outfile.
        - fullday:      (bool=False) Will plot fullday if True.
        - function:     (func) [0] is a dictionary containing keys (e.g. fx),
                        [1] the startvalue, [2] the endvalue
                        Plot the content of function within the plot.
                        CAUTION: Not yet implemented.
        - grid:         (bool=True) If True, will plot grid.
        - gridcolor:    (color='#316931') Colour of grid.
        - includeid:    (bool) If True, sensor IDs will be extracted from header data and
                        plotted alongside corresponding data. Default=False
        - labelcolor:   (color='0.2') Colour of labels.
        - flagontop:    (True/False) define whether flags are shown above or below data.
        - opacity:      (0.0 to 1.0) Opacity applied to fills and bars.
        - legendposition: (str) Position of legend (when var labels is used), e.g. 'upper left'
        - noshow:       (bool) If True, figure object will be returned. Default=False
        - outfile:      (str) Path of file to plot figure to.
        - plottitle:    (str) Title to put at top of plot.
        - plottype:     (NumPy str='discontinuous') Can also be 'continuous'.
        - savedpi:      (float=80) Determines dpi of outfile.
        - symbollist:   (list) List of symbols to plot with. Default= '-' for all.
        - resolution:   (int) Resolution of plot. Amount of points are reduced to this value.
        - t_stormphases:(dict) Dictionary (2 <= len(dict) <= 4) containing datetime objects.
                        dict('ssc') = time of SSC
                        dict('mphase') = time of start of main phase / end of SSC
                        dict('rphase') = time of start of recovery phase / end of main phase
                        dict('stormend') = end of recovery phase
                        WARNING: If recovery phase is defined as past the end of
                        the data to plot, it will be plotted in addition to the
                        actual data.

    RETURNS:
        - plot:         (Pyplot plot) Returns plot as plt.show or saved file
                        if outfile is specified.

    EXAMPLE:
        >>> plotStreams(streamlist, variables, padding=5, outfile='plots.png')

    APPLICATION:
        fge_file = fge_id + '_' + date + '.cdf'
        pos_file = pos_id + '_' + date + '.bin'
        lemi025_file = lemi025_id + '_' + date + '.bin'
        cs_file = cs_id + '_' + date + '.bin'
        fge = read(fge_file)
        pos = read(pos_file)
        lemi025 = read(lemi025_file,tenHz=True)
        cs = read(cs_file)
        streamlist =    [ fge,                  cs,                     lemi025,        pos             ]
        variables =     [ ['x','y','z'],        ['f'],                  ['z'],          ['f']           ]
        specialdict =   [ {},                   {'f':[48413,48414]},    {},             {}              ]
        errorbars =     [ [False,False,False],  [False],                [False],        [True]          ]
        padding =       [ [1,1,1],              [1],                    [1] ,           [1]             ]
        annotate =      [ [False,False,False],  [True],                 [True] ,        [True]          ]

        # TO PLOT FOUR DIFFERENT STREAMS WITH 7 VARIABLES TO A FILE:
        plotStreams(streamlist, variables, padding=padding,specialdict=specialdict,
                annotate=annotate,includeid=True,errorbars=errorbars,
                outfile='plots/all_magn_cut1.png',
                plottitle="WIC: All Magnetometers (%s)" % date)

        # TO PLOT DATA AND RETURN A FIGURE FOR FURTHER:
        plot = plotStreams(streamlist, variables, noshow=True)
        plot.title("New title.")
        plot.savefig("newfig.png")
    '''

    logger = logging.getLogger(__name__+".plotStreams")

    # Preselect only numerical values
    variables = [[el for el in lst if el in NUMKEYLIST] for lst in variables]
    num_of_var = 0
    for item in variables:
        num_of_var += len(item)
    if num_of_var > 9:
        logger.error("Can't plot more than 9 variables, sorry.")
        raise Exception("Can't plot more than 9 variables!")

    # Check lists for variables have correct length:
    if len(symbollist) < num_of_var:
        logger.error("Length of symbol list does not match number of variables.")
        raise Exception("Length of symbol list does not match number of variables.")
    if len(colorlist) < num_of_var:
        logger.error("Length of color list does not match number of variables.")
        raise Exception("Length of color list does not match number of variables.")

    plot_dict = []
    count = 0

    if not resolution:
        resolution = 5000000  # 40 days of 1 second data can be maximaly shown in detail, 5 days of 10 Hz

    # Iterate through each variable, create dict for each:
    for i in range(len(streamlist)):
        stream = streamlist[i]
        ndtype = False
        try:
            t = stream.ndarray[KEYLIST.index('time')]
            lentime = len(t)
            if not lentime > 0:
                x=1/0
            if lentime > resolution:
                logger.info("Reducing data resultion ...")
                stepwidth = int(len(t)/resolution)
                t = t[::stepwidth]
                # Redetermine lentime
                lentime = len(t)
            logger.info("Start plotting of stream with length %i" % len(stream.ndarray[0]))
            ndtype = True
        except:
            t = np.asarray([row[0] for row in stream])
            logger.info("Start plotting of stream with length %i" % len(stream))
        orgt = np.asarray([ti for ti in t]) # orgt is needed if time column is modified (i.e. continuous plots)
        for j in range(len(variables[i])):
            data_dict = {}
            key = variables[i][j]
            logger.info("Determining plot properties for key %s." % key)
            if not key in NUMKEYLIST:
                logger.error("Column key (%s) not valid!" % key)
                raise Exception("Column key (%s) not valid!" % key)
            ind = KEYLIST.index(key)
            try:

                y = stream.ndarray[ind]
                if not len(y) > 0:
                    x=1/0
                if len(y) > resolution:
                    stepwidth = int(len(y)/resolution)
                    y = y[::stepwidth]
                if len(y) != lentime:
                    logger.error("Dimensions of time and %s do not match!" % key)
                    raise Exception("Dimensions of time and %s do not match!")
            except:
                y = np.asarray([float(row[ind]) for row in stream])

            if len(y) == 0:
                logger.error("Cannot plot stream of zero length!")

            # eventually remove flagged:
            dropflagged = False
            if dropflagged:
                flagind = KEYLIST.index('flag')
                flags = stream.ndarray[flagind]
                ind = KEYLIST.index(key)
                flagarray =  np.asarray([list(el)[ind] for el in flags])
                if debug:
                    print("Flagarray", flagarray)
                indicies = np.where(flagarray == '1')
                if debug:
                    print("Indicis", indicis)

            # Fix if NaNs are present:
            if plottype == 'discontinuous':
                y = maskNAN(y)
            else:
                nans, test = nan_helper(y)
                newt = [orgt[idx] for idx, el in enumerate(y) if not nans[idx]]
                t = newt
                y = [el for idx, el in enumerate(y) if not nans[idx]]

            if len(y) == 0:
                logger.error("Cannot plot stream without data - Filling with 9999999!")
                if len(stream.ndarray[0]) > 0:
                    y = np.asarray([9999999 for row in stream.ndarray[0]])
                else:
                    y = np.asarray([9999999 for row in stream])


            data_dict['key'] = key
            data_dict['tdata'] = t
            data_dict['ydata'] = y
            data_dict['color'] = colorlist[count]
            data_dict['symbol'] = symbollist[count]

            # Define padding for each variable:
            if padding:
                if type(padding) == list:
                    ypadding = padding[i][j]
                else:
                    ypadding = padding
            else:
                ypadding = (np.max(y)- np.min(y))*0.05 # 0

            # If limits are specified, use these:
            if specialdict:
                data_dict['ymin'] = np.min(y) - ypadding
                data_dict['ymax'] = np.max(y) + ypadding
                if key in specialdict[i]:
                    specialparams = specialdict[i][key]
                    try:
                        if DataStream()._is_number(specialparams[0]):
                            lowlev = specialparams[0]
                        else:
                            lowlev = np.min(y)
                    except:
                        lowlev = np.min(y)
                    try:
                        if DataStream()._is_number(specialparams[1]):
                            highlev = specialparams[1]
                        else:
                            highlev = np.max(y)
                    except:
                        highlev = np.max(y)
                    data_dict['ymin'] = lowlev - ypadding
                    data_dict['ymax'] = highlev + ypadding
                else:
                    if not (np.min(y) == np.max(y)):
                        data_dict['ymin'] = np.min(y) - ypadding
                        data_dict['ymax'] = np.max(y) + ypadding
                    else:
                        logger.warning('Min and max of key %s are equal. Adjusting axes.' % key)
                        data_dict['ymin'] = np.min(y) - 0.05
                        data_dict['ymax'] = np.max(y) + 0.05
            else:
                if not (np.min(y) == np.max(y)):
                    data_dict['ymin'] = np.min(y) - ypadding
                    data_dict['ymax'] = np.max(y) + ypadding
                else:
                    logger.warning('Min and max of key %s are equal. Adjusting axes.' % key)
                    data_dict['ymin'] = np.min(y) - 0.5
                    data_dict['ymax'] = np.max(y) + 0.5

            # Define y-labels:
            try:
                ylabel = stream.header['col-'+key].upper()
            except:
                ylabel = ''
                pass
            try:
                yunit = stream.header['unit-col-'+key]
            except:
                yunit = ''
                pass
            if not yunit == '':
                yunit = re.sub('[#$%&~_^\{}]', '', yunit)
                label = ylabel+' $['+yunit+']$'
            elif yunit == None:
                logger.warning("No units for key %s! Empty column?" % key)
                label = ylabel
            else:
                label = ylabel
            data_dict['ylabel'] = label

            # Create array for errorbars:
            if errorbars:
                if type(errorbars) == list:
                    try:
                        if errorbars[i][j] and not key.startswith('d'):
                            ind = KEYLIST.index('d'+key)
                            if ndtype:
                                errors = stream.ndarray[ind]
                            else:
                                errors = np.asarray([row[ind] for row in stream])
                            if len(errors) > 0:
                                data_dict['errors'] = errors
                            else:
                                logger.warning("No errors for key %s. Leaving empty." % key)
                    except:
                        logger.warning("No errors for key %s. Leaving empty." % key)
                else:
                    try:
                        ind = KEYLIST.index('d'+key)
                        if ndtype:
                            errors = stream.ndarray[ind]
                        else:
                            errors = np.asarray([row[ind] for row in stream])
                        if len(errors) > 0:
                            data_dict['errors'] = errors
                        else:
                            logger.warning("No errors for key %s. Leaving empty." % key)
                    except:
                        logger.warning("No errors for key %s. Leaving empty." % key)

            # Annotate flagged data points:
            if annotate:
                if type(annotate) == list:
                    if annotate[i][j]:
                        if ndtype:
                            ind = KEYLIST.index('flag')
                            flag = stream.ndarray[ind]
                            ind = KEYLIST.index('comment')
                            comments = stream.ndarray[ind]
                        else:
                            flag = stream._get_column('flag')
                            comments = stream._get_column('comment')
                        flags = array([flag,comments], dtype=object)
                        data_dict['annotate'] = True
                        data_dict['flags'] = flags
                    else:
                        data_dict['annotate'] = False
                else:
                    if ndtype:
                        ind = KEYLIST.index('flag')
                        flag = stream.ndarray[ind]
                        ind = KEYLIST.index('comment')
                        comments = stream.ndarray[ind]
                    else:
                        flag = stream._get_column('flag')
                        comments = stream._get_column('comment')
                    flags = array([flag,comments], dtype=object)
                    #print "plotStreams1", flags
                    data_dict['annotate'] = True
                    data_dict['flags'] = flags
            else:
                data_dict['annotate'] = False

            data_dict['flagontop'] = flagontop

            #print "plotStreams2", data_dict['flags']

            # Get an existing function object from header:
            funclist = stream.header.get('DataFunction',[])
            if len(funclist) > 0:
                data_dict['function'] = funclist

            # Plot a function (overwrite any existing object):
            if function:
                data_dict['function'] = function

            # Plot shaded storm phases:
            if stormphases:
                if not t_stormphases:
                    logger.error("No variable t_stormphases for plotting phases.")
                    raise Exception("Require variable t_stormphases when stormphases=True!")
                if len(t_stormphases) not in [1,2,3,4]:
                    logger.error("Length of variable t_stormphases incorrect.")
                    raise Exception("Something is wrong with length of variable t_stormphases!")
                if type(stormphases) == list:
                    if stormphases[i][j]:
                        data_dict['stormphases'] = t_stormphases
                else:
                    data_dict['stormphases'] = t_stormphases

            # Add labels:
            if labels:
                data_dict['datalabel'] = labels[i][j]
            else:
                data_dict['datalabel'] = ''

            # Include sensor IDs:
            if includeid:
                try:
                    sensor_id = stream.header['SensorID']
                    data_dict['sensorid'] = sensor_id
                except:
                    logger.warning("No sensor ID to put into plot!")

            plot_dict.append(data_dict)
            count += 1

    logger.info("Starting plotting function...")
    if not noshow:
        _plot(plot_dict, **kwargs)
        logger.info("Plotting completed.")
    else:
        fig = _plot(plot_dict, noshow=True, **kwargs)
        logger.info("Plotting completed.")
        return fig


#####################################################################
#                                                                   #
#       EXTENDED PLOTTING FUNCTIONS                                 #
#       (for more advanced functions)                               #
#                                                                   #
#####################################################################



#####################################################################
#               Flagging                                            #
#####################################################################

def toggle_selector(event):
    print (' Key pressed.')
    if event.key in ['Q', 'q'] and toggle_selector.RS.active:
        print(' RectangleSelector deactivated.')
        toggle_selector.RS.set_active(False)
    if event.key in ['A', 'a'] and not toggle_selector.RS.active:
        print(' RectangleSelector activated.')
        toggle_selector.RS.set_active(True)

class figFlagger():

    def __init__(self, data = None, variables=None, figure=False):

        self.data = data

        self.offset = False
        self.mainnum = 1
        self.flagid = 3
        self.reason = 'why because'
        self.idxarray = []
        self.figure = False
        self.axes = False

        self.orgkeylist = self.data._get_key_headers()
        if not variables: #or variables == ['x','y','z','f'] or variables == ['x','y','z']:
            try:
                self.data = self.analyzeData(self.orgkeylist)
            except:
                logger.warning("figFlagger.__init__: You have to provide variables for this data set")


        keylist = self.data._get_key_headers(numerical=True)
        #print keylist
        if variables:
            keylist = variables
        if len(keylist) > 9:
            keylist = keylist[:8]
        #print keylist
        self.keylist = keylist
        annotatelist = [True if elem in self.orgkeylist else False for elem in keylist] # if elem in ['x','y','z'] else False]

        self.fig = plotStreams([self.data], [keylist], noshow=True, annotate=[annotatelist])

        radio, hzfunc = self.startup(self.fig, self.data)
        radio.on_clicked(hzfunc)

        if figure:
            self.figure = self.fig
            self.axes = self.fig.axes
        else:
            plt.show()

    def analyzeData(self,keylist):
        #keylist = self.data._get_key_headers()
        if not len(self.data.ndarray[0]) > 0:
            logger.warning("figFlagger.analyzeData: No ndarray found:")
            logger.warning("figFlagger.analyzeData: stream will be converted to ndarray type")
            self.data = self.data.linestruct2ndarray()

        if 'x' in keylist and 'y' in keylist and 'z' in keylist:
            self.data = self.data.differentiate(keys=['x','y','z'],put2keys = ['dx','dy','dz'])
            if 'f' in keylist:
                self.data = self.data.delta_f()
        return self.data

    def line_select_callback(self, eclick, erelease):
        'eclick and erelease are the press and release events'
        #print  "Selected line---:",self.mainnum
        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata
        #print "(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)
        #print " The button you used were: %s %s" % (eclick.button, erelease.button)
        self.selarray = [x1, y1, x2, y2]
        self.annotate(self.data, self.mainnum, self.selarray)

    def toggle_selector(self, event):
        #print (' Key pressed.')
        if event.key in ['Q', 'q'] and toggle_selector.RS.active:
            print(' RectangleSelector deactivated.')
            toggle_selector.RS.set_active(False)
        if event.key in ['A', 'a'] and not toggle_selector.RS.active:
            print(' RectangleSelector activated.')
            toggle_selector.RS.set_active(True)
        if event.key in ['F', 'f']:
            print(' Flag data:')
            print(' ------------------------------------------')
            print(" Selected data point:", len(self.idxarray))
            plt.clf()
            plt.close()
        if event.key in ['2']:
            print(' Setting default Flag ID to 2.')
            print(' ------------------------------------------')
            print(" -- keep data in any case - Observators decision")
            self.flagid = 2
        if event.key in ['3']:
            print(' Setting default Flag ID to 3.')
            print(' ------------------------------------------')
            print(" -- not to be used for definite - Observators decision")
            self.flagid = 3
        if event.key in ['L', 'l']:
            print(' Data:')
            print(' ------------------------------------------')
            print("Length:", data.length())
            #stream.write("")
        if event.key in ['O', 'o']:
            print(' Apply offset:')
            print(' ------------------------------------------')
            print(" Selected data point:", len(self.idxarray))
            self.offset = True
            plt.clf()
            plt.close()
        if event.key in ['H', 'h']:
            print(' Header:')
            print(' ------------------------------------------')
            print(data.header)
        if event.key in ['c', 'C']:
            print(' Close flagging and store data:')
            print(' ------------------------------------------')
            self.idxarray = 0
            plt.clf()
            plt.close('all')


    def annotate(self, data, numb, selarray):
        # Selecting the time range

        #print "Dataarray", data.ndarray

        selectedndarray = []
        keyar = []
        #print "Selected range:", selarray
        minbound = min([selarray[1],selarray[3]])
        maxbound = max([selarray[1],selarray[3]])
        idxstart = np.abs(data.ndarray[0].astype(float)-min(selarray[0],selarray[2])).argmin()
        idxend = np.abs(data.ndarray[0].astype(float)-max(selarray[0],selarray[2])).argmin()

        for i in range(len(data.ndarray)):
            if len(data.ndarray[i]) > idxstart: # and KEYLIST[i] in self.keylist:
                if KEYLIST[i] in self.keylist or KEYLIST[i] == 'time': #i in range(len(FLAGKEYLIST)) and
                    keyar.append(KEYLIST[i])
                    timear = data.ndarray[i][idxstart:idxend].astype(float)
                    selectedndarray.append(timear)
        selectedndarray = np.asarray(selectedndarray)

        newselectedndarray = []
        for i in range(len(selectedndarray)):
            allar = [elem for idx, elem in enumerate(selectedndarray[i]) if selectedndarray[numb][idx] >= minbound and selectedndarray[numb][idx] <= maxbound ]
            if i == 0:
                self.idxar = [idx+idxstart for idx, elem in enumerate(selectedndarray[i]) if selectedndarray[numb][idx] >= minbound and selectedndarray[numb][idx] <= maxbound ]
            newselectedndarray.append(allar)
        newselectedndarray = np.asarray(newselectedndarray).astype(float)
        self.idxar = np.asarray(self.idxar)
        # Some cleanup
        del selectedndarray

        self.markpoints(newselectedndarray,keyar)
        self.idxarray.extend(self.idxar)
        print("Selected %d points to annotate:" % len(self.idxarray))

    def markpoints(self, dataarray,keyarray):
        for idx,elem in enumerate(dataarray):
            key = keyarray[idx]
            #print "Selected curve - markpoints:", idx
            #print dataarray[idx]
            if not idx == 0 and not len(elem) == 0 and key in self.keylist: #FLAGKEYLIST:
                #print ( idx, self.axlist[idx-1] )
                ax = self.axlist[idx-1]
                #ax.clear()
                #ax.text(dataarray[0][1],dataarray[1][1], "(%s, %3.2f)"%("Hello",3.67), )
                ax.scatter(dataarray[0].astype('<f8'),elem.astype('<f8'), c='r', zorder=100) #, marker='d', c='r') #, zorder=100)

        #plt.connect('key_press_event', toggle_selector)
        plt.draw()

    def hzfunc(self,label):
        ax = self.hzdict[label]
        num = int(label.replace("plot ",""))
        #print "Selected axis number:", num
        #global mainnum
        self.mainnum = num
        # drawtype is 'box' or 'line' or 'none'
        toggle_selector.RS = RectangleSelector(ax, self.line_select_callback,
                                           drawtype='box', useblit=True,
                                           button=[1,3], # don't use middle button
                                           minspanx=5, minspany=5,
                                           spancoords='pixels',
                                           rectprops = dict(facecolor='red', edgecolor = 'black', alpha=0.2, fill=True))

        #plt.connect('key_press_event', toggle_selector)
        plt.draw()

    def flag(self, idxarray , flagid, reason, keylist):

        print("Flagging components %s with flagid %d, because of %s" % (','.join(keylist), flagid, reason))
        self.data = self.data.flagfast(idxarray, flagid, reason, keylist)


    def startup(self, fig, data):
        print("--------------------------------------------")
        print(" you started the build-in flagging function")
        print("--------------------------------------------")
        print("    -- use mouse to select rectangular areas")
        print("    -- press f for flagging this region")
        print("    --      press 2,3 to change default flag ID")
        print("    -- press l to get some basic data info")
        print("    -- press o to apply an offset")
        print("    -- press h to get all meta information")
        print("    -- press c to close the window and allow saving")

        # Arrays to exchange data
        self.selarray = []
        # Globals
        self.idxar = [] # holds all selected index values
        #mainnum = 1 # holds the selected figure axis

        self.axlist = fig.axes

        # #############################################################
        ## Adding Radiobttons to switch selector between different plot
        # #############################################################

        plt.subplots_adjust(left=0.2)
        axcolor = 'lightgoldenrodyellow'
        rax = plt.axes([0.02, 0.8, 0.10, 0.15])
        rax.patch.set_facecolor(axcolor)

        # create dict and list
        numlst = ['plot '+str(idx+1) for idx,elem in enumerate(self.axlist)]
        ## python 2.7 and higher
        #  self.hzdict = {'plot '+str(idx+1):elem for idx,elem in enumerate(self.axlist)}
        ## python 2.6 and lower
        self.hzdict = dict(('plot '+str(idx+1),elem) for idx,elem in enumerate(self.axlist))
        radio = RadioButtons(rax, numlst)

        # #############################################################
        ## Getting a rectangular selector
        # #############################################################

        toggle_selector.RS = RectangleSelector(self.axlist[0], self.line_select_callback, drawtype='box', useblit=True,button=[1,3],minspanx=5, minspany=5,spancoords='pixels', rectprops = dict(facecolor='red', edgecolor = 'black', alpha=0.2, fill=True))

        plt.connect('key_press_event', self.toggle_selector)

        return radio, self.hzfunc

def addFlag(data, flagger, indeciestobeflagged, variables):
        # INPUT section
        print("Provide flag ID (2 or 3):")
        print("  -- 2: keep data")
        print("  -- 3: remove data")
        flagid = raw_input(" -- default: %d \n" % flagger.flagid)
        print(flagid)
        reason = raw_input("Provide reason: \n")
        print(reason)
        flagkeys = raw_input("Keys to flag: e.g. x,y,z\n")
        if not flagkeys == '':
            if ',' in flagkeys:
                keylist = flagkeys.split(',')
                keylist = [elem for elem in keylist if elem in KEYLIST]
            else:
                if flagkeys in KEYLIST:
                    keylist = [flagkeys]
                else:
                    keylist = []
        else:
            keylist = []

        # ANALYSIS section
        try:
            flagid = int(flagid)
        except:
            flagid = flagger.flagid
        if not flagid in [0,1,2,3,4]:
            flagid = flagger.flagid
        if reason == '':
            reason = flagger.reason
        if keylist == []:
            keylist = [key for key in flagger.orgkeylist if key in FLAGKEYLIST]

        try:
            sensid = data.header["SensorID"]
        except:
            print("plotFlag: Flagging requires SensorID - set with stream.header['SensorID'] = 'MyID'")
            sensid = "Dummy_1234_0001"

        flaglst = []
        for k, g in groupby(enumerate(indeciestobeflagged), lambda ix: ix[0] - ix[1]):
            consecutives = map(itemgetter(1), g)
            #print consecutives
            begintime = num2date(data.ndarray[0][consecutives[0]].astype(float)).replace(tzinfo=None)
            endtime = num2date(data.ndarray[0][consecutives[-1]].astype(float)).replace(tzinfo=None)
            modtime = datetime.utcnow()
            #if option o:
            #datastream = datastream._select_timerange(begintime,endtime)
            #datastream = datastream.offset({key:value})
            #orgstream = orgstream.extend(datastream)
            # or  orgstream = datastream.extend(orgstream)
            #orgstream = orgstream.removeduplicates() ##if only last occurence is used
            for key in keylist:
                #print begintime, endtime, key, flagid, reason, sensid, modtime
                if not sensid == '':
                    flaglst.append([begintime, endtime, key, flagid, reason, sensid, modtime])

        # now flag the data and restart figure
        flagger.flag(indeciestobeflagged, flagid, reason, keylist)

        # reduce to original keys
        orgkeys = flagger.orgkeylist
        data = data.selectkeys(orgkeys)
        flagger = figFlagger(data, variables)
        #flagger.flag(data)
        return flagger.idxarray, flaglst

def plotFlag(data,variables=None,figure=False):
    '''
    DEFINITION:
        Creates a plot for flagging.
        Rectangular selection is possible and flagging can be conducted.
        Several additional keys provide data info.
    RETURNS:
        - stream:       (Datastream) ndarray stream to be saved
      optional
        - variables:    (list of keys)

    REQUIRES:
        - class figFlagger
    EXAMPLE:
        >>> flaggedstream = plotFlag(stream)
    '''
    flaglist = []
    flagdata = data.copy()

    flagger = figFlagger(flagdata,variables,figure)
    indeciestobeflagged = flagger.idxarray
    while indeciestobeflagged > 0:
        indeciestobeflagged, flaglst = addFlag(flagger.data, flagger, indeciestobeflagged, variables)
        flaglist.extend(flaglst)

    print("Returning data ....")
    try:
        print("  -- original format: %s " % data.header['DataFormat'])
    except:
        pass

    orgkeys = flagger.orgkeylist
    flagdata = flagger.data.selectkeys(orgkeys)
    return flagdata, flaglist

#####################################################################
#         End   Flagging                                            #
#####################################################################


def plotEMD(stream,key,verbose=False,plottitle=None,
        outfile=None,sratio=0.25,max_modes=20,hht=True,stackvals=[2,8,14]):
    '''
    DEFINITION:
        NOTE: EXPERIMENTAL FUNCTION ONLY.
        Function for plotting Empirical Mode Decomposition of
        DataStream. Currently only optional function.
        (Adapted from RL code in MagPyAnalysis/NoiseFloor_Spectral/magemd.py.)

    PARAMETERS:
    Variables:
        - stream:       (DataStream object) Description.
        - key:          (str) Key in stream to apply EMD to.
    Kwargs:
        - outfile:      (str) Save plot to file. If no file defined, plot
                        will simply be shown.
        - plottitle:    (str) Title to place at top of plot.
        - sratio:       (float) Decomposition percentage. Determines how curve
                        is split. Default = 0.25.
        - stackvals:    (list) provide a list of three intergers defining which components
                        are to be stacked together. e.g. [2,8,14] means: 2 to 7 define the
                        high frequ part, 8 to 14 the mid frequ, >14 the low frequ
        - verbose:      (bool) Print results. Default False.

    RETURNS:
        - plot:         (matplotlib plot) Plot depicting the modes.

    EXAMPLE:
        >>> plotEMDAnalysis(stream,'x')

    APPLICATION:
    '''

    # TODO:
    # - make axes easier to read
    # - add a amplitude statistic (histogram)
    # - add a haeufigkeit plot perpendicular to the diagrams
    import magpy.opt.emd as emd # XXX: add this into main program when method is finalised

    logger.info("plotEMD: Starting EMD calculation.")

    col = stream._get_column(key)
    timecol = stream._get_column('time')
    logger.debug("plotEMD: Amount of values and standard deviation:", len(col), np.std(col))

    res = emd.emd(col,max_modes=max_modes)
    logger.debug("plotEMD: Found the following amount of decomposed modes:", len(res))
    separate = int(np.round(len(res)*sratio,0))
    logger.debug("plotEMD: Separating the last N curves as smooth. N =",separate)
    stdarray = []
    newcurve = [0]*len(res[0])
    noisecurve = [0]*len(res[0])
    midcurve = [0]*len(res[0])
    smoothcurve = [0]*len(res[0])
    f, axarr = plt.subplots(len(res), sharex=True)

    for i, elem in enumerate(res):
        axarr[i].plot(elem)
        newcurve = [x + y for x, y in zip(newcurve, elem)]
        stdarray.append([i,np.std(elem)])
        ds = stream
        ds._put_column(elem,'x')
        ds._put_column(timecol,'time')

        """
        if i >= len(res)-separate:
            if verbose:
                print "Smooth:", i
            smoothcurve = [x + y for x, y in zip(smoothcurve, elem)]
        if i < len(res)-separate:
            if verbose:
                print "Noise:", i
            noisecurve = [x + y for x, y in zip(noisecurve, elem)]
        """
        if i > stackvals[2]: # 14 - add stackvals = [2,8,14]
            logger.debug("plotEMD: Smooth: {}".format(i))
            smoothcurve = [x + y for x, y in zip(smoothcurve, elem)]
        if stackvals[1] <= i <= stackvals[2]:
            logger.debug("plotEMD: Mid: {}".format(i))
            midcurve = [x + y for x, y in zip(midcurve, elem)]
        if stackvals[0] < i < stackvals[1]:
            logger.debug("plotEMD: Noise: {}".format(i))
            noisecurve = [x + y for x, y in zip(noisecurve, elem)]

    plt.show()

    plt.plot(smoothcurve)
    #plt.plot(newcurve)
    plt.title("Variation of IMF 14 to 17 component - low frequency content")
    plt.xlabel("Time [15 min counts]")
    plt.ylabel("Counts/min")
    plt.legend()
    plt.show()

    plt.plot(noisecurve)
    plt.title("Variation of IMF 1 to 8 component - high frequency content")
    plt.xlabel("Time [15 min counts]")
    plt.ylabel("Counts/min")
    plt.show()

    plt.plot(midcurve)
    plt.title("Variation of IMF 9 to 12 - mid frequency content")
    plt.xlabel("Time [15 min counts]")
    plt.ylabel("Counts/min")
    plt.show()

    plt.close()
    stdarray = np.asarray(stdarray)
    ind = stdarray[:,0]
    val = stdarray[:,1]
    plt.bar(ind,val)
    plt.title("Standard deviation of EMD modes")
    plt.xlabel("EMD mode")
    plt.ylabel("Standard deviation [nT]")
    plt.show()

    if hht:
        print(emd.calc_inst_info(res,stream.samplingrate()))


def plotNormStreams(streamlist, key, normalize=True, normalizet=False,
        normtime=None, bgcolor='white', colorlist=colorlist, noshow=False,
        outfile=None, plottitle=None, grid=True, gridcolor=gridcolor,
        labels=None, legendposition='upper right',labelcolor=labelcolor,
        returndata=False,confinex=False,savedpi=80):
    '''
    DEFINITION:
        Will plot normalised streams. Streams will be normalized to a general
        median or to the stream values at a specific point in time.
        Useful for directly comparing streams in different locations.

    PARAMETERS:
    Variables:
        - streamlist:   (list) A list containing the streams to be plotted.
                        e.g.:
                        [ stream1, stream2, etc...]
                        [ lemi1, lemi2, lemi3 ]
        - key:          (str) Variable to be compared
                        'f'
    Args:
        - bgcolor:      (color='white') Background colour of plot.
        - colorlist:    (list(colors)) List of colours to plot with.
                        Default = ['b','g','m','c','y','k','b','g','m','c','y','k']
        - grid:         (bool=True) If True, will plot grid.
        - gridcolor:    (color='#316931') Colour of grid.
        #- labelcolor:  (color='0.2') Colour of labels.
        - labels:       (list) Insert labels and legend for each stream, e.g.:
                        ['WIC', 'WIK', 'OOP']
        - legendposition: (str) Position of legend. Default = "upper right"
        - outfile:      (str) Path of file to plot figure to.
        - normalize:    (bool) If True, variable will be normalized to 0. Default = True.
        - normalizet:   (bool) If True, time variable will be normalized to 0. Default = False
        - normtime:     (datetime object/str) If streams are to be normalized, normtime
                        is the time to use as a reference.
        - noshow:       (bool) Will return figure object at end if True, otherwise only plots
        - plottitle:    (str) Title to put at top of plot.
        #- plottype:    (NumPy str='discontinuous') Can also be 'continuous'.
        - returndata:   (bool) If True, will return normalised data arrays. Default = False.
        #- savedpi:     (float=80) Determines dpi of outfile.

    RETURNS:
        - plot:         (Pyplot plot) Returns plot as plt.show or saved file
                        if outfile is specified.

    EXAMPLE:
        >>>
    '''

    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    arraylist = []

    if labels:
        if len(labels) != len(streamlist):
            logger.warning("plotNormStreams: Number of labels does not match number of streams!")

    for i, stream in enumerate(streamlist):
        logger.info("plotNormStreams: Adding stream %s of %s..." % ((i+1),len(streamlist)))
        y = stream._get_column(key)
        t = stream._get_column('time')
        xlabel = "Time (UTC)"
        color = colorlist[i]

        if len(y) == 0:
            logger.error("plotNormStreams: stream with empty array!")
            return
        try:
            yunit = stream.header['unit-col-'+key]
        except:
            yunit = ''
        #ylabel = stream.header['col-'+key].upper()+' $['+re.sub('[#$%&~_^\{}]', '', yunit)+']$'
        ylabel = stream.header['col-'+key].upper()+' $['+re.sub('[#$%&~_\{}]', '', yunit)+']$'

        # NORMALIZE VARIABLE:
        if normalize:
            if normtime:
                if type(normtime) == list:
                    normtime_start, normtime_end = test_time(normtime[0]), test_time(normtime[1])
                    normarea = stream.trim(normtime_start,normtime_end,newway=True)
                    normvalue = normarea.mean(key,meanfunction='median')
                else:
                    normtime = test_time(normtime)
                    val, idx = find_nearest(t,date2num(normtime))
                    normvalue = y[idx]
            else:
                normvalue = np.median(y)
            y = y - normvalue
            ylabel = "normalized "+ylabel

        # NORMALIZE TIME:
        if normalizet:
            if normtime:
                zerotime = normtime
            else:
                zerotime = t[0]
            t = t - zerotime
            xlabel = "normalized "+xlabel

        if returndata:
            arraylist.append([t,y])

        # CONFINE X
        timeunit = ''
        if confinex:
            tmin = np.min(t)
            tmax = np.max(t)
            # --> If dates to be confined, set value types:
            _confinex(ax, tmax, tmin, timeunit)
        ax.set_xlabel("Time (UTC) %s" % timeunit, color=labelcolor)

        # PLOT DATA:
        if labels:
            ax.plot(t,y,color+'-',label=labels[i])
        else:
            ax.plot(t,y,color+'-')

    # ADD GRID:
    if grid:
        ax.grid(True,color=gridcolor,linewidth=0.5)

    # SET LABELS:
    ax.set_xlabel(xlabel, color=labelcolor)
    ax.set_ylabel(ylabel, color=labelcolor)
    ax.set_title(plottitle)
    ax.set_xlim([t[0],t[-1]])

    # INSERT LEGEND:
    if labels:
        legend = ax.legend(loc=legendposition, shadow=True)
        for label in legend.get_texts():
            label.set_fontsize('small')

    # FINALISE PLOT:
    if noshow == True and returndata == True:
        return [fig, arraylist]
    elif noshow == False and returndata == True:
        return arraylist
    elif noshow == True and returndata == False:
        return fig
    else:
        if outfile:
            plt.savefig(outfile,dpi=savedpi)
        else:
            plt.show()


def plotPS(stream, key, debugmode=False, outfile=None, noshow=False,
        returndata=False, freqlevel=None, marks={}, fmt=None,
        axes=None, plottitle=None, gui=False, **kwargs):
    """
    DEFINITION:
        Calculate the power spectrum following the numpy fft example
        and plot the results.

    PARAMETERS:
    Variables:
        - stream:       (DataStream object) Stream to analyse
        - key:          (str) Key to analyse
    Kwargs:
        - axes:         (?) ?
        - debugmode:    (bool) Variable to show steps
        - fmt:          (str) Format of outfile, e.g. "png"
        - freqlevel:    (float) print noise level at that frequency.
        - gui:          (bool) True if the method is called by the xmagpy GUI
        - marks:        (dict) Contains list of marks to add, e.g:
                        {'here',1}
        - outfile:      (str) Filename to save plot to
        - plottitle:    (str) Title to display on plot
        - returndata:   (bool) Return frequency and asd

    RETURNS:
        - plot:         (matplotlib plot) A plot of the powerspectrum
        If returndata = True:
        - freqm:        (float) Maximum frequency
        - asdm: (float) ?

    EXAMPLE:
        >>> plotPS(stream, 'x')
        OR
        >>> freq, a = plotPS(stream, 'x', returndata=True)

    APPLICATION:
        >>> import magpy
        1. Requires DataStream object:
        >>> data_path = '/usr/lib/python2.7/magpy/examples/*'
        >>> data = read(path_or_url=data_path,
                        starttime='2013-06-10 00:00:00',
                        endtime='2013-06-11 00:00:00')
        2. Call for data stream:
        >>> data.powerspectrum('f',
                        plottitletitle='PSD of f', marks={'day':0.000011574},
                        outfile='ps.png')
    """

    logger.info("plotPS: Starting power spectrum calculation")

    if noshow == True:
        show = False
    elif noshow == False:
        show = True
    else:
        logger.error("plotPS: Incorrect value ({:s}) for variable noshow.".format(noshow))
        raise ValueError("plotPS: Incorrect value ({:s}) for variable noshow.".format(noshow))

    dt = stream.get_sampling_period()*24*3600

    if not stream.length()[0] > 0:
        logger.error("plotPS: Stream of zero length -- aborting.")
        raise Exception("plotPS: Can't analyse power spectrum of stream of zero length!")

    t_new, val_new, nfft = _extract_data_for_PSD(stream, key)

    logger.debug("plotPS: Extracted data for powerspectrum at %s" % datetime.utcnow())

    if not axes:
        fig = plt.figure()
        ax = fig.add_subplot(111)
    else:
        ax = axes

    psdm = mlab.psd(val_new, nfft, 1/dt)
    asdm = np.sqrt(psdm[0])
    freqm = psdm[1]

    ax.loglog(freqm, asdm,'b-')

    logger.debug("Maximum frequency: {}".format(max(freqm)))

    if freqlevel:
        val, idx = find_nearest(freqm, freqlevel)
        logger.debug("Maximum Noise Level at %s Hz: %s" % (val,asdm[idx]))

    if not marks:
        pass
    else:
        for elem in marks:
            ax.annotate(elem, xy=(marks[elem],min(asdm)),
                        xytext=(marks[elem],max(asdm)-(max(asdm)-min(asdm))*0.3),
                        bbox=dict(boxstyle="round", fc="0.95", alpha=0.6),
                        arrowprops=dict(arrowstyle="->",
                        shrinkA=0, shrinkB=1,
                        connectionstyle="angle,angleA=0,angleB=90,rad=10"))

    try:
        unit = stream.header['unit-col-'+key]
    except:
        unit = 'unit'

    ax.set_xlabel('Frequency $[Hz]$')
    ax.set_ylabel(('Amplitude spectral density $[%s/sqrt(Hz)]$') % unit)
    if plottitle:
        ax.set_title(plottitle)

    logger.info("Finished powerspectrum.")

    if outfile:
        if fmt:
            fig.savefig(outfile, format=fmt)
        else:
            fig.savefig(outfile)
    elif returndata:
        plt.close()
        return freqm, asdm
    elif show:
        if gui == True:
            ax.set_title(key.upper())
            fig.show()
        else:
            plt.show()
    else:
        return fig


def plotSatMag(mag_stream,sat_stream,keys,outfile=None,plottype='discontinuous',
        padding=5,plotfunc=True,confinex=True,labelcolor=labelcolor,savedpi=80,
        plotcolors=['#000066', '#C0C0C0'], plottitle=None,legend=True,
        legendlabels=['Magnetic data','Satellite data'], grid=True,specialdict={},
        annotate=False,returnfig=False):
    """
    DEFINITION:
        Plot satellite and magnetic data on same plot for storm comparison.
        Currently only plots 1x mag variable vs. 1x sat variable.

    PARAMETERS:
    Variables:
        - mag_stream:   (DataStream object) Stream of magnetic data
        - sat_stream:   (DataStream object) Stream of satellite data
        - keys:         (list) Keys to analyse [mag_key,sat_key], e.g. ['x','y']
    Kwargs:
        - annotate:     (bool) If True, comments in flagged stream lines will be annotated into plot.
        - confinex:     (bool) If True, time strs on y-axis will be confined depending on scale.
        - grid:         (bool) If True, grid will be added to plot. (Doesn't work yet!)
        - legend:       (bool) If True, legend will be added to plot. Default in legendlabels.
        - legendlabels: (list[str]) List of labels to plot in legend.
        - outfile:      (str) Filepath to save plot to.
        - padding:      (float) Padding to add to plotted variables
        - plotcolors:   (list) List of colors for (0) mag data and (1) sat data lines
        - plotfunc:     (bool) If True, fit function will be plotted against sat data.
        - plottitle:    (str) Title to add to plot
        - plottype:     (str) 'discontinuous' (nans will be masked) or 'continuous'.
        - returnfig:    (bool) Return figure object if True
        - savedpi:      (int) DPI of image if plotting to outfile.
        - specialdict:  (dict) Contains limits for plot axes in list form. NOTE this is not the
                        same as other specialdicts. Dict keys should be "sat" and "mag":
                        specialdict = {'mag':[40,100],'sat':[300,450]}

    RETURNS:
        - plot:         (matplotlib plot) A plot of the spectrogram.

    EXAMPLE:
        >>> plotSatMag(LEMI_data, ACE_data, ['x','y'])

    APPLICATION:
        >>>
    """

    logger.info("plotSatMag - Starting plotting of satellite and magnetic data...")

    key_mag, key_sat = keys[0], keys[1]
    ind_mag, ind_sat, ind_t = KEYLIST.index(key_mag), KEYLIST.index(key_sat), KEYLIST.index('time')

    if len(mag_stream.ndarray) > 0.:
        t_mag = mag_stream.ndarray[ind_t]
        t_sat = sat_stream.ndarray[ind_t]
    else:
        t_mag = np.asarray([row[0] for row in mag_stream])
        t_sat = np.asarray([row[0] for row in sat_stream])
    if key_mag not in KEYLIST:
        raise Exception("Column key (%s) not valid!" % key)
    if key_sat not in KEYLIST:
        raise Exception("Column key (%s) not valid!" % key)

    if len(mag_stream.ndarray) > 0.:
        y_mag = mag_stream.ndarray[ind_mag]
        y_sat = sat_stream.ndarray[ind_sat]
    else:
        y_mag = np.asarray([row[ind_mag] for row in mag_stream])
        y_sat = np.asarray([row[ind_sat] for row in sat_stream])

    # Fix if NaNs are present:
    if plottype == 'discontinuous':
        y_mag = maskNAN(y_mag)
        y_sat = maskNAN(y_sat)
    else:
        nans, test = nan_helper(y_mag)
        newt_mag = [t_mag[idx] for idx, el in enumerate(y_mag) if not nans[idx]]
        t_mag = newt_mag
        y_mag = [el for idx, el in enumerate(y_mag) if not nans[idx]]
        nans, test = nan_helper(y_sat)
        newt_sat = [t_sat[idx] for idx, el in enumerate(y_sat) if not nans[idx]]
        t_sat = newt_sat
        y_sat = [el for idx, el in enumerate(y_sat) if not nans[idx]]


    if (len(y_sat) == 0 or len(y_mag)) == 0:
        logger.error("plotSatMag - Can't plot empty column! Full of nans?")
        raise Exception("plotSatMag - Empty column!")

    # Define y-labels:
    try:
        ylabel_mag = mag_stream.header['col-'+key_mag].upper()
    except:
        ylabel_mag = ''
        pass
    try:
        ylabel_sat = sat_stream.header['col-'+key_sat].upper()
    except:
        ylabel_sat = ''
        pass

    try:
        yunit_mag = mag_stream.header['unit-col-'+key_mag]
    except:
        yunit_mag = ''
        pass
    if not yunit_mag == '':
        yunit_mag = re.sub('[#$%&~_^\{}]', '', yunit_mag)
        label_mag = ylabel_mag+' $['+yunit_mag+']$'
    else:
        label_mag = ylabel_mag

    try:
        yunit_sat = sat_stream.header['unit-col-'+key_sat]
    except:
        yunit_sat = ''
        pass
    if not yunit_sat == '':
        yunit_sat = re.sub('[#$%&~_^\{}]', '', yunit_sat)
        label_sat = ylabel_sat+' $['+yunit_sat+']$'
    else:
        label_sat = ylabel_sat

    # PLOT FIGURE
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax1.set_ylabel(label_sat,color=labelcolor)
    axis1 = ax1.plot_date(t_sat, y_sat, fmt='-', color=plotcolors[1],label=legendlabels[1])

    timeunit = ''
    if confinex:
        tmin = np.min(t_mag)
        tmax = np.max(t_mag)
        # --> If dates to be confined, set value types:
        _confinex(ax1, tmax, tmin, timeunit)
    ax1.set_xlabel("Time (UTC) %s" % timeunit, color=labelcolor)
    if plottitle:
        ax1.set_title(plottitle)

    # NOTE: For mag data to be above sat data in zorder, KEEP THIS AXIS ORDER
    # (twinx() does not play nicely with zorder settings)
    ax2 = ax1.twinx()
    axis2 = ax2.plot_date(t_mag, y_mag, fmt='-', lw=2, color=plotcolors[0],label=legendlabels[0])
    ax2.set_ylabel(label_mag,color=labelcolor)
    ax2.yaxis.set_label_position('left')
    ax2.yaxis.set_ticks_position('left')

    ax1.yaxis.set_label_position('right')
    ax1.yaxis.tick_right()

    # Define y limits:
    if 'mag' in specialdict:
        ax2.set_ylim(specialdict['mag'][0],specialdict['mag'][1])
    else:
        ax2.set_ylim(np.min(y_mag)-padding,np.max(y_mag)+padding)
    if 'sat' in specialdict:
        ax1.set_ylim(specialdict['sat'][0],specialdict['sat'][1])
    else:
        ax1.set_ylim(np.min(y_sat)-padding,np.max(y_sat)+padding)

    # Add a grid:
    # Difficult with a legend and twinx()...
    #if grid:
    #    ax1.grid(zorder=2)
    #    ax2.grid(zorder=1)
    #    ax1.yaxis.grid(False)

    # Add a legend:
    if legend == True:
        axes = axis2 + axis1
        labels = [l.get_label() for l in axes]
        legend = ax1.legend(axes, labels, loc='upper right', shadow=True)
        for label in legend.get_texts():
            label.set_fontsize('small')

    if annotate == True:
        flags = mag_stream.flags
        emptycomment = "-"
        poslst = [ix for ix,el in enumerate(FLAGKEYLIST) if el == key]
        indexflag = int(poslst[0])
        for idx, elem in enumerate(flags[1]):
            if not elem == emptycomment and flags[0][idx][indexflag] in ['0','3']:
                ax.annotate(r'%s' % (elem),
                            xy=(t[idx], y[idx]),
                            xycoords='data', xytext=(20, 20),
                            textcoords='offset points',
                            bbox=dict(boxstyle="round", fc="0.8"),
                            arrowprops=dict(arrowstyle="->",
                            shrinkA=0, shrinkB=1,
                            connectionstyle="angle,angleA=0,angleB=90,rad=10"))

    # Plot a function to the satellite data:
    if plotfunc:
        sat_stream._drop_nans('y')
        func = sat_stream.fit(['y'],knotstep=0.02)

        fkey = 'f'+key_sat
        if fkey in func[0]:
            ttmp = arange(0,1,0.0001)
            ax1.plot_date(denormalize(ttmp,func[1],func[2]),func[0][fkey](ttmp),
                '-',color='gray')

    if returnfig == True:
        fig = plt.gcf()
        return fig

    if outfile:
        plt.savefig(outfile,savedpi=80)
        logger.info("plotSatMag - Plot saved to %s." % outfile)
    else:
        plt.show()
        logger.info("plotSatMag - Plot completed.")


def plotSpectrogram(stream, keys, NFFT=1024, detrend=mlab.detrend_none,
        window=mlab.window_hanning, noverlap=900,
        cmap=cm.Accent, cbar=False, xextent=None, pad_to=None,
        sides='default', scale_by_freq=None, minfreq = None, maxfreq = None,
        plottitle=False, gui=False, figure=False, **kwargs):
    """
    DEFINITION:
        Creates a spectrogram plot of selected keys.
        Parameter description at function obspyspectrogram
        Uses samp_rate_multiplicator (=24*3600): Changes the
        frequency relative to one day sampling rate given as days ->
        multiplied by x to create Hz,

    PARAMETERS:
    Variables:
        - stream:       (DataStream object) Stream to analyse
        - keys:         (list) Keys to analyse
    Kwargs:
        - axes:         (?) ?
        - cbar:         (bool) Plot colorbar alongside spectrogram
        - cmap:         (cm.) Colormap object
        - dbscale:      (?) ?
        - fmt:          (str) Format of outfile, e.g. 'png'
        - gui           (bool) True if the method is called by the xmagpy GUI
        - log:          (bool) ?
        - mult:         (?) ?
        - outfile:      (str) Filename to save plot to
        - per_lap:      (?) ?
        - plottitle:    (?) ?
        - samp_rate_multiplicator:
                        (float=24*3600) Change the frequency relative to one
                        day sampling rate given as days -> multiplied by x to
                        create Hz, Default 24, which means 1/3600 Hz
        - show:         (?) ?
        - sphinx:       (?) ?
        - wlen:         (?) ?
        - zorder:       (?) ?

    RETURNS:
        - plot:         (matplotlib plot) A plot of the spectrogram.

    EXAMPLE:
        >>> plotSpectrogram(stream, ['x','y'])

    APPLICATION:
        >>>
    """

    #if not samp_rate_multiplicator:
    samp_rate_multiplicator = 24*3600

    t = stream._get_column('time')

    if not minfreq:
        minfreq = 0.0001

    if not len(t) > 0:
        logger.error('plotSpectrogram: stream of zero length -- aborting')
        return

    for key in keys:
        val = stream._get_column(key)
        val = maskNAN(val)
        dt = stream.get_sampling_period()*(samp_rate_multiplicator)
        Fs = float(1.0/dt)
        if not maxfreq:
            maxfreq = int(Fs/2.0)
            print ("Maxfreq", maxfreq)
            if maxfreq < 1:
                maxfreq = 1
        if not figure:
            ax1=subplot(211)
            plt.plot_date(t,val,'-')
            ax1.set_ylabel('{} [{}]'.format(stream.header.get('col-'+key,''),
                    stream.header.get('unit-col-'+key,'')))
            ax1.set_xlabel('Time (UTC)')
            ax2=subplot(212)
        else:
            ax2=subplot(111)
        ax2.set_yscale('log')
        #NFFT = 1024
        Pxx, freqs, bins, im = magpySpecgram(val, NFFT=NFFT, Fs=Fs,
                noverlap=noverlap, cmap=cmap, minfreq=minfreq,
                maxfreq=maxfreq)

        if figure:
            fig = plt.gcf()
            axes = fig.axes
            fig.colorbar(im, ax=np.ravel(axes).tolist())
            return fig

        if plottitle:
            ax1.set_title(plottitle)

        if cbar:
            fig = plt.gcf()
            axes = fig.axes
            fig.colorbar(im, ax=np.ravel(axes).tolist())
        if gui == True:
            plt.show(block=False)
        else:
            plt.show()

    """
    if axes:
        return ax

    if not sphinx:
        # ignoring all NumPy warnings during plot
        temp = np.geterr()
        np.seterr(all='ignore')
        plt.draw()
        np.seterr(**temp)

    if outfile:
        fig = plt.gcf()
        if fmt:
            fig.savefig(outfile, format=fmt)
        else:
            return fig
    """

def magpySpecgram(x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none,
             window=mlab.window_hanning, noverlap=128,
             cmap=None, xextent=None, pad_to=None, sides='default',
             scale_by_freq=None, minfreq = None, maxfreq = None, title=False, **kwargs):
    """
    DESCRIPTION
        Compute a spectrogram of data in *x*.  Data are split into
        *NFFT* length segments and the PSD of each section is
        computed.  The windowing function *window* is applied to each
        segment, and the amount of overlap of each segment is
        specified with *noverlap*.
        Taken from http://stackoverflow.com/questions/19468923/cutting-of-unused-frequencies-in-specgram-matplotlib

    APPLICATION:
      specgram(x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none,
               window=mlab.window_hanning, noverlap=128,
               cmap=None, xextent=None, pad_to=None, sides='default',
               scale_by_freq=None, minfreq = None, maxfreq = None, **kwargs)
    VARIABLE:
    %(PSD)s

      *Fc*: integer
        The center frequency of *x* (defaults to 0), which offsets
        the y extents of the plot to reflect the frequency range used
        when a signal is acquired and then filtered and downsampled to
        baseband.

      *cmap*:
        A :class:`matplotlib.cm.Colormap` instance; if *None* use
        default determined by rc

      *xextent*:
        The image extent along the x-axis. xextent = (xmin,xmax)
        The default is (0,max(bins)), where bins is the return
        value from :func:`mlab.specgram`

      *minfreq, maxfreq*
        Limits y-axis. Both required

      *kwargs*:

        Additional kwargs are passed on to imshow which makes the
        specgram image

      RETURNS:
          Return value is (*Pxx*, *freqs*, *bins*, *im*):

          - *bins* are the time points the spectrogram is calculated over
          - *freqs* is an array of frequencies
          - *Pxx* is a len(times) x len(freqs) array of power
          - *im* is a :class:`matplotlib.image.AxesImage` instance


    Note: If *x* is real (i.e. non-complex), only the positive
    spectrum is shown.  If *x* is complex, both positive and
    negative parts of the spectrum are shown.  This can be
    overridden using the *sides* keyword argument.

    **Example:**

    .. plot:: mpl_examples/pylab_examples/specgram_demo.py

    """

    #####################################
    # modified  axes.specgram() to limit
    # the frequencies plotted
    #####################################

    # this will fail if there isn't a current axis in the global scope
    end = len(x) / Fs

    x = x - mean(x)

    ax = gca()
    Pxx, freqs, bins = mlab.specgram(x, NFFT, Fs, detrend,
         window, noverlap, pad_to, sides, scale_by_freq)

    # modified here
    #####################################
    if minfreq is not None and maxfreq is not None:
        Pxx = Pxx[(freqs >= minfreq) & (freqs <= maxfreq)]
        freqs = freqs[(freqs >= minfreq) & (freqs <= maxfreq)]
    #####################################

    Z = 10. * np.log10(Pxx)
    Z = np.flipud(Z)

    if xextent is None: xextent = 0, np.amax(bins)
    xmin, xmax = xextent
    freqs += Fc
    extent = xmin, xmax, freqs[0], freqs[-1]
    im = ax.imshow(Z, cmap, extent=extent, **kwargs)
    ax.axis('auto')

    # set correct way of axis, whitespace before and after with window
    # length
    ax.axis('tight')
    ax.set_xlim(0, end)
    ax.grid(False)

    ax.set_xlabel('Time [s]')
    ax.set_ylabel('Frequency [Hz]')
    if title:
        ax.set_title(title)

    return Pxx, freqs, bins, im


def plotStereoplot(stream,focus='all',colorlist = ['b','r','g','c','m','y','k'],
        bgcolor='#d5de9c',griddeccolor='#316931',gridinccolor='#316931',
        savedpi=80,legend=True,labellimit=11,legendposition="lower left",
        figure=False,noshow=False,plottitle=None,groups=None,outfile=None,**kwargs):
    """
    DEFINITION:
        Plots declination and inclination values in stereographic projection.
        Will abort if no idff typ is provided
        Full circles denote positive inclinations, open negative

    PARAMETERS:
    Variables:
        - stream        (DataStream) a magpy datastream object
    Kwargs:
        - bgcolor:      (colour='#d5de9c') Background colour
        - figure:       (bool) Show figure if True
        - focus:        (str) defines the plot area. Options:
                        all (default) - -90 to 90 deg inc, 360 deg dec
                        q1 - first quadrant
                        q2 - first quadrant
                        q3 - first quadrant
                        q4 - first quadrant
                        data - focus on data (if angular spread is less then 10 deg
        - gridcolor:    (str) Define grid color e.g. '0.5' greyscale, 'r' red, etc
        - griddeccolor: (colour='#316931') Grid colour for inclination
        - gridinccolor: (colour='#316931') Grid colour for declination
        - groups        (KEY) - key of keylist which defines color of points
                        (e.g. ('str2') in absolutes to select
                        different colors for different instruments
        - legend:       (bool) - draws legend only if groups is given - default True
        - legendposition:
                        (str) - draws the legend at chosen position,
                        (e.g. "upper right", "lower center") - default is "lower left"
        - labellimit:   (int)- maximum length of label in legend
        - noshow:       (bool) If True, will not call show at the end,
        - outfile:      (str) to save the figure, if path is not existing it will be created
        - savedpi:      (int) resolution
        - plottitle:    (str) Title at top of plot

    REQUIRES:
        - package operator for color selection

    RETURNS:
        - plot:         (matplotlib plot) The stereoplot.

            ToDo:
                - add alpha 95 calc

    EXAMPLE:
        >>> stream.stereoplot(focus='data',groups='str2')

    APPLICATION:
        >>>
    """

    logger.info('plotStereoplot: Starting plot of stereoplot.')

    if not stream[0].typ == 'idff':
        logger.error('plotStereoplot: idf data required for stereoplot.')
        raise Exception("Idf data required for plotting a stereoplot!")

    inc = stream._get_column('x')
    dec = stream._get_column('y')

    col = ['']
    if groups:
        sel = stream._get_column(groups)
        col = list(set(list(sel)))
        if len(col) > 7:
            col = col[:7]

    if not len(dec) == len(inc):
        logger.error('plotStereoplot: Check input file - unequal inc and dec data?')
        return

    if not figure:
        fig = plt.figure()
    else:
        fig = figure
    ax = plt.gca()
    ax.cla() # clear things for fresh plot
    ax.set_aspect('equal')
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.set_xticks([])
    ax.set_yticks([])
    # Define coordinates:
    basic1=plt.Circle((0,0),90,color=bgcolor,fill=True)
    basic1a=plt.Circle((0,0),90,color=gridinccolor,fill=False)
    basic2=plt.Circle((0,0),30,color=gridinccolor,fill=False,linestyle='dotted')
    basic3=plt.Circle((0,0),60,color=gridinccolor,fill=False,linestyle='dotted')
    basic4=plt.Line2D([0,0],[-90,90],color=griddeccolor,linestyle='dashed')
    basic5=plt.Line2D([-90,90],[0,0],color=griddeccolor,linestyle='dashed')
    fig.gca().add_artist(basic1)
    fig.gca().add_artist(basic1a)
    fig.gca().add_artist(basic2)
    fig.gca().add_artist(basic3)
    fig.gca().add_artist(basic4)
    fig.gca().add_artist(basic5)
    for j in range(len(col)):
        color = colorlist[j]

        xpos,ypos,xneg,yneg,xabs,y = [],[],[],[],[],[]
        for i,el in enumerate(inc):
            if groups:
                if sel[i] == col[j]:
                    coinc = 90-np.abs(el)
                    sindec = np.sin(np.pi/180*dec[i])
                    cosdec = np.cos(np.pi/180*dec[i])
                    xabs.append(coinc*sindec)
                    y.append(coinc*cosdec)
                    if el < 0:
                        xneg.append(coinc*sindec)
                        yneg.append(coinc*cosdec)
                    else:
                        xpos.append(coinc*sindec)
                        ypos.append(coinc*cosdec)
            else:
                coinc = 90-np.abs(el)
                sindec = np.sin(np.pi/180*dec[i])
                cosdec = np.cos(np.pi/180*dec[i])
                xabs.append(coinc*sindec)
                y.append(coinc*cosdec)
                if el < 0:
                    xneg.append(coinc*sindec)
                    yneg.append(coinc*cosdec)
                else:
                    xpos.append(coinc*sindec)
                    ypos.append(coinc*cosdec)

        xmax = np.ceil(max(xabs))
        xmin = np.floor(min(xabs))
        xdif = xmax-xmin
        ymax = np.ceil(max(y))
        ymin = np.floor(min(y))
        ydif = ymax-ymin
        maxdif = max([xdif,ydif])
        mindec = np.floor(min(dec))
        maxdec = np.ceil(max(dec))
        mininc = np.floor(min(np.abs(inc)))
        maxinc = np.ceil(max(np.abs(inc)))

        if focus == 'data' and maxdif <= 10:
            # decs
            startdec = mindec
            decline,inclst = [],[]
            startinc = mininc
            incline = []
            while startdec <= maxdec:
                xl = 90*np.sin(np.pi/180*startdec)
                yl = 90*np.cos(np.pi/180*startdec)
                decline.append([xl,yl,startdec])
                startdec = startdec+1
            while startinc <= maxinc:
                inclst.append(90-np.abs(startinc))
                startinc = startinc+1

        if focus == 'all':
            ax.set_xlim((-90,90))
            ax.set_ylim((-90,90))
        if focus == 'q1':
            ax.set_xlim((0,90))
            ax.set_ylim((0,90))
        if focus == 'q2':
            ax.set_xlim((-90,0))
            ax.set_ylim((0,90))
        if focus == 'q3':
            ax.set_xlim((-90,0))
            ax.set_ylim((-90,0))
        if focus == 'q4':
            ax.set_xlim((0,90))
            ax.set_ylim((-90,0))
        if focus == 'data':
            ax.set_xlim((xmin,xmax))
            ax.set_ylim((ymin,ymax))
            #ax.annotate('Test', xy=(1.2, 25.2))
        ax.plot(xpos,ypos,'o',color=color, label=col[j][:labellimit])
        ax.plot(xneg,yneg,'o',color='white')
        ax.annotate('60', xy=(0, 30))
        ax.annotate('30', xy=(0, 60))
        ax.annotate('0', xy=(0, 90))
        ax.annotate('90', xy=(90, 0))
        ax.annotate('180', xy=(0, -90))
        ax.annotate('270', xy=(-90, 0))

    if focus == 'data' and maxdif <= 10:
        for elem in decline:
            pline = plt.Line2D([0,elem[0]],[0,elem[1]],color=griddeccolor,linestyle='dotted')
            xa = elem[0]/elem[1]*((ymax - ymin)/2+ymin)
            ya = (ymax - ymin)/2 + ymin
            annotext = "D:%i" % int(elem[2])
            ax.annotate(annotext, xy=(xa,ya))
            fig.gca().add_artist(pline)
        for elem in inclst:
            pcirc = plt.Circle((0,0),elem,color=gridinccolor,fill=False,linestyle='dotted')
            xa = (xmax-xmin)/2 + xmin
            ya = sqrt((elem*elem)-(xa*xa))
            annotext = "I:%i" % int(90-elem)
            ax.annotate(annotext, xy=(xa,ya))
            fig.gca().add_artist(pcirc)

    if groups and legend:
        handles, labels = ax.get_legend_handles_labels()
        hl = sorted(zip(handles, labels),key=operator.itemgetter(1))
        handles2, labels2 = zip(*hl)
        ax.legend(handles2, labels2, loc=legendposition)

    if plottitle:
        ax.set_title(plottitle)

    # SAVE TO FILE (or show)
    if figure:
        return ax

    if outfile:
        path = os.path.split(outfile)[0]
        if not path == '':
            if not os.path.exists(path):
                os.makedirs(path)
        if fmt:
            fig.savefig(outfile, format=fmt, dpi=savedpi)
        else:
            fig.savefig(outfile, dpi=savedpi)
    elif noshow:
        return fig
    else:
        plt.show()


#####################################################################
#                                                                   #
#       INTERNAL/HELPER FUNCTIONS                                   #
#       (Best not play with these.)                                 #
#                                                                   #
#####################################################################


def _plot(data,savedpi=80,grid=True,gridcolor=gridcolor,noshow=False,
        bgcolor='white',plottitle=None,fullday=False,bartrange=0.06,
        labelcolor=labelcolor,confinex=False,outfile=None,stormanno_s=True,
        stormanno_m=True,stormanno_r=True,fmt=None,figure=False,fill=[],
        legendposition='upper left',singlesubplot=False,opacity=1.0):
    '''
    For internal use only. Feed a list of dictionaries in here to plot.
    Every dictionary should contain all data needed for one single subplot.
    DICTIONARY STRUCTURE FOR EVERY SUBPLOT:
    [ { ***REQUIRED***
        'key'   : 'x'           (str) MagPy key
        'tdata' : t             (np.ndarray) Time
        'ydata' : y             (np.ndarray) Data y(t)
        'ymin'  : ymin          (float)    Minimum of y-axis
        'ymax'  : ymax          (float)    Maximum of y-axis
        'symbol': '-'           (str)      Symbol for plotting, '-' = line
        'color' : 'b'           (str)      Colour of plotted line
        'ylabel': 'F [nt]'      (str)      Label on y-axis
        'annotate': False       (bool)     If this is True, must have 'flags' key
        'sensorid': 'LEMI025'   (str)      String pulled from header data. If available,
                                will be plotted alongside data for clarity.

        OPTIONAL:
        'errorbars': eb         (np.ndarray) Errorbars to plot in subplot
        'flags' : flags         (np.ndarray) Flags to add into subplot.
                                Note: must be 2-dimensional, flags & comments.
        'function': fn          (function object) Plot a function within the subplot.
        'flagontop': True/False  (bool) define whether flags are on top or not.
        } ,

      { 'key' : ...                             } ... ]

    GENERAL VARIABLES:
    plottitle = "Data from 2014-05-02"
    confinex = False
    bgcolor = 'blue'
    etc. ... (all are listed in plot() and plotStreams() functions)
    figure  -- for GUI
    fill = ['x']
    '''
    logger = logging.getLogger(__name__+"._plot")

    if not figure:
        fig = plt.figure()
    else:
        fig = figure

    # CREATE MATPLOTLIB FIGURE OBJECT:
    #fig = plt.figure()
    plt_fmt = ScalarFormatter(useOffset=False)
    n_subplots = len(data)
    zorder = 2

    for i in range(n_subplots):

        subplt = int("%d%d%d" %(n_subplots,1,i+1))
        if singlesubplot:
            subplt = int("111")

        #------------------------------------------------------------
        # PART 1: Dealing with data
        #------------------------------------------------------------

        # DEFINE DATA:
        key = data[i]['key']
        t = np.asarray(data[i]['tdata']).astype(float)
        y = np.asarray(data[i]['ydata']).astype(float)
        if not len(t) == len(y):
            y = [99999]*len(t)
        # Sort data before plotting - really necessary ? costs 0.1 seconds for 1 day second data
        #datar = sorted([[t[j],y[j]] for j, el in enumerate(t)])
        #t = [datar[j][0] for j, el in enumerate(datar)]
        #y = [datar[j][1] for j, el in enumerate(datar)]
        color = data[i]['color']
        symbol = data[i]['symbol']
        datalabel = data[i]['datalabel']

        if 'flagontop' in data[i]:
            if data[i]['flagontop'] == True:
                zorder = 10
            else:
                zorder = 2

        # CREATE SUBPLOT OBJECT & ADD TITLE:
        logger.info("Adding subplot for key %s..." % data[i]['ylabel'])
        if i == 0:
            ax = fig.add_subplot(subplt)#, axisbg=bgcolor)
            ax.patch.set_facecolor(bgcolor)
            if plottitle:
                ax.set_title(plottitle)
            a = ax
        else:
            ax = fig.add_subplot(subplt, sharex=a) #, axisbg=bgcolor)
            ax.patch.set_facecolor(bgcolor)

        # PLOT DATA:
        # --> If bars are in the data (for e.g. k-index):
        if symbol == 'z':
            xy = range(9)
            for num in range(len(t)):
                if bartrange < t[num] < np.max(t)-bartrange:
                    ax.fill([t[num]-bartrange,t[num]+bartrange,t[num]+bartrange,t[num]-
                                bartrange],[0,0,y[num]+0.1,y[num]+0.1],
                                facecolor=cm.RdYlGn((9-y[num])/9.,1),alpha=opacity,edgecolor='k')
            if datalabel != '':
                ax.plot_date(t,y,color+'|',label=datalabel,alpha=opacity)
            else:
                ax.plot_date(t,y,color+'|',alpha=opacity)

        # --> Otherwise plot as normal:
        else:
            if datalabel != '':
                ax.plot_date(t,y,color+symbol,label=datalabel)
            else:
                ax.plot_date(t,y,color+symbol)
            if key in fill:
                ax.fill_between(t,0,y,color=color,alpha=opacity)

        # PLOT A LEGEND
        if datalabel != '':
            legend = ax.legend(loc=legendposition, shadow=True)
            for label in legend.get_texts():
                label.set_fontsize('small')

        # DEFINE MIN AND MAX ON Y-AXIS:
        ymin = data[i]['ymin']
        ymax = data[i]['ymax']

        # PLOT ERROR BARS (if available):
        if 'errors' in data[i]:
            errorbars = data[i]['errors']
            ax.errorbar(t,y,yerr=errorbars,fmt=color+'o')

        # ANNOTATE:
        if data[i]['annotate'] == True:
            orientationcnt = 0
            flags = data[i]['flags']
            emptycomment = "-"
            indexflag = KEYLIST.index(key)
            # identfy subsequent idx nums in flags[1]
            a_t,a_y,b_t,b_y,c_t,c_y,d_t,d_y = [],[],[],[],[],[],[],[]
            if len(flags[1]) > 0:
                # 1. get different comments
                tmp = DataStream()
                uniqueflags = tmp.union(flags[1])
                #print "Flags", flags,uniqueflags, key
                for fl in uniqueflags:
                    #print ("Flag", fl)
                    flagindicies = []
                    for idx, elem in enumerate(flags[1]):
                        if not elem == '' and elem == fl:
                            #print ("ELEM", elem)
                            flagindicies.append(idx)
                    #print "IDX", np.asarray(flagindicies)
                    # 2. get consecutive groups
                    for k, g in groupby(enumerate(flagindicies), lambda ix: ix[0] - ix[1]):
                        orientationcnt += 1 # used to flip orientation of text box
                        consecutives = list(map(itemgetter(1), g))
                        # 3. add annotation arrow for all but 1
                        cnt0 = consecutives[0]
                        #print ("Cons", np.asarray(consecutives), len(flags[0][cnt0]))
                        if len(flags[0][cnt0]) >= indexflag:
                          try:
                            #print ("Fl", flags[0][cnt0][indexflag], flags[1][cnt0], y[cnt0])
                            if not flags[0][cnt0][indexflag] in ['1','-'] and not flags[1][cnt0] == '-':
                                axisextend = (max(y)-min(y))*0.15 ## 15 percent of axislength
                                if orientationcnt % 2 or y[cnt0]-axisextend < min(y):
                                    xytext=(20, 20)
                                elif y[cnt0]+axisextend > max(y):
                                    xytext=(20, -20)
                                else:
                                    xytext=(20, -20)
                                connstyle = "angle,angleA=0,angleB=90,rad=10"
                                ax.annotate(r'%s' % (flags[1][cnt0]),
                                        xy=(t[cnt0], y[cnt0]),
                                        xycoords='data', xytext=xytext, size=10,
                                        textcoords='offset points',
                                        bbox=dict(boxstyle="round", fc="0.9"),
                                        arrowprops=dict(arrowstyle="->",
                                        shrinkA=0, shrinkB=1, connectionstyle=connstyle))
                            for idx in consecutives:
                                #if not flags[0][idx][indexflag] == '0':
                                #    print "Got", flags[0][idx][indexflag], idx
                                if flags[0][idx][indexflag] in ['3']:
                                    a_t.append(float(t[idx]))
                                    a_y.append(y[idx])
                                elif flags[0][idx][indexflag] in ['1']:
                                    b_t.append(float(t[idx]))
                                    b_y.append(y[idx])
                                elif flags[0][idx][indexflag] in ['2']:
                                    c_t.append(float(t[idx]))
                                    c_y.append(y[idx])
                                elif flags[0][idx][indexflag] in ['4']:
                                    d_t.append(float(t[idx]))
                                    d_y.append(y[idx])
                          except:
                            logger.error("Error when marking flags - check: {} {}".format(flags[0][cnt0], indexflag))
                        else:
                            logger.info("Found problem in flagging information - still to be solved")
                            logger.info("Flag at count and its index position {} {}".format(cnt0, indexflag))
                            logger.info("Flag and Comment (expected -000000000 and comment) {} {}".format(flags[0][cnt0], flags[1][cnt0]))
                linecrit = 2000
                if len(a_t) > 0:
                    if len(a_t) > linecrit:
                        ax.plot(a_t,a_y,'.',c='r', zorder=zorder) ## Use lines if a lot of data is marked
                    else:
                        ax.scatter(a_t,a_y,c='r', zorder=zorder)
                if len(b_t) > 0:
                    if len(b_t) > linecrit:
                        ax.plot(b_t,b_y,'.',c='orange', zorder=zorder)
                    else:
                        ax.scatter(b_t,b_y,c='orange', zorder=zorder)
                if len(c_t) > 0:
                    # TODO Here we have a masked nan warning - too be solved
                    #print np.asarray(c_t)
                    #print np.asarray(c_y)
                    if len(c_t) > linecrit:
                        ax.plot(c_t,c_y,'.',c='g', zorder=zorder)
                    else:
                        ax.scatter(c_t,c_y,c='g', zorder=zorder)
                if len(d_t) > 0:
                    if len(d_t) > linecrit:
                        ax.plot(d_t,d_y,'.',c='b', zorder=zorder)
                    else:
                        ax.scatter(d_t,d_y,c='b', zorder=zorder)

        # PLOT A GIVEN FUNCTION:
        if 'function' in data[i]:
            fkey = 'f'+key
            funclist = data[i]['function']
            if isinstance(funclist[0], dict):
                 funct = [funclist]
            else:
                 funct = funclist
            for function in funct:
                # length of function - an old version just added up functions in one list
                funclength = len(function)
                for nu in range(int(len(function)/funclength)):
                    indexadd = nu*funclength
                    if fkey in function[0+indexadd]:
                        # --> Get the minimum and maximum relative times
                        ttmp = arange(0,1,0.0001)
                        ax.plot_date(denormalize(ttmp,function[1+indexadd],function[2+indexadd]),function[0+indexadd][fkey](ttmp),'r-')

        # PLOT SHADED AND ANNOTATED STORM PHASES:
        if 'stormphases' in data[i]:
            timespan = num2date(t[-1]) - num2date(t[0])
            y_pos = 0.9 # have at height 90% of total plot, x_pos(n)=1-(1-y_pos)*n
            y_anno = ymin + (1-(1-y_pos)*n_subplots)*(ymax-ymin)
            t_phases = data[i]['stormphases']
            if 'ssc' and 'mphase' in t_phases:
                t_ssc = t_phases['ssc']
                t_mphase = t_phases['mphase']
                ax.axvspan(t_ssc, t_mphase, facecolor='red', alpha=0.3, linewidth=0)
                if stormanno_s: # requirement so that only one plot is annotated
                    x_anno = t_ssc-timedelta(seconds=(timespan.seconds*0.1))
                    t_ssc_stream, idx_ssc = find_nearest(t, date2num(t_ssc))
                    y_ssc = y[idx_ssc]
                    ax.annotate('SSC', xy=(t_ssc,y_ssc),
                                xytext=(x_anno,y_anno),
                                bbox=dict(boxstyle="round", fc="0.95", alpha=0.6),
                                arrowprops=dict(arrowstyle="->",
                                shrinkA=0, shrinkB=1,
                                connectionstyle="angle,angleA=0,angleB=90,rad=10"))
                    stormanno_s = False
            if 'mphase' and 'rphase' in t_phases:
                t_mphase = t_phases['mphase']
                t_rphase = t_phases['rphase']
                ax.axvspan(t_mphase, t_rphase, facecolor='yellow', alpha=0.3, linewidth=0)
                if stormanno_m:
                    x_anno = t_mphase+timedelta(seconds=(timespan.seconds*0.03))
                    t_mphase_stream, idx_mphase = find_nearest(t, date2num(t_mphase))
                    y_mphase = y[idx_mphase]
                    ax.annotate('Main\nPhase', xy=(t_mphase,y_mphase),
                                xytext=(x_anno,y_anno),
                                bbox=dict(boxstyle="round", fc="0.95", alpha=0.6))
                    stormanno_m = False
            if 'rphase' and 'stormend' in t_phases:
                t_rphase = t_phases['rphase']
                t_stormend = t_phases['stormend']
                ax.axvspan(t_rphase, t_stormend, facecolor='green', alpha=0.3, linewidth=0)
                if stormanno_r:
                    x_anno = t_rphase+timedelta(seconds=(timespan.seconds*0.03))
                    t_rphase_stream, idx_rphase = find_nearest(t, date2num(t_rphase))
                    y_rphase = y[idx_rphase]
                    ax.annotate('Recovery\nPhase', xy=(t_rphase,y_rphase),
                                xytext=(x_anno,y_anno),
                                bbox=dict(boxstyle="round", fc="0.95", alpha=0.6))
                    stormanno_r = False

        #------------------------------------------------------------
        # PART 2: Formatting the plot
        #------------------------------------------------------------

        # ADD SENSOR IDS TO DATA PLOTS:
        if 'sensorid' in data[i]:
            sensorid = data[i]['sensorid']
            ydistance = [13,13,15,15,15,15,15,15]
            ax.annotate(sensorid, xy=(10, ydistance[n_subplots-1]),
                    xycoords='axes points',
                    horizontalalignment='left', verticalalignment='top')

        # ADD GRID:
        if grid:
            ax.grid(True,color=gridcolor,linewidth=0.5)

        # SET X-LABELS:
        timeunit = ''
        if confinex:
            tmin = np.min(t)
            tmax = np.max(t)
            # --> If dates to be confined, set value types:
            _confinex(ax, tmax, tmin, timeunit)

        if i < n_subplots-1:
            setp(ax.get_xticklabels(), visible=False)
        else:
            ax.set_xlabel("Time (UTC) %s" % timeunit, color=labelcolor)

        # SET TICK TO ALTERNATING SIDES:
        if bool(i & 1):
            ax.yaxis.tick_right()
            ax.yaxis.set_label_position("right")

        # APPLY FORMATTERS:
        label = data[i]['ylabel']
        ax.set_ylim(ymin,ymax)
        ax.set_ylabel(label, color=labelcolor)
        ax.get_yaxis().set_major_formatter(plt_fmt)

    #----------------------------------------------------------------
    # PART 3: Finalising and saving plot
    #----------------------------------------------------------------

    # BUNDLE UP ALL SUBPLOTS:
    fig.subplots_adjust(hspace=0)

    # ADJUST X-AXIS FOR FULLDAY PLOTTING:
    if fullday:
        ax.set_xlim(np.floor(np.round(np.min(t)*100)/100),np.floor(np.max(t)+1))

    # SAVE OR SHOW:
    # TODO the next two line are used for gui
    if figure:
        return ax
    if outfile:
        path = os.path.split(outfile)[0]
        if not path == '':
            if not os.path.exists(path):
                os.makedirs(path)
        if fmt:
            fig.savefig(outfile, format=fmt, dpi=savedpi)
        else:
            fig.savefig(outfile, dpi=savedpi)
    elif noshow:
        return fig
    else:
        plt.show()


def _confinex(ax, tmax, tmin, timeunit):
    """
    Automatically determines t-range so that the x-axis is easier
    on the eye.
    """

    trange = tmax - tmin
    loggerstream.debug('plot: x range = %s' % str(trange))
    if trange < 0.0001: # 8 sec level --> set 0.5 second
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%S'))
        timeunit = '[Sec]'
    elif trange < 0.01: # 13 minute level
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%M:%S'))
        timeunit = '[M:S]'
    elif trange <= 1: # day level -->  set 1 hour
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%H:%M'))
        timeunit = '[H:M]'
    elif trange < 7: # 3 day level
        if trange < 2:
            ax.get_xaxis().set_major_locator(matplotlib.dates.HourLocator(interval=6))
        elif trange < 5:
            ax.get_xaxis().set_major_locator(matplotlib.dates.HourLocator(interval=12))
        else:
            ax.get_xaxis().set_major_locator(matplotlib.dates.WeekdayLocator(byweekday=matplotlib.dates.MO))
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%d.%b\n%H:%M'))
        setp(ax.get_xticklabels(),rotation='0')
        timeunit = '[Day-H:M]'
    elif trange < 60: # month level
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%d.%b'))
        setp(ax.get_xticklabels(),rotation='70')
        timeunit = '[Day]'
    elif trange < 150: # year level
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%d.%b\n%Y'))
        setp(ax.get_xticklabels(),rotation='0')
        timeunit = '[Day]'
    elif trange < 600: # minute level
        if trange < 300:
            ax.get_xaxis().set_major_locator(matplotlib.dates.MonthLocator(interval=1))
        elif trange < 420:
            ax.get_xaxis().set_major_locator(matplotlib.dates.MonthLocator(interval=2))
        else:
            ax.get_xaxis().set_major_locator(matplotlib.dates.MonthLocator(interval=4))
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%b %Y'))
        setp(ax.get_xticklabels(),rotation='0')
        timeunit = '[Month]'
    else:
        ax.get_xaxis().set_major_formatter(matplotlib.dates.DateFormatter('%Y'))
        timeunit = '[Year]'


def _extract_data_for_PSD(stream, key):
    """
    Prepares data for power spectral density evaluation.
    """

    if len(stream.ndarray[0]) > 0:
        pos = KEYLIST.index(key)
        t = stream.ndarray[0]
        val = stream.ndarray[pos]
    else:
        t = np.asarray(stream._get_column('time'))
        val = np.asarray(stream._get_column(key))
    t_min = np.min(t)
    t_new, val_new = [],[]

    nfft = int(nearestPow2(len(t)))

    if nfft > len(t):
        nfft = int(nearestPow2(len(t) / 2.0))

    for idx, elem in enumerate(val):
        if not isnan(elem):
            t_new.append((t[idx]-t_min)*24*3600)
            val_new.append(elem)

    t_new = np.asarray(t_new)
    val_new = np.asarray(val_new)

    return t_new, val_new, nfft


#####################################################################
#                                                                   #
#       TESTING                                                     #
#       Run this after making changes:                              #
#       $ python mpplot.py                                          #
#                                                                   #
#####################################################################

if __name__ == '__main__':

    print()
    print("----------------------------------------------------------")
    print("TESTING: PLOTTING PACKAGE")
    print("All plotting methods will be tested. This may take a while.")
    print("A summary will be presented at the end. Any protocols")
    print("or functions with errors will be listed.")
    print()
    print("NOTE: This test requires graphical user interface")
    print("confirmation of package integrity for the majority of")
    print("functions. The plot titles specify what should be present")
    print("in the plot for it to have plotted successfully.")
    print("    So get comfy and have a good look.")
    print("----------------------------------------------------------")
    print()

    print("Please enter path of a variometer data file for testing:")
    print("(e.g. /srv/archive/WIC/LEMI025/LEMI025_2014-05-07.bin)")
    while True:
        filepath = raw_input("> ")
        if os.path.exists(filepath):
            break
        else:
            print("Sorry, that file doesn't exist. Try again.")
    print()

    now = datetime.utcnow()
    testrun = 'plottest_'+datetime.strftime(now,'%Y%m%d-%H%M')
    t_start_test = time.time()
    errors = {}

    print(datetime.utcnow(), "- Starting plot package test. This run: %s." % testrun)

    while True:

        # Step 1 - Read data
        try:
            teststream = read(filepath,tenHz=True)
            print(datetime.utcnow(), "- Stream read in successfully.")
        except Exception as excep:
            errors['read'] = str(excep)
            print(datetime.utcnow(), "--- ERROR reading stream. Aborting test.")
            break

        # Step 2 - Pick standard key for all other plots
        try:
            keys = teststream._get_key_headers()
            key = [keys[0]]
            key2 = [keys[0], keys[1]]
            print(datetime.utcnow(), "- Using %s key for all subsequent plots." % key[0])
        except Exception as excep:
            errors['_get_key_headers'] = str(excep)
            print(datetime.utcnow(), "--- ERROR getting default keys. Aborting test.")
            break

        # Step 3 - Simple single plot with ploteasy
        try:
            ploteasy(teststream)
            print(datetime.utcnow(), "- Plotted using ploteasy function.")
        except Exception as excep:
            errors['ploteasy'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with ploteasy function. Aborting test.")
            break

        # Step 4 - Standard plot
        try:
            plot_new(teststream,key,
                        plottitle = "Simple plot of %s" % key[0])
            print(datetime.utcnow(), "- Plotted standard plot.")
        except Exception as excep:
            errors['plot-vanilla'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with standard plot. Aborting test.")
            break

        # Step 5 - Multiple streams
        streamlist =    [teststream,    teststream      ]
        variables =     [key,           key2            ]
        try:
            plotStreams(streamlist, variables,
                        plottitle = "Multiple streams: Three bars, top two should match.")
            print(datetime.utcnow(), "- Plotted multiple streams.")
        except Exception as excep:
            errors['plotStreams-vanilla'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with plotting multiple streams. Aborting test.")
            break

        # Step 6 - Normalised stream comparison
        try:
            plotNormStreams([teststream], key[0],
                        confinex = True,
                        plottitle = "Normalized stream: Stream key should be normalized to zero.")
            print(datetime.utcnow(), "- Plotted normalized streams.")
        except Exception as excep:
            errors['plotNormStreams'] = str(excep)
            print(datetime.utcnow(), "--- ERROR plotting normalized streams.")

        # Step 7 - Flagged plot
        # ...

        # Step 8a - Plot with phases (single)
        t_start, t_end = teststream._find_t_limits()
        timespan = t_end - t_start
        t_stormphases = {}
        t_stormphases['ssc'] = t_start + timedelta(seconds=(timespan.seconds*0.2))
        t_stormphases['mphase'] = t_start + timedelta(seconds=(timespan.seconds*0.4))
        t_stormphases['rphase'] = t_start + timedelta(seconds=(timespan.seconds*0.6))
        t_stormphases['stormend'] = t_start + timedelta(seconds=(timespan.seconds*0.8))

        try:
            plot_new(teststream,key,
                        stormphases = True,
                        t_stormphases = t_stormphases,
                        plottitle = "Single plot showing all THREE storm phases, annotated")
            print(datetime.utcnow(), "- Plotted annotated single plot of storm phases.")
        except Exception as excep:
            errors['plot-stormphases'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with storm phases plot.")

        # Step 8b - Plot with phases (multiple)
        try:
            plotStreams(streamlist,variables,
                        stormphases = True,
                        t_stormphases = t_stormphases,
                        plottitle = "Multiple plot showing all THREE storm phases, annotated")
            print(datetime.utcnow(), "- Plotted annotated multiple plot of storm phases.")
        except Exception as excep:
            errors['plotStreams-stormphases'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with storm phases multiple plot.")

        # Step 9 - Plot satellite vs. magnetic data
        try:
            xmin, xmax = np.min(teststream._get_column('x')), np.max(teststream._get_column('x'))
            ymin, ymax = np.min(teststream._get_column('y')), np.max(teststream._get_column('y'))
            plotSatMag(teststream,teststream,['x','y'],
                        specialdict={'mag':[xmin-45,xmax+5],'sat':[ymin-5,ymax+45]},
                        plottitle = "Two variables in same plots with double y axes")
            print(datetime.utcnow(), "- Plotted magnetic/satellite data.")
        except Exception as excep:
            errors['plotSatMag'] = str(excep)
            print(datetime.utcnow(), "--- ERROR with plotSatMagplot.")

        # Step 10 - Plot power spectrum
        try:
            freqm, asdm = plotPS(teststream,key[0],
                        returndata=True,
                        marks={'Look here!':0.0001, '...and here!':0.01},
                        plottitle = "Simple power spectrum plot with two marks")
            print(datetime.utcnow(), "- Plotted power spectrum. Max frequency is at %s." % max(freqm))
        except Exception as excep:
            errors['plotPS'] = str(excep)
            print(datetime.utcnow(), "--- ERROR plotting power spectrum.")

        # Step 11 - Plot normal spectrogram
        try:
            plotSpectrogram(teststream,key2,
                        plottitle = "Spectrogram of two keys")
            print(datetime.utcnow(), "- Plotted spectrogram.")
        except Exception as excep:
            errors['plotSpectrogram'] = str(excep)
            print(datetime.utcnow(), "--- ERROR plotting spectrogram.")

        # Step 12 - Plot function
        try:
            func = teststream.fit(key,knotstep=0.02)
            plot_new(teststream,key,function=func,
                        plottitle = "Fit function plotted over original data.")
        except Exception as excep:
            errors['plot(function)'] = str(excep)
            print(datetime.utcnow(), "--- ERROR plotting function.")

        # Step 13 - Plot normal stereoplot
        # (This should stay as last step due to coordinate conversion.)
        try:
            teststream._convertstream('xyz2idf')
            plotStereoplot(teststream,
                        plottitle="Standard stereoplot")
            print(datetime.utcnow(), "- Plotted stereoplot.")
        except Exception as excep:
            errors['plotStereoplot'] = str(excep)
            print(datetime.utcnow(), "--- ERROR plotting stereoplot.")

        # If end of routine is reached... break.
        break

    t_end_test = time.time()
    time_taken = t_end_test - t_start_test
    print(datetime.utcnow(), "- Stream testing completed in %s s. Results below." % time_taken)

    print()
    print("----------------------------------------------------------")
    if errors == {}:
        print("0 errors! Great! :)")
    else:
        print(len(errors), "errors were found in the following functions:")
        print(str(errors.keys()))
        print()
        print("Would you like to print the exceptions thrown?")
        excep_answer = raw_input("(Y/n) > ")
        if excep_answer.lower() == 'y':
            i = 0
            for item in errors:
                print(errors.keys()[i] + " error string:")
                print("    " + errors[errors.keys()[i]])
                i += 1
    print()
    print("Good-bye!")
    print("----------------------------------------------------------")
