Previous: madrigal.ui   Up: madrigal.ui   Next: madrigal.ui.userData

Top

madrigal.ui.madrigalPlot module

madrigalPlot is the module that produces plots of Madrigal data.

Presently based on madplotlib: http://matplotlib.sourceforge.net/

$Id: madrigalPlot.py 7043 2019-10-07 19:10:59Z brideout $

"""madrigalPlot is the module that produces plots of Madrigal data.

Presently based on madplotlib: http://matplotlib.sourceforge.net/

$Id: madrigalPlot.py 7043 2019-10-07 19:10:59Z brideout $

"""
import sys,os
import os.path
import traceback
import types
import datetime
import bisect
import math
import colorsys
import subprocess

import numpy

import madrigal.metadata
import madrigal._derive


def defineHomeEnvVariable():
    """defineHomeEnvVariable makes sure HOME env variable is defined, as required by matplotlib.

    If not defined, sets HOME to MADROOT/metadata/userdata
    """
    try:
        os.environ['HOME']
        return
    except KeyError:
        import madrigal.metadata
        madDB = madrigal.metadata.MadrigalDB()
        os.environ['HOME'] = os.path.join(madDB.getMadroot(), 'metadata/userdata')


defineHomeEnvVariable()

import matplotlib
# set rendering
matplotlib.use('Agg')
import matplotlib.pylab
import matplotlib.colors
import matplotlib.cm
import numpy.ma
import numpy



def convertToAbsoluteTimeStr(xticks, noTime=False, noDate=False, timezone=0):
    """convertToAbsoluteTimeStr converts a list of strings containing seconds since 1/1/1950 to datetime string.
    
    Input: xticks - a list of strings containing seconds since 1/1/1950
    
    Returns: a list of strings formated as YYYY-MM-DD HH-MM-SS.  If noTime, format as YYYY-MM-DD
    """
    datetime1950 = datetime.datetime(1950,1,1,0,0,0)
    
    newList = []
    seconds = int(xticks[0]) - timezone
    iniDoy = datetime1950 + datetime.timedelta(0, seconds)
    iniDoy = int(iniDoy.strftime('%j'))
    
    for item in xticks:
        seconds = int(item) - timezone
        newDatetime = datetime1950 + datetime.timedelta(0, seconds)
        if noTime:
            newList.append(newDatetime.strftime('%Y-%m-%d'))
        elif noDate:
            curDoy = int(newDatetime.strftime('%j'))
            currHour = newDatetime.hour
            currMin = newDatetime.minute
            currHour += (curDoy - iniDoy)*24
            
            newList.append('%02d:%02d' %(currHour,currMin))

        else:
            newList.append(newDatetime.strftime('%Y-%m-%d %H:%M:%S'))
    
    return newList


def get_vo_cmap():
    """get_vo_cmap is a function to return a colormap optimized to show sign changes in the middle of the range.
    """
    LUTSIZE = matplotlib.rcParams['image.lut']

    _vo_cm_data =     {'red':   ((0, 0.75, 0.75), (0.4, 0.0, 0.0), (0.5, 0.0, 0.0),   (0.6, 1.0, 1.0),   (1.0, 1.0, 1.0)),
                       'green': ((0, 1.0, 1.0), (0.4, 0.5, 0.5), (0.5, 0.25, 0.25), (0.6, 0.25, 0.25), (1.0, 1.0, 1.0)),
                       'blue':  ((0, 1.0, 1.0), (0.4, 1.0, 1.0), (0.5, 0.5, 0.5),   (0.6, 0.0, 0.0),   (1.0, 0.5, 0.5))}


    vo_cm = matplotlib.colors.LinearSegmentedColormap('vo_cm',_vo_cm_data, LUTSIZE)

    return vo_cm


def getIsprintString(filename, parms, filterStr):
    """getIsprintString returns the output of isprint given inputs
    
    Inputs:
        filename - Madrigal filename 
        parms - comma-delimited parameters
        filterStr - arguments to pass to isprint cmd
    """
    madDB = madrigal.metadata.MadrigalDB()
    cmd = '%s/bin/isprint file=%s ' % (madDB.getMadroot(), filename)
    for parm in parms.split(','):
        cmd += '%s ' % (parm)
    cmd += filterStr
    cmd += ' header=f summary=f '
    pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
    result = pipe.read()
    delimiter = '\n'
    if type(result) in (bytes, numpy.bytes_):
        result = result.decode('utf-8')
    lines = result.split(delimiter)
    return(delimiter.join(lines[1:]).strip())



class madScatterPlot:
    """madScatterPlot is the class the produces two dimensional scatter plots of x versus y.


    """
    def __init__(self, isprintText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 noDate = False,
                 yMinimum = None,
                 yMaximum = None,
                 timezone = 0):
        """__init__ writes a madScatter plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
			  to the experiment beginning (UTH) or absolute (UT1).
			  The second must be the parameter to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float. 
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
	    
	        yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
			    
    	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
    	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
    			      parameter in isprintText must be UT1, if false, it must be UTH.
    
    	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
    	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	                startTime must be in units of UTH (hours since midnight UT of first day of
    	                experiment). Default is None, which means start at lowest time found.
    
    	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
    	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	              endTime must be in units of UTH (hours since midnight UT of first day of
    	              experiment). Default is None, which means end at largest time found.
    
    	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
    	                   needed to have at most maxNumPoints lines.
        
            noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum.  If default=None, use data minimum
    
            yMaximum - set y maximum.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
                        
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 2

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
	    
        # find min, max of y, not including missing
        yMin = None
        yMax = None
        for y in array_data[:,1]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
            if yMax == None:
                yMax = y
            elif yMax < y:
                yMax = y
		
        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(9,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
	    
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.14, 0.18, 0.83, 0.7]) 
      
        matplotlib.pylab.scatter(array_data[:,0],array_data[:,1])

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.grid(True)
            
        if useAbsoluteTime:
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
	    
        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)
            
        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()


    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        

    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

        

class madLineTimePlot:
    """madLineTimePlot is the class the produces line plots of one or more parameters versus time.


    """
    def __init__(self, isprintText,
                 yParmList,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 yMinimum = None,
                 yMaximum = None,
                 noDate = False,
                 timezone = 0):
        """__init__ writes a madLineTimePlot plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
			  to the experiment beginning (UTH) or absolute (UT1).
			  The the following must be the parameters to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            yParmList - a list of y parameters (strings).  Length must == num columns in isprintText - 1
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
            
            yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
			    
    	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
    	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
    			      parameter in isprintText must be UT1, if false, it must be UTH.
    
    	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
    	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	                startTime must be in units of UTH (hours since midnight UT of first day of
    	                experiment). Default is None, which means start at lowest time found.
    
    	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
    	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	              endTime must be in units of UTH (hours since midnight UT of first day of
    	              experiment). Default is None, which means end at largest time found.
    
    	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
    	                   needed to have at most maxNumPoints lines.
    
    	    noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
    	    
    	    noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
    	    yMinimum - set y minimum.  If default=None, use data minimum
    
    	    yMaximum - set y maximum.  If default=None, use data maximum
    	    
    	    timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
            
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 1 + len(yParmList)
        self.__colorList = 'gbrcmykw' # a list of colors for the lines

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
            threshold = self.__missing*0.9999
            array_data = numpy.ma.masked_where(array_data > threshold, array_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
	    
        # find yMin just to ensure there is data
        yMin = None
        for column in range(len(yParmList)):
            for y in array_data[:,1+column]:
                if y == self.__missing:
                    continue
                if yMin == None:
                    yMin = y
                elif yMin > y:
                    yMin = y
		
        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
	    
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
        else:
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
      
        for column in range(len(yParmList)):
            color = self.__colorList[column % len(self.__colorList)]
            matplotlib.pylab.plot(array_data[:,0],array_data[:,1 + column], '%so-' % (color), ms=5, mew=0.5)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.grid(True)

        if useAbsoluteTime:
            
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
                
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs, noTime)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                    newXLabels.append(str(i))
                matplotlib.pylab.xticks(newXLocs, newXLabels)

        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)

        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.legend(yParmList, numpoints=1)
          
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        
	
    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

    def writeToFile(self, fullFilename):
        pass

    def displayToScreen(self):
        pass

    def getFigureHandle(self):
        pass
    
    
    
class mad2YTimePlot:
    """mad2YTimePlot is the class the produces line plots of two parameters with different Y axes versus time.


    """
    def __init__(self, isprintText,
                 yParmList,
                 titleStr,
                 xLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 yMinimum = None,
                 yMaximum = None,
                 noDate = False,
                 timezone = 0):
        """__init__ writes a madLineTimePlot plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The following must be two parameters to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            yParmList - a list of two y parameters (strings)
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
                
            useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                              since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                      parameter in isprintText must be UT1, if false, it must be UTH.
    
            startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                        in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                        startTime must be in units of UTH (hours since midnight UT of first day of
                        experiment). Default is None, which means start at lowest time found.
    
            endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
                      in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                      endTime must be in units of UTH (hours since midnight UT of first day of
                      experiment). Default is None, which means end at largest time found.
    
            maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                           needed to have at most maxNumPoints lines.
    
            noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum of first parm.  If default=None, use data minimum
    
            yMaximum - set y maximum of first.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
            
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 1 + len(yParmList)
        self.__colorList = 'gbrcmykw' # a list of colors for the lines

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
            threshold = self.__missing*0.9999
            array_data = numpy.ma.masked_where(array_data > threshold, array_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
        
        # find yMin just to ensure there is data
        yMin = None
        for column in range(len(yParmList)):
            for y in array_data[:,1+column]:
                if y == self.__missing:
                    continue
                if yMin == None:
                    yMin = y
                elif yMin > y:
                    yMin = y
        
        if yMin == None:
            raise ValueError('No valid y data found')


        fig, ax1 = matplotlib.pylab.subplots()
        # select the plot size
        if size == 'small':
            fig.set_size_inches(8, 3)
        elif size == 'wide':
            fig.set_size_inches(10,4)
        elif size == 'large':
            fig.set_size_inches(12,6)
        
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
        else:
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
      
        # plot first
        color = self.__colorList[0]
        ax1.plot(array_data[:,0],array_data[:,1], '%so-' % (color))
        ax1.set_ylabel(yParmList[0], color=color)
        
        ax2 = ax1.twinx()
        color = self.__colorList[1]
        ax2.plot(array_data[:,0],array_data[:,2], '%so-' % (color))
        ax2.set_ylabel(yParmList[1], color=color)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.grid(True)

        if useAbsoluteTime:
            
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
                
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs, noTime)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                ax1.set_xticks(newXLocs)
                ax2.set_xticks(newXLocs)

        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None or yMaximum != None:
            ax1.set_ylim(yMinimum, yMaximum)

        matplotlib.pylab.title(titleStr, fontsize=fontSize)
          
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        
    
    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing




                 
class madXYScatterPlot:
    """madXYScatterPlot is the class the produces XY scatter plots.


        Change History:

            Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug 2, 2006
            Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug 2, 2006

    """

    def __init__(self,inputText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size='small',
                 lowBound=None,
                 highBound=None,
                 maxNumPoints=None,
                 yLegend=None):

        """
        Inputs:

            inputText - string of values to be plotted. The formatting is as
                        follows:

                        xval yval
                        xval yval
                        xval yval

            titleStr - title of the Plot

            xLabelStr - label for the x axis

            yLabelStr - label for the y axis

            fullFilename - full file path to save the resulting picture to

            size - size of plot to be saved. Must be 'small','wide', or 'large'.
                defaults to 'small'.

            lowBound - lower bound on the x-axis value. If no bound is specified,
                    the lowest value found in inputText will be used.

            highBound - upper bound on the x-axis value. If no bound is specified,
                    the highest value found in inputText will be used.

            maxNumPoints - maximum number of points to be plotted.

        Outputs: None

        Affects: Creates a scatter plot using matplotlib and writes that to the
                    file designated by the variable 'fullFilename'

        Exceptions: ValueError if lowBound or highBound cannot be converted to
                    a float value

        Non-standard python modules used:

            matplotlib
        """

        #verify input
        if size not in ('small','wide','large'):
            raise ValueError('size must be "small","wide", or "large", not %s'%(str(size)))

        if size in ('small','wide'):
            fontSize = 12
        elif size in 'large':
            fontSize = 18

        if maxNumPoints !=None:
            inputText = self.__truncateInput(inputText, maxNumPoints)


        if lowBound !=None:
            #check that it is a number
            try:
                lowBound = float(lowBound)
            except ValueError:
                raise ValueError('lowBound not a number')
            try:
                highBound = float(highBound)
            except:
                raise ValueError('lowBound not a number')     
                

        #convert the input data into numeric arrays
        x=[]
        y=[]

        split_data = inputText.split()
        #send x-values to x, y-values to y
        i=0
        while i xhigh:
                    xhigh = i
            xlow = lowBound
        elif lowBound==None and highBound !=None:
            #find the lower bound in x
            for i in x:
                if xlow==None:
                    xlow = i
                if ixhigh:
                    xhigh = i

        #find upper and lower bounds in y
        ylow = None
        yhigh = None
        for i in y:
            if ylow==None:
                ylow = i
            elif iyhigh:
                yhigh = i



        #check for good numbers
        if ylow == None:
            raise ValueError('no valid y data found')

        #select plot sizes
        if size=='small':
            matplotlib.pylab.figure(1,figsize=(6,4),facecolor='w')
        elif size=='wide':
            matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
        elif size=='large':
            matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')


        #draw scatter plot
        #matplotlib.pylab.scatter(x,y)
        matplotlib.pylab.plot(x,y, 'gd',ms=5,mew=0.5)


        thisFont = {'size' : fontSize}
        matplotlib.pylab.rc('font', **thisFont) #pass in font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr,fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr,fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xlow,xhigh)
        matplotlib.pylab.title(titleStr,fontsize=fontSize)
        matplotlib.pylab.grid(True)
        
        if yLegend != None: matplotlib.pylab.legend(yLegend,numpoints=1)

        #save the figure
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()
        
class madXYwithErrorPlot:
    """madXYwithErrorPlot is the class the produces two dimensional scatter plots of x versus y more its error.
    Written by "Miguel Urco":mailto:murco@jro.igp.gob.pe Jul 2, 2009

    """
    def __init__(self, isprintText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 noDate = False,
                 yMinimum = None,
                 yMaximum = None,
                 timezone = 0):
        """__init__ writes a madScatter plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First of three parameters
                          must be UTH or UT1, depending on whether time scale should be relative
                          to the experiment beginning (UTH) or absolute (UT1).
                          The second must be the parameter to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float. 
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
        
            yLabelStr - y label string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
                
            useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                            since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                            parameter in isprintText must be UT1, if false, it must be UTH.

            startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                    in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                    startTime must be in units of UTH (hours since midnight UT of first day of
                    experiment). Default is None, which means start at lowest time found.

            endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
                  in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                  endTime must be in units of UTH (hours since midnight UT of first day of
                  experiment). Default is None, which means end at largest time found.

            maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                       needed to have at most maxNumPoints lines.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum.  If default=None, use data minimum
    
            yMaximum - set y maximum.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
                        
        Returns: void

        Affects: None
        
        Now removes all lines with missing
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 3

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18
            
        # filter out missing lines
        newIsprint = ''
        lines = isprintText.split('\n')
        for line in lines:
            try:
                items = line.split()
                float(items[0])
                float(items[1])
                float(items[2])
                newIsprint += line + ' '
            except:
                continue
        isprintText = newIsprint

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
        
        # find min, max of y, not including missing
        yMin = None
        yMax = None

        for y in array_data[:,1]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
            if yMax == None:
                yMax = y
            elif yMax < y:
                yMax = y

        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
            matplotlib.pylab.axes([0.11, 0.18, 0.78, 0.7])
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
        
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
      
        matplotlib.pylab.errorbar(array_data[:,0],array_data[:,2],array_data[:,1],ecolor='red',fmt='s',mfc='red',mec='green',ms=1,mew=1)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.grid(True)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)

        if useAbsoluteTime:
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0)+1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        
        matplotlib.pylab.xticks(fontsize=fontSize)

        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()
        
    def __truncateInput(self,inputStr,maxLines):
        """__truncateInput truncates inputStr to have maxLines at most
        """
        inputList = inputStr.split('\n')
        if len(inputList) zMax and z != self.__missing:
                        zMax = z
                elif z != self.__missing:
                    zMax = z

            except:
                continue
		
        if zMin == None:
            raise ValueError('No valid z data found')

        # if both minColormap and maxColormap == None, use autoscaling
        if minColormap == None and maxColormap == None:
            zList.sort()
            d10 = zList[int(len(zList)*0.10)]
            d90 = zList[int(len(zList)*0.90)]

            zMin = d10 - (d90-d10) * 0.75
            zMax = d90 + (d90-d10) * 0.75

        # now sort the X and Y axis lists and pull their length
        
        xList.sort()
        if startTime == None:
            xMin = xList[0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = xList[-1]
        else:
            xMax = endTime
        yList.sort()
        max_x_dimension = len(xList)
        max_y_dimension = len(yList)

        if yMinimum == None:
            yMinimum = yList[0]
        if yMaximum == None:
            yMaximum = yList[-1]

        self.truncateAlt = False
        if maxNumAlt != None:
            if max_y_dimension > maxNumAlt:
                self.truncateAlt = True

        # build dictonary of indexes into xList
        self.xListDict = {}
        for i in range(len(xList)):
            self.xListDict[xList[i]] = i

        # if self.truncateAlt == False, build dictonary of indexes into yList,
        # else truncate y values by builing a list of maxNumAlt ranges
        if self.truncateAlt == False:
            self.yListDict = {}
            for i in range(len(yList)):
                self.yListDict[yList[i]] = i
        else:
            self.yListRanges = []
            for i in range(maxNumAlt):
                self.yListRanges.append(yList[int(i*(len(yList)/maxNumAlt))])
            max_y_dimension = maxNumAlt

        # now build arrays to handle the X axis label, Y axis label, and the Z data

        X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)

        # all parameter values default to missing
        Z = Z * self.__missing

        # fill the X and Y arrays

        for i in range(max_x_dimension):
            for j in range(max_y_dimension):
                X[i][j] = float(xList[i])
                if self.truncateAlt:
                    Y[i][j] = float(yList[int(j*(len(yList)/maxNumAlt))])
                else:
                    Y[i][j] = float(yList[j])

    
        # Now load up the data array Z with the array_data measurements as a function of x and y
        previousIndex = None
        previousValue = None
        presentTime = None
        newTimeFound = True

        for k in range(len(array_data)):
            try:

                xdata = array_data[k][0]
                ydata = array_data[k][1]
                zdata = array_data[k][2]

                if zdata == self.__missing:
                    continue

                if xdata != presentTime:
                    newTimeFound = True
                else:
                    newTimeFound = False
                presentTime = xdata

                # now find the right place in the array for this data point
                i = self.xListDict[xdata]
                j = self.__getYIndex(ydata)

                Z[i][j] = zdata
		
                # now see if we need to fill in any gaps
                if (not newTimeFound) and smoothAltitude:
                    if previousIndex < j - 1:
                        # fill in all missing points
                        for l in range(previousIndex + 1, j):
                            # simply average between the points based on index
                            thisValue = previousValue + (zdata - previousValue)*(float(l-previousIndex)/float(j-previousIndex))
                            Z[i][l] = thisValue
			   
                previousIndex = j
                previousValue = zdata
		
                
            except:
                continue
		
        # insert missing data to represent gaps if needed
        if insertDataGap != None:
            # first find the time interval greater than 90% of others
            timeIntervalList = []
            for i in range(len(xList) - 1):
                timeIntervalList.append(xList[i+1] - xList[i])
            timeIntervalList.sort()
            index = int(len(timeIntervalList)*0.9)
            maxInterval = timeIntervalList[index]
	    
            for i in range(len(xList) - 1):
                if xList[i+1] - xList[i] > maxInterval * insertDataGap:
                    Z[i,:] = self.__missing

        # insert missing data to represent gaps if needed in Altitude
        if insertDataGap != None and not smoothAltitude:
            # first find the time interval greater than 90% of others
            altitudeIntervalList = []
            for i in range(len(yList) - 1):
                altitudeIntervalList.append(yList[i+1] - yList[i])
            altitudeIntervalList.sort()
            index = int(len(altitudeIntervalList)*0.9)
            maxInterval = altitudeIntervalList[index]
        
            for j in range(len(yList) - 1):
                if yList[j+1] - yList[j] > maxInterval * insertDataGap and j < max_y_dimension:
                    Z[:,j] = self.__missing
                    
        # make Z a masked array
        Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)

        # set up plotting parameters
        if minColormap == None:
            minColormap = zMin
        if maxColormap == None:
            maxColormap = zMax

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(14,5), facecolor = 'w')
	    
        if useAbsoluteTime:
            # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.1, 0.85, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.97, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
        else:
            if size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
            elif size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.9, 0.73])

        matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap,
                                norm = matplotlib.pylab.Normalize(), edgecolor='face')
        matplotlib.pylab.colorbar()

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)

        if useAbsoluteTime:
            # if plot is less than 49 hours
            xmin, xmax = matplotlib.pylab.xlim()
            xmin = numpy.floor(xmin / (60*60))
            xmax = numpy.ceil(xmax / (60*60))
            if noDate == True:
                newXLabels = []
                if (xmax - xmin) <= 1:
                    newXLocs = []
                    xRange = numpy.arange(0,1,0.25)
                    for i in xRange:
                        tmp=(xmin+i)*60*60
                        newXLocs.append(tmp)
                elif (xmax - xmin) <= 2:
                    newXLocs = []
                    xRange = numpy.arange(0,2,0.5)
                    for i in xRange:
                        tmp=(xmin+i)*60*60
                        newXLocs.append(tmp)
                else:
                    newXLocs = []
                    step = int((xmax-xmin-1)/6)+1
                    xRange =  list(range(0,int(xmax-xmin),step))
                    for i in xRange:
                        tmp = (xmin + i)*60*60
                        newXLocs.append(tmp)

                #newXLocs.append(xmax*60*60)                                                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)

        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                    newXLabels.append(str(i))
                matplotlib.pylab.xticks(newXLocs, newXLabels)


        matplotlib.pylab.xticks(fontsize=fontSize)

        # add second y-axis if desired
        if altYTitle != None and altYLabels != None:
            ax2 = matplotlib.pylab.twinx()
            matplotlib.pylab.ylabel(altYTitle)
            ax2.yaxis.tick_right()
            matplotlib.pylab.yticks(list(range(len(altYLabels))), altYLabels)
            matplotlib.pylab.yticks(fontsize=fontSize)

        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
        if peakAltStr:
            # plot peak altitudes
            data = peakAltStr.split()
            xdata = [float(item) for i, item in enumerate(data) if i % 2 == 0]
            ydata = [float(item) for i, item in enumerate(data) if i % 2 == 1]
            matplotlib.pylab.plot(xdata, ydata, 'b+')
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()



    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

    def __getYIndex(self, yvalue):
        """__getYIndex returns the correct index into the y dimension for a given y value.

        Input: yvalue - value of y parameter

        Returns: the correct index into the y dimension

        Algorithm: if self.truncateAlt == False, simple use the dictionary self.yListDict.  Else
        loop through self.yListRanges and return the first greater than the requested value
        """
        if self.truncateAlt == False:
            return self.yListDict[yvalue]
        else:
            i = bisect.bisect_left(self.yListRanges, yvalue)
            if i >= len(self.yListRanges):
                i = len(self.yListRanges) - 1
            return i
        
            
    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newIsprintText = ''
            for i in range(0,len(isprintList),dropNumber):
                newIsprintText += isprintList[i] + '\n'

            return newIsprintText


        
    def displayToScreen(self):
        " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
        pass 

    def getFigureHandle(self):
        return self.__figure


    def getAverage(self, X):
        """returns the average of items in a float array.  Does not including missing data.
        If all data missing, returns self.__missing
        """
        count = 0
        total = 0.0
        for i in range(X.shape[0]):
            if X[i] != self.__missing:
                count += 1
                total += X[i]

        if count == 0:
            return self.__missing
        else:
            return total / float(count)


    def sortArrayInTime(self, array_data):
        """sortArrayInTime sorts a two-dimensional array so that the first element in each row (time) is in ascending order.

        Input: array_data - two-dimensional array to be sorted by rearranging rows so
               that the first element in each row (time) is in ascending order

        Returns: new_array
        """

        sortIndex = numpy.argsort(array_data[:,0])[0]

        # if already sorted, just return original array
        if sortIndex == numpy.sort(sortIndex):
            return array_data
        
        new_array = numpy.zeros(array_data.shape, array_data.dtype)

        for i in range(len(sortIndex)):
            new_array[sortIndex[i],:] = array_data[i,:]


        return new_array


    def decimateTimes(self, array_data, maxNumTimes, insertDataGap):
        """decimateTimes decimates array_data to have at most maxNumTimes times.

        Input: array_data - two-dimensional array to be decimated by deleting times and missing data.

               maxNumTimes: int representing the maximum number of times to keep in array_data

               insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
	                    being plotted are ordered, and the time gap larger than 90% of the rest is determined.
			    Note that this parameter is used here to stop the truncation of isprint lines that
			    will eventually be considered edge lines.

        Returns: new array built from decimated array_data

        """

        # get the number of times in array_data, and make a list of all unique times
        numTimes = 0
        uniqueTimes = []
        time_array = array_data[:, 0]
        for i in range(len(time_array)):
            if i == 0:
               numTimes =  1
               uniqueTimes.append(time_array[i])
            elif time_array[i-1] != time_array[i]:
                uniqueTimes.append(time_array[i])
                numTimes +=  1

        if numTimes <= maxNumTimes:
            return array_data
        

        # insert missing data to represent gaps if needed
        gapTimes = []
        if insertDataGap != None:
            # first find the time interval greater than 90% of others
            timeIntervalList = []
            for i in range(len(time_array) - 1):
                if time_array[i+1] == time_array[i]:
                    continue
                timeIntervalList.append(time_array[i+1] - time_array[i])
            timeIntervalList.sort()
            index = int(len(timeIntervalList)*0.9)
            maxInterval = timeIntervalList[index]
	    
            for i in range(len(time_array) - 1):
                if time_array[i+1] - time_array[i] > maxInterval * insertDataGap:
                    gapTimes.append(time_array[i+1])
                    gapTimes.append(time_array[i])

        # get the number of times to skip each time
        numSkip = numTimes/maxNumTimes

        # get the number of rows in the new_array
        numRows = 0
        numTimes = 0
        useThisTime = False
        for i in range(len(time_array)):
            if i == 0:
                numTimes =  1
            elif time_array[i-1] != time_array[i]:
                numTimes +=  1
                if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                    useThisTime = True
                    if array_data[i, -1] != self.__missing:
                        numRows += 1
                else:
                    useThisTime = False
            else:
                if useThisTime:
                    if array_data[i, -1] != self.__missing:
                        numRows += 1

        # create new_array
        new_array = numpy.zeros((numRows, array_data.shape[1]), array_data.dtype)

        # copy selected rows to new_array
        numRows = 0
        numTimes = 0
        useThisTime = False
        for i in range(len(time_array)):
            if i == 0:
                numTimes =  1
            elif time_array[i-1] != time_array[i]:
                numTimes +=  1
                if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                    useThisTime = True
                    if array_data[i, -1] != self.__missing:
                        new_array[numRows,:] = array_data[i,:]
                        numRows += 1
                else:
                    useThisTime = False
            else:
                if useThisTime:
                    if array_data[i, -1] != self.__missing:
                        new_array[numRows,:] = array_data[i,:]
                        numRows += 1

        return new_array



class madPcolorScan:
    """madPcolorScan is the class that produces pcolor scans.

    Usage example::

    	obj = madPcolorScan(isprintText,
                        'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                        'Longitude',
                        'Latitude',
                        './isprint.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 20, 2006
    """


    def __init__(self, isprintText,
                 xGridSize,
                 yGridSize,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet,
                 maxNumLines = None):
        """__init__ writes a madPcolorScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be the X axis value, and the second must be the Y axis value.
			  The third column is the value (intensity). Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            xGridSize - grid size for x data (for example 0.1 for 0.1 degree longitude grid)

            yGridSize - grid size for x data (for example 0.1 for 0.1 degree latitude grid)
            
            titleStr - plot title (string) - should describe parameter being plotted
	    
    	    xLabelStr - x label string
    	    
    	    yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found.

            xMaximum = maximum x value.  If None (default), uses highest x value found.

            yMinimum = minumum y value.  If None (default), uses lowest y value found.

            yMaximum = maximum y value.  If None (default), uses highest y value found.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

	    maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation

	              
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, used to create a masked array
        self.__parameter_count = 3
        self.__xGridSize = int(xGridSize)
        self.__yGridSize = int(yGridSize)
        self.__parseCount = 0 # used to tell which column self.__filter_input is working on

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))

        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumLines != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumLines)

        # since matplotlib pcolor wants a regular grid, we create a grid with all altitudes

        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_input, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')

        array_data = self.sortArrayInX(array_data)

        
        # The first pass through is to obtain the number and range of the  x and y variables
        
        xList = []
        yList = []
        zList = []
        zMin = None
        zMax = None

        # loop over all the lines of data in the array
        for j in range(len(array_data)):
            try:
                
                x = array_data[j][0]
                y = array_data[j][1]
                z = array_data[j][2]

                if x not in xList:
                    xList.append(x)

                if y not in yList:
                    yList.append(y)

                if z != self.__missing:
                    zList.append(z)

                if zMin != None:
                    if z < zMin and z != self.__missing:
                        zMin = z
                elif z != self.__missing:
                    zMin = z

                if zMax != None:
                    if z > zMax and z != self.__missing:
                        zMax = z
                elif z != self.__missing:
                    zMax = z

            except:
                continue

		
        if zMin == None:
            raise ValueError('No valid z data found')

        # if both minColormap and maxColormap == None, use autoscaling
        if minColormap == None and maxColormap == None:
            zList.sort()
            d10 = zList[int(len(zList)*0.10)]
            d90 = zList[int(len(zList)*0.90)]

            zMin = d10 - (d90-d10) * 0.75
            zMax = d90 + (d90-d10) * 0.75

        # now sort the X and Y axis lists and pull their length
        
        xList.sort()
        xMin = xList[0]
        xMax = xList[-1]
        yList.sort()
        yMin = yList[0]
        yMax = yList[-1]
        max_x_dimension = len(xList)
        max_y_dimension = len(yList)


        # build dictonary of indexes into xList
        self.xListDict = {}
        for i in range(len(xList)):
            self.xListDict[xList[i]] = i

        # build dictonary of indexes into yList,
        self.yListDict = {}
        for i in range(len(yList)):
            self.yListDict[yList[i]] = i


        # now build arrays to handle the X axis label, Y axis label, and the Z data

        X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)

        # all parameter values default to missing
        Z = Z * self.__missing

        # fill the X and Y arrays

        for i in range(max_x_dimension):
            for j in range(max_y_dimension):
                X[i][j] = float(xList[i])
                Y[i][j] = float(yList[j])

    
        # Now load up the data array Z with the array_data measurements as a function of x and y
        previousIndex = None
        previousValue = None
        presentTime = None
        newTimeFound = True

        for k in range(len(array_data)):
            try:

                xdata = self.__round(array_data[k][0], self.__xGridSize)
                ydata = self.__round(array_data[k][1], self.__yGridSize)
                zdata = array_data[k][2]

                if zdata == self.__missing:
                    continue

                if xdata != presentTime:
                    newTimeFound = True
                else:
                    newTimeFound = False
                presentTime = xdata

                # now find the right place in the array for this data point
                i = self.xListDict[xdata]
                j = self.yListDict[ydata]

                Z[i][j] = zdata
			   
                previousIndex = j
                previousValue = zdata
		
                
            except:
                continue
		

        # make Z a masked array
        Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)

        # set up plotting parameters
        if xMinimum == None:
            xMinimum = xMin
        if xMaximum == None:
            xMaximum = xMax
        if yMinimum == None:
            yMinimum = yMin
        if yMaximum == None:
            yMaximum = yMax
        if minColormap == None:
            minColormap = zMin
        if maxColormap == None:
            maxColormap = zMax

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(6,4), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
	    

        matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap, norm = matplotlib.pylab.Normalize())
        matplotlib.pylab.colorbar()

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMinimum, xMaximum)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)


        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()

        try:
            matplotlib.pylab.savefig(fullFilename)
        except:
            matplotlib.pylab.clf()
            raise
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()


    def __round(self, value, increment):
        """__round returns a value to the nearest increment 
        """
        return value - math.fmod(value, increment)



    def __filter_input(self,x):
        """__filter_input is called in map to convert missing strings to self.__missing, and to
        round x and y vales to the nearest xGridSize or yGridSize
        """
        try:
            value = float(x)
            if self.__parseCount % 3 == 0:
                # x value
                value = self.__round(value, self.__xGridSize)
            elif self.__parseCount % 3 == 1:
                # y value
                value = self.__round(value, self.__yGridSize)
                
        except:
            value = self.__missing

        self.__parseCount += 1
        return value
        
            
    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newIsprintText = ''
            for i in range(0,len(isprintList),dropNumber):
                newIsprintText += isprintList[i] + '\n'

            return newIsprintText


        
    def displayToScreen(self):
        " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
        pass 

    def getFigureHandle(self):
        return self.__figure


    def getAverage(self, X):
        """returns the average of items in a float array.  Does not including missing data.
        If all data missing, returns self.__missing
        """
        count = 0
        total = 0.0
        for i in range(X.shape[0]):
            if X[i] != self.__missing:
                count += 1
                total += X[i]

        if count == 0:
            return self.__missing
        else:
            return total / float(count)


    def sortArrayInX(self, array_data):
        """sortArrayInX sorts a two-dimensional array so that the first element in each row (x) is in ascending order.

        Input: array_data - two-dimensional array to be sorted by rearranging rows so
               that the first element in each row (x) is in ascending order

        Returns: new_array
        """

        sortIndex = numpy.argsort(array_data[:,0])[0]

        # if already sorted, just return original array
        if sortIndex == numpy.sort(sortIndex):
            return array_data
        
        new_array = numpy.zeros(array_data.shape, array_data.dtype)

        for i in range(len(sortIndex)):
            new_array[sortIndex[i],:] = array_data[i,:]


        return new_array
    
    
class madPcolorWedgeScan:
    """madPcolorWedgeScan is the class that produces pcolor scans where data is drawn as wedge shapes.

    Usage example::

        obj = madPcolorScan(isprintText,
                        'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                        'Longitude',
                        'Latitude',
                        './isprint.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 20, 2006
    """


    def __init__(self, isprintText,
                 scanType,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet):
        """__init__ writes a madPcolorWedgeScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. The 9 parameters
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
            
            titleStr - plot title (string) - should describe parameter being plotted
        
            xLabelStr - x label string
        
            yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest value found.

            xMaximum = maximum x value.  If None (default), uses highest value found.

            yMinimum = minumum y value.  If None (default), uses lowest value found.

            yMaximum = maximum y value.  If None (default), uses highest value found.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

        Returns: void

        Affects: Writes fullFilename to disk
        """

        # verify input
        if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
            raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))
        
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))

        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        isprintLines = isprintText.split('\n')
        
        if xMaximum == None or xMinimum == None or yMaximum == None or yMinimum or maxColormap == None or minColormap == None:
            newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
            # set values
            xMaximum, xMinimum, yMaximum, yMinimum = self._setLimits(scanType, xMaximum, xMinimum, yMaximum, yMinimum,
                                                                     newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, 
                                                                     newGreatCDist, newParmMax, newParmMin)
            
            if maxColormap == None:
                maxColormap = newParmMax
            if minColormap == None:
                minColormap = newParmMin
            
        
        # set up wedge generator
        dataGen = self._generateWedges(isprintLines, scanType)
        
        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(6,5), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
            
        try:
            while True: 
                data = next(dataGen)
                x = [data[1][0], data[2][0], data[3][0], data[4][0]]
                y = [data[1][1], data[2][1], data[3][1], data[4][1]]
                dataRatio = (data[0] - minColormap) / (maxColormap - minColormap)
                colorStr = matplotlib.colors.rgb2hex(colorMap(dataRatio)[:3])
                thisPlot = matplotlib.pyplot.fill(x, y, colorStr, edgecolor='none')
        except StopIteration:
            pass

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
        
        scalableMappable = matplotlib.cm.ScalarMappable(cmap=colorMap)
        scalableMappable.set_array(numpy.array([minColormap, maxColormap]))
        scalableMappable.set_clim(minColormap, maxColormap)
        def get_alpha(*args, **kw):
            return(1.0)
        scalableMappable.get_alpha = get_alpha


        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMinimum, xMaximum)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.colorbar(mappable=scalableMappable)
        matplotlib.pylab.gcf().subplots_adjust(bottom=0.15)

        try:
            matplotlib.pylab.savefig(fullFilename)
        except:
            matplotlib.pylab.clf()
            raise

        matplotlib.pylab.clf()
        
        
    def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
        """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
        
        Inputs: gdlatr, glonr: reference point on earth's surface
                gdlat, glon: second point on earth's surface
        """
        dlon = glonr/57.2958 - glon/57.2958
        dlat = gdlatr/57.2958 - gdlat/57.2958;
        a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)
    
        if math.sqrt(a) < 1.0:
            c = 2.0 * math.asin(math.sqrt(a))
        else:
            c = 2.0 * math.asin(1.0)
    
        return(c * 6371.2)
        
        
    def _getLimits(self, isprintLines):
        """returns (newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin) 
        given isprint lines with
        parameters (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, )
        
        We only look at the end points of ranges and the station location
        """
        # set initial values to station location
        items = isprintLines[1].split()
        slat = float(items[0])
        slon = float(items[1])
        if slon > 180.0:
            slon -= 360.0
        salt = float(items[2])
        maxLon = slon
        minLon = slon
        maxLat = slat
        minLat = slat
        maxAlt = salt
        maxGreatCDist = 0.0
        maxParm = -1.0e-30
        minParm = 1.0e30
        
        lastAz = None
        lastEl = None
        lastRange = None
        
        for i, isprintLine in enumerate(isprintLines):
            items = isprintLine.split()
            if len(items) < 9:
                continue
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            thisAz = (az1 + az2)/2
            el1 = float(items[6])
            el2 = float(items[7])
            thisEl = (el1 + el2)/2
            try:
                parm = float(items[8])
                if parm > maxParm:
                    maxParm = parm
                if parm < minParm:
                    minParm = parm
            except ValueError:
                continue
            
            # see if we're at an end point
            if lastAz != None:
                if abs(lastAz - thisAz) > 0.001 or abs(lastEl - thisEl) > 0.001:
                    # the last point was an end point
                    gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,salt,lastAz,lastEl,lastRange)
                    if gdlat > maxLat:
                        maxLat = gdlat
                    if gdlat < minLat:
                        minLat = gdlat
                    if glon > maxLon:
                        maxLon = glon
                    if glon < minLon:
                        minLon = glon
                    if gdalt > maxAlt:
                        maxAlt = gdalt
                    greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
                    if greatCDist > maxGreatCDist:
                        maxGreatCDist = greatCDist
                        
            lastAz = thisAz
            lastEl = thisEl
            lastRange = range
            
        # check last point
        gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,slat,lastAz,lastEl,lastRange)
        if gdlat > maxLat:
            maxLat = gdlat
        if gdlat < minLat:
            minLat = gdlat
        if glon > maxLon:
            maxLon = glon
        if glon < minLon:
            minLon = glon
        if gdalt > maxAlt:
            maxAlt = gdalt
        greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
        if greatCDist > maxGreatCDist:
            maxGreatCDist = greatCDist
            
        return((maxLon, minLon, maxLat, minLat, maxAlt, maxGreatCDist, maxParm, minParm))
    
    
    def _setLimits(self, scanType, xMaximum, xMinimum, yMaximum, yMinimum,
                   newLonMax, newLonMin, newLatMax, newLatMin, newAltMax,
                   newGreatCDist, newParmMax, newParmMin):
        """ _setLimits sets xMaximum, xMinimum, yMaximum, yMinimum based on values
        found in self._getLimits and scanType.  If any value in (xMaximum, xMinimum, yMaximum, yMinimum)
        is not None, it will not be changed. """
        newXMax = xMaximum
        newXMin = xMinimum
        newYMax = yMaximum
        newYMin = yMinimum
        if scanType == 'az':
            if xMaximum == None:
                newXMax = newLonMax
            if xMinimum == None:
                newXMin = newLonMin
            if yMaximum == None:
                newYMax = newLatMax
            if yMinimum == None:
                newYMin = newLatMin
        elif scanType == 'el_lat':
            if xMaximum == None:
                newXMax = newLatMax
            if xMinimum == None:
                newXMin = newLatMin
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        elif scanType == 'el_lon':
            if xMaximum == None:
                newXMax = newLonMax
            if xMinimum == None:
                newXMin = newLonMin
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        elif scanType == 'el_gcdist':
            if xMaximum == None:
                newXMax = newGreatCDist
            if xMinimum == None:
                newXMin = 0.0
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        return((newXMax, newXMin, newYMax, newYMin))
    
    
    def _generateWedges(self, isprintLines, scanType):
        """_generateWedges is a python generator that return a data wedge in the form
        (dataValue, (point1_x, point1_y), (point2_x, point2_y), 
        (point3_x, point3_y), (point4_x, point4_y))
        
        Inputs:
        
            isprintLines - a list of line with 9 parameters that
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location.
                No wedge is returned for a missing data line
                
            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
        """
        # state variables
        lastAz1 = None
        lastAz2 = None
        lastEl1 = None
        lastEl2 = None
        lastRange = None
        lastDiffRangeLow = None
        lastValue = None
        
        for isprintLine in isprintLines:
            items = isprintLine.split()
            if len(items) < 9:
                continue
            gdlatr = float(items[0])
            gdlonr = float(items[1])
            galtr = float(items[2])
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            el1 = float(items[6])
            el2 = float(items[7])
            valueStr = items[8]
            if lastAz1 != None and ( abs(lastAz1-az1) > 0.001 or abs(lastEl1-el1) > 0.001 ):
                # new radar line found, send last point if needed
                if lastValue != None:
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue, scanType)
                    yield(dataWedge)
                # this is the first point in the line, so no lastDiffRangeLow
                lastDiffRangeLow = None
            elif lastAz1 != None and ( abs(lastAz1-az1) < 0.001 and abs(lastEl1-el1) < 0.001 ):
                # continuing radar line
                if lastValue != None:
                    lastDiffRangeLow = (range - lastRange)/2.0
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, (range-lastRange)/2.0, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue, scanType)
                    yield(dataWedge)
                else:
                    lastDiffRangeLow = (range - lastRange)/2.0
            # reset state variables
            lastAz1 = az1
            lastAz2 = az2
            lastEl1 = el1
            lastEl2 = el2
            lastRange = range
            try:
                lastValue = float(valueStr)
            except:
                lastValue = None
                
        # finally, send last wedge if needed
        if lastValue != None:
            dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                       lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                       lastEl1, lastEl2, lastValue, scanType)
            yield(dataWedge)
            


    def _getWedge(self, gdlatr, gdlonr, galtr, range,
                  diffRange1, diffRange2, az1, az2,
                  el1, el2, value, scanType):
        """_getWedge returns return a data wedge in the form
        (dataValue, (point1_x, point1_y), (point2_x, point2_y), 
        (point3_x, point3_y), (point4_x, point4_y)).  The x and y dimensions depend on
        scanType:
            az: x: lon, y: lat
            el_lat: x: lat, y: alt
            el_lon: x: lon, y: alt
            el_gcdist: x: great circle distance, y: alt
            
        Inputs:
            gdlatr, gdlonr, galtr - location of radar
            range, diffRange1, diffRange2 - range of center of wedge, and distance in and out from there
            az1, az2, el1, el2 - start and end aximuth and elevation.  Which is used depends on scanType
            value - float data value
            scanType - az, el_lat, el_lon, el_gcdist
        """
        if scanType == 'az':
            point1_y,point1_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point2_y,point2_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el1,range-diffRange1)
            point3_y,point3_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el1,range+diffRange2)
            point4_y,point4_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_lat':
            point1_x,glon,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point2_x,glon,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            point3_x,glon,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            point4_x,glon,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_lon':
            gdlat,point1_x,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            gdlat,point2_x,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            gdlat,point3_x,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            gdlat,point4_x,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_gcdist':
            gdlat,glon,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point1_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            point2_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            point3_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            point4_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            
        return((value, (point1_x, point1_y), (point2_x, point2_y), (point3_x, point3_y), (point4_x, point4_y)))
    
    
class madPcolorWedgeKmlScan:
    """madPcolorWedgeKmlScan is the class that produces pcolor scan kml files where data is drawn as wedge shapes.

    Usage example::

        obj = madPcolorWedgeKmlScan(isprintText,
                        'az',
                        './isprint.kml',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Dec 15, 2010
    """


    def __init__(self, isprintText,
                 scanType,
                 fullFilename,
                 minColormap = None,
                 maxColormap = None,
                 instName = None,
                 instDesc = None,
                 instLat = None,
                 instLon = None,
                 instAlt = None,
                 colorMap = None):
        """__init__ writes a madPcolorWedgeKmlScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. The 9 parameters
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance

            fullFilename - full path of file containing kml file to be saved.  Extension must
                           be kml, or exception thrown.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.
                          
                          
            instName, instDesc, instLat, instLon, instAlt - used to create a placemark for the radar.  If
                instLat or instLon missing, no placemark added.
                
            colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum

        Returns: void

        Affects: Writes fullFilename to disk
        """

        # verify input
        if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
            raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))

        isprintLines = isprintText.split('\n')
        
        newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
        
        # get look point
        lookLat = (newLatMax + newLatMin)/2.0
        lookLon = (newLonMax + newLonMin)/2.0
        lookAlt = newAltMax * 10.0
        
        if maxColormap == None:
            maxColormap = newParmMax
        if minColormap == None:
            minColormap = newParmMin
            
        
        # set up wedge generator
        dataGen = self._generateWedges(isprintLines, scanType)
        
        text = self._createKmlText(dataGen, maxColormap, minColormap, lookLat, lookLon, lookAlt, scanType,
                                   instName, instDesc, instLat, instLon, instAlt, colorMap)
        
        f = open(fullFilename, 'w')
        f.write(text)
        f.close()
        
        
        
    def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
        """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
        
        Inputs: gdlatr, glonr: reference point on earth's surface
                gdlat, glon: second point on earth's surface
        """
        dlon = glonr/57.2958 - glon/57.2958
        dlat = gdlatr/57.2958 - gdlat/57.2958;
        a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)
    
        if math.sqrt(a) < 1.0:
            c = 2.0 * math.asin(math.sqrt(a))
        else:
            c = 2.0 * math.asin(1.0)
    
        return(c * 6371.2)
        
        
    def _getLimits(self, isprintLines):
        """returns (newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin) 
        given isprint lines with
        parameters (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, )
        
        We only look at the end points of ranges and the station location
        """
        # set initial values to station location
        items = isprintLines[1].split()
        slat = float(items[0])
        slon = float(items[1])
        if slon > 180.0:
            slon -= 360.0
        salt = float(items[2])
        maxLon = slon
        minLon = slon
        maxLat = slat
        minLat = slat
        maxAlt = salt
        maxGreatCDist = 0.0
        maxParm = -1.0e-30
        minParm = 1.0e30
        
        lastAz = None
        lastEl = None
        lastRange = None
        
        for i, isprintLine in enumerate(isprintLines):
            items = isprintLine.split()
            if len(items) < 9:
                continue
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            thisAz = (az1 + az2)/2
            el1 = float(items[6])
            el2 = float(items[7])
            thisEl = (el1 + el2)/2
            try:
                parm = float(items[8])
                if parm > maxParm:
                    maxParm = parm
                if parm < minParm:
                    minParm = parm
            except ValueError:
                continue
            
            # see if we're at an end point
            if lastAz != None:
                if abs(lastAz - thisAz) > 0.001 or abs(lastEl - thisEl) > 0.001:
                    # the last point was an end point
                    gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,salt,lastAz,lastEl,lastRange)
                    if gdlat > maxLat:
                        maxLat = gdlat
                    if gdlat < minLat:
                        minLat = gdlat
                    if glon > maxLon:
                        maxLon = glon
                    if glon < minLon:
                        minLon = glon
                    if gdalt > maxAlt:
                        maxAlt = gdalt
                    greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
                    if greatCDist > maxGreatCDist:
                        maxGreatCDist = greatCDist
                        
            lastAz = thisAz
            lastEl = thisEl
            lastRange = range
            
        # check last point
        gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,slat,lastAz,lastEl,lastRange)
        if gdlat > maxLat:
            maxLat = gdlat
        if gdlat < minLat:
            minLat = gdlat
        if glon > maxLon:
            maxLon = glon
        if glon < minLon:
            minLon = glon
        if gdalt > maxAlt:
            maxAlt = gdalt
        greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
        if greatCDist > maxGreatCDist:
            maxGreatCDist = greatCDist
            
        return((maxLon, minLon, maxLat, minLat, maxAlt, maxGreatCDist, maxParm, minParm))
    
    
    
    def _generateWedges(self, isprintLines, scanType):
        """_generateWedges is a python generator that return a data wedge in the form
        (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
        (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt))
        
        Inputs:
        
            isprintLines - a list of line with 9 parameters that
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location.
                No wedge is returned for a missing data line
                
            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
        """
        # state variables
        lastAz1 = None
        lastAz2 = None
        lastEl1 = None
        lastEl2 = None
        lastRange = None
        lastDiffRangeLow = None
        lastValue = None
        
        for isprintLine in isprintLines:
            items = isprintLine.split()
            if len(items) < 9:
                continue
            gdlatr = float(items[0])
            gdlonr = float(items[1])
            galtr = float(items[2])
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            el1 = float(items[6])
            el2 = float(items[7])
            valueStr = items[8]
            if lastAz1 != None and ( abs(lastAz1-az1) > 0.001 or abs(lastEl1-el1) > 0.001 ):
                # new radar line found, send last point if needed
                if lastValue != None:
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue)
                    yield(dataWedge)
                # this is the first point in the line, so no lastDiffRangeLow
                lastDiffRangeLow = None
            elif lastAz1 != None and ( abs(lastAz1-az1) < 0.001 and abs(lastEl1-el1) < 0.001 ):
                # continuing radar line
                if lastValue != None:
                    lastDiffRangeLow = (range - lastRange)/2.0
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, (range-lastRange)/2.0, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue)
                    yield(dataWedge)
                else:
                    lastDiffRangeLow = (range - lastRange)/2.0
            # reset state variables
            lastAz1 = az1
            lastAz2 = az2
            lastEl1 = el1
            lastEl2 = el2
            lastRange = range
            try:
                lastValue = float(valueStr)
            except:
                lastValue = None
                
        # finally, send last wedge if needed
        if lastValue != None:
            dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                       lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                       lastEl1, lastEl2, lastValue)
            yield(dataWedge)
            


    def _getWedge(self, gdlatr, gdlonr, galtr, range,
                  diffRange1, diffRange2, az1, az2,
                  el1, el2, value):
        """_getWedge returns return a data wedge in the form
        (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
        (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt)).  

            
        Inputs:
            gdlatr, gdlonr, galtr - location of radar
            range, diffRange1, diffRange2 - range of center of wedge, and distance in and out from there
            az1, az2, el1, el2 - start and end aximuth and elevation.  Which is used depends on scanType
            value - float data value
        """
        point1_lat,point1_lon,point1_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
        point2_lat,point2_lon,point2_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el2,range-diffRange1)
        point3_lat,point3_lon,point3_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el2,range+diffRange2)
        point4_lat,point4_lon,point4_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        return((value, (point1_lat,point1_lon,point1_alt), (point2_lat,point2_lon,point2_alt), 
                (point3_lat,point3_lon,point3_alt), (point4_lat,point4_lon,point4_alt)))
        
        
        
    def _createKmlText(self, dataGen, maxColormap, minColormap, lookLat, lookLon, lookAlt, scanType,
                       instName, instDesc, instLat, instLon, instAlt, colorMap):
        """_createKmlText creates kml text
        
        Inputs:
        
            dataGen - list in form (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
                                    (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt))
            maxColormap 
            minColormap
            lookLat
            lookLon
            lookAlt
            scanType
            instName, instDesc, instLat, instLon, instAlt - - used to create a placemark for the radar.  If
                instLat or instLon missing, no placemark added.
            colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum
        """
        
        # need to fill in id and color
        styleTemplate = """
        
        """
        
        # need to fill in id and coordinates
        placemarkTemplate = """
        
            #%s
            
                absolute
                
                    
                         %s 
                    
                
            
        
        """
        
        instTemplate = """
              %s
              %s
              
                %f,%f,%f
              
        
        """
        
        # need to fill in styles and placemarks
        documentTemplate = """
        
        
        ScanKmlFile
            
                %f
                %f
                %f
                %f
                50
                %f
                relativeToGround
            
            
        %s
        %s
        %s
        
        
        """
        
        retStr  = ''
        
        styleStr = ''
        
        # create 100 colors
        for i in range(100):
            if not colorMap:
                colorStr = self._getKmlRGB(i/100.0)
            else:
                matLabColorStr = matplotlib.colors.rgb2hex(colorMap(i/100.0)[:3])
                red = matLabColorStr[1:3]
                green = matLabColorStr[3:5]
                blue = matLabColorStr[5:7]
                colorStr = 'ff' + blue + green + red # this is the google earth standard for opaque color
            styleStr += styleTemplate % (i, colorStr)
            
        placemarkStr = ''
        # loop through data adding strings to placemark string
        while(True):
            try:
                p = next(dataGen)
            except StopIteration:
                break
            data = p[0]
            if data > maxColormap:
                styleIndex = 99
            elif data < minColormap:
                styleIndex = 0
            else:
                styleIndex = int(99.0 * ((data-minColormap)/(maxColormap-minColormap)))

            styleId = 'p%i' % (styleIndex)
            coordStr = ('%f,%f,%f %f,%f,%f %f,%f,%f %f,%f,%f %f,%f,%f' ) % \
                        (p[1][1], p[1][0], p[1][2]*1000.0,
                         p[2][1], p[2][0], p[2][2]*1000.0,
                         p[3][1], p[3][0], p[3][2]*1000.0,
                         p[4][1], p[4][0], p[4][2]*1000.0,
                         p[1][1], p[1][0], p[1][2]*1000.0)
            placemarkStr += placemarkTemplate % (styleId, coordStr)
            
            
        if scanType in ('el_lat'):
            heading = 90.0
            distance = 200.0
        elif scanType in ('el_lon', 'el_gcdist'):
            heading = 0.0
            distance = 200.0
        else:
            heading = 0.0
            distance = 1000.0
            
            
        # create instStr
        instStr = ''
        if instLat != None and instLon != None:
            if instAlt == None:
                instAlt = 0.0
            if instName == None:
                instName = 'Radar'
            if instDesc == None:
                instDesc = 'This is the location of the radar that produced this scan.'
            instStr += instTemplate % (instName, instDesc, instLon, instLat, instAlt)
        
        
        retStr += documentTemplate % (lookLon, lookLat, lookAlt*100.0, heading, lookAlt*distance, styleStr, instStr, placemarkStr)
        
        return(retStr)
    
    
    def _getKmlRGB(self, value, transparent=False, wrap=False):
        """_getKmlRGB returns a Kml color string given a value between 0 qnd 1.
        
        If transparent == False, solid color.  Otherwise transpanency set to 50%
        
        If wrap, colors at zero and 1 wrap.  Otherwise, extremes red and blue
        
        If invert, use value = 1 - value
        """
        if value < 0.0 or value > 1.0:
            raise ValueError('value must be between 0 and 1, not %f' % (value))
        
        if wrap:
            r,g,b = colorsys.hsv_to_rgb(value, 0.9, 0.9)
        else:
            r,g,b = colorsys.hsv_to_rgb(value*0.67, 0.9, 0.9)
        
        if not transparent:
            transStr = 'ff'
        else:
            transStr = '80'
        
        retStr = '%s%s%s%s' % (transStr,
                               hex(int(r*255))[-2:],
                               hex(int(g*255))[-2:],
                               hex(int(b*255))[-2:])
        
        return(retStr)
    
            

class scanPlotter:
    """scanPlotter is the class that produces a series of scan plots for a single Madrigal file

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 26, 2006
    """
    def __init__(self, madFile,
                 madDBObj=None):
        """__init__ initializes a scanPlotter object.

        Inputs:

            madFile - the Madrigal file to be analyzed.  Must contain SCNTYP and CYCN parameters.

            madDBObj - a madrigal.metadata.MadrigalDB object.  If None (default),
            one created

	              
        Returns: void

        Affects: sets self.madFile, self.madDBObj
        """
        if os.access(madFile, os.R_OK):
            self.madFile = madFile
        else:
            raise IOError('unable to read %s' % (str(madFile)))

        if madDBObj == None:
            self.madDBObj = madrigal.metadata.MadrigalDB()
        else:
            self.madDBObj = madDBObj


    def plotAllScans(self,
                     scanType,
                     fullFilenameTemplate,
                     plotParm,
                     size = 'small',
                     xMinimum = None,
                     xMaximum = None,
                     yMinimum = None,
                     yMaximum = None,
                     xGridSize = None,
                     yGridSize = None,
                     minColormap = None,
                     maxColormap = None,
                     colorMap = matplotlib.cm.jet,
                     maxNumLines = None,
                     filterStr = ''):
        """plotAllScans creates a series of az or el scans.

        Inputs:

            scanType - must be 'az' or 'el'

            fullFilename - full path of file containing pcolor plot to be saved. Each image
            created will have _#.png appended, with # starting at 0

            plotParm - mnemonic of parameter to be plotted.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

            xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

            yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

            yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

    	    maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
    	    
    	    filterStr - a filter string to pass to isprint.  Defaults to no filtering
    
    	Returns - a list of tuples, where each tuple has two items: 1. time string,
    	          2. metadata string in form for scanTab.txt.  List length = number
    	          of plots created
	"""
        
        if scanType not in ('az', 'el'):
            raise ValueError('scantype must be az or el, not %s' % (str(scanType)))

        self.scanType = scanType

        retList = []

        # handle ion velocity
        if plotParm.lower() == 'vo':
            cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
        else:
            cmap = matplotlib.cm.jet

        # create isprint string
        parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist' % (plotParm.strip())
        isprintStr = getIsprintString(self.madFile, parms, filterStr)

        try:
            scanList = self.createScanInfoList(isprintStr)
        except:
            traceback.print_exc()
            return (retList)

        # loop through each scan
        scanFailureCount = 0 # keep track of how many scans fail
        for i in range(len(scanList)):
            scanCount = i - scanFailureCount
            scanInfo = scanList[i]
            try:
                startDateStr = self.getDateStrFromUT(scanInfo[1])
            except:
                # no scans may have been found
                continue
            endDateStr = self.getTimeStrFromUT(scanInfo[4])
            # create title
            
            if scanType == 'el':
                if scanInfo[3] > scanInfo[6]:
                    ind = 'down'
                    direction = 1
                else:
                    ind = 'up'
                    direction = 0
                titleStr = 'El %s scan (%s) %s-%s, az=%i' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[2]))
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[3],
                                                                                                scanInfo[2],
                                                                                                scanInfo[4],
                                                                                                scanInfo[6],
                                                                                                scanInfo[5],
                                                                                                direction)
                retList.append((timeStr,metaStr))
                
                if scanInfo[7] == 'Gdlat':
                    xLabelStr = 'Geodetic latitude'
                    xGridSize = 1.0
                elif scanInfo[7] == 'Glon':
                    xLabelStr = 'Longitude'
                    xGridSize = 1.0
                elif scanInfo[7] == 'Gcdist':
                    xLabelStr = 'Ground distance in km'
                    xGridSize = 100.0

                xLabelStr = ''
                yLabelStr = 'Altitude (km)'
                if xGridSize == None:
                    xGridSize = 1.0
                if yGridSize == None:
                    yGridSize = 20.0
                
            else:
                if scanInfo[2] > scanInfo[5]:
                    ind = 'ccw'
                    direction = 1
                else:
                    ind = 'cw'
                    direction = 0
                titleStr = 'Az %s scan (%s) %s-%s, el=%i' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[3]))
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[2],
                                                                                                scanInfo[3],
                                                                                                scanInfo[4],
                                                                                                scanInfo[5],
                                                                                                scanInfo[6],
                                                                                                direction)

                retList.append((timeStr,metaStr))

                xLabelStr = 'Longitude'
                yLabelStr = 'Geodetic latitude'
                xGridSize = 1.0
                yGridSize = 1.0

            name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)

            try:
                madPcolorScan(scanInfo[0],
                              xGridSize,
                              yGridSize,
                              titleStr,
                              xLabelStr,
                              yLabelStr,
                              name,
                              minColormap = minColormap,
                              maxColormap = maxColormap,
                              colorMap = cmap)
            except:
                print('Problem creating scan %s' % (str(retList[-1])))
                traceback.print_exc()
                scanFailureCount += 1
                retList.remove(retList[-1])

        return (retList)
    
    
    def plotAllWedgeScans(self,
                     scanType,
                     fullFilenameTemplate,
                     plotParm,
                     size = 'small',
                     xMinimum = None,
                     xMaximum = None,
                     yMinimum = None,
                     yMaximum = None,
                     minColormap = None,
                     maxColormap = None,
                     colorMap = matplotlib.cm.jet,
                     maxNumLines = None,
                     filterStr = '',
                     addTitle = '',
                     includeKml = False,
                     radarName = None,
                     radarDesc = None):
        """plotAllWedgeScans creates a series of az or el scans.  Similar to plotAllScans, except produces
        fancier wedge plots with uniform scalling.

        Inputs:

            scanType - must be 'az' or 'el'

            fullFilename - full path of file containing pcolor plot to be saved. Each image
            created will have _#.png appended, with # starting at 0

            plotParm - mnemonic of parameter to be plotted.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

            xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

            yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

            yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

            maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
            
            filterStr - a filter string to pass to isprint.  Defaults to no filtering
            
            addTitle - a string with additional title.  If empty string (the default), no additional title string.
            
            includeKml - if True, create a corresponding kml file for every png file.  Same names
                    as png files, except extension = .kml.  If False, do not.
                    
            radarName - name to label radar placemark if kml generated.
            
            radarDesc - description text for radar placemark if kml generated
    
        Returns - a list of tuples, where each tuple has two items: 1. time string,
                  2. metadata string in form for scanTab.txt.  List length = number
                  of plots created
    """
        
        if scanType not in ('az', 'el'):
            raise ValueError('scantype must be az or el, not %s' % (str(scanType)))

        self.scanType = scanType
        
        if scanType == 'az':
            wedgeScanType = 'az'

        retList = []

        # handle ion velocity
        if plotParm.lower() == 'vo':
            cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
        else:
            cmap = matplotlib.cm.jet

        # create isprint string
        parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist,gdlatr,gdlonr,galtr,range,az1,az2,el1,el2' % (plotParm.strip())

        filterStr += ' filter=%s,, ' % (plotParm.strip())
        isprintStr = getIsprintString(self.madFile, parms, filterStr)

        try:
            scanList = self.createWedgeScanInfoList(isprintStr)
        except:
            traceback.print_exc()
            return (retList)
        
        gdlatr, gdlonr, galtr = self._getInstLocation(isprintStr)
        
        # create limit dictionary used to keep all scans of the same type with the same limits
        limitDict = self._getLimits(scanList, xMinimum, xMaximum, yMinimum, yMaximum)

        # loop through each scan
        scanFailureCount = 0 # keep track of how many scans fail
        for i in range(len(scanList)):
            scanCount = i - scanFailureCount
            scanInfo = scanList[i]
            try:
                startDateStr = self.getDateStrFromUT(scanInfo[1])
            except:
                # no scans may have been found
                continue
            endDateStr = self.getTimeStrFromUT(scanInfo[4])
            # create title
            
            if scanType == 'el':
                if scanInfo[3] > scanInfo[6]:
                    ind = 'down'
                    direction = 1
                else:
                    ind = 'up'
                    direction = 0
                if int(scanInfo[2]) == int(scanInfo[5]):
                    # no change in az
                    titleStr = 'El %s scan (%s) %s-%s, az=%i %s' % (plotParm,
                                                                    ind,
                                                                    startDateStr,
                                                                    endDateStr,
                                                                    int(scanInfo[2]),
                                                                    addTitle)
                else:
                    # az changed
                    titleStr = 'El (%i-%i) %s scan %s-%s, az=%i-%i %s' % (int(scanInfo[3]),
                                                                          int(scanInfo[6]),
                                                                          plotParm,
                                                                          startDateStr,
                                                                          endDateStr,
                                                                          int(scanInfo[2]),
                                                                          int(scanInfo[5]),
                                                                          addTitle)
                    
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[3],
                                                                                                scanInfo[2],
                                                                                                scanInfo[4],
                                                                                                scanInfo[6],
                                                                                                scanInfo[5],
                                                                                                direction)
                retList.append((timeStr,metaStr))
                
                if scanInfo[7] == 'Gdlat':
                    xLabelStr = 'Geodetic latitude'
                    wedgeScanType = 'el_lat'
                elif scanInfo[7] == 'Glon':
                    xLabelStr = 'Longitude'
                    wedgeScanType = 'el_lon'
                elif scanInfo[7] == 'Gcdist':
                    xLabelStr = 'Ground distance in km'
                    wedgeScanType = 'el_gcdist'

                xLabelStr = ''
                yLabelStr = 'Altitude (km)'
                
            else:
                if scanInfo[2] > scanInfo[5]:
                    ind = 'ccw'
                    direction = 1
                else:
                    ind = 'cw'
                    direction = 0
                titleStr = 'Az %s scan (%s) %s-%s, el=%i %s' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[3]),
                                                             addTitle)
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[2],
                                                                                                scanInfo[3],
                                                                                                scanInfo[4],
                                                                                                scanInfo[5],
                                                                                                scanInfo[6],
                                                                                                direction)

                retList.append((timeStr,metaStr))

                xLabelStr = 'Longitude'
                yLabelStr = 'Geodetic latitude'

            name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)

            try:
                madPcolorWedgeScan(scanInfo[0],
                                   wedgeScanType,
                                   titleStr,
                                   xLabelStr,
                                   yLabelStr,
                                   name,
                                   xMinimum = limitDict[wedgeScanType][0],
                                   xMaximum = limitDict[wedgeScanType][1],
                                   yMinimum = limitDict[wedgeScanType][2],
                                   yMaximum = limitDict[wedgeScanType][3],
                                   minColormap = minColormap,
                                   maxColormap = maxColormap,
                                   colorMap = cmap)
                success = True
            except:
                print('Problem creating scan %s' % (str(retList[-1])))
                traceback.print_exc()
                scanFailureCount += 1
                retList.remove(retList[-1])
                success = False
                
            if includeKml and success:
                kmlName = name[:-3] + 'kml'
                try:
                    madPcolorWedgeKmlScan(scanInfo[0],
                                          wedgeScanType,
                                          kmlName,
                                          minColormap = minColormap,
                                          maxColormap = maxColormap,
                                          instName = radarName,
                                          instDesc = radarDesc,
                                          instLat = gdlatr,
                                          instLon = gdlonr,
                                          instAlt = galtr,
                                          colorMap = cmap)
                except:
                    print('Problem creating kml scan')
                    traceback.print_exc()
                                          
                

        return (retList)
    



    def getDateStrFromUT(self, ut):
        """getDateStrFromUT returns a date string formated as YYYY-MM-DD HH:MM:SS from a ut time
        (seconds since 1/1/1950)
        """
        timeList = madrigal._derive.getDateFromUt(float(ut))
        return '%04i-%02i-%02i %02i:%02i:%02i' % (timeList[0],
                                                  timeList[1],
                                                  timeList[2],
                                                  timeList[3],
                                                  timeList[4],
                                                  timeList[5])


    def getTimeStrFromUT(self, ut):
        """getTimeStrFromUT returns a time string formated as HH:MM:SS from a ut time
        (seconds since 1/1/1950)
        """
        timeList = madrigal._derive.getDateFromUt(float(ut))
        return '%02i:%02i:%02i' % (timeList[3],
                                   timeList[4],
                                   timeList[5])



    def createScanInfoList(self, isprintStr):
        """createScanInfoList creates a list of tuples, which each tuple representing a single scan, and
        values of:

        (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
        minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  Isprint string
        has values (x,y,plotParm), where for az scan x=lon, y=lat, and for el scan y=alt, x=lat if starting
        az within 15 degrees of north or south, x=lon if starting az within 15 degrees of east or west,
        of x=gcdist if other az. Type is Gdlat or Glon or Gcdist for el scans, None for az scans.

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist

        Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or one 
        north-south scans, zero or one east-west scans, and zero or more off azimuth scans.  An off azimuth
        scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
        scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
        sign.
        """
        isprintItems = isprintStr.split()

        retList = []

        # loop variables
        thisIsprintStr = []
        cycNum = None
        startUT = []
        startAz = []
        startEl = []
        endUT = []
        endAz = []
        endEl = []
        elScan = []
        minGdlat = []
        maxGdlat = []
        minGlon = []
        maxGlon = []
        minGdalt = []
        maxGdalt = []
        minGcdist = []
        maxGcdist = []
        thisAzDirection = None # used to detect new azimuth scans
        lastAzDirection = None # used to detect new azimuth scans
        lastEl = None # used to detect new azimuth scans
        thisAz = None
        lastUt = None # keep track of new ut
        isNewUt = None # true only when ut changes
        inAzScan = False
        

        for i in range(len(isprintItems)):
            item = isprintItems[i]
            mod = i % 10
            if mod == 0:
                thisUt = float(item)
                if lastUt == None:
                    lastUt = thisUt
            elif mod == 1:
                thisScntyp = int(item)
            elif mod == 2:
                thisCycn = int(float(item))
                if cycNum == None:
                    cycNum = thisCycn
            elif mod == 3:
                thisParm = item
            elif mod == 4:
                thisGdalt = float(item)
            elif mod == 5:
                thisGdlat = float(item)
            elif mod == 6:
                thisGlon = float(item)
            elif mod == 7:
                if thisAz != None and lastUt < thisUt and inAzScan:
                    if thisAz > float(item):
                        thisAzDirection = 'cw'
                    elif thisAz < float(item):
                        thisAzDirection = 'ccw'
                thisAz = float(item)
            elif mod == 8:
                thisEl = float(item)
            elif mod == 9:
                thisGcdist = float(item)
                
                # check whether this is a new time
                if lastUt < thisUt:
                    isNewUt = True
                    lastUt = thisUt
                else:
                    isNewUt = False

                # if new cycle, close out value if data exists
                if thisCycn != cycNum:
                    for i in range(len(thisIsprintStr)):
                        if len(thisIsprintStr[i]) == 0:
                            continue
                        retList.append((thisIsprintStr[i],
                                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                        minGcdist[i], maxGcdist[i]))
                    thisIsprintStr = []
                    cycNum = None
                    startUT = []
                    startAz = []
                    startEl = []
                    endUT = []
                    endAz = []
                    endEl = []
                    elScan = []
                    minGdlat = []
                    maxGdlat = []
                    minGlon = []
                    maxGlon = []
                    minGdalt = []
                    maxGdalt = []
                    minGcdist = []
                    maxGcdist = []
                    thisAzDirection = None
                    lastAzDirection = None
                    lastEl = None
                    inAzScan = False

                # end of line - process it
                if self.scanType == 'el' and thisScntyp == 3:
                    # add this elevation scan point
                    # check type of el scan
                    inAzScan = False
                    if -15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0:
                        thisElScan = 'Gdlat'
                    elif 75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0:
                        thisElScan = 'Glon'
                    else:
                        thisElScan = 'Gcdist'
                    # get index of this scan, None if a new scan
                    index = None
                    for i, item in enumerate(elScan):
                        if item == 'Gdlat' and thisElScan == 'Gdlat':
                            index = i
                            break
                        elif item == 'Glon' and thisElScan == 'Glon':
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                            index = i
                            break
                    if index == None:
                        # a new scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(thisElScan)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                            
                    endUT[index] = thisUt
                    endAz[index] = thisAz
                    endEl[index] = thisEl
                    if thisElScan == 'Gdlat':
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGdlat, thisGdalt, thisParm)
                    elif thisElScan == 'Glon':
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGlon, thisGdalt, thisParm)
                    else:
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGcdist, thisGdalt, thisParm)

                elif self.scanType == 'az' and thisScntyp == 2:
                    # az scan
                    inAzScan = True
                    # add this azimuth scan point
                    # check if this is a new scan
                    newAzScan = False
                    if lastEl == None:
                        newAzScan = True
                    # check for elevation change
                    elif abs(lastEl - int(thisEl)) > 3:
                        newAzScan = True
                    # check for direction change
                    if lastAzDirection != None:
                        if lastAzDirection != thisAzDirection:
                            newAzScan = True
                            thisAzDirection = None
                    if newAzScan:
                        # a new azimuth scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(None)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        lastEl = int(thisEl)
                        lastAzDirection = None
                    else:
                        # this is at least the second line of the scan
                        if not thisAzDirection is None:
                            lastAzDirection = thisAzDirection
                            
                    endUT[-1] = thisUt
                    endAz[-1] = thisAz
                    endEl[-1] = thisEl
                    thisIsprintStr[-1] += '%f %f %s\n' % (thisGlon, thisGdlat, thisParm)
                    
                else:
                    # not a scan line
                    inAzScan = False
                    continue

                if minGdlat[index] == None:
                    minGdlat[index] = thisGdlat
                    maxGdlat[index] = thisGdlat
                    minGlon[index] = thisGlon
                    maxGlon[index] = thisGlon
                    minGdalt[index] = thisGdalt
                    maxGdalt[index] = thisGdalt
                    minGcdist[index] = thisGcdist
                    maxGcdist[index] = thisGcdist
                else:
                    if minGdlat[index] > thisGdlat:
                        minGdlat[index] = thisGdlat
                    if maxGdlat[index] < thisGdlat:
                        maxGdlat[index] = thisGdlat
                    if minGlon[index] > thisGlon:
                        minGlon[index] = thisGlon
                    if maxGlon[index] < thisGlon:
                        maxGlon[index] = thisGlon
                    if minGdalt[index] > thisGdalt:
                        minGdalt[index] = thisGdalt
                    if maxGdalt[index] < thisGdalt:
                        maxGdalt[index] = thisGdalt
                    if minGcdist[index] > thisGcdist:
                        minGcdist[index] = thisGcdist
                    if maxGcdist[index] < thisGcdist:
                        maxGcdist[index] = thisGcdist

        # end, close out last cycle
        for i in range(len(thisIsprintStr)):
            if len(thisIsprintStr[i]) == 0:
                continue
            retList.append((thisIsprintStr[i],
                            startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                            minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                            minGcdist[i], maxGcdist[i]))

        return(retList)
    
    
    def createWedgeScanInfoList(self, isprintStr):
        """createWedgeScanInfoList creates a list of tuples, which each tuple representing a single scan, and
        values of:

        (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
        minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist,
        gdlatr,gdlonr,galtr,range,az1,az2,el1,el2

        Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or more 
        north-south scans, zero or more east-west scans, and zero or more off azimuth scans.  A south to north
        elevation scan or a west to east elevation scan is called clockwise, and a switch from clockwise
        to counterclockwise will create a new elevation scan. An off azimuth
        scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
        scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
        sign.
        """
        isprintItems = isprintStr.split()

        retList = []

        # loop variables
        thisIsprintStr = []
        cycNum = None
        startUT = []
        startAz = []
        startEl = []
        endUT = []
        endAz = []
        endEl = []
        elScan = []
        minGdlat = []
        maxGdlat = []
        minGlon = []
        maxGlon = []
        minGdalt = []
        maxGdalt = []
        minGcdist = []
        maxGcdist = []
        thisAzDirection = None # used to detect new azimuth scans
        lastAzDirection = None # used to detect new azimuth scans
        thisElDirection = None # used to detect new elevation scans (cw or ccw)
        lastElDirection = None # used to detect new elevation scans (cw or ccw)
        lastEl = None # used to detect new azimuth scans
        thisAz = None
        thisEl = None
        lastUt = None # keep track of new ut
        isNewUt = None # true only when ut changes
        inAzScan = False
        inElScan = False
        

        for i in range(len(isprintItems)):
            item = isprintItems[i]
            mod = i % 18
            if mod == 0:
                thisUt = float(item)
                if lastUt == None:
                    lastUt = thisUt
            elif mod == 1:
                thisScntyp = int(item)
            elif mod == 2:
                thisCycn = int(float(item))
                if cycNum == None:
                    cycNum = thisCycn
            elif mod == 3:
                thisParm = item
            elif mod == 4:
                thisGdalt = float(item)
            elif mod == 5:
                thisGdlat = float(item)
            elif mod == 6:
                thisGlon = float(item)
            elif mod == 7:
                if thisAz != None and lastUt < thisUt and inAzScan:
                    if thisAz > float(item):
                        thisAzDirection = 'cw'
                    elif thisAz < float(item):
                        thisAzDirection = 'ccw'
                thisAz = float(item)
            elif mod == 8:
                if thisEl != None and inElScan:
                    if abs(float(item) - thisEl) > 1.0:
                        if thisElScan == 'Gdlat':
                            if -165.0 >= thisAz or thisAz >= 165.0:
                                # southern scan
                                if float(item) > thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                            else:
                                # northern scan
                                if float(item) < thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                        elif thisElScan == 'Glon':
                            if -105.0 <= thisAz <= -75.0:
                                # western scan
                                if float(item) > thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                            else:
                                # eastern scan
                                if float(item) < thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                        elif thisElScan == 'Gcdist':
                            if float(item) > thisEl:
                                thisElDirection = 'up'
                            else:
                                thisElDirection = 'down'

                thisEl = float(item)
                
            elif mod == 9:
                thisGcdist = float(item)
            elif mod == 10:
                thisGdlatr = float(item)
            elif mod == 11:
                thisGdlonr = float(item)
            elif mod == 12:
                thisGaltr = float(item)
            elif mod == 13:
                thisRange = float(item)
            elif mod == 14:
                thisAz1 = float(item)
            elif mod == 15:
                thisAz2 = float(item)
            elif mod == 16:
                thisEl1 = float(item)
            elif mod == 17:
                thisEl2 = float(item)
                
                # check whether this is a new time
                if lastUt < thisUt:
                    isNewUt = True
                    lastUt = thisUt
                else:
                    isNewUt = False

                # if new cycle or elevation issue, close out value if data exists
                if (thisCycn != cycNum) or \
                    (thisElDirection == 'cw' and lastElDirection == 'ccw') or \
                    (thisElDirection == 'ccw' and lastElDirection == 'cw') or \
                    (thisElDirection == 'down' and lastElDirection == 'up') or \
                    (thisElDirection == 'up' and lastElDirection == 'down') or \
                    (thisElDirection != None and not inElScan) or \
                    (thisAzDirection != None and not inAzScan):
                    
                    for i in range(len(thisIsprintStr)):
                        if len(thisIsprintStr[i]) == 0:
                            continue
                        retList.append((thisIsprintStr[i],
                                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                        minGcdist[i], maxGcdist[i]))
                    thisIsprintStr = []
                    cycNum = None
                    startUT = []
                    startAz = []
                    startEl = []
                    endUT = []
                    endAz = []
                    endEl = []
                    elScan = []
                    minGdlat = []
                    maxGdlat = []
                    minGlon = []
                    maxGlon = []
                    minGdalt = []
                    maxGdalt = []
                    minGcdist = []
                    maxGcdist = []
                    thisAzDirection = None
                    lastAzDirection = None
                    thisElDirection = None # used to detect new elevation scans (cw or ccw)
                    lastElDirection = None 
                    lastEl = None
                    inAzScan = False
                    inElScan = False

                # end of line - process it
                if self.scanType == 'el' and thisScntyp == 3:
                    # add this elevation scan point
                    # check type of el scan
                    inAzScan = False
                    inElScan = True
                    if (-15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0) and \
                        abs(thisAz1 - thisAz2) < 0.5:
                        thisElScan = 'Gdlat'
                    elif (75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0) and \
                        abs(thisAz1 - thisAz2) < 0.5:
                        thisElScan = 'Glon'
                    else:
                        thisElScan = 'Gcdist'
                    # get index of this scan, None if a new scan
                    index = None
                    for i, item in enumerate(elScan):
                        
                        if item == 'Gdlat' and thisElScan == 'Gdlat':
                            index = i
                            break
                        elif item == 'Glon' and thisElScan == 'Glon':
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and i == len(elScan)-1:
                            index = i
                            break
                    
                    if index == None:
                        # a new scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(thisElScan)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        
                    lastElDirection = thisElDirection  
                    endUT[index] = thisUt
                    endAz[index] = thisAz
                    endEl[index] = thisEl
                    # gdlatr, gdlonr, galtr, range, az1, az2, el1, el2,plotParm
                    thisIsprintStr[index] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                               thisRange, thisAz1, thisAz2,
                                                                               thisEl1, thisEl2, thisParm)

                elif self.scanType == 'az' and thisScntyp == 2:
                    # az scan
                    inAzScan = True
                    inElScan = False
                    # add this azimuth scan point
                    # check if this is a new scan
                    newAzScan = False
                    if lastEl == None:
                        newAzScan = True
                    # check for elevation change
                    elif abs(lastEl - int(thisEl)) > 3:
                        newAzScan = True
                    # check for direction change
                    if lastAzDirection != None:
                        if lastAzDirection != thisAzDirection:
                            newAzScan = True
                            thisAzDirection = None
                    if newAzScan:
                        # a new azimuth scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(None)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        lastEl = int(thisEl)
                        lastAzDirection = None
                    else:
                        # this is at least the second line of the scan
                        if not thisAzDirection is None:
                            lastAzDirection = thisAzDirection
                            
                    endUT[-1] = thisUt
                    endAz[-1] = thisAz
                    endEl[-1] = thisEl
                    thisIsprintStr[-1] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                               thisRange, thisAz1, thisAz2,
                                                                               thisEl1, thisEl2, thisParm)
                    
                else:
                    # not a scan line
                    inAzScan = False
                    inElScan = False
                    continue

                if minGdlat[index] == None:
                    minGdlat[index] = thisGdlat
                    maxGdlat[index] = thisGdlat
                    minGlon[index] = thisGlon
                    maxGlon[index] = thisGlon
                    minGdalt[index] = thisGdalt
                    maxGdalt[index] = thisGdalt
                    minGcdist[index] = thisGcdist
                    maxGcdist[index] = thisGcdist
                else:
                    if minGdlat[index] > thisGdlat:
                        minGdlat[index] = thisGdlat
                    if maxGdlat[index] < thisGdlat:
                        maxGdlat[index] = thisGdlat
                    if minGlon[index] > thisGlon:
                        minGlon[index] = thisGlon
                    if maxGlon[index] < thisGlon:
                        maxGlon[index] = thisGlon
                    if minGdalt[index] > thisGdalt:
                        minGdalt[index] = thisGdalt
                    if maxGdalt[index] < thisGdalt:
                        maxGdalt[index] = thisGdalt
                    if minGcdist[index] > thisGcdist:
                        minGcdist[index] = thisGcdist
                    if maxGcdist[index] < thisGcdist:
                        maxGcdist[index] = thisGcdist

        # end, close out last cycle
        for i in range(len(thisIsprintStr)):
            if len(thisIsprintStr[i]) == 0:
                continue
            retList.append((thisIsprintStr[i],
                            startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                            minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                            minGcdist[i], maxGcdist[i]))

        return(retList)
    
    
    def _getLimits(self, scanList, xMinimum, xMaximum, yMinimum, yMaximum):
        """_getLimits returns a dictionary with keys that may include ('az', 'el_lat', 'el_lon', 'el_gcdist'),
        though not all will neccessarily be there.  Values are overall tuple of
        (xMinimum, xMaximum, yMinimum, yMaximum). These values are set by the input
        arguments unless it is None, in which case the scanList limits set the values.
        
        Inputs: 
        
            scanList: a list of tuples, which each tuple representing a single scan, and
                has values of: (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
                minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  Isprint string
                has values (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2,plotParm),. Type is Gdlat or Glon or 
                Gcdist for el scans, None for az scans.
                
            xMinimum, xMaximum, yMinimum, yMaximum - as passed into scanPlotter
        """
        retDict = {}
        
        xMinAz = None
        xMaxAz = None
        yMinAz = None
        yMaxAz = None
        xMinElLat = None
        xMaxElLat = None
        yMinElLat = None
        yMaxElLat = None
        xMinElLon = None
        xMaxElLon = None
        yMinElLon = None
        yMaxElLon = None
        xMinElGcdist = None
        xMaxElGcdist = None
        yMinElGcdist = None
        yMaxElGcdist = None
        minValue = None
        maxValue = None
        
        for scanItem in scanList:
            if scanItem[7] == None: # az scan
                if xMinAz == None:
                    xMinAz = scanItem[10]
                elif xMinAz > scanItem[10]:
                    xMinAz = scanItem[10]
                if xMaxAz == None:
                    xMaxAz = scanItem[11]
                elif xMaxAz < scanItem[11]:
                    xMaxAz = scanItem[11]
                if yMinAz == None:
                    yMinAz = scanItem[8]
                elif yMinAz > scanItem[8]:
                    yMinAz = scanItem[8]
                if yMaxAz == None:
                    yMaxAz = scanItem[9]
                elif yMaxAz < scanItem[9]:
                    yMaxAz = scanItem[9]
            elif scanItem[7] == 'Gdlat': # el_lat scan
                if xMinElLat == None:
                    xMinElLat = scanItem[8]
                elif xMinElLat > scanItem[8]:
                    xMinElLat = scanItem[8]
                if xMaxElLat == None:
                    xMaxElLat = scanItem[9]
                elif xMaxElLat < scanItem[9]:
                    xMaxElLat = scanItem[9]
                if yMinElLat == None:
                    yMinElLat = scanItem[12]
                elif yMinElLat > scanItem[12]:
                    yMinElLat = scanItem[12]
                if yMaxElLat == None:
                    yMaxElLat = scanItem[13]
                elif yMaxElLat < scanItem[13]:
                    yMaxElLat = scanItem[13]
            elif scanItem[7] == 'Glon': # el_lon scan
                if xMinElLon == None:
                    xMinElLon = scanItem[10]
                elif xMinElLon > scanItem[10]:
                    xMinElLon = scanItem[10]
                if xMaxElLon == None:
                    xMaxElLon = scanItem[11]
                elif xMaxElLon < scanItem[11]:
                    xMaxElLon = scanItem[11]
                if yMinElLon == None:
                    yMinElLon = scanItem[12]
                elif yMinElLon > scanItem[12]:
                    yMinElLon = scanItem[12]
                if yMaxElLon == None:
                    yMaxElLon = scanItem[13]
                elif yMaxElLon < scanItem[13]:
                    yMaxElLon = scanItem[13]
            elif scanItem[7] == 'Gcdist': # el_gcdist scan
                if xMinElGcdist == None:
                    xMinElGcdist = scanItem[14]
                elif xMinElGcdist > scanItem[14]:
                    xMinElGcdist = scanItem[14]
                if xMaxElGcdist == None:
                    xMaxElGcdist = scanItem[15]
                elif xMaxElGcdist < scanItem[15]:
                    xMaxElGcdist = scanItem[15]
                if yMinElGcdist == None:
                    yMinElGcdist = scanItem[12]
                elif yMinElGcdist > scanItem[12]:
                    yMinElGcdist = scanItem[12]
                if yMaxElGcdist == None:
                    yMaxElGcdist = scanItem[13]
                elif yMaxElGcdist < scanItem[13]:
                    yMaxElGcdist = scanItem[13]
                    
        # fill out the dictionary
        if xMinAz != None:
            if xMinimum == None:
                xMin = xMinAz
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxAz
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinAz
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxAz
            else:
                yMax = yMaximum
            retDict['az'] = (xMin, xMax, yMin, yMax)
            
        if xMinElLat != None:
            if xMinimum == None:
                xMin = xMinElLat
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElLat
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElLat
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElLat
            else:
                yMax = yMaximum
            retDict['el_lat'] = (xMin, xMax, yMin, yMax)
            
        if xMinElLon != None:
            if xMinimum == None:
                xMin = xMinElLon
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElLon
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElLon
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElLon
            else:
                yMax = yMaximum
            retDict['el_lon'] = (xMin, xMax, yMin, yMax)
            
        if xMinElGcdist != None:
            if xMinimum == None:
                xMin = xMinElGcdist
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElGcdist
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElGcdist
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElGcdist
            else:
                yMax = yMaximum
            retDict['el_gcdist'] = (xMin, xMax, yMin, yMax)
            
        return(retDict)
    
    
    def _getInstLocation(self, isprintStr):
        """_getInstLocation returns a tuple of instrument location (gdlatr, gdlonr, galtr)

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist,
        gdlatr,gdlonr,galtr,range,az1,az2,el1,el2
        """
        isprintItems = isprintStr.split()
        gdlatr = float(isprintItems[10])
        gdlonr = float(isprintItems[11])
        if gdlonr > 180.0:
            gdlonr -= 360.0
        galtr = float(isprintItems[12])
        return((gdlatr, gdlonr, galtr))
        

    
class madHistogram:
    """madHistogram is the class that produces Histogram plots of Madrigal
        Data.

        Usage example::

            obj = madHistogram(isprintText,
                                'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2005 - Alt. code',
                                'Hours since midnight UT Oct. 30,2003'
                                'Altitude',
                                './isprint.png'
                                size = 'large')

        Inputs:

            isprintText - a string giving isprint output without headers. Any missing data should
                            be written as 'missing' or other string that cannot be converted into
                            a float.  Only one column.

            TitleStr - plot title(string) - should describe the plot being made

            xLabelStr - Label for x axis

            fullFilename - full path of file containing histogram plot to be saved. Extension must be .jpeg
                            or .png, or exception thrown

            size - size of plot to be saved. Must be 'small','wide', or 'large'. Defaults to small

            maxNumPoints - maximum number of points to be considered. Defaults to None, so all points in string shown


            numBins - number of bins to be used to store the data. More bins means closer resolution in Histogram.
                        Default is 30

            orientation - whether histogram shows horizontal or vertical. Must be written as 'horizontal' or
                            'vertical', or exception thrown--defaults to 'vertical'

            isNorm - whether histogram should be normalized or not. Default is 0, or False

            isBottom - If true, sets the lowest passed value as zero

            sdevs - number of standard deviations to take into account. When 0 is passed to it, this option is ignored and all data
                    is included in the distribution; otherwise, the range of values accepted is sdevs*(standard deviation of sample)

        Outputs:

            A .png file is written to the fullFilename path given, resulting in a Histogram drawn by matplotlib

            
        Change History:

            Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug. 1,2006
            Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug. 1,2006
 
    """
    def __init__(self,isprintText,
                 titleStr,
                 xLabelStr,
                 fullFilename,
                 size = 'small',
                 maxNumPoints = None,
                 numBins = 30,
                 Orientation='vertical',
                 isNorm =0,
                 isBottom=0,
                 sdevs=0):
        """__init__ writes a madHistogram string to file

        """
        self.__missing = 1.0E30 #special value, since numpy doesn't Handle NaN
        self.__parameter_count = 2

        #verify input
        if not size in ('small','wide','large'):
            raise ValueError('size must be "small","wide" or "large", not %s'%(str(size)))

        if size in ('small','wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        # remove all non-float values
        items = isprintText.split()
        keptItems = []
        for item in items:
            try:
                float(item)
                keptItems.append(item)
            except:
                continue
        delimiter = ' '
        isprintText = delimiter.join(keptItems)

        if maxNumPoints !=None:
            isprintText = self._truncateIsprint(isprintText, maxNumPoints)

        #convert the input data into numeric arrays of floats assuming no headers and filter the missing values

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing,split_data))
            array_data = numpy.asarray(float_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')

        #if needed, throw away outliers
        if sdevs !=0:
            array_data = self.removeOutliers(sdevs,array_data)

        #select the plot size
        if size=='small':
            matplotlib.pylab.figure(1,figsize=(6,4), facecolor = 'w')
        elif size=='wide':
            matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
        elif size=='large':
            matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')


        matplotlib.pylab.hist(array_data,bins = numBins,normed=isNorm,bottom=isBottom,orientation = Orientation)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont) #pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.title(titleStr,fontsize=fontSize)

        #self.__figure = matplotlib.pylab.gcf()

        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprintStr(self,isprintText, maxLines):
        """__truncateIsprintStr truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList)=ndist) and (i<=pdist):
                retArray.append(i)

        return retArray

    #end eliminateOutliers()


class madIsrRecordSummary:
    """madIsrRecordSummary is the class that produces a summary plot of a single ISR record.


    """
    def __init__(self, TiData,
                 TiError,
                 TeData,
                 TeError,
                 VoData,
                 VoError,
                 NelData,
                 NelError,
		 isPopl,
                 description,
                 fullFilename,
                 useRange = False,
                 altResl = None):
        """__init__ writes a madIsrRecordSummary  to a file.

        Inputs:

            TiData - an Nx2 numeric array of Ti (col 0) and altitude in km (col 1)

            TiError - an N length array of error in Ti
            
            TeData - an Nx2 numeric array of Te (col 0) and altitude in km (col 1)

            TeError - an N length array of error in Te

            VoData - an Nx2 numeric array of Vo (col 0) and altitude in km (col 1)

            VoError - an N length array of error in Vo

            NelData - an Nx2 numeric array of (Popl or Nel in m^-3) (col 0) and altitude in km (col 1)

            NelError - an 2xN length array of lower, upper error in Popl or Nel
            
            isPopl - true if Nel contains Popl data, false if Nel data

            description - text to put in fourth panel of plot - use carriage returns to separate lines

            fullFilename - full filename to save output png file.

            useRange - if False (the default), y axis = Altitude.  If True, y axis = Range.

            altResl - altitude resolution in km.  If not None, show altitude resolution on each graph.
        
        
        Returns: void

        Affects: None
        """
        if useRange:
            yAxisStr = 'Range (km)'
        else:
            yAxisStr = 'Altitude (km)'
            
        # upper left - Ti and Te
        fig = matplotlib.pylab.figure(figsize=(10,10))
        axis = matplotlib.pylab.subplot(2,2,1)
        if TiData.shape[0] > 0 and TeData.shape[0] > 0:
            axis.errorbar(TiData[:,0], TiData[:,1], xerr=TiError, fmt='bo-')   
            axis.errorbar(TeData[:,0], TeData[:,1], xerr=TeError, fmt='rD-')
            matplotlib.pylab.text(0.65, 0.1, 'Ti: blue circle\nTe: red diamond',
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
            matplotlib.pylab.xlabel('Temperature (K)')
            matplotlib.pylab.ylabel(yAxisStr)
            matplotlib.pylab.grid(True)
            # set limits based on data only
            tiMax = numpy.nanmax(TiData[:,0])
            tiMin = numpy.nanmin(TiData[:,0])
            teMax = numpy.nanmax(TeData[:,0])
            teMin = numpy.nanmin(TeData[:,0])
            lowerLimit = (int(min(tiMin, teMin)) / 1000) * 1000
            upperLimit = (1+int(max(tiMax, teMax)) / 1000) * 1000            
            
            # alt resolution
            if altResl != None:
                altLabel = 'Alt res: \n%i km' % (int(altResl))
                y_min, y_max = matplotlib.pylab.ylim()
                startX = lowerLimit + 0.8*(upperLimit - lowerLimit)
                startY = 0.15
                reslPercentage = altResl/(y_max-y_min)
                matplotlib.pylab.axvline(x=startX,
                                         ymin=startY,
                                         ymax=reslPercentage + startY,
                                         linewidth=2, color='b')
                matplotlib.pylab.text(0.83, 0.2, altLabel,
                                      horizontalalignment='left',
                                      verticalalignment='top',
                                      transform = axis.transAxes,
                                      fontsize=10)
                
            matplotlib.pylab.xlim(lowerLimit, upperLimit)
            
                
                                         

        # upper right - Vo and 10*Vo
        axis = matplotlib.pylab.subplot(2,2,2)
        if VoData.shape[0] > 0:
            axis.errorbar(VoData[:,0], VoData[:,1], xerr=VoError, fmt='bo-')
            axis.errorbar(10.0*VoData[:,0], VoData[:,1], xerr=10.0*VoError, fmt='rD-')
            # set x limits to be even around 0
            voMax = numpy.nanmax(10.0*VoData[:,0])
            voMin = numpy.nanmin(10.0*VoData[:,0])
            limit = max(abs(voMax), abs(voMin))
            matplotlib.pylab.xlim(-1.0*limit, limit)
            matplotlib.pylab.text(0.58, 0.1, 'Vo: blue circle\n10*Vo: red diamond',
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
            matplotlib.pylab.xlabel('Line of sight drift (m/s)')
            matplotlib.pylab.ylabel(yAxisStr)
            matplotlib.pylab.grid(True)

            # alt resolution
            if altResl != None:
                altLabel = 'Alt res: \n%i km' % (int(altResl))
                y_min, y_max = matplotlib.pylab.ylim()
                startX = limit*0.6
                startY = 0.15
                reslPercentage = altResl/(y_max-y_min)
                matplotlib.pylab.axvline(x=startX,
                                         ymin=startY,
                                         ymax=reslPercentage + startY,
                                         linewidth=2, color='b')
                matplotlib.pylab.text(0.83, 0.2, altLabel,
                                      horizontalalignment='left',
                                      verticalalignment='top',
                                      transform = axis.transAxes,
                                      fontsize=10)
                
            matplotlib.pylab.xlim(-1.0*limit, limit)

            

        # lower left - Nel or Popl
        axis = matplotlib.pylab.subplot(2,2,3)
        if isPopl:
            labelStr = 'Log Power (m^-3)'
        else:
            labelStr = 'Log Electron density (m^-3)'
        if NelData.shape[0] > 0:
            # check that not every point equal
            if numpy.nanmax(NelData[:,0]) == numpy.nanmin(NelData[:,0]):
                pass
            else:
                axis.errorbar(NelData[:,0], NelData[:,1], xerr=NelError, fmt='bo-')
                matplotlib.pylab.xlabel(labelStr)
                matplotlib.pylab.ylabel(yAxisStr)
                matplotlib.pylab.grid(True)
                nelMax = numpy.nanmax(NelData[:,0])
                nelMin = numpy.nanmin(NelData[:,0])
                matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
                matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
                
                # alt resolution
                if altResl != None:
                    altLabel = 'Alt res: \n%i km' % (int(altResl))
                    y_min, y_max = matplotlib.pylab.ylim()
                    x_min, x_max = matplotlib.pylab.xlim()
                    startX = x_min + 0.8*(x_max-x_min)
                    startY = 0.15
                    reslPercentage = altResl/(y_max-y_min)
                    matplotlib.pylab.axvline(x=startX,
                                             ymin=startY,
                                             ymax=reslPercentage + startY,
                                             linewidth=2, color='b')
                    matplotlib.pylab.text(0.83, 0.2, altLabel,
                                          horizontalalignment='left',
                                          verticalalignment='top',
                                          transform = axis.transAxes,
                                          fontsize=10)

                    matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
        

        # lower right - text
        axis = matplotlib.pylab.subplot(2,2,4)
        matplotlib.pylab.text(0.05, 0.9, description,
                              horizontalalignment='left',
                              verticalalignment='top',
                              transform = axis.transAxes,
                              fontsize=9)
        matplotlib.pylab.xticks([])
        matplotlib.pylab.yticks([])
        
        

        matplotlib.pylab.subplots_adjust(wspace = 0.25)
        

        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()
        matplotlib.pylab.close()

        
if __name__ == '__main__':

    import time

    # test madPcolorScan

    # az scan
    f = open('testFiles/isprint_az_scan.txt')
    isprintText = f.read()
    f.close()

    print('creating az scan at /tmp/isprint_az_scan.png')
    obj = madPcolorScan(isprintText,
                        1.0, 1.0,
                        'Az scan - NEL 2006-06-26 13:49:43-14:07:36',
                        'Longitude',
                        'Latitude',
                        '/tmp/isprint_az_scan.png',
                        'small',
                        -100,-50,40,60,
                        9,12)
    
    # el scan
    f = open('testFiles/isprint_el_scan.txt')
    isprintText = f.read()
    f.close()
    
    print('creating el scan at /tmp/isprint_el_scan.png')
    obj = madPcolorScan(isprintText,
                        1.0, 40.0,
                        'El scan - NEL 2006-06-26 13:49:43-14:07:36',
                        'Latitude',
                        'Altitude (km)',
                        '/tmp/isprint_el_scan.png',
                        'small')

    # test scanPlotter
    print('creating az scan series using mlh060622a.000')
    scanObj = scanPlotter('/home/grail/brideout/madroot/experiments/2006/mlh/22jun06/mlh060622a.000')
    scanObj.plotAllScans('az','/tmp/junkAz', 'Nel', filterStr='filter=nel,,11.0')
    
    t = time.time()

    # test madIsrRecordSummary
    TiData = numpy.array([[800, 150],[900, 200],[910, 300], [945, 500], [980, 600]])
    TiError = numpy.array([50,100,50,100,50])
    TeData = numpy.array([[850, 150],[1000, 200],[1800, 300], [2000, 500], [2000, 600]])
    TeError = numpy.array([50,100,50,100,50])
    VoData = numpy.array([[-30, 150],[0, 200],[10, 300], [35, 500], [10, 600]])
    VoError = numpy.array([5,10,5,10,5])
    NelData = numpy.array([[11, 150],[11.5, 200],[11.3, 300], [11, 500], [10.5, 600]])
    NelError = numpy.array(((2.0,0.5),(2.0,1.0), (2.0,0.5), (2.0,1.0), (2.0,0.5)))
    isPopl = True
    text = 'Kinst = Millstone Zenith Antenna\nMore stuff here\nand more\nand more'
    fullFilename = '/tmp/junk.png'
    madIsrRecordSummary(TiData,TiError,TeData,TeError,VoData,VoError,NelData,numpy.transpose(NelError),isPopl,text,fullFilename)


    
    
    f = open('testFiles/isprint_uth_fof2.txt')
    isprintText = f.read()
    f.close()
    
    print('creating scatterplot at /tmp/isprint_fof2.png')
    obj = madScatterPlot(isprintText,
                        'Fof2 - Millstone Hill - Oct. 30, 2003',
                        'Hours since midnight UT Oct. 30, 2003',
                        'Fof2',
                        '/tmp/isprint_fof2.png',
                        size = 'large',
			useAbsoluteTime = False)
			

    f = open('testFiles/isprint_nel.txt')
    isprintText = f.read()
    f.close()

    print('creating pcolor plot at /tmp/isprint_nel.png')
    obj = madPcolorPlot(isprintText,
                        'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2003 - Alt code',
                        'Hours since midnight UT Oct. 30, 2003',
                        'Altitude (km)',
                        '/tmp/isprint_nel.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12,
			smoothAltitude = True,
			insertDataGap = 5,
			useAbsoluteTime = False,
			startTime = None,
			endTime = None,
			sortTimeFlag = False,
			maxNumTimes = None,
			maxNumAlt = 50,
                        truncateIsprint = True,
                        colorMap = matplotlib.cm.spring,
                        altYTitle = 'Latitude',
                        altYLabels = ('40', '41', '42', '43', '44', '45'))
    
			
    print('took %i secs' % (time.time() - t))

Functions

def convertToAbsoluteTimeStr(

xticks, noTime=False, noDate=False, timezone=0)

convertToAbsoluteTimeStr converts a list of strings containing seconds since 1/1/1950 to datetime string.

Input: xticks - a list of strings containing seconds since 1/1/1950

Returns: a list of strings formated as YYYY-MM-DD HH-MM-SS. If noTime, format as YYYY-MM-DD

def convertToAbsoluteTimeStr(xticks, noTime=False, noDate=False, timezone=0):
    """convertToAbsoluteTimeStr converts a list of strings containing seconds since 1/1/1950 to datetime string.
    
    Input: xticks - a list of strings containing seconds since 1/1/1950
    
    Returns: a list of strings formated as YYYY-MM-DD HH-MM-SS.  If noTime, format as YYYY-MM-DD
    """
    datetime1950 = datetime.datetime(1950,1,1,0,0,0)
    
    newList = []
    seconds = int(xticks[0]) - timezone
    iniDoy = datetime1950 + datetime.timedelta(0, seconds)
    iniDoy = int(iniDoy.strftime('%j'))
    
    for item in xticks:
        seconds = int(item) - timezone
        newDatetime = datetime1950 + datetime.timedelta(0, seconds)
        if noTime:
            newList.append(newDatetime.strftime('%Y-%m-%d'))
        elif noDate:
            curDoy = int(newDatetime.strftime('%j'))
            currHour = newDatetime.hour
            currMin = newDatetime.minute
            currHour += (curDoy - iniDoy)*24
            
            newList.append('%02d:%02d' %(currHour,currMin))

        else:
            newList.append(newDatetime.strftime('%Y-%m-%d %H:%M:%S'))
    
    return newList

def defineHomeEnvVariable(

)

defineHomeEnvVariable makes sure HOME env variable is defined, as required by matplotlib.

If not defined, sets HOME to MADROOT/metadata/userdata

def defineHomeEnvVariable():
    """defineHomeEnvVariable makes sure HOME env variable is defined, as required by matplotlib.

    If not defined, sets HOME to MADROOT/metadata/userdata
    """
    try:
        os.environ['HOME']
        return
    except KeyError:
        import madrigal.metadata
        madDB = madrigal.metadata.MadrigalDB()
        os.environ['HOME'] = os.path.join(madDB.getMadroot(), 'metadata/userdata')

def getIsprintString(

filename, parms, filterStr)

getIsprintString returns the output of isprint given inputs

Inputs: filename - Madrigal filename parms - comma-delimited parameters filterStr - arguments to pass to isprint cmd

def getIsprintString(filename, parms, filterStr):
    """getIsprintString returns the output of isprint given inputs
    
    Inputs:
        filename - Madrigal filename 
        parms - comma-delimited parameters
        filterStr - arguments to pass to isprint cmd
    """
    madDB = madrigal.metadata.MadrigalDB()
    cmd = '%s/bin/isprint file=%s ' % (madDB.getMadroot(), filename)
    for parm in parms.split(','):
        cmd += '%s ' % (parm)
    cmd += filterStr
    cmd += ' header=f summary=f '
    pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
    result = pipe.read()
    delimiter = '\n'
    if type(result) in (bytes, numpy.bytes_):
        result = result.decode('utf-8')
    lines = result.split(delimiter)
    return(delimiter.join(lines[1:]).strip())

def get_vo_cmap(

)

get_vo_cmap is a function to return a colormap optimized to show sign changes in the middle of the range.

def get_vo_cmap():
    """get_vo_cmap is a function to return a colormap optimized to show sign changes in the middle of the range.
    """
    LUTSIZE = matplotlib.rcParams['image.lut']

    _vo_cm_data =     {'red':   ((0, 0.75, 0.75), (0.4, 0.0, 0.0), (0.5, 0.0, 0.0),   (0.6, 1.0, 1.0),   (1.0, 1.0, 1.0)),
                       'green': ((0, 1.0, 1.0), (0.4, 0.5, 0.5), (0.5, 0.25, 0.25), (0.6, 0.25, 0.25), (1.0, 1.0, 1.0)),
                       'blue':  ((0, 1.0, 1.0), (0.4, 1.0, 1.0), (0.5, 0.5, 0.5),   (0.6, 0.0, 0.0),   (1.0, 0.5, 0.5))}


    vo_cm = matplotlib.colors.LinearSegmentedColormap('vo_cm',_vo_cm_data, LUTSIZE)

    return vo_cm

Classes

class mad2YTimePlot

mad2YTimePlot is the class the produces line plots of two parameters with different Y axes versus time.

class mad2YTimePlot:
    """mad2YTimePlot is the class the produces line plots of two parameters with different Y axes versus time.


    """
    def __init__(self, isprintText,
                 yParmList,
                 titleStr,
                 xLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 yMinimum = None,
                 yMaximum = None,
                 noDate = False,
                 timezone = 0):
        """__init__ writes a madLineTimePlot plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The following must be two parameters to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            yParmList - a list of two y parameters (strings)
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
                
            useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                              since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                      parameter in isprintText must be UT1, if false, it must be UTH.
    
            startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                        in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                        startTime must be in units of UTH (hours since midnight UT of first day of
                        experiment). Default is None, which means start at lowest time found.
    
            endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
                      in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                      endTime must be in units of UTH (hours since midnight UT of first day of
                      experiment). Default is None, which means end at largest time found.
    
            maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                           needed to have at most maxNumPoints lines.
    
            noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum of first parm.  If default=None, use data minimum
    
            yMaximum - set y maximum of first.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
            
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 1 + len(yParmList)
        self.__colorList = 'gbrcmykw' # a list of colors for the lines

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
            threshold = self.__missing*0.9999
            array_data = numpy.ma.masked_where(array_data > threshold, array_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
        
        # find yMin just to ensure there is data
        yMin = None
        for column in range(len(yParmList)):
            for y in array_data[:,1+column]:
                if y == self.__missing:
                    continue
                if yMin == None:
                    yMin = y
                elif yMin > y:
                    yMin = y
        
        if yMin == None:
            raise ValueError('No valid y data found')


        fig, ax1 = matplotlib.pylab.subplots()
        # select the plot size
        if size == 'small':
            fig.set_size_inches(8, 3)
        elif size == 'wide':
            fig.set_size_inches(10,4)
        elif size == 'large':
            fig.set_size_inches(12,6)
        
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
        else:
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
      
        # plot first
        color = self.__colorList[0]
        ax1.plot(array_data[:,0],array_data[:,1], '%so-' % (color))
        ax1.set_ylabel(yParmList[0], color=color)
        
        ax2 = ax1.twinx()
        color = self.__colorList[1]
        ax2.plot(array_data[:,0],array_data[:,2], '%so-' % (color))
        ax2.set_ylabel(yParmList[1], color=color)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.grid(True)

        if useAbsoluteTime:
            
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
                
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs, noTime)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                ax1.set_xticks(newXLocs)
                ax2.set_xticks(newXLocs)

        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None or yMaximum != None:
            ax1.set_ylim(yMinimum, yMaximum)

        matplotlib.pylab.title(titleStr, fontsize=fontSize)
          
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        
    
    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, yParmList, titleStr, xLabelStr, fullFilename, size='small', useAbsoluteTime=False, startTime=None, endTime=None, maxNumPoints=None, noTime=False, yMinimum=None, yMaximum=None, noDate=False, timezone=0)

init writes a madLineTimePlot plot to a file.

Inputs:

isprintText - a string giving isprint output without headers. First parameter
              must be UTH or UT1, depending on whether time scale should be relative
  to the experiment beginning (UTH) or absolute (UT1).
  The following must be two parameters to be plotted. Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

yParmList - a list of two y parameters (strings)

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                  since beginning of experiment (UTH).  If useAbsoluteTime is true, first
          parameter in isprintText must be UT1, if false, it must be UTH.

startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
            in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
            startTime must be in units of UTH (hours since midnight UT of first day of
            experiment). Default is None, which means start at lowest time found.

endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
          in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
          endTime must be in units of UTH (hours since midnight UT of first day of
          experiment). Default is None, which means end at largest time found.

maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
               needed to have at most maxNumPoints lines.

noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.

noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

yMinimum - set y minimum of first parm.  If default=None, use data minimum

yMaximum - set y maximum of first.  If default=None, use data maximum

timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
            (negative in most of Western Europe, positive in the US, zero in the UK).

Returns: void

Affects: None

def __init__(self, isprintText,
             yParmList,
             titleStr,
             xLabelStr,
             fullFilename,
             size = 'small',
             useAbsoluteTime = False,
             startTime = None,
             endTime = None,
             maxNumPoints = None,
             noTime = False,
             yMinimum = None,
             yMaximum = None,
             noDate = False,
             timezone = 0):
    """__init__ writes a madLineTimePlot plot to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First parameter
                      must be UTH or UT1, depending on whether time scale should be relative
          to the experiment beginning (UTH) or absolute (UT1).
          The following must be two parameters to be plotted. Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float.
        yParmList - a list of two y parameters (strings)
        
        titleStr - plot title (string) - should describe parameter being plotted
        
        xLabelStr - x label string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
            
        useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                          since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                  parameter in isprintText must be UT1, if false, it must be UTH.

        startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                    in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                    startTime must be in units of UTH (hours since midnight UT of first day of
                    experiment). Default is None, which means start at lowest time found.

        endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
                  in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                  endTime must be in units of UTH (hours since midnight UT of first day of
                  experiment). Default is None, which means end at largest time found.

        maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                       needed to have at most maxNumPoints lines.

        noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
        
        noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

        yMinimum - set y minimum of first parm.  If default=None, use data minimum

        yMaximum - set y maximum of first.  If default=None, use data maximum
        
        timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                    (negative in most of Western Europe, positive in the US, zero in the UK).
        
    Returns: void
    Affects: None
    """
    self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
    self.__parameter_count = 1 + len(yParmList)
    self.__colorList = 'gbrcmykw' # a list of colors for the lines
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    if maxNumPoints != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
           
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        threshold = self.__missing*0.9999
        array_data = numpy.ma.masked_where(array_data > threshold, array_data)
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    
    # find min, max of x (time)
    if startTime == None:
        xMin = array_data[0,0]
    else:
        xMin = startTime
    if endTime == None:
        xMax = array_data[-1,0]
    else:
        xMax = endTime
    
    
    # find yMin just to ensure there is data
    yMin = None
    for column in range(len(yParmList)):
        for y in array_data[:,1+column]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
    
    if yMin == None:
        raise ValueError('No valid y data found')
    fig, ax1 = matplotlib.pylab.subplots()
    # select the plot size
    if size == 'small':
        fig.set_size_inches(8, 3)
    elif size == 'wide':
        fig.set_size_inches(10,4)
    elif size == 'large':
        fig.set_size_inches(12,6)
    
    if useAbsoluteTime:
         # leave room for rotated datetime string
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
        elif size == 'wide':
            matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
        elif size == 'small':
            matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
    else:
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
  
    # plot first
    color = self.__colorList[0]
    ax1.plot(array_data[:,0],array_data[:,1], '%so-' % (color))
    ax1.set_ylabel(yParmList[0], color=color)
    
    ax2 = ax1.twinx()
    color = self.__colorList[1]
    ax2.plot(array_data[:,0],array_data[:,2], '%so-' % (color))
    ax2.set_ylabel(yParmList[1], color=color)
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.xlim(xMin, xMax)
    matplotlib.pylab.grid(True)
    if useAbsoluteTime:
        
        # if plot is less than 24 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim() 
        xmin = xmin / (60*60)
        xmax = xmax / (60*60)
        if noDate == True:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            step = int((xmax-xmin-1)/6)+1
            xRange =  list(range(0,int(newXmax-xmin),step))
            for i in xRange:
                tmp = (xmin + i)*60*60
                newXLocs.append(tmp)
            
            if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
            
            newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
            matplotlib.pylab.xticks(newXLocs, newXLabels)
            
        else:
            locs, labels = matplotlib.pylab.xticks()
            if len(locs) > 5:
                # truncate by len(locs) / 5
                scale = 1 + int(len(locs) / 5)
                new_locs = []
                for i in range(len(locs)):
                    if i % scale == 0:
                        new_locs.append(locs[i])
                locs = new_locs
            newXTicks = convertToAbsoluteTimeStr(locs, noTime)
            matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
    else:
        # if plot is less than 48 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim()
        if xmax < 49:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            for i in range(0,int(4+newXmax),4):
                newXLocs.append(i)
            ax1.set_xticks(newXLocs)
            ax2.set_xticks(newXLocs)
    matplotlib.pylab.xticks(fontsize=fontSize)
    if yMinimum != None or yMaximum != None:
        ax1.set_ylim(yMinimum, yMaximum)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
      
    matplotlib.pylab.savefig(fullFilename)
    matplotlib.pylab.clf()

class madHistogram

madHistogram is the class that produces Histogram plots of Madrigal Data.

Usage example::

obj = madHistogram(isprintText,
                    'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2005 - Alt. code',
                    'Hours since midnight UT Oct. 30,2003'
                    'Altitude',
                    './isprint.png'
                    size = 'large')

Inputs:

isprintText - a string giving isprint output without headers. Any missing data should
                be written as 'missing' or other string that cannot be converted into
                a float.  Only one column.

TitleStr - plot title(string) - should describe the plot being made

xLabelStr - Label for x axis

fullFilename - full path of file containing histogram plot to be saved. Extension must be .jpeg
                or .png, or exception thrown

size - size of plot to be saved. Must be 'small','wide', or 'large'. Defaults to small

maxNumPoints - maximum number of points to be considered. Defaults to None, so all points in string shown


numBins - number of bins to be used to store the data. More bins means closer resolution in Histogram.
            Default is 30

orientation - whether histogram shows horizontal or vertical. Must be written as 'horizontal' or
                'vertical', or exception thrown--defaults to 'vertical'

isNorm - whether histogram should be normalized or not. Default is 0, or False

isBottom - If true, sets the lowest passed value as zero

sdevs - number of standard deviations to take into account. When 0 is passed to it, this option is ignored and all data
        is included in the distribution; otherwise, the range of values accepted is sdevs*(standard deviation of sample)

Outputs:

A .png file is written to the fullFilename path given, resulting in a Histogram drawn by matplotlib

Change History:

Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug. 1,2006
Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug. 1,2006
class madHistogram:
    """madHistogram is the class that produces Histogram plots of Madrigal
        Data.

        Usage example::

            obj = madHistogram(isprintText,
                                'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2005 - Alt. code',
                                'Hours since midnight UT Oct. 30,2003'
                                'Altitude',
                                './isprint.png'
                                size = 'large')

        Inputs:

            isprintText - a string giving isprint output without headers. Any missing data should
                            be written as 'missing' or other string that cannot be converted into
                            a float.  Only one column.

            TitleStr - plot title(string) - should describe the plot being made

            xLabelStr - Label for x axis

            fullFilename - full path of file containing histogram plot to be saved. Extension must be .jpeg
                            or .png, or exception thrown

            size - size of plot to be saved. Must be 'small','wide', or 'large'. Defaults to small

            maxNumPoints - maximum number of points to be considered. Defaults to None, so all points in string shown


            numBins - number of bins to be used to store the data. More bins means closer resolution in Histogram.
                        Default is 30

            orientation - whether histogram shows horizontal or vertical. Must be written as 'horizontal' or
                            'vertical', or exception thrown--defaults to 'vertical'

            isNorm - whether histogram should be normalized or not. Default is 0, or False

            isBottom - If true, sets the lowest passed value as zero

            sdevs - number of standard deviations to take into account. When 0 is passed to it, this option is ignored and all data
                    is included in the distribution; otherwise, the range of values accepted is sdevs*(standard deviation of sample)

        Outputs:

            A .png file is written to the fullFilename path given, resulting in a Histogram drawn by matplotlib

            
        Change History:

            Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug. 1,2006
            Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug. 1,2006
 
    """
    def __init__(self,isprintText,
                 titleStr,
                 xLabelStr,
                 fullFilename,
                 size = 'small',
                 maxNumPoints = None,
                 numBins = 30,
                 Orientation='vertical',
                 isNorm =0,
                 isBottom=0,
                 sdevs=0):
        """__init__ writes a madHistogram string to file

        """
        self.__missing = 1.0E30 #special value, since numpy doesn't Handle NaN
        self.__parameter_count = 2

        #verify input
        if not size in ('small','wide','large'):
            raise ValueError('size must be "small","wide" or "large", not %s'%(str(size)))

        if size in ('small','wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        # remove all non-float values
        items = isprintText.split()
        keptItems = []
        for item in items:
            try:
                float(item)
                keptItems.append(item)
            except:
                continue
        delimiter = ' '
        isprintText = delimiter.join(keptItems)

        if maxNumPoints !=None:
            isprintText = self._truncateIsprint(isprintText, maxNumPoints)

        #convert the input data into numeric arrays of floats assuming no headers and filter the missing values

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing,split_data))
            array_data = numpy.asarray(float_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')

        #if needed, throw away outliers
        if sdevs !=0:
            array_data = self.removeOutliers(sdevs,array_data)

        #select the plot size
        if size=='small':
            matplotlib.pylab.figure(1,figsize=(6,4), facecolor = 'w')
        elif size=='wide':
            matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
        elif size=='large':
            matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')


        matplotlib.pylab.hist(array_data,bins = numBins,normed=isNorm,bottom=isBottom,orientation = Orientation)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont) #pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.title(titleStr,fontsize=fontSize)

        #self.__figure = matplotlib.pylab.gcf()

        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprintStr(self,isprintText, maxLines):
        """__truncateIsprintStr truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList)=ndist) and (i<=pdist):
                retArray.append(i)

        return retArray

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, titleStr, xLabelStr, fullFilename, size='small', maxNumPoints=None, numBins=30, Orientation='vertical', isNorm=0, isBottom=0, sdevs=0)

init writes a madHistogram string to file

def __init__(self,isprintText,
             titleStr,
             xLabelStr,
             fullFilename,
             size = 'small',
             maxNumPoints = None,
             numBins = 30,
             Orientation='vertical',
             isNorm =0,
             isBottom=0,
             sdevs=0):
    """__init__ writes a madHistogram string to file
    """
    self.__missing = 1.0E30 #special value, since numpy doesn't Handle NaN
    self.__parameter_count = 2
    #verify input
    if not size in ('small','wide','large'):
        raise ValueError('size must be "small","wide" or "large", not %s'%(str(size)))
    if size in ('small','wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    # remove all non-float values
    items = isprintText.split()
    keptItems = []
    for item in items:
        try:
            float(item)
            keptItems.append(item)
        except:
            continue
    delimiter = ' '
    isprintText = delimiter.join(keptItems)
    if maxNumPoints !=None:
        isprintText = self._truncateIsprint(isprintText, maxNumPoints)
    #convert the input data into numeric arrays of floats assuming no headers and filter the missing values
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing,split_data))
        array_data = numpy.asarray(float_data)
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    #if needed, throw away outliers
    if sdevs !=0:
        array_data = self.removeOutliers(sdevs,array_data)
    #select the plot size
    if size=='small':
        matplotlib.pylab.figure(1,figsize=(6,4), facecolor = 'w')
    elif size=='wide':
        matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
    elif size=='large':
        matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')
    matplotlib.pylab.hist(array_data,bins = numBins,normed=isNorm,bottom=isBottom,orientation = Orientation)
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont) #pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.title(titleStr,fontsize=fontSize)
    #self.__figure = matplotlib.pylab.gcf()
    matplotlib.pylab.savefig(fullFilename)
    matplotlib.pylab.clf()

def removeOutliers(

self, ndevs, values=[])

def removeOutliers(self,ndevs,values=[]):
    
    #find the mean value in values
    sums = 0.0
    count = 0
    for i in values:
        sums = sums+i
        count = count+1
    mean = sums/count
    
    #calculate a standard deviation
    innersum = 0
    for i in values:
        innersum = innersum + math.pow((i-mean),2)
    sdev = math.sqrt((1.0/count)*innersum)
    #throw away all data points falling outside three standard deviations of the mean, then return the resulting list
    retArray = []
    ndist = mean-ndevs*sdev
    pdist = mean+ndevs*sdev
    for i in values:
        if (i>=ndist) and (i<=pdist):
            retArray.append(i)
    return retArray

class madIsrRecordSummary

madIsrRecordSummary is the class that produces a summary plot of a single ISR record.

class madIsrRecordSummary:
    """madIsrRecordSummary is the class that produces a summary plot of a single ISR record.


    """
    def __init__(self, TiData,
                 TiError,
                 TeData,
                 TeError,
                 VoData,
                 VoError,
                 NelData,
                 NelError,
		 isPopl,
                 description,
                 fullFilename,
                 useRange = False,
                 altResl = None):
        """__init__ writes a madIsrRecordSummary  to a file.

        Inputs:

            TiData - an Nx2 numeric array of Ti (col 0) and altitude in km (col 1)

            TiError - an N length array of error in Ti
            
            TeData - an Nx2 numeric array of Te (col 0) and altitude in km (col 1)

            TeError - an N length array of error in Te

            VoData - an Nx2 numeric array of Vo (col 0) and altitude in km (col 1)

            VoError - an N length array of error in Vo

            NelData - an Nx2 numeric array of (Popl or Nel in m^-3) (col 0) and altitude in km (col 1)

            NelError - an 2xN length array of lower, upper error in Popl or Nel
            
            isPopl - true if Nel contains Popl data, false if Nel data

            description - text to put in fourth panel of plot - use carriage returns to separate lines

            fullFilename - full filename to save output png file.

            useRange - if False (the default), y axis = Altitude.  If True, y axis = Range.

            altResl - altitude resolution in km.  If not None, show altitude resolution on each graph.
        
        
        Returns: void

        Affects: None
        """
        if useRange:
            yAxisStr = 'Range (km)'
        else:
            yAxisStr = 'Altitude (km)'
            
        # upper left - Ti and Te
        fig = matplotlib.pylab.figure(figsize=(10,10))
        axis = matplotlib.pylab.subplot(2,2,1)
        if TiData.shape[0] > 0 and TeData.shape[0] > 0:
            axis.errorbar(TiData[:,0], TiData[:,1], xerr=TiError, fmt='bo-')   
            axis.errorbar(TeData[:,0], TeData[:,1], xerr=TeError, fmt='rD-')
            matplotlib.pylab.text(0.65, 0.1, 'Ti: blue circle\nTe: red diamond',
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
            matplotlib.pylab.xlabel('Temperature (K)')
            matplotlib.pylab.ylabel(yAxisStr)
            matplotlib.pylab.grid(True)
            # set limits based on data only
            tiMax = numpy.nanmax(TiData[:,0])
            tiMin = numpy.nanmin(TiData[:,0])
            teMax = numpy.nanmax(TeData[:,0])
            teMin = numpy.nanmin(TeData[:,0])
            lowerLimit = (int(min(tiMin, teMin)) / 1000) * 1000
            upperLimit = (1+int(max(tiMax, teMax)) / 1000) * 1000            
            
            # alt resolution
            if altResl != None:
                altLabel = 'Alt res: \n%i km' % (int(altResl))
                y_min, y_max = matplotlib.pylab.ylim()
                startX = lowerLimit + 0.8*(upperLimit - lowerLimit)
                startY = 0.15
                reslPercentage = altResl/(y_max-y_min)
                matplotlib.pylab.axvline(x=startX,
                                         ymin=startY,
                                         ymax=reslPercentage + startY,
                                         linewidth=2, color='b')
                matplotlib.pylab.text(0.83, 0.2, altLabel,
                                      horizontalalignment='left',
                                      verticalalignment='top',
                                      transform = axis.transAxes,
                                      fontsize=10)
                
            matplotlib.pylab.xlim(lowerLimit, upperLimit)
            
                
                                         

        # upper right - Vo and 10*Vo
        axis = matplotlib.pylab.subplot(2,2,2)
        if VoData.shape[0] > 0:
            axis.errorbar(VoData[:,0], VoData[:,1], xerr=VoError, fmt='bo-')
            axis.errorbar(10.0*VoData[:,0], VoData[:,1], xerr=10.0*VoError, fmt='rD-')
            # set x limits to be even around 0
            voMax = numpy.nanmax(10.0*VoData[:,0])
            voMin = numpy.nanmin(10.0*VoData[:,0])
            limit = max(abs(voMax), abs(voMin))
            matplotlib.pylab.xlim(-1.0*limit, limit)
            matplotlib.pylab.text(0.58, 0.1, 'Vo: blue circle\n10*Vo: red diamond',
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
            matplotlib.pylab.xlabel('Line of sight drift (m/s)')
            matplotlib.pylab.ylabel(yAxisStr)
            matplotlib.pylab.grid(True)

            # alt resolution
            if altResl != None:
                altLabel = 'Alt res: \n%i km' % (int(altResl))
                y_min, y_max = matplotlib.pylab.ylim()
                startX = limit*0.6
                startY = 0.15
                reslPercentage = altResl/(y_max-y_min)
                matplotlib.pylab.axvline(x=startX,
                                         ymin=startY,
                                         ymax=reslPercentage + startY,
                                         linewidth=2, color='b')
                matplotlib.pylab.text(0.83, 0.2, altLabel,
                                      horizontalalignment='left',
                                      verticalalignment='top',
                                      transform = axis.transAxes,
                                      fontsize=10)
                
            matplotlib.pylab.xlim(-1.0*limit, limit)

            

        # lower left - Nel or Popl
        axis = matplotlib.pylab.subplot(2,2,3)
        if isPopl:
            labelStr = 'Log Power (m^-3)'
        else:
            labelStr = 'Log Electron density (m^-3)'
        if NelData.shape[0] > 0:
            # check that not every point equal
            if numpy.nanmax(NelData[:,0]) == numpy.nanmin(NelData[:,0]):
                pass
            else:
                axis.errorbar(NelData[:,0], NelData[:,1], xerr=NelError, fmt='bo-')
                matplotlib.pylab.xlabel(labelStr)
                matplotlib.pylab.ylabel(yAxisStr)
                matplotlib.pylab.grid(True)
                nelMax = numpy.nanmax(NelData[:,0])
                nelMin = numpy.nanmin(NelData[:,0])
                matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
                matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  fontsize=18,
                                  transform = axis.transAxes, alpha=0.2)
                
                # alt resolution
                if altResl != None:
                    altLabel = 'Alt res: \n%i km' % (int(altResl))
                    y_min, y_max = matplotlib.pylab.ylim()
                    x_min, x_max = matplotlib.pylab.xlim()
                    startX = x_min + 0.8*(x_max-x_min)
                    startY = 0.15
                    reslPercentage = altResl/(y_max-y_min)
                    matplotlib.pylab.axvline(x=startX,
                                             ymin=startY,
                                             ymax=reslPercentage + startY,
                                             linewidth=2, color='b')
                    matplotlib.pylab.text(0.83, 0.2, altLabel,
                                          horizontalalignment='left',
                                          verticalalignment='top',
                                          transform = axis.transAxes,
                                          fontsize=10)

                    matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
        

        # lower right - text
        axis = matplotlib.pylab.subplot(2,2,4)
        matplotlib.pylab.text(0.05, 0.9, description,
                              horizontalalignment='left',
                              verticalalignment='top',
                              transform = axis.transAxes,
                              fontsize=9)
        matplotlib.pylab.xticks([])
        matplotlib.pylab.yticks([])
        
        

        matplotlib.pylab.subplots_adjust(wspace = 0.25)
        

        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()
        matplotlib.pylab.close()

Ancestors (in MRO)

Static methods

def __init__(

self, TiData, TiError, TeData, TeError, VoData, VoError, NelData, NelError, isPopl, description, fullFilename, useRange=False, altResl=None)

init writes a madIsrRecordSummary to a file.

Inputs:

TiData - an Nx2 numeric array of Ti (col 0) and altitude in km (col 1)

TiError - an N length array of error in Ti

TeData - an Nx2 numeric array of Te (col 0) and altitude in km (col 1)

TeError - an N length array of error in Te

VoData - an Nx2 numeric array of Vo (col 0) and altitude in km (col 1)

VoError - an N length array of error in Vo

NelData - an Nx2 numeric array of (Popl or Nel in m^-3) (col 0) and altitude in km (col 1)

NelError - an 2xN length array of lower, upper error in Popl or Nel

isPopl - true if Nel contains Popl data, false if Nel data

description - text to put in fourth panel of plot - use carriage returns to separate lines

fullFilename - full filename to save output png file.

useRange - if False (the default), y axis = Altitude.  If True, y axis = Range.

altResl - altitude resolution in km.  If not None, show altitude resolution on each graph.

Returns: void

Affects: None

def __init__(self, TiData,
             TiError,
             TeData,
             TeError,
             VoData,
             VoError,
             NelData,
             NelError,
sPopl,
             description,
             fullFilename,
             useRange = False,
             altResl = None):
    """__init__ writes a madIsrRecordSummary  to a file.
    Inputs:
        TiData - an Nx2 numeric array of Ti (col 0) and altitude in km (col 1)
        TiError - an N length array of error in Ti
        
        TeData - an Nx2 numeric array of Te (col 0) and altitude in km (col 1)
        TeError - an N length array of error in Te
        VoData - an Nx2 numeric array of Vo (col 0) and altitude in km (col 1)
        VoError - an N length array of error in Vo
        NelData - an Nx2 numeric array of (Popl or Nel in m^-3) (col 0) and altitude in km (col 1)
        NelError - an 2xN length array of lower, upper error in Popl or Nel
        
        isPopl - true if Nel contains Popl data, false if Nel data
        description - text to put in fourth panel of plot - use carriage returns to separate lines
        fullFilename - full filename to save output png file.
        useRange - if False (the default), y axis = Altitude.  If True, y axis = Range.
        altResl - altitude resolution in km.  If not None, show altitude resolution on each graph.
    
    
    Returns: void
    Affects: None
    """
    if useRange:
        yAxisStr = 'Range (km)'
    else:
        yAxisStr = 'Altitude (km)'
        
    # upper left - Ti and Te
    fig = matplotlib.pylab.figure(figsize=(10,10))
    axis = matplotlib.pylab.subplot(2,2,1)
    if TiData.shape[0] > 0 and TeData.shape[0] > 0:
        axis.errorbar(TiData[:,0], TiData[:,1], xerr=TiError, fmt='bo-')   
        axis.errorbar(TeData[:,0], TeData[:,1], xerr=TeError, fmt='rD-')
        matplotlib.pylab.text(0.65, 0.1, 'Ti: blue circle\nTe: red diamond',
                              horizontalalignment='left',
                              verticalalignment='top',
                              transform = axis.transAxes,
                              fontsize=10)
        matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                              horizontalalignment='center',
                              verticalalignment='center',
                              fontsize=18,
                              transform = axis.transAxes, alpha=0.2)
        matplotlib.pylab.xlabel('Temperature (K)')
        matplotlib.pylab.ylabel(yAxisStr)
        matplotlib.pylab.grid(True)
        # set limits based on data only
        tiMax = numpy.nanmax(TiData[:,0])
        tiMin = numpy.nanmin(TiData[:,0])
        teMax = numpy.nanmax(TeData[:,0])
        teMin = numpy.nanmin(TeData[:,0])
        lowerLimit = (int(min(tiMin, teMin)) / 1000) * 1000
        upperLimit = (1+int(max(tiMax, teMax)) / 1000) * 1000            
        
        # alt resolution
        if altResl != None:
            altLabel = 'Alt res: \n%i km' % (int(altResl))
            y_min, y_max = matplotlib.pylab.ylim()
            startX = lowerLimit + 0.8*(upperLimit - lowerLimit)
            startY = 0.15
            reslPercentage = altResl/(y_max-y_min)
            matplotlib.pylab.axvline(x=startX,
                                     ymin=startY,
                                     ymax=reslPercentage + startY,
                                     linewidth=2, color='b')
            matplotlib.pylab.text(0.83, 0.2, altLabel,
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            
        matplotlib.pylab.xlim(lowerLimit, upperLimit)
        
            
                                     
    # upper right - Vo and 10*Vo
    axis = matplotlib.pylab.subplot(2,2,2)
    if VoData.shape[0] > 0:
        axis.errorbar(VoData[:,0], VoData[:,1], xerr=VoError, fmt='bo-')
        axis.errorbar(10.0*VoData[:,0], VoData[:,1], xerr=10.0*VoError, fmt='rD-')
        # set x limits to be even around 0
        voMax = numpy.nanmax(10.0*VoData[:,0])
        voMin = numpy.nanmin(10.0*VoData[:,0])
        limit = max(abs(voMax), abs(voMin))
        matplotlib.pylab.xlim(-1.0*limit, limit)
        matplotlib.pylab.text(0.58, 0.1, 'Vo: blue circle\n10*Vo: red diamond',
                              horizontalalignment='left',
                              verticalalignment='top',
                              transform = axis.transAxes,
                              fontsize=10)
        matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                              horizontalalignment='center',
                              verticalalignment='center',
                              fontsize=18,
                              transform = axis.transAxes, alpha=0.2)
        matplotlib.pylab.xlabel('Line of sight drift (m/s)')
        matplotlib.pylab.ylabel(yAxisStr)
        matplotlib.pylab.grid(True)
        # alt resolution
        if altResl != None:
            altLabel = 'Alt res: \n%i km' % (int(altResl))
            y_min, y_max = matplotlib.pylab.ylim()
            startX = limit*0.6
            startY = 0.15
            reslPercentage = altResl/(y_max-y_min)
            matplotlib.pylab.axvline(x=startX,
                                     ymin=startY,
                                     ymax=reslPercentage + startY,
                                     linewidth=2, color='b')
            matplotlib.pylab.text(0.83, 0.2, altLabel,
                                  horizontalalignment='left',
                                  verticalalignment='top',
                                  transform = axis.transAxes,
                                  fontsize=10)
            
        matplotlib.pylab.xlim(-1.0*limit, limit)
        
    # lower left - Nel or Popl
    axis = matplotlib.pylab.subplot(2,2,3)
    if isPopl:
        labelStr = 'Log Power (m^-3)'
    else:
        labelStr = 'Log Electron density (m^-3)'
    if NelData.shape[0] > 0:
        # check that not every point equal
        if numpy.nanmax(NelData[:,0]) == numpy.nanmin(NelData[:,0]):
            pass
        else:
            axis.errorbar(NelData[:,0], NelData[:,1], xerr=NelError, fmt='bo-')
            matplotlib.pylab.xlabel(labelStr)
            matplotlib.pylab.ylabel(yAxisStr)
            matplotlib.pylab.grid(True)
            nelMax = numpy.nanmax(NelData[:,0])
            nelMin = numpy.nanmin(NelData[:,0])
            matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
            matplotlib.pylab.text(0.5,0.5, 'Preliminary data\n\n\nfrom the\n\n\nMadrigal database',
                              horizontalalignment='center',
                              verticalalignment='center',
                              fontsize=18,
                              transform = axis.transAxes, alpha=0.2)
            
            # alt resolution
            if altResl != None:
                altLabel = 'Alt res: \n%i km' % (int(altResl))
                y_min, y_max = matplotlib.pylab.ylim()
                x_min, x_max = matplotlib.pylab.xlim()
                startX = x_min + 0.8*(x_max-x_min)
                startY = 0.15
                reslPercentage = altResl/(y_max-y_min)
                matplotlib.pylab.axvline(x=startX,
                                         ymin=startY,
                                         ymax=reslPercentage + startY,
                                         linewidth=2, color='b')
                matplotlib.pylab.text(0.83, 0.2, altLabel,
                                      horizontalalignment='left',
                                      verticalalignment='top',
                                      transform = axis.transAxes,
                                      fontsize=10)
                matplotlib.pylab.xlim(math.floor(nelMin), math.ceil(nelMax))
    
    # lower right - text
    axis = matplotlib.pylab.subplot(2,2,4)
    matplotlib.pylab.text(0.05, 0.9, description,
                          horizontalalignment='left',
                          verticalalignment='top',
                          transform = axis.transAxes,
                          fontsize=9)
    matplotlib.pylab.xticks([])
    matplotlib.pylab.yticks([])
    
    
    matplotlib.pylab.subplots_adjust(wspace = 0.25)
    
    matplotlib.pylab.savefig(fullFilename)
    #matplotlib.pylab.show()
    matplotlib.pylab.clf()
    matplotlib.pylab.close()

class madLineTimePlot

madLineTimePlot is the class the produces line plots of one or more parameters versus time.

class madLineTimePlot:
    """madLineTimePlot is the class the produces line plots of one or more parameters versus time.


    """
    def __init__(self, isprintText,
                 yParmList,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 yMinimum = None,
                 yMaximum = None,
                 noDate = False,
                 timezone = 0):
        """__init__ writes a madLineTimePlot plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
			  to the experiment beginning (UTH) or absolute (UT1).
			  The the following must be the parameters to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            yParmList - a list of y parameters (strings).  Length must == num columns in isprintText - 1
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
            
            yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
			    
    	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
    	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
    			      parameter in isprintText must be UT1, if false, it must be UTH.
    
    	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
    	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	                startTime must be in units of UTH (hours since midnight UT of first day of
    	                experiment). Default is None, which means start at lowest time found.
    
    	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
    	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	              endTime must be in units of UTH (hours since midnight UT of first day of
    	              experiment). Default is None, which means end at largest time found.
    
    	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
    	                   needed to have at most maxNumPoints lines.
    
    	    noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
    	    
    	    noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
    	    yMinimum - set y minimum.  If default=None, use data minimum
    
    	    yMaximum - set y maximum.  If default=None, use data maximum
    	    
    	    timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
            
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 1 + len(yParmList)
        self.__colorList = 'gbrcmykw' # a list of colors for the lines

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
            threshold = self.__missing*0.9999
            array_data = numpy.ma.masked_where(array_data > threshold, array_data)
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
	    
        # find yMin just to ensure there is data
        yMin = None
        for column in range(len(yParmList)):
            for y in array_data[:,1+column]:
                if y == self.__missing:
                    continue
                if yMin == None:
                    yMin = y
                elif yMin > y:
                    yMin = y
		
        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
	    
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
        else:
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
      
        for column in range(len(yParmList)):
            color = self.__colorList[column % len(self.__colorList)]
            matplotlib.pylab.plot(array_data[:,0],array_data[:,1 + column], '%so-' % (color), ms=5, mew=0.5)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.grid(True)

        if useAbsoluteTime:
            
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
                
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs, noTime)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                    newXLabels.append(str(i))
                matplotlib.pylab.xticks(newXLocs, newXLabels)

        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)

        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.legend(yParmList, numpoints=1)
          
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        
	
    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

    def writeToFile(self, fullFilename):
        pass

    def displayToScreen(self):
        pass

    def getFigureHandle(self):
        pass

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, yParmList, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', useAbsoluteTime=False, startTime=None, endTime=None, maxNumPoints=None, noTime=False, yMinimum=None, yMaximum=None, noDate=False, timezone=0)

init writes a madLineTimePlot plot to a file.

Inputs:

isprintText - a string giving isprint output without headers. First parameter
              must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The the following must be the parameters to be plotted. Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

yParmList - a list of y parameters (strings).  Length must == num columns in isprintText - 1

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

yLabelStr - ylabel string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                  since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                  parameter in isprintText must be UT1, if false, it must be UTH.

startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
            in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
            startTime must be in units of UTH (hours since midnight UT of first day of
            experiment). Default is None, which means start at lowest time found.

endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
          in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
          endTime must be in units of UTH (hours since midnight UT of first day of
          experiment). Default is None, which means end at largest time found.

maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
               needed to have at most maxNumPoints lines.

noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.

noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

yMinimum - set y minimum.  If default=None, use data minimum

yMaximum - set y maximum.  If default=None, use data maximum

timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
            (negative in most of Western Europe, positive in the US, zero in the UK).

Returns: void

Affects: None

def __init__(self, isprintText,
             yParmList,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             useAbsoluteTime = False,
             startTime = None,
             endTime = None,
             maxNumPoints = None,
             noTime = False,
             yMinimum = None,
             yMaximum = None,
             noDate = False,
             timezone = 0):
    """__init__ writes a madLineTimePlot plot to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First parameter
                      must be UTH or UT1, depending on whether time scale should be relative
 to the experiment beginning (UTH) or absolute (UT1).
 The the following must be the parameters to be plotted. Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float.
        yParmList - a list of y parameters (strings).  Length must == num columns in isprintText - 1
        
        titleStr - plot title (string) - should describe parameter being plotted
        
        xLabelStr - x label string
        
        yLabelStr - ylabel string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
   
	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
			      parameter in isprintText must be UT1, if false, it must be UTH.

	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	                startTime must be in units of UTH (hours since midnight UT of first day of
	                experiment). Default is None, which means start at lowest time found.

	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	              endTime must be in units of UTH (hours since midnight UT of first day of
	              experiment). Default is None, which means end at largest time found.

	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
	                   needed to have at most maxNumPoints lines.

	    noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
	    
	    noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

	    yMinimum - set y minimum.  If default=None, use data minimum

	    yMaximum - set y maximum.  If default=None, use data maximum
	    
	    timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                    (negative in most of Western Europe, positive in the US, zero in the UK).
        
    Returns: void
    Affects: None
    """
    self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
    self.__parameter_count = 1 + len(yParmList)
    self.__colorList = 'gbrcmykw' # a list of colors for the lines
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    if maxNumPoints != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
           
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        threshold = self.__missing*0.9999
        array_data = numpy.ma.masked_where(array_data > threshold, array_data)
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    
    # find min, max of x (time)
    if startTime == None:
        xMin = array_data[0,0]
    else:
        xMin = startTime
    if endTime == None:
        xMax = array_data[-1,0]
    else:
        xMax = endTime
    
 
    # find yMin just to ensure there is data
    yMin = None
    for column in range(len(yParmList)):
        for y in array_data[:,1+column]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
    if yMin == None:
        raise ValueError('No valid y data found')
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
 
    if useAbsoluteTime:
         # leave room for rotated datetime string
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
        elif size == 'wide':
            matplotlib.pylab.axes([0.07, 0.2, 0.9, 0.7])
        elif size == 'small':
            matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
    else:
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.15, 0.71, 0.73])
  
    for column in range(len(yParmList)):
        color = self.__colorList[column % len(self.__colorList)]
        matplotlib.pylab.plot(array_data[:,0],array_data[:,1 + column], '%so-' % (color), ms=5, mew=0.5)
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.xlim(xMin, xMax)
    matplotlib.pylab.grid(True)
    if useAbsoluteTime:
        
        # if plot is less than 24 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim() 
        xmin = xmin / (60*60)
        xmax = xmax / (60*60)
        if noDate == True:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            step = int((xmax-xmin-1)/6)+1
            xRange =  list(range(0,int(newXmax-xmin),step))
            for i in xRange:
                tmp = (xmin + i)*60*60
                newXLocs.append(tmp)
            
            if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
            
            newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
            matplotlib.pylab.xticks(newXLocs, newXLabels)
            
        else:
            locs, labels = matplotlib.pylab.xticks()
            if len(locs) > 5:
                # truncate by len(locs) / 5
                scale = 1 + int(len(locs) / 5)
                new_locs = []
                for i in range(len(locs)):
                    if i % scale == 0:
                        new_locs.append(locs[i])
                locs = new_locs
            newXTicks = convertToAbsoluteTimeStr(locs, noTime)
            matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
    else:
        # if plot is less than 48 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim()
        if xmax < 49:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            for i in range(0,int(4+newXmax),4):
                newXLocs.append(i)
                newXLabels.append(str(i))
            matplotlib.pylab.xticks(newXLocs, newXLabels)
    matplotlib.pylab.xticks(fontsize=fontSize)
    if yMinimum != None and yMaximum != None:
        matplotlib.pylab.ylim(yMinimum, yMaximum)
    elif yMinimum != None and yMaximum == None:
        matplotlib.pylab.ylim(yMin=yMinimum)
    elif yMinimum == None and yMaximum != None:
        matplotlib.pylab.ylim(yMax=yMaximum)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    matplotlib.pylab.legend(yParmList, numpoints=1)
      
    matplotlib.pylab.savefig(fullFilename)
    matplotlib.pylab.clf()

def displayToScreen(

self)

def displayToScreen(self):
    pass

def getFigureHandle(

self)

def getFigureHandle(self):
    pass

def writeToFile(

self, fullFilename)

def writeToFile(self, fullFilename):
    pass

class madPcolorPlot

madPcolorPlot is the class that produces pcolor plots of x versus y with z intensity.

Assumes the x axis is time.

Usage example::

obj = madPcolorPlot(isprintText,
                'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2003 - Alt code',
                'Hours since midnight UT Oct. 30, 2003',
                'Altitude (km)',
                './isprint.png',
                size = 'large',
                minColormap = 9,
                maxColormap = 12,
                smoothAltitude = False)

Non-standard Python modules used: matplotlib

Change history:

Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu Mar. 31, 2005

class madPcolorPlot:
    """madPcolorPlot is the class that produces pcolor plots of x versus y with z intensity.

    Assumes the x axis is time.

    Usage example::

    	obj = madPcolorPlot(isprintText,
                        'Nel (log(m^-3)) - Millstone Hill - Oct. 30, 2003 - Alt code',
                        'Hours since midnight UT Oct. 30, 2003',
                        'Altitude (km)',
                        './isprint.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12,
			smoothAltitude = False)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Mar. 31, 2005
    """


    def __init__(self, isprintText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 minColormap = None,
                 maxColormap = None,
                 smoothAltitude = True,
                 insertDataGap = 5,
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 sortTimeFlag = False,
                 maxNumTimes = None,
                 maxNumAlt = None,
                 truncateIsprint = False,
                 colorMap = matplotlib.cm.jet,
                 yMinimum = None,
                 yMaximum = None,
                 altYTitle = None,
                 altYLabels = None,
                 noTime = False,
                 noDate = False,
                 timezone = 0,
                 background = 'w',
                 peakAltStr = None):
        
        """__init__ writes a madPColorPlot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
			  to the experiment beginning (UTH) or absolute (UT1).
			  The second must be gdalt, and third parameter to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float. 
            
            titleStr - plot title (string) - should describe parameter being plotted
	    
    	    xLabelStr - x label string
    	    
    	    yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.
	    
    	    smoothAltitude - if True, extrapolate between existing data between altitudes to fill
    	                     in missing data; if False, leave missing
    			     
    	    insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
    	                    being plotted are ordered, and the time gap larger than 90% of the rest is determined.
    			    Any time interval more than insertDataGap times bigger is then considered missing
    			    data.  Defaults to five.  If None, no gaps are ever inserted.  For data with close
    			    to uniform time intervals, no gaps will be inserted.
    			    
    	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
    	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
    			      parameter in isprintText must be UT1, if false, it must be UTH.
        
            startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	                startTime must be in units of UTH (hours since midnight UT of first day of
	                experiment). Default is None, which means start at lowest time found.

    	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
    	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	              endTime must be in units of UTH (hours since midnight UT of first day of
    	              experiment). Default is None, which means end at largest time found.
    
    	    sortTimeFlag - if true, check that time is correctly sorted.  If false (the default),
    	                   assume time already sorted
    
    	    maxNumTimes - if not None, decimate the number of times in the isprint string to
    	                  maxNumTimes.  If None (the default), plot all times.
    
    	    maxNumAlt - if not None, reduce the number of altitudes to maxNumAlt.  If None (the default),
    	                plot all altitudes.
    
    	    truncateIsprint - if True, and both maxNumTimes and maxNumAlt not = None, then truncate
    	                       the number of isprint lines to be maxNumTimes * maxNumAlt
    
    	    colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet
    
    	    yMinimum - minumum y value.  If None (default), set by data y minimum.
    
    	    yMaximum - maximum y value.  If None (default), set by data y maximum.
    
    	    altYTitle - title of right (second) y axis.  If None (the default),
    	                 no Y axis on the right side.
    
    	    altYLabels - a list of Y labels (strings) for the right axis.  If None (the default),
    	                 no Y labels on the right axis.

            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
                        
            peakAltStr - if not None (the default), plot the peak altitude with x.  Format is a
                str with each line (time in same format as data, peak altitude)
                  
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, used to create a masked array
        self.__parameter_count = 3

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))

        if size in ('small'):
            fontSize = 10
        elif size in ('wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if truncateIsprint and maxNumTimes != None and maxNumAlt != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumTimes * maxNumAlt)

        # since matplotlib pcolor wants a regular grid, we create a grid with all altitudes

        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')

        if sortTimeFlag:
            array_data = self.sortArrayInTime(array_data)

        if maxNumTimes != None and maxNumTimes != 0 and not truncateIsprint:
            array_data = self.decimateTimes(array_data, int(maxNumTimes), insertDataGap)
        
        # The first pass through is to obtain the number and range of the  x and y variables
        
        xList = []
        yList = []
        zList = []
        zMin = None
        zMax = None

        # loop over all the lines of data in the array
        for j in range(len(array_data)):
            try:
                
                x = array_data[j][0]
                y = array_data[j][1]
                z = array_data[j][2]

                if x not in xList:
                    xList.append(x)

                if y not in yList:
                    yList.append(y)

                if z != self.__missing:
                    zList.append(z)

                if zMin != None:
                    if z < zMin and z != self.__missing:
                        zMin = z
                elif z != self.__missing:
                    zMin = z

                if zMax != None:
                    if z > zMax and z != self.__missing:
                        zMax = z
                elif z != self.__missing:
                    zMax = z

            except:
                continue
		
        if zMin == None:
            raise ValueError('No valid z data found')

        # if both minColormap and maxColormap == None, use autoscaling
        if minColormap == None and maxColormap == None:
            zList.sort()
            d10 = zList[int(len(zList)*0.10)]
            d90 = zList[int(len(zList)*0.90)]

            zMin = d10 - (d90-d10) * 0.75
            zMax = d90 + (d90-d10) * 0.75

        # now sort the X and Y axis lists and pull their length
        
        xList.sort()
        if startTime == None:
            xMin = xList[0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = xList[-1]
        else:
            xMax = endTime
        yList.sort()
        max_x_dimension = len(xList)
        max_y_dimension = len(yList)

        if yMinimum == None:
            yMinimum = yList[0]
        if yMaximum == None:
            yMaximum = yList[-1]

        self.truncateAlt = False
        if maxNumAlt != None:
            if max_y_dimension > maxNumAlt:
                self.truncateAlt = True

        # build dictonary of indexes into xList
        self.xListDict = {}
        for i in range(len(xList)):
            self.xListDict[xList[i]] = i

        # if self.truncateAlt == False, build dictonary of indexes into yList,
        # else truncate y values by builing a list of maxNumAlt ranges
        if self.truncateAlt == False:
            self.yListDict = {}
            for i in range(len(yList)):
                self.yListDict[yList[i]] = i
        else:
            self.yListRanges = []
            for i in range(maxNumAlt):
                self.yListRanges.append(yList[int(i*(len(yList)/maxNumAlt))])
            max_y_dimension = maxNumAlt

        # now build arrays to handle the X axis label, Y axis label, and the Z data

        X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)

        # all parameter values default to missing
        Z = Z * self.__missing

        # fill the X and Y arrays

        for i in range(max_x_dimension):
            for j in range(max_y_dimension):
                X[i][j] = float(xList[i])
                if self.truncateAlt:
                    Y[i][j] = float(yList[int(j*(len(yList)/maxNumAlt))])
                else:
                    Y[i][j] = float(yList[j])

    
        # Now load up the data array Z with the array_data measurements as a function of x and y
        previousIndex = None
        previousValue = None
        presentTime = None
        newTimeFound = True

        for k in range(len(array_data)):
            try:

                xdata = array_data[k][0]
                ydata = array_data[k][1]
                zdata = array_data[k][2]

                if zdata == self.__missing:
                    continue

                if xdata != presentTime:
                    newTimeFound = True
                else:
                    newTimeFound = False
                presentTime = xdata

                # now find the right place in the array for this data point
                i = self.xListDict[xdata]
                j = self.__getYIndex(ydata)

                Z[i][j] = zdata
		
                # now see if we need to fill in any gaps
                if (not newTimeFound) and smoothAltitude:
                    if previousIndex < j - 1:
                        # fill in all missing points
                        for l in range(previousIndex + 1, j):
                            # simply average between the points based on index
                            thisValue = previousValue + (zdata - previousValue)*(float(l-previousIndex)/float(j-previousIndex))
                            Z[i][l] = thisValue
			   
                previousIndex = j
                previousValue = zdata
		
                
            except:
                continue
		
        # insert missing data to represent gaps if needed
        if insertDataGap != None:
            # first find the time interval greater than 90% of others
            timeIntervalList = []
            for i in range(len(xList) - 1):
                timeIntervalList.append(xList[i+1] - xList[i])
            timeIntervalList.sort()
            index = int(len(timeIntervalList)*0.9)
            maxInterval = timeIntervalList[index]
	    
            for i in range(len(xList) - 1):
                if xList[i+1] - xList[i] > maxInterval * insertDataGap:
                    Z[i,:] = self.__missing

        # insert missing data to represent gaps if needed in Altitude
        if insertDataGap != None and not smoothAltitude:
            # first find the time interval greater than 90% of others
            altitudeIntervalList = []
            for i in range(len(yList) - 1):
                altitudeIntervalList.append(yList[i+1] - yList[i])
            altitudeIntervalList.sort()
            index = int(len(altitudeIntervalList)*0.9)
            maxInterval = altitudeIntervalList[index]
        
            for j in range(len(yList) - 1):
                if yList[j+1] - yList[j] > maxInterval * insertDataGap and j < max_y_dimension:
                    Z[:,j] = self.__missing
                    
        # make Z a masked array
        Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)

        # set up plotting parameters
        if minColormap == None:
            minColormap = zMin
        if maxColormap == None:
            maxColormap = zMax

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(14,5), facecolor = 'w')
	    
        if useAbsoluteTime:
            # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.1, 0.85, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.97, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
        else:
            if size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
            elif size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.9, 0.73])

        matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap,
                                norm = matplotlib.pylab.Normalize(), edgecolor='face')
        matplotlib.pylab.colorbar()

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)

        if useAbsoluteTime:
            # if plot is less than 49 hours
            xmin, xmax = matplotlib.pylab.xlim()
            xmin = numpy.floor(xmin / (60*60))
            xmax = numpy.ceil(xmax / (60*60))
            if noDate == True:
                newXLabels = []
                if (xmax - xmin) <= 1:
                    newXLocs = []
                    xRange = numpy.arange(0,1,0.25)
                    for i in xRange:
                        tmp=(xmin+i)*60*60
                        newXLocs.append(tmp)
                elif (xmax - xmin) <= 2:
                    newXLocs = []
                    xRange = numpy.arange(0,2,0.5)
                    for i in xRange:
                        tmp=(xmin+i)*60*60
                        newXLocs.append(tmp)
                else:
                    newXLocs = []
                    step = int((xmax-xmin-1)/6)+1
                    xRange =  list(range(0,int(xmax-xmin),step))
                    for i in xRange:
                        tmp = (xmin + i)*60*60
                        newXLocs.append(tmp)

                #newXLocs.append(xmax*60*60)                                                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)

        else:
            # if plot is less than 48 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            if xmax < 49:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                for i in range(0,int(4+newXmax),4):
                    newXLocs.append(i)
                    newXLabels.append(str(i))
                matplotlib.pylab.xticks(newXLocs, newXLabels)


        matplotlib.pylab.xticks(fontsize=fontSize)

        # add second y-axis if desired
        if altYTitle != None and altYLabels != None:
            ax2 = matplotlib.pylab.twinx()
            matplotlib.pylab.ylabel(altYTitle)
            ax2.yaxis.tick_right()
            matplotlib.pylab.yticks(list(range(len(altYLabels))), altYLabels)
            matplotlib.pylab.yticks(fontsize=fontSize)

        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
        if peakAltStr:
            # plot peak altitudes
            data = peakAltStr.split()
            xdata = [float(item) for i, item in enumerate(data) if i % 2 == 0]
            ydata = [float(item) for i, item in enumerate(data) if i % 2 == 1]
            matplotlib.pylab.plot(xdata, ydata, 'b+')
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()



    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

    def __getYIndex(self, yvalue):
        """__getYIndex returns the correct index into the y dimension for a given y value.

        Input: yvalue - value of y parameter

        Returns: the correct index into the y dimension

        Algorithm: if self.truncateAlt == False, simple use the dictionary self.yListDict.  Else
        loop through self.yListRanges and return the first greater than the requested value
        """
        if self.truncateAlt == False:
            return self.yListDict[yvalue]
        else:
            i = bisect.bisect_left(self.yListRanges, yvalue)
            if i >= len(self.yListRanges):
                i = len(self.yListRanges) - 1
            return i
        
            
    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newIsprintText = ''
            for i in range(0,len(isprintList),dropNumber):
                newIsprintText += isprintList[i] + '\n'

            return newIsprintText


        
    def displayToScreen(self):
        " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
        pass 

    def getFigureHandle(self):
        return self.__figure


    def getAverage(self, X):
        """returns the average of items in a float array.  Does not including missing data.
        If all data missing, returns self.__missing
        """
        count = 0
        total = 0.0
        for i in range(X.shape[0]):
            if X[i] != self.__missing:
                count += 1
                total += X[i]

        if count == 0:
            return self.__missing
        else:
            return total / float(count)


    def sortArrayInTime(self, array_data):
        """sortArrayInTime sorts a two-dimensional array so that the first element in each row (time) is in ascending order.

        Input: array_data - two-dimensional array to be sorted by rearranging rows so
               that the first element in each row (time) is in ascending order

        Returns: new_array
        """

        sortIndex = numpy.argsort(array_data[:,0])[0]

        # if already sorted, just return original array
        if sortIndex == numpy.sort(sortIndex):
            return array_data
        
        new_array = numpy.zeros(array_data.shape, array_data.dtype)

        for i in range(len(sortIndex)):
            new_array[sortIndex[i],:] = array_data[i,:]


        return new_array


    def decimateTimes(self, array_data, maxNumTimes, insertDataGap):
        """decimateTimes decimates array_data to have at most maxNumTimes times.

        Input: array_data - two-dimensional array to be decimated by deleting times and missing data.

               maxNumTimes: int representing the maximum number of times to keep in array_data

               insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
	                    being plotted are ordered, and the time gap larger than 90% of the rest is determined.
			    Note that this parameter is used here to stop the truncation of isprint lines that
			    will eventually be considered edge lines.

        Returns: new array built from decimated array_data

        """

        # get the number of times in array_data, and make a list of all unique times
        numTimes = 0
        uniqueTimes = []
        time_array = array_data[:, 0]
        for i in range(len(time_array)):
            if i == 0:
               numTimes =  1
               uniqueTimes.append(time_array[i])
            elif time_array[i-1] != time_array[i]:
                uniqueTimes.append(time_array[i])
                numTimes +=  1

        if numTimes <= maxNumTimes:
            return array_data
        

        # insert missing data to represent gaps if needed
        gapTimes = []
        if insertDataGap != None:
            # first find the time interval greater than 90% of others
            timeIntervalList = []
            for i in range(len(time_array) - 1):
                if time_array[i+1] == time_array[i]:
                    continue
                timeIntervalList.append(time_array[i+1] - time_array[i])
            timeIntervalList.sort()
            index = int(len(timeIntervalList)*0.9)
            maxInterval = timeIntervalList[index]
	    
            for i in range(len(time_array) - 1):
                if time_array[i+1] - time_array[i] > maxInterval * insertDataGap:
                    gapTimes.append(time_array[i+1])
                    gapTimes.append(time_array[i])

        # get the number of times to skip each time
        numSkip = numTimes/maxNumTimes

        # get the number of rows in the new_array
        numRows = 0
        numTimes = 0
        useThisTime = False
        for i in range(len(time_array)):
            if i == 0:
                numTimes =  1
            elif time_array[i-1] != time_array[i]:
                numTimes +=  1
                if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                    useThisTime = True
                    if array_data[i, -1] != self.__missing:
                        numRows += 1
                else:
                    useThisTime = False
            else:
                if useThisTime:
                    if array_data[i, -1] != self.__missing:
                        numRows += 1

        # create new_array
        new_array = numpy.zeros((numRows, array_data.shape[1]), array_data.dtype)

        # copy selected rows to new_array
        numRows = 0
        numTimes = 0
        useThisTime = False
        for i in range(len(time_array)):
            if i == 0:
                numTimes =  1
            elif time_array[i-1] != time_array[i]:
                numTimes +=  1
                if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                    useThisTime = True
                    if array_data[i, -1] != self.__missing:
                        new_array[numRows,:] = array_data[i,:]
                        numRows += 1
                else:
                    useThisTime = False
            else:
                if useThisTime:
                    if array_data[i, -1] != self.__missing:
                        new_array[numRows,:] = array_data[i,:]
                        numRows += 1

        return new_array

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', minColormap=None, maxColormap=None, smoothAltitude=True, insertDataGap=5, useAbsoluteTime=False, startTime=None, endTime=None, sortTimeFlag=False, maxNumTimes=None, maxNumAlt=None, truncateIsprint=False, colorMap=<matplotlib.colors.LinearSegmentedColormap object at 0x1169fb450>, yMinimum=None, yMaximum=None, altYTitle=None, altYLabels=None, noTime=False, noDate=False, timezone=0, background='w', peakAltStr=None)

init writes a madPColorPlot to a file.

Inputs:

isprintText - a string giving isprint output without headers. First parameter
              must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The second must be gdalt, and third parameter to be plotted. Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

yLabelStr - ylabel string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.

smoothAltitude - if True, extrapolate between existing data between altitudes to fill
                 in missing data; if False, leave missing

insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
                being plotted are ordered, and the time gap larger than 90% of the rest is determined.
                Any time interval more than insertDataGap times bigger is then considered missing
                data.  Defaults to five.  If None, no gaps are ever inserted.  For data with close
                to uniform time intervals, no gaps will be inserted.

useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                  since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                  parameter in isprintText must be UT1, if false, it must be UTH.

startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
            in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
            startTime must be in units of UTH (hours since midnight UT of first day of
            experiment). Default is None, which means start at lowest time found.

endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
          in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
          endTime must be in units of UTH (hours since midnight UT of first day of
          experiment). Default is None, which means end at largest time found.

sortTimeFlag - if true, check that time is correctly sorted.  If false (the default),
               assume time already sorted

maxNumTimes - if not None, decimate the number of times in the isprint string to
              maxNumTimes.  If None (the default), plot all times.

maxNumAlt - if not None, reduce the number of altitudes to maxNumAlt.  If None (the default),
            plot all altitudes.

truncateIsprint - if True, and both maxNumTimes and maxNumAlt not = None, then truncate
                   the number of isprint lines to be maxNumTimes * maxNumAlt

colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

yMinimum - minumum y value.  If None (default), set by data y minimum.

yMaximum - maximum y value.  If None (default), set by data y maximum.

altYTitle - title of right (second) y axis.  If None (the default),
             no Y axis on the right side.

altYLabels - a list of Y labels (strings) for the right axis.  If None (the default),
             no Y labels on the right axis.

noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
            (negative in most of Western Europe, positive in the US, zero in the UK).

peakAltStr - if not None (the default), plot the peak altitude with x.  Format is a
    str with each line (time in same format as data, peak altitude)

Returns: void

Affects: None

def __init__(self, isprintText,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             minColormap = None,
             maxColormap = None,
             smoothAltitude = True,
             insertDataGap = 5,
             useAbsoluteTime = False,
             startTime = None,
             endTime = None,
             sortTimeFlag = False,
             maxNumTimes = None,
             maxNumAlt = None,
             truncateIsprint = False,
             colorMap = matplotlib.cm.jet,
             yMinimum = None,
             yMaximum = None,
             altYTitle = None,
             altYLabels = None,
             noTime = False,
             noDate = False,
             timezone = 0,
             background = 'w',
             peakAltStr = None):
    
    """__init__ writes a madPColorPlot to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First parameter
                      must be UTH or UT1, depending on whether time scale should be relative
 to the experiment beginning (UTH) or absolute (UT1).
 The second must be gdalt, and third parameter to be plotted. Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float. 
        
        titleStr - plot title (string) - should describe parameter being plotted
 
	    xLabelStr - x label string
	    
	    yLabelStr - ylabel string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
 
	    smoothAltitude - if True, extrapolate between existing data between altitudes to fill
	                     in missing data; if False, leave missing
			     
	    insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
	                    being plotted are ordered, and the time gap larger than 90% of the rest is determined.
			    Any time interval more than insertDataGap times bigger is then considered missing
			    data.  Defaults to five.  If None, no gaps are ever inserted.  For data with close
			    to uniform time intervals, no gaps will be inserted.
			    
	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
			      parameter in isprintText must be UT1, if false, it must be UTH.
    
        startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
             in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
             startTime must be in units of UTH (hours since midnight UT of first day of
             experiment). Default is None, which means start at lowest time found.
	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	              endTime must be in units of UTH (hours since midnight UT of first day of
	              experiment). Default is None, which means end at largest time found.

	    sortTimeFlag - if true, check that time is correctly sorted.  If false (the default),
	                   assume time already sorted

	    maxNumTimes - if not None, decimate the number of times in the isprint string to
	                  maxNumTimes.  If None (the default), plot all times.

	    maxNumAlt - if not None, reduce the number of altitudes to maxNumAlt.  If None (the default),
	                plot all altitudes.

	    truncateIsprint - if True, and both maxNumTimes and maxNumAlt not = None, then truncate
	                       the number of isprint lines to be maxNumTimes * maxNumAlt

	    colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

	    yMinimum - minumum y value.  If None (default), set by data y minimum.

	    yMaximum - maximum y value.  If None (default), set by data y maximum.

	    altYTitle - title of right (second) y axis.  If None (the default),
	                 no Y axis on the right side.

	    altYLabels - a list of Y labels (strings) for the right axis.  If None (the default),
	                 no Y labels on the right axis.
        noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
        
        timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                    (negative in most of Western Europe, positive in the US, zero in the UK).
                    
        peakAltStr - if not None (the default), plot the peak altitude with x.  Format is a
            str with each line (time in same format as data, peak altitude)
              
    Returns: void
    Affects: None
    """
    self.__missing = 1.0E30 # special value, used to create a masked array
    self.__parameter_count = 3
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    if size in ('small'):
        fontSize = 10
    elif size in ('wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    if truncateIsprint and maxNumTimes != None and maxNumAlt != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumTimes * maxNumAlt)
    # since matplotlib pcolor wants a regular grid, we create a grid with all altitudes
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    if sortTimeFlag:
        array_data = self.sortArrayInTime(array_data)
    if maxNumTimes != None and maxNumTimes != 0 and not truncateIsprint:
        array_data = self.decimateTimes(array_data, int(maxNumTimes), insertDataGap)
    
    # The first pass through is to obtain the number and range of the  x and y variables
    
    xList = []
    yList = []
    zList = []
    zMin = None
    zMax = None
    # loop over all the lines of data in the array
    for j in range(len(array_data)):
        try:
            
            x = array_data[j][0]
            y = array_data[j][1]
            z = array_data[j][2]
            if x not in xList:
                xList.append(x)
            if y not in yList:
                yList.append(y)
            if z != self.__missing:
                zList.append(z)
            if zMin != None:
                if z < zMin and z != self.__missing:
                    zMin = z
            elif z != self.__missing:
                zMin = z
            if zMax != None:
                if z > zMax and z != self.__missing:
                    zMax = z
            elif z != self.__missing:
                zMax = z
        except:
            continue
    if zMin == None:
        raise ValueError('No valid z data found')
    # if both minColormap and maxColormap == None, use autoscaling
    if minColormap == None and maxColormap == None:
        zList.sort()
        d10 = zList[int(len(zList)*0.10)]
        d90 = zList[int(len(zList)*0.90)]
        zMin = d10 - (d90-d10) * 0.75
        zMax = d90 + (d90-d10) * 0.75
    # now sort the X and Y axis lists and pull their length
    
    xList.sort()
    if startTime == None:
        xMin = xList[0]
    else:
        xMin = startTime
    if endTime == None:
        xMax = xList[-1]
    else:
        xMax = endTime
    yList.sort()
    max_x_dimension = len(xList)
    max_y_dimension = len(yList)
    if yMinimum == None:
        yMinimum = yList[0]
    if yMaximum == None:
        yMaximum = yList[-1]
    self.truncateAlt = False
    if maxNumAlt != None:
        if max_y_dimension > maxNumAlt:
            self.truncateAlt = True
    # build dictonary of indexes into xList
    self.xListDict = {}
    for i in range(len(xList)):
        self.xListDict[xList[i]] = i
    # if self.truncateAlt == False, build dictonary of indexes into yList,
    # else truncate y values by builing a list of maxNumAlt ranges
    if self.truncateAlt == False:
        self.yListDict = {}
        for i in range(len(yList)):
            self.yListDict[yList[i]] = i
    else:
        self.yListRanges = []
        for i in range(maxNumAlt):
            self.yListRanges.append(yList[int(i*(len(yList)/maxNumAlt))])
        max_y_dimension = maxNumAlt
    # now build arrays to handle the X axis label, Y axis label, and the Z data
    X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
    Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
    Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)
    # all parameter values default to missing
    Z = Z * self.__missing
    # fill the X and Y arrays
    for i in range(max_x_dimension):
        for j in range(max_y_dimension):
            X[i][j] = float(xList[i])
            if self.truncateAlt:
                Y[i][j] = float(yList[int(j*(len(yList)/maxNumAlt))])
            else:
                Y[i][j] = float(yList[j])

    # Now load up the data array Z with the array_data measurements as a function of x and y
    previousIndex = None
    previousValue = None
    presentTime = None
    newTimeFound = True
    for k in range(len(array_data)):
        try:
            xdata = array_data[k][0]
            ydata = array_data[k][1]
            zdata = array_data[k][2]
            if zdata == self.__missing:
                continue
            if xdata != presentTime:
                newTimeFound = True
            else:
                newTimeFound = False
            presentTime = xdata
            # now find the right place in the array for this data point
            i = self.xListDict[xdata]
            j = self.__getYIndex(ydata)
            Z[i][j] = zdata
            # now see if we need to fill in any gaps
            if (not newTimeFound) and smoothAltitude:
                if previousIndex < j - 1:
                    # fill in all missing points
                    for l in range(previousIndex + 1, j):
                        # simply average between the points based on index
                        thisValue = previousValue + (zdata - previousValue)*(float(l-previousIndex)/float(j-previousIndex))
                        Z[i][l] = thisValue
  
            previousIndex = j
            previousValue = zdata
            
        except:
            continue
    # insert missing data to represent gaps if needed
    if insertDataGap != None:
        # first find the time interval greater than 90% of others
        timeIntervalList = []
        for i in range(len(xList) - 1):
            timeIntervalList.append(xList[i+1] - xList[i])
        timeIntervalList.sort()
        index = int(len(timeIntervalList)*0.9)
        maxInterval = timeIntervalList[index]
 
        for i in range(len(xList) - 1):
            if xList[i+1] - xList[i] > maxInterval * insertDataGap:
                Z[i,:] = self.__missing
    # insert missing data to represent gaps if needed in Altitude
    if insertDataGap != None and not smoothAltitude:
        # first find the time interval greater than 90% of others
        altitudeIntervalList = []
        for i in range(len(yList) - 1):
            altitudeIntervalList.append(yList[i+1] - yList[i])
        altitudeIntervalList.sort()
        index = int(len(altitudeIntervalList)*0.9)
        maxInterval = altitudeIntervalList[index]
    
        for j in range(len(yList) - 1):
            if yList[j+1] - yList[j] > maxInterval * insertDataGap and j < max_y_dimension:
                Z[:,j] = self.__missing
                
    # make Z a masked array
    Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)
    # set up plotting parameters
    if minColormap == None:
        minColormap = zMin
    if maxColormap == None:
        maxColormap = zMax
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(14,5), facecolor = 'w')
 
    if useAbsoluteTime:
        # leave room for rotated datetime string
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.1, 0.85, 0.7])
        elif size == 'wide':
            matplotlib.pylab.axes([0.07, 0.2, 0.97, 0.7])
        elif size == 'small':
            matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
    else:
        if size == 'small':
            matplotlib.pylab.axes([0.1, 0.18, 0.97, 0.7])
        elif size == 'large':
            matplotlib.pylab.axes([0.1, 0.2, 0.9, 0.73])
    matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap,
                            norm = matplotlib.pylab.Normalize(), edgecolor='face')
    matplotlib.pylab.colorbar()
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.xlim(xMin, xMax)
    matplotlib.pylab.ylim(yMinimum, yMaximum)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    if useAbsoluteTime:
        # if plot is less than 49 hours
        xmin, xmax = matplotlib.pylab.xlim()
        xmin = numpy.floor(xmin / (60*60))
        xmax = numpy.ceil(xmax / (60*60))
        if noDate == True:
            newXLabels = []
            if (xmax - xmin) <= 1:
                newXLocs = []
                xRange = numpy.arange(0,1,0.25)
                for i in xRange:
                    tmp=(xmin+i)*60*60
                    newXLocs.append(tmp)
            elif (xmax - xmin) <= 2:
                newXLocs = []
                xRange = numpy.arange(0,2,0.5)
                for i in xRange:
                    tmp=(xmin+i)*60*60
                    newXLocs.append(tmp)
            else:
                newXLocs = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(xmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
            #newXLocs.append(xmax*60*60)                                                
            newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
            matplotlib.pylab.xticks(newXLocs, newXLabels)
        else:
            locs, labels = matplotlib.pylab.xticks()
            if len(locs) > 5:
                # truncate by len(locs) / 5
                scale = 1 + int(len(locs) / 5)
                new_locs = []
                for i in range(len(locs)):
                    if i % scale == 0:
                        new_locs.append(locs[i])
                locs = new_locs
            newXTicks = convertToAbsoluteTimeStr(locs)
            matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
    else:
        # if plot is less than 48 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim()
        if xmax < 49:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            for i in range(0,int(4+newXmax),4):
                newXLocs.append(i)
                newXLabels.append(str(i))
            matplotlib.pylab.xticks(newXLocs, newXLabels)
    matplotlib.pylab.xticks(fontsize=fontSize)
    # add second y-axis if desired
    if altYTitle != None and altYLabels != None:
        ax2 = matplotlib.pylab.twinx()
        matplotlib.pylab.ylabel(altYTitle)
        ax2.yaxis.tick_right()
        matplotlib.pylab.yticks(list(range(len(altYLabels))), altYLabels)
        matplotlib.pylab.yticks(fontsize=fontSize)
    # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
    
    #self.__figure = matplotlib.pylab.gcf()
    if peakAltStr:
        # plot peak altitudes
        data = peakAltStr.split()
        xdata = [float(item) for i, item in enumerate(data) if i % 2 == 0]
        ydata = [float(item) for i, item in enumerate(data) if i % 2 == 1]
        matplotlib.pylab.plot(xdata, ydata, 'b+')
      
    matplotlib.pylab.savefig(fullFilename)
    #matplotlib.pylab.show()
    matplotlib.pylab.clf()

def decimateTimes(

self, array_data, maxNumTimes, insertDataGap)

decimateTimes decimates array_data to have at most maxNumTimes times.

Input: array_data - two-dimensional array to be decimated by deleting times and missing data.

   maxNumTimes: int representing the maximum number of times to keep in array_data

   insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
                being plotted are ordered, and the time gap larger than 90% of the rest is determined.
                Note that this parameter is used here to stop the truncation of isprint lines that
                will eventually be considered edge lines.

Returns: new array built from decimated array_data

def decimateTimes(self, array_data, maxNumTimes, insertDataGap):
    """decimateTimes decimates array_data to have at most maxNumTimes times.
    Input: array_data - two-dimensional array to be decimated by deleting times and missing data.
           maxNumTimes: int representing the maximum number of times to keep in array_data
           insertDataGap - this parameter sets the threshold for inserting a data gap.  The time intervals
                 being plotted are ordered, and the time gap larger than 90% of the rest is determined.
   Note that this parameter is used here to stop the truncation of isprint lines that
   will eventually be considered edge lines.
    Returns: new array built from decimated array_data
    """
    # get the number of times in array_data, and make a list of all unique times
    numTimes = 0
    uniqueTimes = []
    time_array = array_data[:, 0]
    for i in range(len(time_array)):
        if i == 0:
           numTimes =  1
           uniqueTimes.append(time_array[i])
        elif time_array[i-1] != time_array[i]:
            uniqueTimes.append(time_array[i])
            numTimes +=  1
    if numTimes <= maxNumTimes:
        return array_data
    
    # insert missing data to represent gaps if needed
    gapTimes = []
    if insertDataGap != None:
        # first find the time interval greater than 90% of others
        timeIntervalList = []
        for i in range(len(time_array) - 1):
            if time_array[i+1] == time_array[i]:
                continue
            timeIntervalList.append(time_array[i+1] - time_array[i])
        timeIntervalList.sort()
        index = int(len(timeIntervalList)*0.9)
        maxInterval = timeIntervalList[index]
 
        for i in range(len(time_array) - 1):
            if time_array[i+1] - time_array[i] > maxInterval * insertDataGap:
                gapTimes.append(time_array[i+1])
                gapTimes.append(time_array[i])
    # get the number of times to skip each time
    numSkip = numTimes/maxNumTimes
    # get the number of rows in the new_array
    numRows = 0
    numTimes = 0
    useThisTime = False
    for i in range(len(time_array)):
        if i == 0:
            numTimes =  1
        elif time_array[i-1] != time_array[i]:
            numTimes +=  1
            if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                useThisTime = True
                if array_data[i, -1] != self.__missing:
                    numRows += 1
            else:
                useThisTime = False
        else:
            if useThisTime:
                if array_data[i, -1] != self.__missing:
                    numRows += 1
    # create new_array
    new_array = numpy.zeros((numRows, array_data.shape[1]), array_data.dtype)
    # copy selected rows to new_array
    numRows = 0
    numTimes = 0
    useThisTime = False
    for i in range(len(time_array)):
        if i == 0:
            numTimes =  1
        elif time_array[i-1] != time_array[i]:
            numTimes +=  1
            if numTimes % (numSkip + 1) == 0 or time_array[i] in gapTimes:
                useThisTime = True
                if array_data[i, -1] != self.__missing:
                    new_array[numRows,:] = array_data[i,:]
                    numRows += 1
            else:
                useThisTime = False
        else:
            if useThisTime:
                if array_data[i, -1] != self.__missing:
                    new_array[numRows,:] = array_data[i,:]
                    numRows += 1
    return new_array

def displayToScreen(

self)

to implement this takes a reworking away from pylab to use the underlying matplotlib code

def displayToScreen(self):
    " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
    pass 

def getAverage(

self, X)

returns the average of items in a float array. Does not including missing data. If all data missing, returns self.__missing

def getAverage(self, X):
    """returns the average of items in a float array.  Does not including missing data.
    If all data missing, returns self.__missing
    """
    count = 0
    total = 0.0
    for i in range(X.shape[0]):
        if X[i] != self.__missing:
            count += 1
            total += X[i]
    if count == 0:
        return self.__missing
    else:
        return total / float(count)

def getFigureHandle(

self)

def getFigureHandle(self):
    return self.__figure

def sortArrayInTime(

self, array_data)

sortArrayInTime sorts a two-dimensional array so that the first element in each row (time) is in ascending order.

Input: array_data - two-dimensional array to be sorted by rearranging rows so that the first element in each row (time) is in ascending order

Returns: new_array

def sortArrayInTime(self, array_data):
    """sortArrayInTime sorts a two-dimensional array so that the first element in each row (time) is in ascending order.
    Input: array_data - two-dimensional array to be sorted by rearranging rows so
           that the first element in each row (time) is in ascending order
    Returns: new_array
    """
    sortIndex = numpy.argsort(array_data[:,0])[0]
    # if already sorted, just return original array
    if sortIndex == numpy.sort(sortIndex):
        return array_data
    
    new_array = numpy.zeros(array_data.shape, array_data.dtype)
    for i in range(len(sortIndex)):
        new_array[sortIndex[i],:] = array_data[i,:]
    return new_array

Instance variables

var truncateAlt

var xListDict

class madPcolorScan

madPcolorScan is the class that produces pcolor scans.

Usage example::

obj = madPcolorScan(isprintText,
                'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                'Longitude',
                'Latitude',
                './isprint.png',
                size = 'large',
                minColormap = 9,
                maxColormap = 12)

Non-standard Python modules used: matplotlib

Change history:

Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu Jul. 20, 2006

class madPcolorScan:
    """madPcolorScan is the class that produces pcolor scans.

    Usage example::

    	obj = madPcolorScan(isprintText,
                        'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                        'Longitude',
                        'Latitude',
                        './isprint.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 20, 2006
    """


    def __init__(self, isprintText,
                 xGridSize,
                 yGridSize,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet,
                 maxNumLines = None):
        """__init__ writes a madPcolorScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be the X axis value, and the second must be the Y axis value.
			  The third column is the value (intensity). Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float.

            xGridSize - grid size for x data (for example 0.1 for 0.1 degree longitude grid)

            yGridSize - grid size for x data (for example 0.1 for 0.1 degree latitude grid)
            
            titleStr - plot title (string) - should describe parameter being plotted
	    
    	    xLabelStr - x label string
    	    
    	    yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found.

            xMaximum = maximum x value.  If None (default), uses highest x value found.

            yMinimum = minumum y value.  If None (default), uses lowest y value found.

            yMaximum = maximum y value.  If None (default), uses highest y value found.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

	    maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation

	              
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, used to create a masked array
        self.__parameter_count = 3
        self.__xGridSize = int(xGridSize)
        self.__yGridSize = int(yGridSize)
        self.__parseCount = 0 # used to tell which column self.__filter_input is working on

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))

        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumLines != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumLines)

        # since matplotlib pcolor wants a regular grid, we create a grid with all altitudes

        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_input, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')

        array_data = self.sortArrayInX(array_data)

        
        # The first pass through is to obtain the number and range of the  x and y variables
        
        xList = []
        yList = []
        zList = []
        zMin = None
        zMax = None

        # loop over all the lines of data in the array
        for j in range(len(array_data)):
            try:
                
                x = array_data[j][0]
                y = array_data[j][1]
                z = array_data[j][2]

                if x not in xList:
                    xList.append(x)

                if y not in yList:
                    yList.append(y)

                if z != self.__missing:
                    zList.append(z)

                if zMin != None:
                    if z < zMin and z != self.__missing:
                        zMin = z
                elif z != self.__missing:
                    zMin = z

                if zMax != None:
                    if z > zMax and z != self.__missing:
                        zMax = z
                elif z != self.__missing:
                    zMax = z

            except:
                continue

		
        if zMin == None:
            raise ValueError('No valid z data found')

        # if both minColormap and maxColormap == None, use autoscaling
        if minColormap == None and maxColormap == None:
            zList.sort()
            d10 = zList[int(len(zList)*0.10)]
            d90 = zList[int(len(zList)*0.90)]

            zMin = d10 - (d90-d10) * 0.75
            zMax = d90 + (d90-d10) * 0.75

        # now sort the X and Y axis lists and pull their length
        
        xList.sort()
        xMin = xList[0]
        xMax = xList[-1]
        yList.sort()
        yMin = yList[0]
        yMax = yList[-1]
        max_x_dimension = len(xList)
        max_y_dimension = len(yList)


        # build dictonary of indexes into xList
        self.xListDict = {}
        for i in range(len(xList)):
            self.xListDict[xList[i]] = i

        # build dictonary of indexes into yList,
        self.yListDict = {}
        for i in range(len(yList)):
            self.yListDict[yList[i]] = i


        # now build arrays to handle the X axis label, Y axis label, and the Z data

        X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
        Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)

        # all parameter values default to missing
        Z = Z * self.__missing

        # fill the X and Y arrays

        for i in range(max_x_dimension):
            for j in range(max_y_dimension):
                X[i][j] = float(xList[i])
                Y[i][j] = float(yList[j])

    
        # Now load up the data array Z with the array_data measurements as a function of x and y
        previousIndex = None
        previousValue = None
        presentTime = None
        newTimeFound = True

        for k in range(len(array_data)):
            try:

                xdata = self.__round(array_data[k][0], self.__xGridSize)
                ydata = self.__round(array_data[k][1], self.__yGridSize)
                zdata = array_data[k][2]

                if zdata == self.__missing:
                    continue

                if xdata != presentTime:
                    newTimeFound = True
                else:
                    newTimeFound = False
                presentTime = xdata

                # now find the right place in the array for this data point
                i = self.xListDict[xdata]
                j = self.yListDict[ydata]

                Z[i][j] = zdata
			   
                previousIndex = j
                previousValue = zdata
		
                
            except:
                continue
		

        # make Z a masked array
        Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)

        # set up plotting parameters
        if xMinimum == None:
            xMinimum = xMin
        if xMaximum == None:
            xMaximum = xMax
        if yMinimum == None:
            yMinimum = yMin
        if yMaximum == None:
            yMaximum = yMax
        if minColormap == None:
            minColormap = zMin
        if maxColormap == None:
            maxColormap = zMax

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(6,4), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
	    

        matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap, norm = matplotlib.pylab.Normalize())
        matplotlib.pylab.colorbar()

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMinimum, xMaximum)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)


        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()

        try:
            matplotlib.pylab.savefig(fullFilename)
        except:
            matplotlib.pylab.clf()
            raise
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()


    def __round(self, value, increment):
        """__round returns a value to the nearest increment 
        """
        return value - math.fmod(value, increment)



    def __filter_input(self,x):
        """__filter_input is called in map to convert missing strings to self.__missing, and to
        round x and y vales to the nearest xGridSize or yGridSize
        """
        try:
            value = float(x)
            if self.__parseCount % 3 == 0:
                # x value
                value = self.__round(value, self.__xGridSize)
            elif self.__parseCount % 3 == 1:
                # y value
                value = self.__round(value, self.__yGridSize)
                
        except:
            value = self.__missing

        self.__parseCount += 1
        return value
        
            
    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newIsprintText = ''
            for i in range(0,len(isprintList),dropNumber):
                newIsprintText += isprintList[i] + '\n'

            return newIsprintText


        
    def displayToScreen(self):
        " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
        pass 

    def getFigureHandle(self):
        return self.__figure


    def getAverage(self, X):
        """returns the average of items in a float array.  Does not including missing data.
        If all data missing, returns self.__missing
        """
        count = 0
        total = 0.0
        for i in range(X.shape[0]):
            if X[i] != self.__missing:
                count += 1
                total += X[i]

        if count == 0:
            return self.__missing
        else:
            return total / float(count)


    def sortArrayInX(self, array_data):
        """sortArrayInX sorts a two-dimensional array so that the first element in each row (x) is in ascending order.

        Input: array_data - two-dimensional array to be sorted by rearranging rows so
               that the first element in each row (x) is in ascending order

        Returns: new_array
        """

        sortIndex = numpy.argsort(array_data[:,0])[0]

        # if already sorted, just return original array
        if sortIndex == numpy.sort(sortIndex):
            return array_data
        
        new_array = numpy.zeros(array_data.shape, array_data.dtype)

        for i in range(len(sortIndex)):
            new_array[sortIndex[i],:] = array_data[i,:]


        return new_array

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, xGridSize, yGridSize, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', xMinimum=None, xMaximum=None, yMinimum=None, yMaximum=None, minColormap=None, maxColormap=None, colorMap=<matplotlib.colors.LinearSegmentedColormap object at 0x1169fb450>, maxNumLines=None)

init writes a madPcolorScan to a file.

Inputs:

isprintText - a string giving isprint output without headers. First parameter
              must be the X axis value, and the second must be the Y axis value.
              The third column is the value (intensity). Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

xGridSize - grid size for x data (for example 0.1 for 0.1 degree longitude grid)

yGridSize - grid size for x data (for example 0.1 for 0.1 degree latitude grid)

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

yLabelStr - ylabel string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

xMinimum = minumum x value.  If None (default), uses lowest x value found.

xMaximum = maximum x value.  If None (default), uses highest x value found.

yMinimum = minumum y value.  If None (default), uses lowest y value found.

yMaximum = maximum y value.  If None (default), uses highest y value found.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.

colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation

Returns: void

Affects: None

def __init__(self, isprintText,
             xGridSize,
             yGridSize,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             xMinimum = None,
             xMaximum = None,
             yMinimum = None,
             yMaximum = None,
             minColormap = None,
             maxColormap = None,
             colorMap = matplotlib.cm.jet,
             maxNumLines = None):
    """__init__ writes a madPcolorScan to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First parameter
                      must be the X axis value, and the second must be the Y axis value.
 The third column is the value (intensity). Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float.
        xGridSize - grid size for x data (for example 0.1 for 0.1 degree longitude grid)
        yGridSize - grid size for x data (for example 0.1 for 0.1 degree latitude grid)
        
        titleStr - plot title (string) - should describe parameter being plotted
 
	    xLabelStr - x label string
	    
	    yLabelStr - ylabel string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
        xMinimum = minumum x value.  If None (default), uses lowest x value found.
        xMaximum = maximum x value.  If None (default), uses highest x value found.
        yMinimum = minumum y value.  If None (default), uses lowest y value found.
        yMaximum = maximum y value.  If None (default), uses highest y value found.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
        colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet
 maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
           
    Returns: void
    Affects: None
    """
    self.__missing = 1.0E30 # special value, used to create a masked array
    self.__parameter_count = 3
    self.__xGridSize = int(xGridSize)
    self.__yGridSize = int(yGridSize)
    self.__parseCount = 0 # used to tell which column self.__filter_input is working on
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    if maxNumLines != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumLines)
    # since matplotlib pcolor wants a regular grid, we create a grid with all altitudes
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_input, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    array_data = self.sortArrayInX(array_data)
    
    # The first pass through is to obtain the number and range of the  x and y variables
    
    xList = []
    yList = []
    zList = []
    zMin = None
    zMax = None
    # loop over all the lines of data in the array
    for j in range(len(array_data)):
        try:
            
            x = array_data[j][0]
            y = array_data[j][1]
            z = array_data[j][2]
            if x not in xList:
                xList.append(x)
            if y not in yList:
                yList.append(y)
            if z != self.__missing:
                zList.append(z)
            if zMin != None:
                if z < zMin and z != self.__missing:
                    zMin = z
            elif z != self.__missing:
                zMin = z
            if zMax != None:
                if z > zMax and z != self.__missing:
                    zMax = z
            elif z != self.__missing:
                zMax = z
        except:
            continue
    if zMin == None:
        raise ValueError('No valid z data found')
    # if both minColormap and maxColormap == None, use autoscaling
    if minColormap == None and maxColormap == None:
        zList.sort()
        d10 = zList[int(len(zList)*0.10)]
        d90 = zList[int(len(zList)*0.90)]
        zMin = d10 - (d90-d10) * 0.75
        zMax = d90 + (d90-d10) * 0.75
    # now sort the X and Y axis lists and pull their length
    
    xList.sort()
    xMin = xList[0]
    xMax = xList[-1]
    yList.sort()
    yMin = yList[0]
    yMax = yList[-1]
    max_x_dimension = len(xList)
    max_y_dimension = len(yList)
    # build dictonary of indexes into xList
    self.xListDict = {}
    for i in range(len(xList)):
        self.xListDict[xList[i]] = i
    # build dictonary of indexes into yList,
    self.yListDict = {}
    for i in range(len(yList)):
        self.yListDict[yList[i]] = i
    # now build arrays to handle the X axis label, Y axis label, and the Z data
    X = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
    Y = numpy.zeros((max_x_dimension, max_y_dimension), numpy.float32)
    Z = numpy.ones((max_x_dimension, max_y_dimension), numpy.float32)
    # all parameter values default to missing
    Z = Z * self.__missing
    # fill the X and Y arrays
    for i in range(max_x_dimension):
        for j in range(max_y_dimension):
            X[i][j] = float(xList[i])
            Y[i][j] = float(yList[j])

    # Now load up the data array Z with the array_data measurements as a function of x and y
    previousIndex = None
    previousValue = None
    presentTime = None
    newTimeFound = True
    for k in range(len(array_data)):
        try:
            xdata = self.__round(array_data[k][0], self.__xGridSize)
            ydata = self.__round(array_data[k][1], self.__yGridSize)
            zdata = array_data[k][2]
            if zdata == self.__missing:
                continue
            if xdata != presentTime:
                newTimeFound = True
            else:
                newTimeFound = False
            presentTime = xdata
            # now find the right place in the array for this data point
            i = self.xListDict[xdata]
            j = self.yListDict[ydata]
            Z[i][j] = zdata
  
            previousIndex = j
            previousValue = zdata
            
        except:
            continue
    # make Z a masked array
    Znew = numpy.ma.masked_inside(Z, 0.99*self.__missing, 1.01*self.__missing)
    # set up plotting parameters
    if xMinimum == None:
        xMinimum = xMin
    if xMaximum == None:
        xMaximum = xMax
    if yMinimum == None:
        yMinimum = yMin
    if yMaximum == None:
        yMaximum = yMax
    if minColormap == None:
        minColormap = zMin
    if maxColormap == None:
        maxColormap = zMax
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(6,4), facecolor = 'w')
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
 
    matplotlib.pylab.pcolor(X,Y,Znew, edgecolors='none', vmin=minColormap, vmax=maxColormap, cmap = colorMap, norm = matplotlib.pylab.Normalize())
    matplotlib.pylab.colorbar()
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.xlim(xMinimum, xMaximum)
    matplotlib.pylab.ylim(yMinimum, yMaximum)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
    
    #self.__figure = matplotlib.pylab.gcf()
    try:
        matplotlib.pylab.savefig(fullFilename)
    except:
        matplotlib.pylab.clf()
        raise
    #matplotlib.pylab.show()
    matplotlib.pylab.clf()

def displayToScreen(

self)

to implement this takes a reworking away from pylab to use the underlying matplotlib code

def displayToScreen(self):
    " to implement this takes a reworking away from pylab to use the underlying matplotlib code "
    pass 

def getAverage(

self, X)

returns the average of items in a float array. Does not including missing data. If all data missing, returns self.__missing

def getAverage(self, X):
    """returns the average of items in a float array.  Does not including missing data.
    If all data missing, returns self.__missing
    """
    count = 0
    total = 0.0
    for i in range(X.shape[0]):
        if X[i] != self.__missing:
            count += 1
            total += X[i]
    if count == 0:
        return self.__missing
    else:
        return total / float(count)

def getFigureHandle(

self)

def getFigureHandle(self):
    return self.__figure

def sortArrayInX(

self, array_data)

sortArrayInX sorts a two-dimensional array so that the first element in each row (x) is in ascending order.

Input: array_data - two-dimensional array to be sorted by rearranging rows so that the first element in each row (x) is in ascending order

Returns: new_array

def sortArrayInX(self, array_data):
    """sortArrayInX sorts a two-dimensional array so that the first element in each row (x) is in ascending order.
    Input: array_data - two-dimensional array to be sorted by rearranging rows so
           that the first element in each row (x) is in ascending order
    Returns: new_array
    """
    sortIndex = numpy.argsort(array_data[:,0])[0]
    # if already sorted, just return original array
    if sortIndex == numpy.sort(sortIndex):
        return array_data
    
    new_array = numpy.zeros(array_data.shape, array_data.dtype)
    for i in range(len(sortIndex)):
        new_array[sortIndex[i],:] = array_data[i,:]
    return new_array

Instance variables

var xListDict

var yListDict

class madPcolorWedgeKmlScan

madPcolorWedgeKmlScan is the class that produces pcolor scan kml files where data is drawn as wedge shapes.

Usage example::

obj = madPcolorWedgeKmlScan(isprintText,
                'az',
                './isprint.kml',
                minColormap = 9,
                maxColormap = 12)

Non-standard Python modules used: matplotlib

Change history:

Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu Dec 15, 2010

class madPcolorWedgeKmlScan:
    """madPcolorWedgeKmlScan is the class that produces pcolor scan kml files where data is drawn as wedge shapes.

    Usage example::

        obj = madPcolorWedgeKmlScan(isprintText,
                        'az',
                        './isprint.kml',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Dec 15, 2010
    """


    def __init__(self, isprintText,
                 scanType,
                 fullFilename,
                 minColormap = None,
                 maxColormap = None,
                 instName = None,
                 instDesc = None,
                 instLat = None,
                 instLon = None,
                 instAlt = None,
                 colorMap = None):
        """__init__ writes a madPcolorWedgeKmlScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. The 9 parameters
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance

            fullFilename - full path of file containing kml file to be saved.  Extension must
                           be kml, or exception thrown.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.
                          
                          
            instName, instDesc, instLat, instLon, instAlt - used to create a placemark for the radar.  If
                instLat or instLon missing, no placemark added.
                
            colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum

        Returns: void

        Affects: Writes fullFilename to disk
        """

        # verify input
        if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
            raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))

        isprintLines = isprintText.split('\n')
        
        newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
        
        # get look point
        lookLat = (newLatMax + newLatMin)/2.0
        lookLon = (newLonMax + newLonMin)/2.0
        lookAlt = newAltMax * 10.0
        
        if maxColormap == None:
            maxColormap = newParmMax
        if minColormap == None:
            minColormap = newParmMin
            
        
        # set up wedge generator
        dataGen = self._generateWedges(isprintLines, scanType)
        
        text = self._createKmlText(dataGen, maxColormap, minColormap, lookLat, lookLon, lookAlt, scanType,
                                   instName, instDesc, instLat, instLon, instAlt, colorMap)
        
        f = open(fullFilename, 'w')
        f.write(text)
        f.close()
        
        
        
    def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
        """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
        
        Inputs: gdlatr, glonr: reference point on earth's surface
                gdlat, glon: second point on earth's surface
        """
        dlon = glonr/57.2958 - glon/57.2958
        dlat = gdlatr/57.2958 - gdlat/57.2958;
        a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)
    
        if math.sqrt(a) < 1.0:
            c = 2.0 * math.asin(math.sqrt(a))
        else:
            c = 2.0 * math.asin(1.0)
    
        return(c * 6371.2)
        
        
    def _getLimits(self, isprintLines):
        """returns (newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin) 
        given isprint lines with
        parameters (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, )
        
        We only look at the end points of ranges and the station location
        """
        # set initial values to station location
        items = isprintLines[1].split()
        slat = float(items[0])
        slon = float(items[1])
        if slon > 180.0:
            slon -= 360.0
        salt = float(items[2])
        maxLon = slon
        minLon = slon
        maxLat = slat
        minLat = slat
        maxAlt = salt
        maxGreatCDist = 0.0
        maxParm = -1.0e-30
        minParm = 1.0e30
        
        lastAz = None
        lastEl = None
        lastRange = None
        
        for i, isprintLine in enumerate(isprintLines):
            items = isprintLine.split()
            if len(items) < 9:
                continue
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            thisAz = (az1 + az2)/2
            el1 = float(items[6])
            el2 = float(items[7])
            thisEl = (el1 + el2)/2
            try:
                parm = float(items[8])
                if parm > maxParm:
                    maxParm = parm
                if parm < minParm:
                    minParm = parm
            except ValueError:
                continue
            
            # see if we're at an end point
            if lastAz != None:
                if abs(lastAz - thisAz) > 0.001 or abs(lastEl - thisEl) > 0.001:
                    # the last point was an end point
                    gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,salt,lastAz,lastEl,lastRange)
                    if gdlat > maxLat:
                        maxLat = gdlat
                    if gdlat < minLat:
                        minLat = gdlat
                    if glon > maxLon:
                        maxLon = glon
                    if glon < minLon:
                        minLon = glon
                    if gdalt > maxAlt:
                        maxAlt = gdalt
                    greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
                    if greatCDist > maxGreatCDist:
                        maxGreatCDist = greatCDist
                        
            lastAz = thisAz
            lastEl = thisEl
            lastRange = range
            
        # check last point
        gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,slat,lastAz,lastEl,lastRange)
        if gdlat > maxLat:
            maxLat = gdlat
        if gdlat < minLat:
            minLat = gdlat
        if glon > maxLon:
            maxLon = glon
        if glon < minLon:
            minLon = glon
        if gdalt > maxAlt:
            maxAlt = gdalt
        greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
        if greatCDist > maxGreatCDist:
            maxGreatCDist = greatCDist
            
        return((maxLon, minLon, maxLat, minLat, maxAlt, maxGreatCDist, maxParm, minParm))
    
    
    
    def _generateWedges(self, isprintLines, scanType):
        """_generateWedges is a python generator that return a data wedge in the form
        (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
        (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt))
        
        Inputs:
        
            isprintLines - a list of line with 9 parameters that
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location.
                No wedge is returned for a missing data line
                
            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
        """
        # state variables
        lastAz1 = None
        lastAz2 = None
        lastEl1 = None
        lastEl2 = None
        lastRange = None
        lastDiffRangeLow = None
        lastValue = None
        
        for isprintLine in isprintLines:
            items = isprintLine.split()
            if len(items) < 9:
                continue
            gdlatr = float(items[0])
            gdlonr = float(items[1])
            galtr = float(items[2])
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            el1 = float(items[6])
            el2 = float(items[7])
            valueStr = items[8]
            if lastAz1 != None and ( abs(lastAz1-az1) > 0.001 or abs(lastEl1-el1) > 0.001 ):
                # new radar line found, send last point if needed
                if lastValue != None:
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue)
                    yield(dataWedge)
                # this is the first point in the line, so no lastDiffRangeLow
                lastDiffRangeLow = None
            elif lastAz1 != None and ( abs(lastAz1-az1) < 0.001 and abs(lastEl1-el1) < 0.001 ):
                # continuing radar line
                if lastValue != None:
                    lastDiffRangeLow = (range - lastRange)/2.0
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, (range-lastRange)/2.0, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue)
                    yield(dataWedge)
                else:
                    lastDiffRangeLow = (range - lastRange)/2.0
            # reset state variables
            lastAz1 = az1
            lastAz2 = az2
            lastEl1 = el1
            lastEl2 = el2
            lastRange = range
            try:
                lastValue = float(valueStr)
            except:
                lastValue = None
                
        # finally, send last wedge if needed
        if lastValue != None:
            dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                       lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                       lastEl1, lastEl2, lastValue)
            yield(dataWedge)
            


    def _getWedge(self, gdlatr, gdlonr, galtr, range,
                  diffRange1, diffRange2, az1, az2,
                  el1, el2, value):
        """_getWedge returns return a data wedge in the form
        (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
        (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt)).  

            
        Inputs:
            gdlatr, gdlonr, galtr - location of radar
            range, diffRange1, diffRange2 - range of center of wedge, and distance in and out from there
            az1, az2, el1, el2 - start and end aximuth and elevation.  Which is used depends on scanType
            value - float data value
        """
        point1_lat,point1_lon,point1_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
        point2_lat,point2_lon,point2_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el2,range-diffRange1)
        point3_lat,point3_lon,point3_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el2,range+diffRange2)
        point4_lat,point4_lon,point4_alt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        return((value, (point1_lat,point1_lon,point1_alt), (point2_lat,point2_lon,point2_alt), 
                (point3_lat,point3_lon,point3_alt), (point4_lat,point4_lon,point4_alt)))
        
        
        
    def _createKmlText(self, dataGen, maxColormap, minColormap, lookLat, lookLon, lookAlt, scanType,
                       instName, instDesc, instLat, instLon, instAlt, colorMap):
        """_createKmlText creates kml text
        
        Inputs:
        
            dataGen - list in form (dataValue, (point1_lat, point1_lon, point1_alt), (point2_lat, point2_lon, point2_alt), 
                                    (point3_lat, point3_lon, point3_alt), (point4_lat, point4_lon, point4_alt))
            maxColormap 
            minColormap
            lookLat
            lookLon
            lookAlt
            scanType
            instName, instDesc, instLat, instLon, instAlt - - used to create a placemark for the radar.  If
                instLat or instLon missing, no placemark added.
            colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum
        """
        
        # need to fill in id and color
        styleTemplate = """
        
        """
        
        # need to fill in id and coordinates
        placemarkTemplate = """
        
            #%s
            
                absolute
                
                    
                         %s 
                    
                
            
        
        """
        
        instTemplate = """
              %s
              %s
              
                %f,%f,%f
              
        
        """
        
        # need to fill in styles and placemarks
        documentTemplate = """
        
        
        ScanKmlFile
            
                %f
                %f
                %f
                %f
                50
                %f
                relativeToGround
            
            
        %s
        %s
        %s
        
        
        """
        
        retStr  = ''
        
        styleStr = ''
        
        # create 100 colors
        for i in range(100):
            if not colorMap:
                colorStr = self._getKmlRGB(i/100.0)
            else:
                matLabColorStr = matplotlib.colors.rgb2hex(colorMap(i/100.0)[:3])
                red = matLabColorStr[1:3]
                green = matLabColorStr[3:5]
                blue = matLabColorStr[5:7]
                colorStr = 'ff' + blue + green + red # this is the google earth standard for opaque color
            styleStr += styleTemplate % (i, colorStr)
            
        placemarkStr = ''
        # loop through data adding strings to placemark string
        while(True):
            try:
                p = next(dataGen)
            except StopIteration:
                break
            data = p[0]
            if data > maxColormap:
                styleIndex = 99
            elif data < minColormap:
                styleIndex = 0
            else:
                styleIndex = int(99.0 * ((data-minColormap)/(maxColormap-minColormap)))

            styleId = 'p%i' % (styleIndex)
            coordStr = ('%f,%f,%f %f,%f,%f %f,%f,%f %f,%f,%f %f,%f,%f' ) % \
                        (p[1][1], p[1][0], p[1][2]*1000.0,
                         p[2][1], p[2][0], p[2][2]*1000.0,
                         p[3][1], p[3][0], p[3][2]*1000.0,
                         p[4][1], p[4][0], p[4][2]*1000.0,
                         p[1][1], p[1][0], p[1][2]*1000.0)
            placemarkStr += placemarkTemplate % (styleId, coordStr)
            
            
        if scanType in ('el_lat'):
            heading = 90.0
            distance = 200.0
        elif scanType in ('el_lon', 'el_gcdist'):
            heading = 0.0
            distance = 200.0
        else:
            heading = 0.0
            distance = 1000.0
            
            
        # create instStr
        instStr = ''
        if instLat != None and instLon != None:
            if instAlt == None:
                instAlt = 0.0
            if instName == None:
                instName = 'Radar'
            if instDesc == None:
                instDesc = 'This is the location of the radar that produced this scan.'
            instStr += instTemplate % (instName, instDesc, instLon, instLat, instAlt)
        
        
        retStr += documentTemplate % (lookLon, lookLat, lookAlt*100.0, heading, lookAlt*distance, styleStr, instStr, placemarkStr)
        
        return(retStr)
    
    
    def _getKmlRGB(self, value, transparent=False, wrap=False):
        """_getKmlRGB returns a Kml color string given a value between 0 qnd 1.
        
        If transparent == False, solid color.  Otherwise transpanency set to 50%
        
        If wrap, colors at zero and 1 wrap.  Otherwise, extremes red and blue
        
        If invert, use value = 1 - value
        """
        if value < 0.0 or value > 1.0:
            raise ValueError('value must be between 0 and 1, not %f' % (value))
        
        if wrap:
            r,g,b = colorsys.hsv_to_rgb(value, 0.9, 0.9)
        else:
            r,g,b = colorsys.hsv_to_rgb(value*0.67, 0.9, 0.9)
        
        if not transparent:
            transStr = 'ff'
        else:
            transStr = '80'
        
        retStr = '%s%s%s%s' % (transStr,
                               hex(int(r*255))[-2:],
                               hex(int(g*255))[-2:],
                               hex(int(b*255))[-2:])
        
        return(retStr)

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, scanType, fullFilename, minColormap=None, maxColormap=None, instName=None, instDesc=None, instLat=None, instLon=None, instAlt=None, colorMap=None)

init writes a madPcolorWedgeKmlScan to a file.

Inputs:

isprintText - a string giving isprint output without headers. The 9 parameters
    are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, <parameter value>).  Any
    missing data should be written as "missing" or other string that
    cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
    (for east or west el scans) or 'el_gcdist' (for all other el scans.
    For az - x is long, y is lat
    for el_* y is always alt, and x is either lat, lon, or great circle distance

fullFilename - full path of file containing kml file to be saved.  Extension must
               be kml, or exception thrown.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.


instName, instDesc, instLat, instLon, instAlt - used to create a placemark for the radar.  If
    instLat or instLon missing, no placemark added.

colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum

Returns: void

Affects: Writes fullFilename to disk

def __init__(self, isprintText,
             scanType,
             fullFilename,
             minColormap = None,
             maxColormap = None,
             instName = None,
             instDesc = None,
             instLat = None,
             instLon = None,
             instAlt = None,
             colorMap = None):
    """__init__ writes a madPcolorWedgeKmlScan to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. The 9 parameters
            are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
            missing data should be written as "missing" or other string that
            cannot be converted to a float. gdlatr, gdlonr, galtr are the station location
        scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
            (for east or west el scans) or 'el_gcdist' (for all other el scans.
            For az - x is long, y is lat
            for el_* y is always alt, and x is either lat, lon, or great circle distance
        fullFilename - full path of file containing kml file to be saved.  Extension must
                       be kml, or exception thrown.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
                      
                      
        instName, instDesc, instLat, instLon, instAlt - used to create a placemark for the radar.  If
            instLat or instLon missing, no placemark added.
            
        colorMap - A matplotlib colormap to use to create colors.  If None, then blue minimum and red maximum
    Returns: void
    Affects: Writes fullFilename to disk
    """
    # verify input
    if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
        raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))
    isprintLines = isprintText.split('\n')
    
    newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
    
    # get look point
    lookLat = (newLatMax + newLatMin)/2.0
    lookLon = (newLonMax + newLonMin)/2.0
    lookAlt = newAltMax * 10.0
    
    if maxColormap == None:
        maxColormap = newParmMax
    if minColormap == None:
        minColormap = newParmMin
        
    
    # set up wedge generator
    dataGen = self._generateWedges(isprintLines, scanType)
    
    text = self._createKmlText(dataGen, maxColormap, minColormap, lookLat, lookLon, lookAlt, scanType,
                               instName, instDesc, instLat, instLon, instAlt, colorMap)
    
    f = open(fullFilename, 'w')
    f.write(text)
    f.close()

def getGreatCircleDist(

self, gdlatr, glonr, gdlat, glon)

getGreatCircleDist return the great circle distance in. Taken from madDeriveMethods c code.

Inputs: gdlatr, glonr: reference point on earth's surface gdlat, glon: second point on earth's surface

def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
    """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
    
    Inputs: gdlatr, glonr: reference point on earth's surface
            gdlat, glon: second point on earth's surface
    """
    dlon = glonr/57.2958 - glon/57.2958
    dlat = gdlatr/57.2958 - gdlat/57.2958;
    a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)

    if math.sqrt(a) < 1.0:
        c = 2.0 * math.asin(math.sqrt(a))
    else:
        c = 2.0 * math.asin(1.0)

    return(c * 6371.2)

class madPcolorWedgeScan

madPcolorWedgeScan is the class that produces pcolor scans where data is drawn as wedge shapes.

Usage example::

obj = madPcolorScan(isprintText,
                'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                'Longitude',
                'Latitude',
                './isprint.png',
                size = 'large',
                minColormap = 9,
                maxColormap = 12)

Non-standard Python modules used: matplotlib

Change history:

Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu Jul. 20, 2006

class madPcolorWedgeScan:
    """madPcolorWedgeScan is the class that produces pcolor scans where data is drawn as wedge shapes.

    Usage example::

        obj = madPcolorScan(isprintText,
                        'Nel (log(m^-3)) - 26 June 2006 13:49:43-14:07:36',
                        'Longitude',
                        'Latitude',
                        './isprint.png',
                        size = 'large',
                        minColormap = 9,
                        maxColormap = 12)    

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 20, 2006
    """


    def __init__(self, isprintText,
                 scanType,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet):
        """__init__ writes a madPcolorWedgeScan to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. The 9 parameters
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
            
            titleStr - plot title (string) - should describe parameter being plotted
        
            xLabelStr - x label string
        
            yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest value found.

            xMaximum = maximum x value.  If None (default), uses highest value found.

            yMinimum = minumum y value.  If None (default), uses lowest value found.

            yMaximum = maximum y value.  If None (default), uses highest value found.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

        Returns: void

        Affects: Writes fullFilename to disk
        """

        # verify input
        if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
            raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))
        
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))

        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        isprintLines = isprintText.split('\n')
        
        if xMaximum == None or xMinimum == None or yMaximum == None or yMinimum or maxColormap == None or minColormap == None:
            newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
            # set values
            xMaximum, xMinimum, yMaximum, yMinimum = self._setLimits(scanType, xMaximum, xMinimum, yMaximum, yMinimum,
                                                                     newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, 
                                                                     newGreatCDist, newParmMax, newParmMin)
            
            if maxColormap == None:
                maxColormap = newParmMax
            if minColormap == None:
                minColormap = newParmMin
            
        
        # set up wedge generator
        dataGen = self._generateWedges(isprintLines, scanType)
        
        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(6,5), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
            
        try:
            while True: 
                data = next(dataGen)
                x = [data[1][0], data[2][0], data[3][0], data[4][0]]
                y = [data[1][1], data[2][1], data[3][1], data[4][1]]
                dataRatio = (data[0] - minColormap) / (maxColormap - minColormap)
                colorStr = matplotlib.colors.rgb2hex(colorMap(dataRatio)[:3])
                thisPlot = matplotlib.pyplot.fill(x, y, colorStr, edgecolor='none')
        except StopIteration:
            pass

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
        
        scalableMappable = matplotlib.cm.ScalarMappable(cmap=colorMap)
        scalableMappable.set_array(numpy.array([minColormap, maxColormap]))
        scalableMappable.set_clim(minColormap, maxColormap)
        def get_alpha(*args, **kw):
            return(1.0)
        scalableMappable.get_alpha = get_alpha


        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.xlim(xMinimum, xMaximum)
        matplotlib.pylab.ylim(yMinimum, yMaximum)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.colorbar(mappable=scalableMappable)
        matplotlib.pylab.gcf().subplots_adjust(bottom=0.15)

        try:
            matplotlib.pylab.savefig(fullFilename)
        except:
            matplotlib.pylab.clf()
            raise

        matplotlib.pylab.clf()
        
        
    def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
        """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
        
        Inputs: gdlatr, glonr: reference point on earth's surface
                gdlat, glon: second point on earth's surface
        """
        dlon = glonr/57.2958 - glon/57.2958
        dlat = gdlatr/57.2958 - gdlat/57.2958;
        a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)
    
        if math.sqrt(a) < 1.0:
            c = 2.0 * math.asin(math.sqrt(a))
        else:
            c = 2.0 * math.asin(1.0)
    
        return(c * 6371.2)
        
        
    def _getLimits(self, isprintLines):
        """returns (newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin) 
        given isprint lines with
        parameters (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, )
        
        We only look at the end points of ranges and the station location
        """
        # set initial values to station location
        items = isprintLines[1].split()
        slat = float(items[0])
        slon = float(items[1])
        if slon > 180.0:
            slon -= 360.0
        salt = float(items[2])
        maxLon = slon
        minLon = slon
        maxLat = slat
        minLat = slat
        maxAlt = salt
        maxGreatCDist = 0.0
        maxParm = -1.0e-30
        minParm = 1.0e30
        
        lastAz = None
        lastEl = None
        lastRange = None
        
        for i, isprintLine in enumerate(isprintLines):
            items = isprintLine.split()
            if len(items) < 9:
                continue
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            thisAz = (az1 + az2)/2
            el1 = float(items[6])
            el2 = float(items[7])
            thisEl = (el1 + el2)/2
            try:
                parm = float(items[8])
                if parm > maxParm:
                    maxParm = parm
                if parm < minParm:
                    minParm = parm
            except ValueError:
                continue
            
            # see if we're at an end point
            if lastAz != None:
                if abs(lastAz - thisAz) > 0.001 or abs(lastEl - thisEl) > 0.001:
                    # the last point was an end point
                    gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,salt,lastAz,lastEl,lastRange)
                    if gdlat > maxLat:
                        maxLat = gdlat
                    if gdlat < minLat:
                        minLat = gdlat
                    if glon > maxLon:
                        maxLon = glon
                    if glon < minLon:
                        minLon = glon
                    if gdalt > maxAlt:
                        maxAlt = gdalt
                    greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
                    if greatCDist > maxGreatCDist:
                        maxGreatCDist = greatCDist
                        
            lastAz = thisAz
            lastEl = thisEl
            lastRange = range
            
        # check last point
        gdlat,glon,gdalt = madrigal._derive.radarToGeodetic(slat,slon,slat,lastAz,lastEl,lastRange)
        if gdlat > maxLat:
            maxLat = gdlat
        if gdlat < minLat:
            minLat = gdlat
        if glon > maxLon:
            maxLon = glon
        if glon < minLon:
            minLon = glon
        if gdalt > maxAlt:
            maxAlt = gdalt
        greatCDist = self.getGreatCircleDist(slat, slon, gdlat, glon)
        if greatCDist > maxGreatCDist:
            maxGreatCDist = greatCDist
            
        return((maxLon, minLon, maxLat, minLat, maxAlt, maxGreatCDist, maxParm, minParm))
    
    
    def _setLimits(self, scanType, xMaximum, xMinimum, yMaximum, yMinimum,
                   newLonMax, newLonMin, newLatMax, newLatMin, newAltMax,
                   newGreatCDist, newParmMax, newParmMin):
        """ _setLimits sets xMaximum, xMinimum, yMaximum, yMinimum based on values
        found in self._getLimits and scanType.  If any value in (xMaximum, xMinimum, yMaximum, yMinimum)
        is not None, it will not be changed. """
        newXMax = xMaximum
        newXMin = xMinimum
        newYMax = yMaximum
        newYMin = yMinimum
        if scanType == 'az':
            if xMaximum == None:
                newXMax = newLonMax
            if xMinimum == None:
                newXMin = newLonMin
            if yMaximum == None:
                newYMax = newLatMax
            if yMinimum == None:
                newYMin = newLatMin
        elif scanType == 'el_lat':
            if xMaximum == None:
                newXMax = newLatMax
            if xMinimum == None:
                newXMin = newLatMin
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        elif scanType == 'el_lon':
            if xMaximum == None:
                newXMax = newLonMax
            if xMinimum == None:
                newXMin = newLonMin
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        elif scanType == 'el_gcdist':
            if xMaximum == None:
                newXMax = newGreatCDist
            if xMinimum == None:
                newXMin = 0.0
            if yMaximum == None:
                newYMax = newAltMax
            if yMinimum == None:
                newYMin = 0.0
        return((newXMax, newXMin, newYMax, newYMin))
    
    
    def _generateWedges(self, isprintLines, scanType):
        """_generateWedges is a python generator that return a data wedge in the form
        (dataValue, (point1_x, point1_y), (point2_x, point2_y), 
        (point3_x, point3_y), (point4_x, point4_y))
        
        Inputs:
        
            isprintLines - a list of line with 9 parameters that
                are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
                missing data should be written as "missing" or other string that
                cannot be converted to a float. gdlatr, gdlonr, galtr are the station location.
                No wedge is returned for a missing data line
                
            scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
                (for east or west el scans) or 'el_gcdist' (for all other el scans.
                For az - x is long, y is lat
                for el_* y is always alt, and x is either lat, lon, or great circle distance
        """
        # state variables
        lastAz1 = None
        lastAz2 = None
        lastEl1 = None
        lastEl2 = None
        lastRange = None
        lastDiffRangeLow = None
        lastValue = None
        
        for isprintLine in isprintLines:
            items = isprintLine.split()
            if len(items) < 9:
                continue
            gdlatr = float(items[0])
            gdlonr = float(items[1])
            galtr = float(items[2])
            range = float(items[3])
            az1 = float(items[4])
            az2 = float(items[5])
            el1 = float(items[6])
            el2 = float(items[7])
            valueStr = items[8]
            if lastAz1 != None and ( abs(lastAz1-az1) > 0.001 or abs(lastEl1-el1) > 0.001 ):
                # new radar line found, send last point if needed
                if lastValue != None:
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue, scanType)
                    yield(dataWedge)
                # this is the first point in the line, so no lastDiffRangeLow
                lastDiffRangeLow = None
            elif lastAz1 != None and ( abs(lastAz1-az1) < 0.001 and abs(lastEl1-el1) < 0.001 ):
                # continuing radar line
                if lastValue != None:
                    lastDiffRangeLow = (range - lastRange)/2.0
                    dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                               lastDiffRangeLow, (range-lastRange)/2.0, lastAz1, lastAz2,
                                               lastEl1, lastEl2, lastValue, scanType)
                    yield(dataWedge)
                else:
                    lastDiffRangeLow = (range - lastRange)/2.0
            # reset state variables
            lastAz1 = az1
            lastAz2 = az2
            lastEl1 = el1
            lastEl2 = el2
            lastRange = range
            try:
                lastValue = float(valueStr)
            except:
                lastValue = None
                
        # finally, send last wedge if needed
        if lastValue != None:
            dataWedge = self._getWedge(gdlatr, gdlonr, galtr, lastRange, 
                                       lastDiffRangeLow, lastDiffRangeLow, lastAz1, lastAz2,
                                       lastEl1, lastEl2, lastValue, scanType)
            yield(dataWedge)
            


    def _getWedge(self, gdlatr, gdlonr, galtr, range,
                  diffRange1, diffRange2, az1, az2,
                  el1, el2, value, scanType):
        """_getWedge returns return a data wedge in the form
        (dataValue, (point1_x, point1_y), (point2_x, point2_y), 
        (point3_x, point3_y), (point4_x, point4_y)).  The x and y dimensions depend on
        scanType:
            az: x: lon, y: lat
            el_lat: x: lat, y: alt
            el_lon: x: lon, y: alt
            el_gcdist: x: great circle distance, y: alt
            
        Inputs:
            gdlatr, gdlonr, galtr - location of radar
            range, diffRange1, diffRange2 - range of center of wedge, and distance in and out from there
            az1, az2, el1, el2 - start and end aximuth and elevation.  Which is used depends on scanType
            value - float data value
            scanType - az, el_lat, el_lon, el_gcdist
        """
        if scanType == 'az':
            point1_y,point1_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point2_y,point2_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el1,range-diffRange1)
            point3_y,point3_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az2,el1,range+diffRange2)
            point4_y,point4_x,gdalt = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_lat':
            point1_x,glon,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point2_x,glon,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            point3_x,glon,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            point4_x,glon,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_lon':
            gdlat,point1_x,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            gdlat,point2_x,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            gdlat,point3_x,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            gdlat,point4_x,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            
        elif scanType == 'el_gcdist':
            gdlat,glon,point1_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range-diffRange1)
            point1_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point2_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range-diffRange1)
            point2_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point3_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el2,range+diffRange2)
            point3_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            gdlat,glon,point4_y = madrigal._derive.radarToGeodetic(gdlatr, gdlonr, galtr,az1,el1,range+diffRange2)
            point4_x = self.getGreatCircleDist(gdlatr, gdlonr, gdlat, glon)
            
        return((value, (point1_x, point1_y), (point2_x, point2_y), (point3_x, point3_y), (point4_x, point4_y)))

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, scanType, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', xMinimum=None, xMaximum=None, yMinimum=None, yMaximum=None, minColormap=None, maxColormap=None, colorMap=<matplotlib.colors.LinearSegmentedColormap object at 0x1169fb450>)

init writes a madPcolorWedgeScan to a file.

Inputs:

isprintText - a string giving isprint output without headers. The 9 parameters
    are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, <parameter value>).  Any
    missing data should be written as "missing" or other string that
    cannot be converted to a float. gdlatr, gdlonr, galtr are the station location

scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
    (for east or west el scans) or 'el_gcdist' (for all other el scans.
    For az - x is long, y is lat
    for el_* y is always alt, and x is either lat, lon, or great circle distance

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

yLabelStr - ylabel string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

xMinimum = minumum x value.  If None (default), uses lowest value found.

xMaximum = maximum x value.  If None (default), uses highest value found.

yMinimum = minumum y value.  If None (default), uses lowest value found.

yMaximum = maximum y value.  If None (default), uses highest value found.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.

colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

Returns: void

Affects: Writes fullFilename to disk

def __init__(self, isprintText,
             scanType,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             xMinimum = None,
             xMaximum = None,
             yMinimum = None,
             yMaximum = None,
             minColormap = None,
             maxColormap = None,
             colorMap = matplotlib.cm.jet):
    """__init__ writes a madPcolorWedgeScan to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. The 9 parameters
            are (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2, ).  Any
            missing data should be written as "missing" or other string that
            cannot be converted to a float. gdlatr, gdlonr, galtr are the station location
        scanType - must be 'az' or 'el_lat' (for north or south el scans), 'el_lon'
            (for east or west el scans) or 'el_gcdist' (for all other el scans.
            For az - x is long, y is lat
            for el_* y is always alt, and x is either lat, lon, or great circle distance
        
        titleStr - plot title (string) - should describe parameter being plotted
    
        xLabelStr - x label string
    
        yLabelStr - ylabel string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
        xMinimum = minumum x value.  If None (default), uses lowest value found.
        xMaximum = maximum x value.  If None (default), uses highest value found.
        yMinimum = minumum y value.  If None (default), uses lowest value found.
        yMaximum = maximum y value.  If None (default), uses highest value found.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
        colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet
    Returns: void
    Affects: Writes fullFilename to disk
    """
    # verify input
    if scanType not in ('az', 'el_lat', 'el_lon', 'el_gcdist'):
        raise ValueError('scanType must be "az", "el_lat", "el_lon", "el_gcdist", not %s' % (str(scanType)))
    
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    isprintLines = isprintText.split('\n')
    
    if xMaximum == None or xMinimum == None or yMaximum == None or yMinimum or maxColormap == None or minColormap == None:
        newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, newGreatCDist, newParmMax, newParmMin = self._getLimits(isprintLines)
        # set values
        xMaximum, xMinimum, yMaximum, yMinimum = self._setLimits(scanType, xMaximum, xMinimum, yMaximum, yMinimum,
                                                                 newLonMax, newLonMin, newLatMax, newLatMin, newAltMax, 
                                                                 newGreatCDist, newParmMax, newParmMin)
        
        if maxColormap == None:
            maxColormap = newParmMax
        if minColormap == None:
            minColormap = newParmMin
        
    
    # set up wedge generator
    dataGen = self._generateWedges(isprintLines, scanType)
    
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(6,5), facecolor = 'w')
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(12,6), facecolor = 'w')
        
    try:
        while True: 
            data = next(dataGen)
            x = [data[1][0], data[2][0], data[3][0], data[4][0]]
            y = [data[1][1], data[2][1], data[3][1], data[4][1]]
            dataRatio = (data[0] - minColormap) / (maxColormap - minColormap)
            colorStr = matplotlib.colors.rgb2hex(colorMap(dataRatio)[:3])
            thisPlot = matplotlib.pyplot.fill(x, y, colorStr, edgecolor='none')
    except StopIteration:
        pass
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    
    scalableMappable = matplotlib.cm.ScalarMappable(cmap=colorMap)
    scalableMappable.set_array(numpy.array([minColormap, maxColormap]))
    scalableMappable.set_clim(minColormap, maxColormap)
    def get_alpha(*args, **kw):
        return(1.0)
    scalableMappable.get_alpha = get_alpha
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.xlim(xMinimum, xMaximum)
    matplotlib.pylab.ylim(yMinimum, yMaximum)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    matplotlib.pylab.colorbar(mappable=scalableMappable)
    matplotlib.pylab.gcf().subplots_adjust(bottom=0.15)
    try:
        matplotlib.pylab.savefig(fullFilename)
    except:
        matplotlib.pylab.clf()
        raise
    matplotlib.pylab.clf()

def getGreatCircleDist(

self, gdlatr, glonr, gdlat, glon)

getGreatCircleDist return the great circle distance in. Taken from madDeriveMethods c code.

Inputs: gdlatr, glonr: reference point on earth's surface gdlat, glon: second point on earth's surface

def getGreatCircleDist(self, gdlatr, glonr, gdlat, glon):
    """getGreatCircleDist return the great circle distance in.  Taken from madDeriveMethods c code.
    
    Inputs: gdlatr, glonr: reference point on earth's surface
            gdlat, glon: second point on earth's surface
    """
    dlon = glonr/57.2958 - glon/57.2958
    dlat = gdlatr/57.2958 - gdlat/57.2958;
    a = math.sin(dlat/2.0)*math.sin(dlat/2.0) + math.cos(gdlatr/57.2958)*math.cos(gdlat/57.2958) * math.sin(dlon/2.0)*math.sin(dlon/2.0)

    if math.sqrt(a) < 1.0:
        c = 2.0 * math.asin(math.sqrt(a))
    else:
        c = 2.0 * math.asin(1.0)

    return(c * 6371.2)

class madScatterPlot

madScatterPlot is the class the produces two dimensional scatter plots of x versus y.

class madScatterPlot:
    """madScatterPlot is the class the produces two dimensional scatter plots of x versus y.


    """
    def __init__(self, isprintText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 noDate = False,
                 yMinimum = None,
                 yMaximum = None,
                 timezone = 0):
        """__init__ writes a madScatter plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First parameter
                          must be UTH or UT1, depending on whether time scale should be relative
			  to the experiment beginning (UTH) or absolute (UT1).
			  The second must be the parameter to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float. 
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
	    
	        yLabelStr - ylabel string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
			    
    	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
    	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
    			      parameter in isprintText must be UT1, if false, it must be UTH.
    
    	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
    	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	                startTime must be in units of UTH (hours since midnight UT of first day of
    	                experiment). Default is None, which means start at lowest time found.
    
    	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
    	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
    	              endTime must be in units of UTH (hours since midnight UT of first day of
    	              experiment). Default is None, which means end at largest time found.
    
    	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
    	                   needed to have at most maxNumPoints lines.
        
            noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum.  If default=None, use data minimum
    
            yMaximum - set y maximum.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
                        
        Returns: void

        Affects: None
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 2

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
	    
        # find min, max of y, not including missing
        yMin = None
        yMax = None
        for y in array_data[:,1]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
            if yMax == None:
                yMax = y
            elif yMax < y:
                yMax = y
		
        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(9,3), facecolor = 'w')
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
	    
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.14, 0.18, 0.83, 0.7]) 
      
        matplotlib.pylab.scatter(array_data[:,0],array_data[:,1])

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.grid(True)
            
        if useAbsoluteTime:
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim() 
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0) + 1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
	    
        matplotlib.pylab.xticks(fontsize=fontSize)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)
            
        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()


    def __truncateIsprint(self, isprintText, maxLines):
        """__truncateIsprint truncates isprintText to have maxLines at most.
        """
        isprintList = isprintText.split('\n')
        if len(isprintList) < maxLines:
            return isprintText
        else:
            dropNumber = int(1 + len(isprintList)/maxLines)
            newline = '\n'
            newIsprintText = newline.join(isprintList[::dropNumber])

            return newIsprintText
        

    def __filter_missing(self,x):
        try:
            return float(x)
        except:
            return self.__missing

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', useAbsoluteTime=False, startTime=None, endTime=None, maxNumPoints=None, noTime=False, noDate=False, yMinimum=None, yMaximum=None, timezone=0)

init writes a madScatter plot to a file.

Inputs:

isprintText - a string giving isprint output without headers. First parameter
              must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The second must be the parameter to be plotted. Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

    yLabelStr - ylabel string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                  since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                  parameter in isprintText must be UT1, if false, it must be UTH.

startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
            in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
            startTime must be in units of UTH (hours since midnight UT of first day of
            experiment). Default is None, which means start at lowest time found.

endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
          in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
          endTime must be in units of UTH (hours since midnight UT of first day of
          experiment). Default is None, which means end at largest time found.

maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
               needed to have at most maxNumPoints lines.

noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.

noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

yMinimum - set y minimum.  If default=None, use data minimum

yMaximum - set y maximum.  If default=None, use data maximum

timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
            (negative in most of Western Europe, positive in the US, zero in the UK).

Returns: void

Affects: None

def __init__(self, isprintText,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             useAbsoluteTime = False,
             startTime = None,
             endTime = None,
             maxNumPoints = None,
             noTime = False,
             noDate = False,
             yMinimum = None,
             yMaximum = None,
             timezone = 0):
    """__init__ writes a madScatter plot to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First parameter
                      must be UTH or UT1, depending on whether time scale should be relative
 to the experiment beginning (UTH) or absolute (UT1).
 The second must be the parameter to be plotted. Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float. 
        
        titleStr - plot title (string) - should describe parameter being plotted
        
        xLabelStr - x label string
 
     yLabelStr - ylabel string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
   
	    useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
	                      since beginning of experiment (UTH).  If useAbsoluteTime is true, first
			      parameter in isprintText must be UT1, if false, it must be UTH.

	    startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
	                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	                startTime must be in units of UTH (hours since midnight UT of first day of
	                experiment). Default is None, which means start at lowest time found.

	    endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
	              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
	              endTime must be in units of UTH (hours since midnight UT of first day of
	              experiment). Default is None, which means end at largest time found.

	    maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
	                   needed to have at most maxNumPoints lines.
    
        noTime - if True and useAbsoluteTime True, then dates without times printed on x axis.
        
        noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

        yMinimum - set y minimum.  If default=None, use data minimum

        yMaximum - set y maximum.  If default=None, use data maximum
        
        timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                    (negative in most of Western Europe, positive in the US, zero in the UK).
                    
    Returns: void
    Affects: None
    """
    self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
    self.__parameter_count = 2
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
    if maxNumPoints != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
           
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    
    # find min, max of x (time)
    if startTime == None:
        xMin = array_data[0,0]
    else:
        xMin = startTime
    if endTime == None:
        xMax = array_data[-1,0]
    else:
        xMax = endTime
    
 
    # find min, max of y, not including missing
    yMin = None
    yMax = None
    for y in array_data[:,1]:
        if y == self.__missing:
            continue
        if yMin == None:
            yMin = y
        elif yMin > y:
            yMin = y
        if yMax == None:
            yMax = y
        elif yMax < y:
            yMax = y
    if yMin == None:
        raise ValueError('No valid y data found')
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(9,3), facecolor = 'w')
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
 
    if useAbsoluteTime:
         # leave room for rotated datetime string
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
        elif size == 'wide':
            matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
        elif size == 'small':
            matplotlib.pylab.axes([0.14, 0.18, 0.83, 0.7]) 
  
    matplotlib.pylab.scatter(array_data[:,0],array_data[:,1])
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.xlim(xMin, xMax)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    matplotlib.pylab.grid(True)
        
    if useAbsoluteTime:
        # if plot is less than 24 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim() 
        xmin = xmin / (60*60)
        xmax = xmax / (60*60)
        if noDate == True:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0) + 1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            step = int((xmax-xmin-1)/6)+1
            xRange =  list(range(0,int(newXmax-xmin),step))
            for i in xRange:
                tmp = (xmin + i)*60*60
                newXLocs.append(tmp)
            
            if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
            
            newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
            matplotlib.pylab.xticks(newXLocs, newXLabels)
        
        else:
            locs, labels = matplotlib.pylab.xticks()
            if len(locs) > 5:
                # truncate by len(locs) / 5
                scale = 1 + int(len(locs) / 5)
                new_locs = []
                for i in range(len(locs)):
                    if i % scale == 0:
                        new_locs.append(locs[i])
                locs = new_locs
            newXTicks = convertToAbsoluteTimeStr(locs)
            matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
 
    matplotlib.pylab.xticks(fontsize=fontSize)
    if yMinimum != None and yMaximum != None:
        matplotlib.pylab.ylim(yMinimum, yMaximum)
    elif yMinimum != None and yMaximum == None:
        matplotlib.pylab.ylim(yMin=yMinimum)
    elif yMinimum == None and yMaximum != None:
        matplotlib.pylab.ylim(yMax=yMaximum)
        
    # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
    
    #self.__figure = matplotlib.pylab.gcf()
      
    matplotlib.pylab.savefig(fullFilename)
    #matplotlib.pylab.show()
    matplotlib.pylab.clf()

class madXYScatterPlot

madXYScatterPlot is the class the produces XY scatter plots.

Change History:

Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug 2, 2006
Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug 2, 2006
class madXYScatterPlot:
    """madXYScatterPlot is the class the produces XY scatter plots.


        Change History:

            Written by "Brandon Scott Fines":mailto:bfines@haystack.mit.edu Aug 2, 2006
            Written by "Bill Rideout":mailto:brideout@haystack.mit.edu Aug 2, 2006

    """

    def __init__(self,inputText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size='small',
                 lowBound=None,
                 highBound=None,
                 maxNumPoints=None,
                 yLegend=None):

        """
        Inputs:

            inputText - string of values to be plotted. The formatting is as
                        follows:

                        xval yval
                        xval yval
                        xval yval

            titleStr - title of the Plot

            xLabelStr - label for the x axis

            yLabelStr - label for the y axis

            fullFilename - full file path to save the resulting picture to

            size - size of plot to be saved. Must be 'small','wide', or 'large'.
                defaults to 'small'.

            lowBound - lower bound on the x-axis value. If no bound is specified,
                    the lowest value found in inputText will be used.

            highBound - upper bound on the x-axis value. If no bound is specified,
                    the highest value found in inputText will be used.

            maxNumPoints - maximum number of points to be plotted.

        Outputs: None

        Affects: Creates a scatter plot using matplotlib and writes that to the
                    file designated by the variable 'fullFilename'

        Exceptions: ValueError if lowBound or highBound cannot be converted to
                    a float value

        Non-standard python modules used:

            matplotlib
        """

        #verify input
        if size not in ('small','wide','large'):
            raise ValueError('size must be "small","wide", or "large", not %s'%(str(size)))

        if size in ('small','wide'):
            fontSize = 12
        elif size in 'large':
            fontSize = 18

        if maxNumPoints !=None:
            inputText = self.__truncateInput(inputText, maxNumPoints)


        if lowBound !=None:
            #check that it is a number
            try:
                lowBound = float(lowBound)
            except ValueError:
                raise ValueError('lowBound not a number')
            try:
                highBound = float(highBound)
            except:
                raise ValueError('lowBound not a number')     
                

        #convert the input data into numeric arrays
        x=[]
        y=[]

        split_data = inputText.split()
        #send x-values to x, y-values to y
        i=0
        while i xhigh:
                    xhigh = i
            xlow = lowBound
        elif lowBound==None and highBound !=None:
            #find the lower bound in x
            for i in x:
                if xlow==None:
                    xlow = i
                if ixhigh:
                    xhigh = i

        #find upper and lower bounds in y
        ylow = None
        yhigh = None
        for i in y:
            if ylow==None:
                ylow = i
            elif iyhigh:
                yhigh = i



        #check for good numbers
        if ylow == None:
            raise ValueError('no valid y data found')

        #select plot sizes
        if size=='small':
            matplotlib.pylab.figure(1,figsize=(6,4),facecolor='w')
        elif size=='wide':
            matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
        elif size=='large':
            matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')


        #draw scatter plot
        #matplotlib.pylab.scatter(x,y)
        matplotlib.pylab.plot(x,y, 'gd',ms=5,mew=0.5)


        thisFont = {'size' : fontSize}
        matplotlib.pylab.rc('font', **thisFont) #pass in font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr,fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr,fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xlow,xhigh)
        matplotlib.pylab.title(titleStr,fontsize=fontSize)
        matplotlib.pylab.grid(True)
        
        if yLegend != None: matplotlib.pylab.legend(yLegend,numpoints=1)

        #save the figure
        matplotlib.pylab.savefig(fullFilename)

        matplotlib.pylab.clf()

Ancestors (in MRO)

Static methods

def __init__(

self, inputText, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', lowBound=None, highBound=None, maxNumPoints=None, yLegend=None)

Inputs:

inputText - string of values to be plotted. The formatting is as
            follows:

            xval yval
            xval yval
            xval yval

titleStr - title of the Plot

xLabelStr - label for the x axis

yLabelStr - label for the y axis

fullFilename - full file path to save the resulting picture to

size - size of plot to be saved. Must be 'small','wide', or 'large'.
    defaults to 'small'.

lowBound - lower bound on the x-axis value. If no bound is specified,
        the lowest value found in inputText will be used.

highBound - upper bound on the x-axis value. If no bound is specified,
        the highest value found in inputText will be used.

maxNumPoints - maximum number of points to be plotted.

Outputs: None

Affects: Creates a scatter plot using matplotlib and writes that to the file designated by the variable 'fullFilename'

Exceptions: ValueError if lowBound or highBound cannot be converted to a float value

Non-standard python modules used:

matplotlib
def __init__(self,inputText,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size='small',
             lowBound=None,
             highBound=None,
             maxNumPoints=None,
             yLegend=None):
    """
    Inputs:
        inputText - string of values to be plotted. The formatting is as
                    follows:
                    xval yval
                    xval yval
                    xval yval
        titleStr - title of the Plot
        xLabelStr - label for the x axis
        yLabelStr - label for the y axis
        fullFilename - full file path to save the resulting picture to
        size - size of plot to be saved. Must be 'small','wide', or 'large'.
            defaults to 'small'.
        lowBound - lower bound on the x-axis value. If no bound is specified,
                the lowest value found in inputText will be used.
        highBound - upper bound on the x-axis value. If no bound is specified,
                the highest value found in inputText will be used.
        maxNumPoints - maximum number of points to be plotted.
    Outputs: None
    Affects: Creates a scatter plot using matplotlib and writes that to the
                file designated by the variable 'fullFilename'
    Exceptions: ValueError if lowBound or highBound cannot be converted to
                a float value
    Non-standard python modules used:
        matplotlib
    """
    #verify input
    if size not in ('small','wide','large'):
        raise ValueError('size must be "small","wide", or "large", not %s'%(str(size)))
    if size in ('small','wide'):
        fontSize = 12
    elif size in 'large':
        fontSize = 18
    if maxNumPoints !=None:
        inputText = self.__truncateInput(inputText, maxNumPoints)
    if lowBound !=None:
        #check that it is a number
        try:
            lowBound = float(lowBound)
        except ValueError:
            raise ValueError('lowBound not a number')
        try:
            highBound = float(highBound)
        except:
            raise ValueError('lowBound not a number')     
            
    #convert the input data into numeric arrays
    x=[]
    y=[]
    split_data = inputText.split()
    #send x-values to x, y-values to y
    i=0
    while i xhigh:
                xhigh = i
        xlow = lowBound
    elif lowBound==None and highBound !=None:
        #find the lower bound in x
        for i in x:
            if xlow==None:
                xlow = i
            if ixhigh:
                xhigh = i
    #find upper and lower bounds in y
    ylow = None
    yhigh = None
    for i in y:
        if ylow==None:
            ylow = i
        elif iyhigh:
            yhigh = i
    #check for good numbers
    if ylow == None:
        raise ValueError('no valid y data found')
    #select plot sizes
    if size=='small':
        matplotlib.pylab.figure(1,figsize=(6,4),facecolor='w')
    elif size=='wide':
        matplotlib.pylab.figure(1,figsize=(10,4),facecolor='w')
    elif size=='large':
        matplotlib.pylab.figure(1,figsize=(12,6),facecolor='w')
    #draw scatter plot
    #matplotlib.pylab.scatter(x,y)
    matplotlib.pylab.plot(x,y, 'gd',ms=5,mew=0.5)
    thisFont = {'size' : fontSize}
    matplotlib.pylab.rc('font', **thisFont) #pass in font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr,fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr,fontsize=fontSize)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.xticks(fontsize=fontSize)
    matplotlib.pylab.xlim(xlow,xhigh)
    matplotlib.pylab.title(titleStr,fontsize=fontSize)
    matplotlib.pylab.grid(True)
    
    if yLegend != None: matplotlib.pylab.legend(yLegend,numpoints=1)
    #save the figure
    matplotlib.pylab.savefig(fullFilename)
    matplotlib.pylab.clf()

class madXYwithErrorPlot

madXYwithErrorPlot is the class the produces two dimensional scatter plots of x versus y more its error. Written by "Miguel Urco":mailto:murco@jro.igp.gob.pe Jul 2, 2009

class madXYwithErrorPlot:
    """madXYwithErrorPlot is the class the produces two dimensional scatter plots of x versus y more its error.
    Written by "Miguel Urco":mailto:murco@jro.igp.gob.pe Jul 2, 2009

    """
    def __init__(self, isprintText,
                 titleStr,
                 xLabelStr,
                 yLabelStr,
                 fullFilename,
                 size = 'small',
                 useAbsoluteTime = False,
                 startTime = None,
                 endTime = None,
                 maxNumPoints = None,
                 noTime = False,
                 noDate = False,
                 yMinimum = None,
                 yMaximum = None,
                 timezone = 0):
        """__init__ writes a madScatter plot to a file.

        Inputs:

            isprintText - a string giving isprint output without headers. First of three parameters
                          must be UTH or UT1, depending on whether time scale should be relative
                          to the experiment beginning (UTH) or absolute (UT1).
                          The second must be the parameter to be plotted. Any
                          missing data should be written as "missing" or other string that
                          cannot be converted to a float. 
            
            titleStr - plot title (string) - should describe parameter being plotted
            
            xLabelStr - x label string
        
            yLabelStr - y label string

            fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                           be jpeg or png, or exception thrown.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
                
            useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                            since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                            parameter in isprintText must be UT1, if false, it must be UTH.

            startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                    in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                    startTime must be in units of UTH (hours since midnight UT of first day of
                    experiment). Default is None, which means start at lowest time found.

            endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
                  in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                  endTime must be in units of UTH (hours since midnight UT of first day of
                  experiment). Default is None, which means end at largest time found.

            maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                       needed to have at most maxNumPoints lines.
            
            noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.
    
            yMinimum - set y minimum.  If default=None, use data minimum
    
            yMaximum - set y maximum.  If default=None, use data maximum
            
            timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                        (negative in most of Western Europe, positive in the US, zero in the UK).
                        
        Returns: void

        Affects: None
        
        Now removes all lines with missing
        """
        self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
        self.__parameter_count = 3

        # verify input
        if size not in ('small', 'wide', 'large'):
            raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
        
        if size in ('small', 'wide'):
            fontSize = 12
        elif size in ('large'):
            fontSize = 18
            
        # filter out missing lines
        newIsprint = ''
        lines = isprintText.split('\n')
        for line in lines:
            try:
                items = line.split()
                float(items[0])
                float(items[1])
                float(items[2])
                newIsprint += line + ' '
            except:
                continue
        isprintText = newIsprint

        if maxNumPoints != None:
            isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
               
        # convert the input data into numeric arrays of float assuming no headers and filter the missing values.

        try:
            split_data = isprintText.split()
            float_data = list(map(self.__filter_missing, split_data))
            array_data = numpy.asarray(float_data)
            array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
        except:
            traceback.print_exc()
            raise ValueError('input text is not parseable')
        
        # find min, max of x (time)
        if startTime == None:
            xMin = array_data[0,0]
        else:
            xMin = startTime
        if endTime == None:
            xMax = array_data[-1,0]
        else:
            xMax = endTime
        
        
        # find min, max of y, not including missing
        yMin = None
        yMax = None

        for y in array_data[:,1]:
            if y == self.__missing:
                continue
            if yMin == None:
                yMin = y
            elif yMin > y:
                yMin = y
            if yMax == None:
                yMax = y
            elif yMax < y:
                yMax = y

        if yMin == None:
            raise ValueError('No valid y data found')

        # select the plot size
        if size == 'small':
            matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
            matplotlib.pylab.axes([0.11, 0.18, 0.78, 0.7])
        elif size == 'wide':
            matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
        elif size == 'large':
            matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
        
        if useAbsoluteTime:
             # leave room for rotated datetime string
            if size == 'large':
                matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
            elif size == 'wide':
                matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
            elif size == 'small':
                matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
      
        matplotlib.pylab.errorbar(array_data[:,0],array_data[:,2],array_data[:,1],ecolor='red',fmt='s',mfc='red',mec='green',ms=1,mew=1)

        thisFont = {'size': fontSize}
        matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs

        matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
        matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
        matplotlib.pylab.yticks(fontsize=fontSize)
        matplotlib.pylab.xlim(xMin, xMax)
        matplotlib.pylab.title(titleStr, fontsize=fontSize)
        matplotlib.pylab.grid(True)

        if yMinimum != None and yMaximum != None:
            matplotlib.pylab.ylim(yMinimum, yMaximum)
        elif yMinimum != None and yMaximum == None:
            matplotlib.pylab.ylim(yMin=yMinimum)
        elif yMinimum == None and yMaximum != None:
            matplotlib.pylab.ylim(yMax=yMaximum)

        if useAbsoluteTime:
            # if plot is less than 24 hours, force ticks to be every 4 hours
            xmin, xmax = matplotlib.pylab.xlim()
            xmin = xmin / (60*60)
            xmax = xmax / (60*60)
            if noDate == True:
                if xmax % 4 != 0:
                    newXmax = 4 * (int(xmax/4.0)+1)
                else:
                    newXmax = xmax
                newXLocs = []
                newXLabels = []
                step = int((xmax-xmin-1)/6)+1
                xRange =  list(range(0,int(newXmax-xmin),step))
                for i in xRange:
                    tmp = (xmin + i)*60*60
                    newXLocs.append(tmp)
                
                if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
                
                newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
                matplotlib.pylab.xticks(newXLocs, newXLabels)
            else:
                locs, labels = matplotlib.pylab.xticks()
                if len(locs) > 5:
                    # truncate by len(locs) / 5
                    scale = 1 + int(len(locs) / 5)
                    new_locs = []
                    for i in range(len(locs)):
                        if i % scale == 0:
                            new_locs.append(locs[i])
                    locs = new_locs
                newXTicks = convertToAbsoluteTimeStr(locs)
                matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
        
        matplotlib.pylab.xticks(fontsize=fontSize)

        # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
        
        #self.__figure = matplotlib.pylab.gcf()
          
        matplotlib.pylab.savefig(fullFilename)
        #matplotlib.pylab.show()

        matplotlib.pylab.clf()
        
    def __truncateInput(self,inputStr,maxLines):
        """__truncateInput truncates inputStr to have maxLines at most
        """
        inputList = inputStr.split('\n')
        if len(inputList)

Ancestors (in MRO)

Static methods

def __init__(

self, isprintText, titleStr, xLabelStr, yLabelStr, fullFilename, size='small', useAbsoluteTime=False, startTime=None, endTime=None, maxNumPoints=None, noTime=False, noDate=False, yMinimum=None, yMaximum=None, timezone=0)

init writes a madScatter plot to a file.

Inputs:

isprintText - a string giving isprint output without headers. First of three parameters
              must be UTH or UT1, depending on whether time scale should be relative
              to the experiment beginning (UTH) or absolute (UT1).
              The second must be the parameter to be plotted. Any
              missing data should be written as "missing" or other string that
              cannot be converted to a float.

titleStr - plot title (string) - should describe parameter being plotted

xLabelStr - x label string

yLabelStr - y label string

fullFilename - full path of file containing pcolor plot to be saved.  Extension must
               be jpeg or png, or exception thrown.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                parameter in isprintText must be UT1, if false, it must be UTH.

startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
        in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
        startTime must be in units of UTH (hours since midnight UT of first day of
        experiment). Default is None, which means start at lowest time found.

endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
      in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
      endTime must be in units of UTH (hours since midnight UT of first day of
      experiment). Default is None, which means end at largest time found.

maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
           needed to have at most maxNumPoints lines.

noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

yMinimum - set y minimum.  If default=None, use data minimum

yMaximum - set y maximum.  If default=None, use data maximum

timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
            (negative in most of Western Europe, positive in the US, zero in the UK).

Returns: void

Affects: None

Now removes all lines with missing

def __init__(self, isprintText,
             titleStr,
             xLabelStr,
             yLabelStr,
             fullFilename,
             size = 'small',
             useAbsoluteTime = False,
             startTime = None,
             endTime = None,
             maxNumPoints = None,
             noTime = False,
             noDate = False,
             yMinimum = None,
             yMaximum = None,
             timezone = 0):
    """__init__ writes a madScatter plot to a file.
    Inputs:
        isprintText - a string giving isprint output without headers. First of three parameters
                      must be UTH or UT1, depending on whether time scale should be relative
                      to the experiment beginning (UTH) or absolute (UT1).
                      The second must be the parameter to be plotted. Any
                      missing data should be written as "missing" or other string that
                      cannot be converted to a float. 
        
        titleStr - plot title (string) - should describe parameter being plotted
        
        xLabelStr - x label string
    
        yLabelStr - y label string
        fullFilename - full path of file containing pcolor plot to be saved.  Extension must
                       be jpeg or png, or exception thrown.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
            
        useAbsoluteTime - if true, print time as YYYY-MM-DD HH:MM:SS.  If false (default), print time as hour
                        since beginning of experiment (UTH).  If useAbsoluteTime is true, first
                        parameter in isprintText must be UT1, if false, it must be UTH.
        startTime - start plot at given time.  If useAbsoluteTime == True, then startTime must be
                in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
                startTime must be in units of UTH (hours since midnight UT of first day of
                experiment). Default is None, which means start at lowest time found.
        endTime - end plot at given time.  If useAbsoluteTime == True, then endTime must be
              in units of seconds since 1/1/1950.  If useAbsoluteTime == False, then
              endTime must be in units of UTH (hours since midnight UT of first day of
              experiment). Default is None, which means end at largest time found.
        maxNumPoints - maximum number of points to plot.  If not None, truncate isprintText if
                   needed to have at most maxNumPoints lines.
        
        noDate - if True and useAbsoluteTime True, then times without dates printed on x axis.

        yMinimum - set y minimum.  If default=None, use data minimum

        yMaximum - set y maximum.  If default=None, use data maximum
        
        timezone - The offset of the local (non-DST) timezone, in seconds west of UTC 
                    (negative in most of Western Europe, positive in the US, zero in the UK).
                    
    Returns: void
    Affects: None
    
    Now removes all lines with missing
    """
    self.__missing = 1.0E30 # special value, since numpy doesn't handle NaN
    self.__parameter_count = 3
    # verify input
    if size not in ('small', 'wide', 'large'):
        raise ValueError('size must be "small", "wide", or "large", not %s' % (str(size)))
    
    if size in ('small', 'wide'):
        fontSize = 12
    elif size in ('large'):
        fontSize = 18
        
    # filter out missing lines
    newIsprint = ''
    lines = isprintText.split('\n')
    for line in lines:
        try:
            items = line.split()
            float(items[0])
            float(items[1])
            float(items[2])
            newIsprint += line + ' '
        except:
            continue
    isprintText = newIsprint
    if maxNumPoints != None:
        isprintText = self.__truncateIsprint(isprintText, maxNumPoints)
           
    # convert the input data into numeric arrays of float assuming no headers and filter the missing values.
    try:
        split_data = isprintText.split()
        float_data = list(map(self.__filter_missing, split_data))
        array_data = numpy.asarray(float_data)
        array_data = numpy.reshape(array_data,(-1,self.__parameter_count))
    except:
        traceback.print_exc()
        raise ValueError('input text is not parseable')
    
    # find min, max of x (time)
    if startTime == None:
        xMin = array_data[0,0]
    else:
        xMin = startTime
    if endTime == None:
        xMax = array_data[-1,0]
    else:
        xMax = endTime
    
    
    # find min, max of y, not including missing
    yMin = None
    yMax = None
    for y in array_data[:,1]:
        if y == self.__missing:
            continue
        if yMin == None:
            yMin = y
        elif yMin > y:
            yMin = y
        if yMax == None:
            yMax = y
        elif yMax < y:
            yMax = y
    if yMin == None:
        raise ValueError('No valid y data found')
    # select the plot size
    if size == 'small':
        matplotlib.pylab.figure(1, figsize=(8,3), facecolor = 'w')
        matplotlib.pylab.axes([0.11, 0.18, 0.78, 0.7])
    elif size == 'wide':
        matplotlib.pylab.figure(1, figsize=(10,4), facecolor = 'w')
    elif size == 'large':
        matplotlib.pylab.figure(1, figsize=(10,6), facecolor = 'w')
    
    if useAbsoluteTime:
         # leave room for rotated datetime string
        if size == 'large':
            matplotlib.pylab.axes([0.1, 0.2, 0.8, 0.7])
        elif size == 'wide':
            matplotlib.pylab.axes([0.07, 0.2, 0.66, 0.7])
        elif size == 'small':
            matplotlib.pylab.axes([0.1, 0.18, 0.87, 0.7]) 
  
    matplotlib.pylab.errorbar(array_data[:,0],array_data[:,2],array_data[:,1],ecolor='red',fmt='s',mfc='red',mec='green',ms=1,mew=1)
    thisFont = {'size': fontSize}
    matplotlib.pylab.rc('font', **thisFont)  # pass in the font dict as kwargs
    matplotlib.pylab.xlabel(xLabelStr, fontsize=fontSize)
    matplotlib.pylab.ylabel(yLabelStr, fontsize=fontSize)
    matplotlib.pylab.yticks(fontsize=fontSize)
    matplotlib.pylab.xlim(xMin, xMax)
    matplotlib.pylab.title(titleStr, fontsize=fontSize)
    matplotlib.pylab.grid(True)
    if yMinimum != None and yMaximum != None:
        matplotlib.pylab.ylim(yMinimum, yMaximum)
    elif yMinimum != None and yMaximum == None:
        matplotlib.pylab.ylim(yMin=yMinimum)
    elif yMinimum == None and yMaximum != None:
        matplotlib.pylab.ylim(yMax=yMaximum)
    if useAbsoluteTime:
        # if plot is less than 24 hours, force ticks to be every 4 hours
        xmin, xmax = matplotlib.pylab.xlim()
        xmin = xmin / (60*60)
        xmax = xmax / (60*60)
        if noDate == True:
            if xmax % 4 != 0:
                newXmax = 4 * (int(xmax/4.0)+1)
            else:
                newXmax = xmax
            newXLocs = []
            newXLabels = []
            step = int((xmax-xmin-1)/6)+1
            xRange =  list(range(0,int(newXmax-xmin),step))
            for i in xRange:
                tmp = (xmin + i)*60*60
                newXLocs.append(tmp)
            
            if (xmax - xmin) < 1: newXLocs.append(xmax*60*60)
            
            newXLabels = convertToAbsoluteTimeStr(newXLocs, noDate=noDate, timezone=timezone)
            matplotlib.pylab.xticks(newXLocs, newXLabels)
        else:
            locs, labels = matplotlib.pylab.xticks()
            if len(locs) > 5:
                # truncate by len(locs) / 5
                scale = 1 + int(len(locs) / 5)
                new_locs = []
                for i in range(len(locs)):
                    if i % scale == 0:
                        new_locs.append(locs[i])
                locs = new_locs
            newXTicks = convertToAbsoluteTimeStr(locs)
            matplotlib.pylab.xticks(locs, newXTicks, rotation=15)
    
    matplotlib.pylab.xticks(fontsize=fontSize)
    # get the handle to the figure now that it has been created so we can manipulate on subsequent calls.
    
    #self.__figure = matplotlib.pylab.gcf()
      
    matplotlib.pylab.savefig(fullFilename)
    #matplotlib.pylab.show()
    matplotlib.pylab.clf()

class scanPlotter

scanPlotter is the class that produces a series of scan plots for a single Madrigal file

Non-standard Python modules used: matplotlib

Change history:

Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu Jul. 26, 2006

class scanPlotter:
    """scanPlotter is the class that produces a series of scan plots for a single Madrigal file

    Non-standard Python modules used:
    matplotlib

    Change history:

    Written by "Bill Rideout":mailto:wrideout@haystack.mit.edu  Jul. 26, 2006
    """
    def __init__(self, madFile,
                 madDBObj=None):
        """__init__ initializes a scanPlotter object.

        Inputs:

            madFile - the Madrigal file to be analyzed.  Must contain SCNTYP and CYCN parameters.

            madDBObj - a madrigal.metadata.MadrigalDB object.  If None (default),
            one created

	              
        Returns: void

        Affects: sets self.madFile, self.madDBObj
        """
        if os.access(madFile, os.R_OK):
            self.madFile = madFile
        else:
            raise IOError('unable to read %s' % (str(madFile)))

        if madDBObj == None:
            self.madDBObj = madrigal.metadata.MadrigalDB()
        else:
            self.madDBObj = madDBObj


    def plotAllScans(self,
                     scanType,
                     fullFilenameTemplate,
                     plotParm,
                     size = 'small',
                     xMinimum = None,
                     xMaximum = None,
                     yMinimum = None,
                     yMaximum = None,
                     xGridSize = None,
                     yGridSize = None,
                     minColormap = None,
                     maxColormap = None,
                     colorMap = matplotlib.cm.jet,
                     maxNumLines = None,
                     filterStr = ''):
        """plotAllScans creates a series of az or el scans.

        Inputs:

            scanType - must be 'az' or 'el'

            fullFilename - full path of file containing pcolor plot to be saved. Each image
            created will have _#.png appended, with # starting at 0

            plotParm - mnemonic of parameter to be plotted.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

            xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

            yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

            yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

    	    maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
    	    
    	    filterStr - a filter string to pass to isprint.  Defaults to no filtering
    
    	Returns - a list of tuples, where each tuple has two items: 1. time string,
    	          2. metadata string in form for scanTab.txt.  List length = number
    	          of plots created
	"""
        
        if scanType not in ('az', 'el'):
            raise ValueError('scantype must be az or el, not %s' % (str(scanType)))

        self.scanType = scanType

        retList = []

        # handle ion velocity
        if plotParm.lower() == 'vo':
            cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
        else:
            cmap = matplotlib.cm.jet

        # create isprint string
        parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist' % (plotParm.strip())
        isprintStr = getIsprintString(self.madFile, parms, filterStr)

        try:
            scanList = self.createScanInfoList(isprintStr)
        except:
            traceback.print_exc()
            return (retList)

        # loop through each scan
        scanFailureCount = 0 # keep track of how many scans fail
        for i in range(len(scanList)):
            scanCount = i - scanFailureCount
            scanInfo = scanList[i]
            try:
                startDateStr = self.getDateStrFromUT(scanInfo[1])
            except:
                # no scans may have been found
                continue
            endDateStr = self.getTimeStrFromUT(scanInfo[4])
            # create title
            
            if scanType == 'el':
                if scanInfo[3] > scanInfo[6]:
                    ind = 'down'
                    direction = 1
                else:
                    ind = 'up'
                    direction = 0
                titleStr = 'El %s scan (%s) %s-%s, az=%i' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[2]))
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[3],
                                                                                                scanInfo[2],
                                                                                                scanInfo[4],
                                                                                                scanInfo[6],
                                                                                                scanInfo[5],
                                                                                                direction)
                retList.append((timeStr,metaStr))
                
                if scanInfo[7] == 'Gdlat':
                    xLabelStr = 'Geodetic latitude'
                    xGridSize = 1.0
                elif scanInfo[7] == 'Glon':
                    xLabelStr = 'Longitude'
                    xGridSize = 1.0
                elif scanInfo[7] == 'Gcdist':
                    xLabelStr = 'Ground distance in km'
                    xGridSize = 100.0

                xLabelStr = ''
                yLabelStr = 'Altitude (km)'
                if xGridSize == None:
                    xGridSize = 1.0
                if yGridSize == None:
                    yGridSize = 20.0
                
            else:
                if scanInfo[2] > scanInfo[5]:
                    ind = 'ccw'
                    direction = 1
                else:
                    ind = 'cw'
                    direction = 0
                titleStr = 'Az %s scan (%s) %s-%s, el=%i' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[3]))
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[2],
                                                                                                scanInfo[3],
                                                                                                scanInfo[4],
                                                                                                scanInfo[5],
                                                                                                scanInfo[6],
                                                                                                direction)

                retList.append((timeStr,metaStr))

                xLabelStr = 'Longitude'
                yLabelStr = 'Geodetic latitude'
                xGridSize = 1.0
                yGridSize = 1.0

            name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)

            try:
                madPcolorScan(scanInfo[0],
                              xGridSize,
                              yGridSize,
                              titleStr,
                              xLabelStr,
                              yLabelStr,
                              name,
                              minColormap = minColormap,
                              maxColormap = maxColormap,
                              colorMap = cmap)
            except:
                print('Problem creating scan %s' % (str(retList[-1])))
                traceback.print_exc()
                scanFailureCount += 1
                retList.remove(retList[-1])

        return (retList)
    
    
    def plotAllWedgeScans(self,
                     scanType,
                     fullFilenameTemplate,
                     plotParm,
                     size = 'small',
                     xMinimum = None,
                     xMaximum = None,
                     yMinimum = None,
                     yMaximum = None,
                     minColormap = None,
                     maxColormap = None,
                     colorMap = matplotlib.cm.jet,
                     maxNumLines = None,
                     filterStr = '',
                     addTitle = '',
                     includeKml = False,
                     radarName = None,
                     radarDesc = None):
        """plotAllWedgeScans creates a series of az or el scans.  Similar to plotAllScans, except produces
        fancier wedge plots with uniform scalling.

        Inputs:

            scanType - must be 'az' or 'el'

            fullFilename - full path of file containing pcolor plot to be saved. Each image
            created will have _#.png appended, with # starting at 0

            plotParm - mnemonic of parameter to be plotted.

            size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

            xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

            xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

            yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

            yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
            
            minColormap - minimum parameter value (defaults to lowest parameter value)

            maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                          both minColormap and maxColormap == None, autoscaling applied.

            colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

            maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
            
            filterStr - a filter string to pass to isprint.  Defaults to no filtering
            
            addTitle - a string with additional title.  If empty string (the default), no additional title string.
            
            includeKml - if True, create a corresponding kml file for every png file.  Same names
                    as png files, except extension = .kml.  If False, do not.
                    
            radarName - name to label radar placemark if kml generated.
            
            radarDesc - description text for radar placemark if kml generated
    
        Returns - a list of tuples, where each tuple has two items: 1. time string,
                  2. metadata string in form for scanTab.txt.  List length = number
                  of plots created
    """
        
        if scanType not in ('az', 'el'):
            raise ValueError('scantype must be az or el, not %s' % (str(scanType)))

        self.scanType = scanType
        
        if scanType == 'az':
            wedgeScanType = 'az'

        retList = []

        # handle ion velocity
        if plotParm.lower() == 'vo':
            cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
        else:
            cmap = matplotlib.cm.jet

        # create isprint string
        parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist,gdlatr,gdlonr,galtr,range,az1,az2,el1,el2' % (plotParm.strip())

        filterStr += ' filter=%s,, ' % (plotParm.strip())
        isprintStr = getIsprintString(self.madFile, parms, filterStr)

        try:
            scanList = self.createWedgeScanInfoList(isprintStr)
        except:
            traceback.print_exc()
            return (retList)
        
        gdlatr, gdlonr, galtr = self._getInstLocation(isprintStr)
        
        # create limit dictionary used to keep all scans of the same type with the same limits
        limitDict = self._getLimits(scanList, xMinimum, xMaximum, yMinimum, yMaximum)

        # loop through each scan
        scanFailureCount = 0 # keep track of how many scans fail
        for i in range(len(scanList)):
            scanCount = i - scanFailureCount
            scanInfo = scanList[i]
            try:
                startDateStr = self.getDateStrFromUT(scanInfo[1])
            except:
                # no scans may have been found
                continue
            endDateStr = self.getTimeStrFromUT(scanInfo[4])
            # create title
            
            if scanType == 'el':
                if scanInfo[3] > scanInfo[6]:
                    ind = 'down'
                    direction = 1
                else:
                    ind = 'up'
                    direction = 0
                if int(scanInfo[2]) == int(scanInfo[5]):
                    # no change in az
                    titleStr = 'El %s scan (%s) %s-%s, az=%i %s' % (plotParm,
                                                                    ind,
                                                                    startDateStr,
                                                                    endDateStr,
                                                                    int(scanInfo[2]),
                                                                    addTitle)
                else:
                    # az changed
                    titleStr = 'El (%i-%i) %s scan %s-%s, az=%i-%i %s' % (int(scanInfo[3]),
                                                                          int(scanInfo[6]),
                                                                          plotParm,
                                                                          startDateStr,
                                                                          endDateStr,
                                                                          int(scanInfo[2]),
                                                                          int(scanInfo[5]),
                                                                          addTitle)
                    
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[3],
                                                                                                scanInfo[2],
                                                                                                scanInfo[4],
                                                                                                scanInfo[6],
                                                                                                scanInfo[5],
                                                                                                direction)
                retList.append((timeStr,metaStr))
                
                if scanInfo[7] == 'Gdlat':
                    xLabelStr = 'Geodetic latitude'
                    wedgeScanType = 'el_lat'
                elif scanInfo[7] == 'Glon':
                    xLabelStr = 'Longitude'
                    wedgeScanType = 'el_lon'
                elif scanInfo[7] == 'Gcdist':
                    xLabelStr = 'Ground distance in km'
                    wedgeScanType = 'el_gcdist'

                xLabelStr = ''
                yLabelStr = 'Altitude (km)'
                
            else:
                if scanInfo[2] > scanInfo[5]:
                    ind = 'ccw'
                    direction = 1
                else:
                    ind = 'cw'
                    direction = 0
                titleStr = 'Az %s scan (%s) %s-%s, el=%i %s' % (plotParm,
                                                             ind,
                                                             startDateStr,
                                                             endDateStr,
                                                             int(scanInfo[3]),
                                                             addTitle)
                timeStr = '%s-%s' % (startDateStr, endDateStr)
                metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                                scanInfo[2],
                                                                                                scanInfo[3],
                                                                                                scanInfo[4],
                                                                                                scanInfo[5],
                                                                                                scanInfo[6],
                                                                                                direction)

                retList.append((timeStr,metaStr))

                xLabelStr = 'Longitude'
                yLabelStr = 'Geodetic latitude'

            name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)

            try:
                madPcolorWedgeScan(scanInfo[0],
                                   wedgeScanType,
                                   titleStr,
                                   xLabelStr,
                                   yLabelStr,
                                   name,
                                   xMinimum = limitDict[wedgeScanType][0],
                                   xMaximum = limitDict[wedgeScanType][1],
                                   yMinimum = limitDict[wedgeScanType][2],
                                   yMaximum = limitDict[wedgeScanType][3],
                                   minColormap = minColormap,
                                   maxColormap = maxColormap,
                                   colorMap = cmap)
                success = True
            except:
                print('Problem creating scan %s' % (str(retList[-1])))
                traceback.print_exc()
                scanFailureCount += 1
                retList.remove(retList[-1])
                success = False
                
            if includeKml and success:
                kmlName = name[:-3] + 'kml'
                try:
                    madPcolorWedgeKmlScan(scanInfo[0],
                                          wedgeScanType,
                                          kmlName,
                                          minColormap = minColormap,
                                          maxColormap = maxColormap,
                                          instName = radarName,
                                          instDesc = radarDesc,
                                          instLat = gdlatr,
                                          instLon = gdlonr,
                                          instAlt = galtr,
                                          colorMap = cmap)
                except:
                    print('Problem creating kml scan')
                    traceback.print_exc()
                                          
                

        return (retList)
    



    def getDateStrFromUT(self, ut):
        """getDateStrFromUT returns a date string formated as YYYY-MM-DD HH:MM:SS from a ut time
        (seconds since 1/1/1950)
        """
        timeList = madrigal._derive.getDateFromUt(float(ut))
        return '%04i-%02i-%02i %02i:%02i:%02i' % (timeList[0],
                                                  timeList[1],
                                                  timeList[2],
                                                  timeList[3],
                                                  timeList[4],
                                                  timeList[5])


    def getTimeStrFromUT(self, ut):
        """getTimeStrFromUT returns a time string formated as HH:MM:SS from a ut time
        (seconds since 1/1/1950)
        """
        timeList = madrigal._derive.getDateFromUt(float(ut))
        return '%02i:%02i:%02i' % (timeList[3],
                                   timeList[4],
                                   timeList[5])



    def createScanInfoList(self, isprintStr):
        """createScanInfoList creates a list of tuples, which each tuple representing a single scan, and
        values of:

        (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
        minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  Isprint string
        has values (x,y,plotParm), where for az scan x=lon, y=lat, and for el scan y=alt, x=lat if starting
        az within 15 degrees of north or south, x=lon if starting az within 15 degrees of east or west,
        of x=gcdist if other az. Type is Gdlat or Glon or Gcdist for el scans, None for az scans.

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist

        Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or one 
        north-south scans, zero or one east-west scans, and zero or more off azimuth scans.  An off azimuth
        scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
        scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
        sign.
        """
        isprintItems = isprintStr.split()

        retList = []

        # loop variables
        thisIsprintStr = []
        cycNum = None
        startUT = []
        startAz = []
        startEl = []
        endUT = []
        endAz = []
        endEl = []
        elScan = []
        minGdlat = []
        maxGdlat = []
        minGlon = []
        maxGlon = []
        minGdalt = []
        maxGdalt = []
        minGcdist = []
        maxGcdist = []
        thisAzDirection = None # used to detect new azimuth scans
        lastAzDirection = None # used to detect new azimuth scans
        lastEl = None # used to detect new azimuth scans
        thisAz = None
        lastUt = None # keep track of new ut
        isNewUt = None # true only when ut changes
        inAzScan = False
        

        for i in range(len(isprintItems)):
            item = isprintItems[i]
            mod = i % 10
            if mod == 0:
                thisUt = float(item)
                if lastUt == None:
                    lastUt = thisUt
            elif mod == 1:
                thisScntyp = int(item)
            elif mod == 2:
                thisCycn = int(float(item))
                if cycNum == None:
                    cycNum = thisCycn
            elif mod == 3:
                thisParm = item
            elif mod == 4:
                thisGdalt = float(item)
            elif mod == 5:
                thisGdlat = float(item)
            elif mod == 6:
                thisGlon = float(item)
            elif mod == 7:
                if thisAz != None and lastUt < thisUt and inAzScan:
                    if thisAz > float(item):
                        thisAzDirection = 'cw'
                    elif thisAz < float(item):
                        thisAzDirection = 'ccw'
                thisAz = float(item)
            elif mod == 8:
                thisEl = float(item)
            elif mod == 9:
                thisGcdist = float(item)
                
                # check whether this is a new time
                if lastUt < thisUt:
                    isNewUt = True
                    lastUt = thisUt
                else:
                    isNewUt = False

                # if new cycle, close out value if data exists
                if thisCycn != cycNum:
                    for i in range(len(thisIsprintStr)):
                        if len(thisIsprintStr[i]) == 0:
                            continue
                        retList.append((thisIsprintStr[i],
                                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                        minGcdist[i], maxGcdist[i]))
                    thisIsprintStr = []
                    cycNum = None
                    startUT = []
                    startAz = []
                    startEl = []
                    endUT = []
                    endAz = []
                    endEl = []
                    elScan = []
                    minGdlat = []
                    maxGdlat = []
                    minGlon = []
                    maxGlon = []
                    minGdalt = []
                    maxGdalt = []
                    minGcdist = []
                    maxGcdist = []
                    thisAzDirection = None
                    lastAzDirection = None
                    lastEl = None
                    inAzScan = False

                # end of line - process it
                if self.scanType == 'el' and thisScntyp == 3:
                    # add this elevation scan point
                    # check type of el scan
                    inAzScan = False
                    if -15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0:
                        thisElScan = 'Gdlat'
                    elif 75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0:
                        thisElScan = 'Glon'
                    else:
                        thisElScan = 'Gcdist'
                    # get index of this scan, None if a new scan
                    index = None
                    for i, item in enumerate(elScan):
                        if item == 'Gdlat' and thisElScan == 'Gdlat':
                            index = i
                            break
                        elif item == 'Glon' and thisElScan == 'Glon':
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                            index = i
                            break
                    if index == None:
                        # a new scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(thisElScan)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                            
                    endUT[index] = thisUt
                    endAz[index] = thisAz
                    endEl[index] = thisEl
                    if thisElScan == 'Gdlat':
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGdlat, thisGdalt, thisParm)
                    elif thisElScan == 'Glon':
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGlon, thisGdalt, thisParm)
                    else:
                        thisIsprintStr[index] += '%f %f %s\n' % (thisGcdist, thisGdalt, thisParm)

                elif self.scanType == 'az' and thisScntyp == 2:
                    # az scan
                    inAzScan = True
                    # add this azimuth scan point
                    # check if this is a new scan
                    newAzScan = False
                    if lastEl == None:
                        newAzScan = True
                    # check for elevation change
                    elif abs(lastEl - int(thisEl)) > 3:
                        newAzScan = True
                    # check for direction change
                    if lastAzDirection != None:
                        if lastAzDirection != thisAzDirection:
                            newAzScan = True
                            thisAzDirection = None
                    if newAzScan:
                        # a new azimuth scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(None)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        lastEl = int(thisEl)
                        lastAzDirection = None
                    else:
                        # this is at least the second line of the scan
                        if not thisAzDirection is None:
                            lastAzDirection = thisAzDirection
                            
                    endUT[-1] = thisUt
                    endAz[-1] = thisAz
                    endEl[-1] = thisEl
                    thisIsprintStr[-1] += '%f %f %s\n' % (thisGlon, thisGdlat, thisParm)
                    
                else:
                    # not a scan line
                    inAzScan = False
                    continue

                if minGdlat[index] == None:
                    minGdlat[index] = thisGdlat
                    maxGdlat[index] = thisGdlat
                    minGlon[index] = thisGlon
                    maxGlon[index] = thisGlon
                    minGdalt[index] = thisGdalt
                    maxGdalt[index] = thisGdalt
                    minGcdist[index] = thisGcdist
                    maxGcdist[index] = thisGcdist
                else:
                    if minGdlat[index] > thisGdlat:
                        minGdlat[index] = thisGdlat
                    if maxGdlat[index] < thisGdlat:
                        maxGdlat[index] = thisGdlat
                    if minGlon[index] > thisGlon:
                        minGlon[index] = thisGlon
                    if maxGlon[index] < thisGlon:
                        maxGlon[index] = thisGlon
                    if minGdalt[index] > thisGdalt:
                        minGdalt[index] = thisGdalt
                    if maxGdalt[index] < thisGdalt:
                        maxGdalt[index] = thisGdalt
                    if minGcdist[index] > thisGcdist:
                        minGcdist[index] = thisGcdist
                    if maxGcdist[index] < thisGcdist:
                        maxGcdist[index] = thisGcdist

        # end, close out last cycle
        for i in range(len(thisIsprintStr)):
            if len(thisIsprintStr[i]) == 0:
                continue
            retList.append((thisIsprintStr[i],
                            startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                            minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                            minGcdist[i], maxGcdist[i]))

        return(retList)
    
    
    def createWedgeScanInfoList(self, isprintStr):
        """createWedgeScanInfoList creates a list of tuples, which each tuple representing a single scan, and
        values of:

        (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
        minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist,
        gdlatr,gdlonr,galtr,range,az1,az2,el1,el2

        Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or more 
        north-south scans, zero or more east-west scans, and zero or more off azimuth scans.  A south to north
        elevation scan or a west to east elevation scan is called clockwise, and a switch from clockwise
        to counterclockwise will create a new elevation scan. An off azimuth
        scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
        scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
        sign.
        """
        isprintItems = isprintStr.split()

        retList = []

        # loop variables
        thisIsprintStr = []
        cycNum = None
        startUT = []
        startAz = []
        startEl = []
        endUT = []
        endAz = []
        endEl = []
        elScan = []
        minGdlat = []
        maxGdlat = []
        minGlon = []
        maxGlon = []
        minGdalt = []
        maxGdalt = []
        minGcdist = []
        maxGcdist = []
        thisAzDirection = None # used to detect new azimuth scans
        lastAzDirection = None # used to detect new azimuth scans
        thisElDirection = None # used to detect new elevation scans (cw or ccw)
        lastElDirection = None # used to detect new elevation scans (cw or ccw)
        lastEl = None # used to detect new azimuth scans
        thisAz = None
        thisEl = None
        lastUt = None # keep track of new ut
        isNewUt = None # true only when ut changes
        inAzScan = False
        inElScan = False
        

        for i in range(len(isprintItems)):
            item = isprintItems[i]
            mod = i % 18
            if mod == 0:
                thisUt = float(item)
                if lastUt == None:
                    lastUt = thisUt
            elif mod == 1:
                thisScntyp = int(item)
            elif mod == 2:
                thisCycn = int(float(item))
                if cycNum == None:
                    cycNum = thisCycn
            elif mod == 3:
                thisParm = item
            elif mod == 4:
                thisGdalt = float(item)
            elif mod == 5:
                thisGdlat = float(item)
            elif mod == 6:
                thisGlon = float(item)
            elif mod == 7:
                if thisAz != None and lastUt < thisUt and inAzScan:
                    if thisAz > float(item):
                        thisAzDirection = 'cw'
                    elif thisAz < float(item):
                        thisAzDirection = 'ccw'
                thisAz = float(item)
            elif mod == 8:
                if thisEl != None and inElScan:
                    if abs(float(item) - thisEl) > 1.0:
                        if thisElScan == 'Gdlat':
                            if -165.0 >= thisAz or thisAz >= 165.0:
                                # southern scan
                                if float(item) > thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                            else:
                                # northern scan
                                if float(item) < thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                        elif thisElScan == 'Glon':
                            if -105.0 <= thisAz <= -75.0:
                                # western scan
                                if float(item) > thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                            else:
                                # eastern scan
                                if float(item) < thisEl:
                                    thisElDirection = 'cw'
                                else:
                                    thisElDirection = 'ccw'
                        elif thisElScan == 'Gcdist':
                            if float(item) > thisEl:
                                thisElDirection = 'up'
                            else:
                                thisElDirection = 'down'

                thisEl = float(item)
                
            elif mod == 9:
                thisGcdist = float(item)
            elif mod == 10:
                thisGdlatr = float(item)
            elif mod == 11:
                thisGdlonr = float(item)
            elif mod == 12:
                thisGaltr = float(item)
            elif mod == 13:
                thisRange = float(item)
            elif mod == 14:
                thisAz1 = float(item)
            elif mod == 15:
                thisAz2 = float(item)
            elif mod == 16:
                thisEl1 = float(item)
            elif mod == 17:
                thisEl2 = float(item)
                
                # check whether this is a new time
                if lastUt < thisUt:
                    isNewUt = True
                    lastUt = thisUt
                else:
                    isNewUt = False

                # if new cycle or elevation issue, close out value if data exists
                if (thisCycn != cycNum) or \
                    (thisElDirection == 'cw' and lastElDirection == 'ccw') or \
                    (thisElDirection == 'ccw' and lastElDirection == 'cw') or \
                    (thisElDirection == 'down' and lastElDirection == 'up') or \
                    (thisElDirection == 'up' and lastElDirection == 'down') or \
                    (thisElDirection != None and not inElScan) or \
                    (thisAzDirection != None and not inAzScan):
                    
                    for i in range(len(thisIsprintStr)):
                        if len(thisIsprintStr[i]) == 0:
                            continue
                        retList.append((thisIsprintStr[i],
                                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                        minGcdist[i], maxGcdist[i]))
                    thisIsprintStr = []
                    cycNum = None
                    startUT = []
                    startAz = []
                    startEl = []
                    endUT = []
                    endAz = []
                    endEl = []
                    elScan = []
                    minGdlat = []
                    maxGdlat = []
                    minGlon = []
                    maxGlon = []
                    minGdalt = []
                    maxGdalt = []
                    minGcdist = []
                    maxGcdist = []
                    thisAzDirection = None
                    lastAzDirection = None
                    thisElDirection = None # used to detect new elevation scans (cw or ccw)
                    lastElDirection = None 
                    lastEl = None
                    inAzScan = False
                    inElScan = False

                # end of line - process it
                if self.scanType == 'el' and thisScntyp == 3:
                    # add this elevation scan point
                    # check type of el scan
                    inAzScan = False
                    inElScan = True
                    if (-15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0) and \
                        abs(thisAz1 - thisAz2) < 0.5:
                        thisElScan = 'Gdlat'
                    elif (75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0) and \
                        abs(thisAz1 - thisAz2) < 0.5:
                        thisElScan = 'Glon'
                    else:
                        thisElScan = 'Gcdist'
                    # get index of this scan, None if a new scan
                    index = None
                    for i, item in enumerate(elScan):
                        
                        if item == 'Gdlat' and thisElScan == 'Gdlat':
                            index = i
                            break
                        elif item == 'Glon' and thisElScan == 'Glon':
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                            index = i
                            break
                        elif item == 'Gcdist' and thisElScan == 'Gcdist' and i == len(elScan)-1:
                            index = i
                            break
                    
                    if index == None:
                        # a new scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(thisElScan)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        
                    lastElDirection = thisElDirection  
                    endUT[index] = thisUt
                    endAz[index] = thisAz
                    endEl[index] = thisEl
                    # gdlatr, gdlonr, galtr, range, az1, az2, el1, el2,plotParm
                    thisIsprintStr[index] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                               thisRange, thisAz1, thisAz2,
                                                                               thisEl1, thisEl2, thisParm)

                elif self.scanType == 'az' and thisScntyp == 2:
                    # az scan
                    inAzScan = True
                    inElScan = False
                    # add this azimuth scan point
                    # check if this is a new scan
                    newAzScan = False
                    if lastEl == None:
                        newAzScan = True
                    # check for elevation change
                    elif abs(lastEl - int(thisEl)) > 3:
                        newAzScan = True
                    # check for direction change
                    if lastAzDirection != None:
                        if lastAzDirection != thisAzDirection:
                            newAzScan = True
                            thisAzDirection = None
                    if newAzScan:
                        # a new azimuth scan has been found
                        thisIsprintStr.append('')
                        startUT.append(thisUt)
                        startAz.append(thisAz)
                        startEl.append(thisEl)
                        endUT.append(None)
                        endAz.append(None)
                        endEl.append(None)
                        elScan.append(None)
                        minGdlat.append(None)
                        maxGdlat.append(None)
                        minGlon.append(None)
                        maxGlon.append(None)
                        minGdalt.append(None)
                        maxGdalt.append(None)
                        minGcdist.append(None)
                        maxGcdist.append(None)
                        index = len(thisIsprintStr) - 1
                        lastEl = int(thisEl)
                        lastAzDirection = None
                    else:
                        # this is at least the second line of the scan
                        if not thisAzDirection is None:
                            lastAzDirection = thisAzDirection
                            
                    endUT[-1] = thisUt
                    endAz[-1] = thisAz
                    endEl[-1] = thisEl
                    thisIsprintStr[-1] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                               thisRange, thisAz1, thisAz2,
                                                                               thisEl1, thisEl2, thisParm)
                    
                else:
                    # not a scan line
                    inAzScan = False
                    inElScan = False
                    continue

                if minGdlat[index] == None:
                    minGdlat[index] = thisGdlat
                    maxGdlat[index] = thisGdlat
                    minGlon[index] = thisGlon
                    maxGlon[index] = thisGlon
                    minGdalt[index] = thisGdalt
                    maxGdalt[index] = thisGdalt
                    minGcdist[index] = thisGcdist
                    maxGcdist[index] = thisGcdist
                else:
                    if minGdlat[index] > thisGdlat:
                        minGdlat[index] = thisGdlat
                    if maxGdlat[index] < thisGdlat:
                        maxGdlat[index] = thisGdlat
                    if minGlon[index] > thisGlon:
                        minGlon[index] = thisGlon
                    if maxGlon[index] < thisGlon:
                        maxGlon[index] = thisGlon
                    if minGdalt[index] > thisGdalt:
                        minGdalt[index] = thisGdalt
                    if maxGdalt[index] < thisGdalt:
                        maxGdalt[index] = thisGdalt
                    if minGcdist[index] > thisGcdist:
                        minGcdist[index] = thisGcdist
                    if maxGcdist[index] < thisGcdist:
                        maxGcdist[index] = thisGcdist

        # end, close out last cycle
        for i in range(len(thisIsprintStr)):
            if len(thisIsprintStr[i]) == 0:
                continue
            retList.append((thisIsprintStr[i],
                            startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                            minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                            minGcdist[i], maxGcdist[i]))

        return(retList)
    
    
    def _getLimits(self, scanList, xMinimum, xMaximum, yMinimum, yMaximum):
        """_getLimits returns a dictionary with keys that may include ('az', 'el_lat', 'el_lon', 'el_gcdist'),
        though not all will neccessarily be there.  Values are overall tuple of
        (xMinimum, xMaximum, yMinimum, yMaximum). These values are set by the input
        arguments unless it is None, in which case the scanList limits set the values.
        
        Inputs: 
        
            scanList: a list of tuples, which each tuple representing a single scan, and
                has values of: (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
                minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  Isprint string
                has values (gdlatr, gdlonr, galtr, range, az1, az2, el1, el2,plotParm),. Type is Gdlat or Glon or 
                Gcdist for el scans, None for az scans.
                
            xMinimum, xMaximum, yMinimum, yMaximum - as passed into scanPlotter
        """
        retDict = {}
        
        xMinAz = None
        xMaxAz = None
        yMinAz = None
        yMaxAz = None
        xMinElLat = None
        xMaxElLat = None
        yMinElLat = None
        yMaxElLat = None
        xMinElLon = None
        xMaxElLon = None
        yMinElLon = None
        yMaxElLon = None
        xMinElGcdist = None
        xMaxElGcdist = None
        yMinElGcdist = None
        yMaxElGcdist = None
        minValue = None
        maxValue = None
        
        for scanItem in scanList:
            if scanItem[7] == None: # az scan
                if xMinAz == None:
                    xMinAz = scanItem[10]
                elif xMinAz > scanItem[10]:
                    xMinAz = scanItem[10]
                if xMaxAz == None:
                    xMaxAz = scanItem[11]
                elif xMaxAz < scanItem[11]:
                    xMaxAz = scanItem[11]
                if yMinAz == None:
                    yMinAz = scanItem[8]
                elif yMinAz > scanItem[8]:
                    yMinAz = scanItem[8]
                if yMaxAz == None:
                    yMaxAz = scanItem[9]
                elif yMaxAz < scanItem[9]:
                    yMaxAz = scanItem[9]
            elif scanItem[7] == 'Gdlat': # el_lat scan
                if xMinElLat == None:
                    xMinElLat = scanItem[8]
                elif xMinElLat > scanItem[8]:
                    xMinElLat = scanItem[8]
                if xMaxElLat == None:
                    xMaxElLat = scanItem[9]
                elif xMaxElLat < scanItem[9]:
                    xMaxElLat = scanItem[9]
                if yMinElLat == None:
                    yMinElLat = scanItem[12]
                elif yMinElLat > scanItem[12]:
                    yMinElLat = scanItem[12]
                if yMaxElLat == None:
                    yMaxElLat = scanItem[13]
                elif yMaxElLat < scanItem[13]:
                    yMaxElLat = scanItem[13]
            elif scanItem[7] == 'Glon': # el_lon scan
                if xMinElLon == None:
                    xMinElLon = scanItem[10]
                elif xMinElLon > scanItem[10]:
                    xMinElLon = scanItem[10]
                if xMaxElLon == None:
                    xMaxElLon = scanItem[11]
                elif xMaxElLon < scanItem[11]:
                    xMaxElLon = scanItem[11]
                if yMinElLon == None:
                    yMinElLon = scanItem[12]
                elif yMinElLon > scanItem[12]:
                    yMinElLon = scanItem[12]
                if yMaxElLon == None:
                    yMaxElLon = scanItem[13]
                elif yMaxElLon < scanItem[13]:
                    yMaxElLon = scanItem[13]
            elif scanItem[7] == 'Gcdist': # el_gcdist scan
                if xMinElGcdist == None:
                    xMinElGcdist = scanItem[14]
                elif xMinElGcdist > scanItem[14]:
                    xMinElGcdist = scanItem[14]
                if xMaxElGcdist == None:
                    xMaxElGcdist = scanItem[15]
                elif xMaxElGcdist < scanItem[15]:
                    xMaxElGcdist = scanItem[15]
                if yMinElGcdist == None:
                    yMinElGcdist = scanItem[12]
                elif yMinElGcdist > scanItem[12]:
                    yMinElGcdist = scanItem[12]
                if yMaxElGcdist == None:
                    yMaxElGcdist = scanItem[13]
                elif yMaxElGcdist < scanItem[13]:
                    yMaxElGcdist = scanItem[13]
                    
        # fill out the dictionary
        if xMinAz != None:
            if xMinimum == None:
                xMin = xMinAz
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxAz
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinAz
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxAz
            else:
                yMax = yMaximum
            retDict['az'] = (xMin, xMax, yMin, yMax)
            
        if xMinElLat != None:
            if xMinimum == None:
                xMin = xMinElLat
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElLat
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElLat
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElLat
            else:
                yMax = yMaximum
            retDict['el_lat'] = (xMin, xMax, yMin, yMax)
            
        if xMinElLon != None:
            if xMinimum == None:
                xMin = xMinElLon
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElLon
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElLon
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElLon
            else:
                yMax = yMaximum
            retDict['el_lon'] = (xMin, xMax, yMin, yMax)
            
        if xMinElGcdist != None:
            if xMinimum == None:
                xMin = xMinElGcdist
            else:
                xMin = xMinimum
            if xMaximum == None:
                xMax = xMaxElGcdist
            else:
                xMax = xMaximum
            if yMinimum == None:
                yMin = yMinElGcdist
            else:
                yMin = yMinimum
            if yMaximum == None:
                yMax = yMaxElGcdist
            else:
                yMax = yMaximum
            retDict['el_gcdist'] = (xMin, xMax, yMin, yMax)
            
        return(retDict)
    
    
    def _getInstLocation(self, isprintStr):
        """_getInstLocation returns a tuple of instrument location (gdlatr, gdlonr, galtr)

        Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist,
        gdlatr,gdlonr,galtr,range,az1,az2,el1,el2
        """
        isprintItems = isprintStr.split()
        gdlatr = float(isprintItems[10])
        gdlonr = float(isprintItems[11])
        if gdlonr > 180.0:
            gdlonr -= 360.0
        galtr = float(isprintItems[12])
        return((gdlatr, gdlonr, galtr))

Ancestors (in MRO)

Static methods

def __init__(

self, madFile, madDBObj=None)

init initializes a scanPlotter object.

Inputs:

madFile - the Madrigal file to be analyzed.  Must contain SCNTYP and CYCN parameters.

madDBObj - a madrigal.metadata.MadrigalDB object.  If None (default),
one created

Returns: void

Affects: sets self.madFile, self.madDBObj

def __init__(self, madFile,
             madDBObj=None):
    """__init__ initializes a scanPlotter object.
    Inputs:
        madFile - the Madrigal file to be analyzed.  Must contain SCNTYP and CYCN parameters.
        madDBObj - a madrigal.metadata.MadrigalDB object.  If None (default),
        one created
           
    Returns: void
    Affects: sets self.madFile, self.madDBObj
    """
    if os.access(madFile, os.R_OK):
        self.madFile = madFile
    else:
        raise IOError('unable to read %s' % (str(madFile)))
    if madDBObj == None:
        self.madDBObj = madrigal.metadata.MadrigalDB()
    else:
        self.madDBObj = madDBObj

def createScanInfoList(

self, isprintStr)

createScanInfoList creates a list of tuples, which each tuple representing a single scan, and values of:

(isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type, minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist). Isprint string has values (x,y,plotParm), where for az scan x=lon, y=lat, and for el scan y=alt, x=lat if starting az within 15 degrees of north or south, x=lon if starting az within 15 degrees of east or west, of x=gcdist if other az. Type is Gdlat or Glon or Gcdist for el scans, None for az scans.

Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist

Azimuth scans are split whenever direction or elevation changes. For elevation scans, there will be zero or one north-south scans, zero or one east-west scans, and zero or more off azimuth scans. An off azimuth scan is not within 15 degrees of north, south, east or west. Off azimuth scans are not combined with scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse sign.

def createScanInfoList(self, isprintStr):
    """createScanInfoList creates a list of tuples, which each tuple representing a single scan, and
    values of:
    (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
    minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  Isprint string
    has values (x,y,plotParm), where for az scan x=lon, y=lat, and for el scan y=alt, x=lat if starting
    az within 15 degrees of north or south, x=lon if starting az within 15 degrees of east or west,
    of x=gcdist if other az. Type is Gdlat or Glon or Gcdist for el scans, None for az scans.
    Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist
    Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or one 
    north-south scans, zero or one east-west scans, and zero or more off azimuth scans.  An off azimuth
    scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
    scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
    sign.
    """
    isprintItems = isprintStr.split()
    retList = []
    # loop variables
    thisIsprintStr = []
    cycNum = None
    startUT = []
    startAz = []
    startEl = []
    endUT = []
    endAz = []
    endEl = []
    elScan = []
    minGdlat = []
    maxGdlat = []
    minGlon = []
    maxGlon = []
    minGdalt = []
    maxGdalt = []
    minGcdist = []
    maxGcdist = []
    thisAzDirection = None # used to detect new azimuth scans
    lastAzDirection = None # used to detect new azimuth scans
    lastEl = None # used to detect new azimuth scans
    thisAz = None
    lastUt = None # keep track of new ut
    isNewUt = None # true only when ut changes
    inAzScan = False
    
    for i in range(len(isprintItems)):
        item = isprintItems[i]
        mod = i % 10
        if mod == 0:
            thisUt = float(item)
            if lastUt == None:
                lastUt = thisUt
        elif mod == 1:
            thisScntyp = int(item)
        elif mod == 2:
            thisCycn = int(float(item))
            if cycNum == None:
                cycNum = thisCycn
        elif mod == 3:
            thisParm = item
        elif mod == 4:
            thisGdalt = float(item)
        elif mod == 5:
            thisGdlat = float(item)
        elif mod == 6:
            thisGlon = float(item)
        elif mod == 7:
            if thisAz != None and lastUt < thisUt and inAzScan:
                if thisAz > float(item):
                    thisAzDirection = 'cw'
                elif thisAz < float(item):
                    thisAzDirection = 'ccw'
            thisAz = float(item)
        elif mod == 8:
            thisEl = float(item)
        elif mod == 9:
            thisGcdist = float(item)
            
            # check whether this is a new time
            if lastUt < thisUt:
                isNewUt = True
                lastUt = thisUt
            else:
                isNewUt = False
            # if new cycle, close out value if data exists
            if thisCycn != cycNum:
                for i in range(len(thisIsprintStr)):
                    if len(thisIsprintStr[i]) == 0:
                        continue
                    retList.append((thisIsprintStr[i],
                                    startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                    minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                    minGcdist[i], maxGcdist[i]))
                thisIsprintStr = []
                cycNum = None
                startUT = []
                startAz = []
                startEl = []
                endUT = []
                endAz = []
                endEl = []
                elScan = []
                minGdlat = []
                maxGdlat = []
                minGlon = []
                maxGlon = []
                minGdalt = []
                maxGdalt = []
                minGcdist = []
                maxGcdist = []
                thisAzDirection = None
                lastAzDirection = None
                lastEl = None
                inAzScan = False
            # end of line - process it
            if self.scanType == 'el' and thisScntyp == 3:
                # add this elevation scan point
                # check type of el scan
                inAzScan = False
                if -15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0:
                    thisElScan = 'Gdlat'
                elif 75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0:
                    thisElScan = 'Glon'
                else:
                    thisElScan = 'Gcdist'
                # get index of this scan, None if a new scan
                index = None
                for i, item in enumerate(elScan):
                    if item == 'Gdlat' and thisElScan == 'Gdlat':
                        index = i
                        break
                    elif item == 'Glon' and thisElScan == 'Glon':
                        index = i
                        break
                    elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                        index = i
                        break
                if index == None:
                    # a new scan has been found
                    thisIsprintStr.append('')
                    startUT.append(thisUt)
                    startAz.append(thisAz)
                    startEl.append(thisEl)
                    endUT.append(None)
                    endAz.append(None)
                    endEl.append(None)
                    elScan.append(thisElScan)
                    minGdlat.append(None)
                    maxGdlat.append(None)
                    minGlon.append(None)
                    maxGlon.append(None)
                    minGdalt.append(None)
                    maxGdalt.append(None)
                    minGcdist.append(None)
                    maxGcdist.append(None)
                    index = len(thisIsprintStr) - 1
                        
                endUT[index] = thisUt
                endAz[index] = thisAz
                endEl[index] = thisEl
                if thisElScan == 'Gdlat':
                    thisIsprintStr[index] += '%f %f %s\n' % (thisGdlat, thisGdalt, thisParm)
                elif thisElScan == 'Glon':
                    thisIsprintStr[index] += '%f %f %s\n' % (thisGlon, thisGdalt, thisParm)
                else:
                    thisIsprintStr[index] += '%f %f %s\n' % (thisGcdist, thisGdalt, thisParm)
            elif self.scanType == 'az' and thisScntyp == 2:
                # az scan
                inAzScan = True
                # add this azimuth scan point
                # check if this is a new scan
                newAzScan = False
                if lastEl == None:
                    newAzScan = True
                # check for elevation change
                elif abs(lastEl - int(thisEl)) > 3:
                    newAzScan = True
                # check for direction change
                if lastAzDirection != None:
                    if lastAzDirection != thisAzDirection:
                        newAzScan = True
                        thisAzDirection = None
                if newAzScan:
                    # a new azimuth scan has been found
                    thisIsprintStr.append('')
                    startUT.append(thisUt)
                    startAz.append(thisAz)
                    startEl.append(thisEl)
                    endUT.append(None)
                    endAz.append(None)
                    endEl.append(None)
                    elScan.append(None)
                    minGdlat.append(None)
                    maxGdlat.append(None)
                    minGlon.append(None)
                    maxGlon.append(None)
                    minGdalt.append(None)
                    maxGdalt.append(None)
                    minGcdist.append(None)
                    maxGcdist.append(None)
                    index = len(thisIsprintStr) - 1
                    lastEl = int(thisEl)
                    lastAzDirection = None
                else:
                    # this is at least the second line of the scan
                    if not thisAzDirection is None:
                        lastAzDirection = thisAzDirection
                        
                endUT[-1] = thisUt
                endAz[-1] = thisAz
                endEl[-1] = thisEl
                thisIsprintStr[-1] += '%f %f %s\n' % (thisGlon, thisGdlat, thisParm)
                
            else:
                # not a scan line
                inAzScan = False
                continue
            if minGdlat[index] == None:
                minGdlat[index] = thisGdlat
                maxGdlat[index] = thisGdlat
                minGlon[index] = thisGlon
                maxGlon[index] = thisGlon
                minGdalt[index] = thisGdalt
                maxGdalt[index] = thisGdalt
                minGcdist[index] = thisGcdist
                maxGcdist[index] = thisGcdist
            else:
                if minGdlat[index] > thisGdlat:
                    minGdlat[index] = thisGdlat
                if maxGdlat[index] < thisGdlat:
                    maxGdlat[index] = thisGdlat
                if minGlon[index] > thisGlon:
                    minGlon[index] = thisGlon
                if maxGlon[index] < thisGlon:
                    maxGlon[index] = thisGlon
                if minGdalt[index] > thisGdalt:
                    minGdalt[index] = thisGdalt
                if maxGdalt[index] < thisGdalt:
                    maxGdalt[index] = thisGdalt
                if minGcdist[index] > thisGcdist:
                    minGcdist[index] = thisGcdist
                if maxGcdist[index] < thisGcdist:
                    maxGcdist[index] = thisGcdist
    # end, close out last cycle
    for i in range(len(thisIsprintStr)):
        if len(thisIsprintStr[i]) == 0:
            continue
        retList.append((thisIsprintStr[i],
                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                        minGcdist[i], maxGcdist[i]))
    return(retList)

def createWedgeScanInfoList(

self, isprintStr)

createWedgeScanInfoList creates a list of tuples, which each tuple representing a single scan, and values of:

(isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type, minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).

Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist, gdlatr,gdlonr,galtr,range,az1,az2,el1,el2

Azimuth scans are split whenever direction or elevation changes. For elevation scans, there will be zero or more north-south scans, zero or more east-west scans, and zero or more off azimuth scans. A south to north elevation scan or a west to east elevation scan is called clockwise, and a switch from clockwise to counterclockwise will create a new elevation scan. An off azimuth scan is not within 15 degrees of north, south, east or west. Off azimuth scans are not combined with scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse sign.

def createWedgeScanInfoList(self, isprintStr):
    """createWedgeScanInfoList creates a list of tuples, which each tuple representing a single scan, and
    values of:
    (isprintString, startUT, startAz, startEl, endUT, endAz, endEl, type,
    minGdlat, maxGdlat,minGlon, maxGdlon,minGdalt, maxGdalt, minGcdist, maxGcdist).  
    Input isprint string has parameters ut1,scntyp,cycn,,gdalt,gdlat,glon,azm,elm,gcdist,
    gdlatr,gdlonr,galtr,range,az1,az2,el1,el2
    Azimuth scans are split whenever direction or elevation changes.  For elevation scans, there will be zero or more 
    north-south scans, zero or more east-west scans, and zero or more off azimuth scans.  A south to north
    elevation scan or a west to east elevation scan is called clockwise, and a switch from clockwise
    to counterclockwise will create a new elevation scan. An off azimuth
    scan is not within 15 degrees of north, south, east or west.  Off azimuth scans are not combined with
    scans 180 degrees in the other direction, because the x axis is ground distance, which does not reverse
    sign.
    """
    isprintItems = isprintStr.split()
    retList = []
    # loop variables
    thisIsprintStr = []
    cycNum = None
    startUT = []
    startAz = []
    startEl = []
    endUT = []
    endAz = []
    endEl = []
    elScan = []
    minGdlat = []
    maxGdlat = []
    minGlon = []
    maxGlon = []
    minGdalt = []
    maxGdalt = []
    minGcdist = []
    maxGcdist = []
    thisAzDirection = None # used to detect new azimuth scans
    lastAzDirection = None # used to detect new azimuth scans
    thisElDirection = None # used to detect new elevation scans (cw or ccw)
    lastElDirection = None # used to detect new elevation scans (cw or ccw)
    lastEl = None # used to detect new azimuth scans
    thisAz = None
    thisEl = None
    lastUt = None # keep track of new ut
    isNewUt = None # true only when ut changes
    inAzScan = False
    inElScan = False
    
    for i in range(len(isprintItems)):
        item = isprintItems[i]
        mod = i % 18
        if mod == 0:
            thisUt = float(item)
            if lastUt == None:
                lastUt = thisUt
        elif mod == 1:
            thisScntyp = int(item)
        elif mod == 2:
            thisCycn = int(float(item))
            if cycNum == None:
                cycNum = thisCycn
        elif mod == 3:
            thisParm = item
        elif mod == 4:
            thisGdalt = float(item)
        elif mod == 5:
            thisGdlat = float(item)
        elif mod == 6:
            thisGlon = float(item)
        elif mod == 7:
            if thisAz != None and lastUt < thisUt and inAzScan:
                if thisAz > float(item):
                    thisAzDirection = 'cw'
                elif thisAz < float(item):
                    thisAzDirection = 'ccw'
            thisAz = float(item)
        elif mod == 8:
            if thisEl != None and inElScan:
                if abs(float(item) - thisEl) > 1.0:
                    if thisElScan == 'Gdlat':
                        if -165.0 >= thisAz or thisAz >= 165.0:
                            # southern scan
                            if float(item) > thisEl:
                                thisElDirection = 'cw'
                            else:
                                thisElDirection = 'ccw'
                        else:
                            # northern scan
                            if float(item) < thisEl:
                                thisElDirection = 'cw'
                            else:
                                thisElDirection = 'ccw'
                    elif thisElScan == 'Glon':
                        if -105.0 <= thisAz <= -75.0:
                            # western scan
                            if float(item) > thisEl:
                                thisElDirection = 'cw'
                            else:
                                thisElDirection = 'ccw'
                        else:
                            # eastern scan
                            if float(item) < thisEl:
                                thisElDirection = 'cw'
                            else:
                                thisElDirection = 'ccw'
                    elif thisElScan == 'Gcdist':
                        if float(item) > thisEl:
                            thisElDirection = 'up'
                        else:
                            thisElDirection = 'down'
            thisEl = float(item)
            
        elif mod == 9:
            thisGcdist = float(item)
        elif mod == 10:
            thisGdlatr = float(item)
        elif mod == 11:
            thisGdlonr = float(item)
        elif mod == 12:
            thisGaltr = float(item)
        elif mod == 13:
            thisRange = float(item)
        elif mod == 14:
            thisAz1 = float(item)
        elif mod == 15:
            thisAz2 = float(item)
        elif mod == 16:
            thisEl1 = float(item)
        elif mod == 17:
            thisEl2 = float(item)
            
            # check whether this is a new time
            if lastUt < thisUt:
                isNewUt = True
                lastUt = thisUt
            else:
                isNewUt = False
            # if new cycle or elevation issue, close out value if data exists
            if (thisCycn != cycNum) or \
                (thisElDirection == 'cw' and lastElDirection == 'ccw') or \
                (thisElDirection == 'ccw' and lastElDirection == 'cw') or \
                (thisElDirection == 'down' and lastElDirection == 'up') or \
                (thisElDirection == 'up' and lastElDirection == 'down') or \
                (thisElDirection != None and not inElScan) or \
                (thisAzDirection != None and not inAzScan):
                
                for i in range(len(thisIsprintStr)):
                    if len(thisIsprintStr[i]) == 0:
                        continue
                    retList.append((thisIsprintStr[i],
                                    startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                                    minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                                    minGcdist[i], maxGcdist[i]))
                thisIsprintStr = []
                cycNum = None
                startUT = []
                startAz = []
                startEl = []
                endUT = []
                endAz = []
                endEl = []
                elScan = []
                minGdlat = []
                maxGdlat = []
                minGlon = []
                maxGlon = []
                minGdalt = []
                maxGdalt = []
                minGcdist = []
                maxGcdist = []
                thisAzDirection = None
                lastAzDirection = None
                thisElDirection = None # used to detect new elevation scans (cw or ccw)
                lastElDirection = None 
                lastEl = None
                inAzScan = False
                inElScan = False
            # end of line - process it
            if self.scanType == 'el' and thisScntyp == 3:
                # add this elevation scan point
                # check type of el scan
                inAzScan = False
                inElScan = True
                if (-15.0 <= thisAz <= 15.0 or -165.0 >= thisAz or thisAz >= 165.0) and \
                    abs(thisAz1 - thisAz2) < 0.5:
                    thisElScan = 'Gdlat'
                elif (75.0 <= thisAz <= 105.0 or -105.0 <= thisAz <= -75.0) and \
                    abs(thisAz1 - thisAz2) < 0.5:
                    thisElScan = 'Glon'
                else:
                    thisElScan = 'Gcdist'
                # get index of this scan, None if a new scan
                index = None
                for i, item in enumerate(elScan):
                    
                    if item == 'Gdlat' and thisElScan == 'Gdlat':
                        index = i
                        break
                    elif item == 'Glon' and thisElScan == 'Glon':
                        index = i
                        break
                    elif item == 'Gcdist' and thisElScan == 'Gcdist' and int(thisAz) == int(startAz[i]):
                        index = i
                        break
                    elif item == 'Gcdist' and thisElScan == 'Gcdist' and i == len(elScan)-1:
                        index = i
                        break
                
                if index == None:
                    # a new scan has been found
                    thisIsprintStr.append('')
                    startUT.append(thisUt)
                    startAz.append(thisAz)
                    startEl.append(thisEl)
                    endUT.append(None)
                    endAz.append(None)
                    endEl.append(None)
                    elScan.append(thisElScan)
                    minGdlat.append(None)
                    maxGdlat.append(None)
                    minGlon.append(None)
                    maxGlon.append(None)
                    minGdalt.append(None)
                    maxGdalt.append(None)
                    minGcdist.append(None)
                    maxGcdist.append(None)
                    index = len(thisIsprintStr) - 1
                    
                lastElDirection = thisElDirection  
                endUT[index] = thisUt
                endAz[index] = thisAz
                endEl[index] = thisEl
                # gdlatr, gdlonr, galtr, range, az1, az2, el1, el2,plotParm
                thisIsprintStr[index] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                           thisRange, thisAz1, thisAz2,
                                                                           thisEl1, thisEl2, thisParm)
            elif self.scanType == 'az' and thisScntyp == 2:
                # az scan
                inAzScan = True
                inElScan = False
                # add this azimuth scan point
                # check if this is a new scan
                newAzScan = False
                if lastEl == None:
                    newAzScan = True
                # check for elevation change
                elif abs(lastEl - int(thisEl)) > 3:
                    newAzScan = True
                # check for direction change
                if lastAzDirection != None:
                    if lastAzDirection != thisAzDirection:
                        newAzScan = True
                        thisAzDirection = None
                if newAzScan:
                    # a new azimuth scan has been found
                    thisIsprintStr.append('')
                    startUT.append(thisUt)
                    startAz.append(thisAz)
                    startEl.append(thisEl)
                    endUT.append(None)
                    endAz.append(None)
                    endEl.append(None)
                    elScan.append(None)
                    minGdlat.append(None)
                    maxGdlat.append(None)
                    minGlon.append(None)
                    maxGlon.append(None)
                    minGdalt.append(None)
                    maxGdalt.append(None)
                    minGcdist.append(None)
                    maxGcdist.append(None)
                    index = len(thisIsprintStr) - 1
                    lastEl = int(thisEl)
                    lastAzDirection = None
                else:
                    # this is at least the second line of the scan
                    if not thisAzDirection is None:
                        lastAzDirection = thisAzDirection
                        
                endUT[-1] = thisUt
                endAz[-1] = thisAz
                endEl[-1] = thisEl
                thisIsprintStr[-1] += '%f %f %f %f %f %f %f %f %s\n' % (thisGdlatr, thisGdlonr, thisGaltr,
                                                                           thisRange, thisAz1, thisAz2,
                                                                           thisEl1, thisEl2, thisParm)
                
            else:
                # not a scan line
                inAzScan = False
                inElScan = False
                continue
            if minGdlat[index] == None:
                minGdlat[index] = thisGdlat
                maxGdlat[index] = thisGdlat
                minGlon[index] = thisGlon
                maxGlon[index] = thisGlon
                minGdalt[index] = thisGdalt
                maxGdalt[index] = thisGdalt
                minGcdist[index] = thisGcdist
                maxGcdist[index] = thisGcdist
            else:
                if minGdlat[index] > thisGdlat:
                    minGdlat[index] = thisGdlat
                if maxGdlat[index] < thisGdlat:
                    maxGdlat[index] = thisGdlat
                if minGlon[index] > thisGlon:
                    minGlon[index] = thisGlon
                if maxGlon[index] < thisGlon:
                    maxGlon[index] = thisGlon
                if minGdalt[index] > thisGdalt:
                    minGdalt[index] = thisGdalt
                if maxGdalt[index] < thisGdalt:
                    maxGdalt[index] = thisGdalt
                if minGcdist[index] > thisGcdist:
                    minGcdist[index] = thisGcdist
                if maxGcdist[index] < thisGcdist:
                    maxGcdist[index] = thisGcdist
    # end, close out last cycle
    for i in range(len(thisIsprintStr)):
        if len(thisIsprintStr[i]) == 0:
            continue
        retList.append((thisIsprintStr[i],
                        startUT[i], startAz[i], startEl[i], endUT[i], endAz[i], endEl[i], elScan[i],
                        minGdlat[i], maxGdlat[i], minGlon[i], maxGlon[i], minGdalt[i], maxGdalt[i],
                        minGcdist[i], maxGcdist[i]))
    return(retList)

def getDateStrFromUT(

self, ut)

getDateStrFromUT returns a date string formated as YYYY-MM-DD HH:MM:SS from a ut time (seconds since 1/1/1950)

def getDateStrFromUT(self, ut):
    """getDateStrFromUT returns a date string formated as YYYY-MM-DD HH:MM:SS from a ut time
    (seconds since 1/1/1950)
    """
    timeList = madrigal._derive.getDateFromUt(float(ut))
    return '%04i-%02i-%02i %02i:%02i:%02i' % (timeList[0],
                                              timeList[1],
                                              timeList[2],
                                              timeList[3],
                                              timeList[4],
                                              timeList[5])

def getTimeStrFromUT(

self, ut)

getTimeStrFromUT returns a time string formated as HH:MM:SS from a ut time (seconds since 1/1/1950)

def getTimeStrFromUT(self, ut):
    """getTimeStrFromUT returns a time string formated as HH:MM:SS from a ut time
    (seconds since 1/1/1950)
    """
    timeList = madrigal._derive.getDateFromUt(float(ut))
    return '%02i:%02i:%02i' % (timeList[3],
                               timeList[4],
                               timeList[5])

def plotAllScans(

self, scanType, fullFilenameTemplate, plotParm, size='small', xMinimum=None, xMaximum=None, yMinimum=None, yMaximum=None, xGridSize=None, yGridSize=None, minColormap=None, maxColormap=None, colorMap=<matplotlib.colors.LinearSegmentedColormap object at 0x1169fb450>, maxNumLines=None, filterStr='')

plotAllScans creates a series of az or el scans.

Inputs:

scanType - must be 'az' or 'el'

fullFilename - full path of file containing pcolor plot to be saved. Each image
created will have _#.png appended, with # starting at 0

plotParm - mnemonic of parameter to be plotted.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.

colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation

filterStr - a filter string to pass to isprint.  Defaults to no filtering

Returns - a list of tuples, where each tuple has two items: 1. time string, 2. metadata string in form for scanTab.txt. List length = number of plots created

def plotAllScans(self,
                 scanType,
                 fullFilenameTemplate,
                 plotParm,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 xGridSize = None,
                 yGridSize = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet,
                 maxNumLines = None,
                 filterStr = ''):
    """plotAllScans creates a series of az or el scans.
    Inputs:
        scanType - must be 'az' or 'el'
        fullFilename - full path of file containing pcolor plot to be saved. Each image
        created will have _#.png appended, with # starting at 0
        plotParm - mnemonic of parameter to be plotted.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
        xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.
        xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.
        yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.
        yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
        colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet
	    maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
	    
	    filterStr - a filter string to pass to isprint.  Defaults to no filtering

	Returns - a list of tuples, where each tuple has two items: 1. time string,
	          2. metadata string in form for scanTab.txt.  List length = number
	          of plots created

    
    if scanType not in ('az', 'el'):
        raise ValueError('scantype must be az or el, not %s' % (str(scanType)))
    self.scanType = scanType
    retList = []
    # handle ion velocity
    if plotParm.lower() == 'vo':
        cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
    else:
        cmap = matplotlib.cm.jet
    # create isprint string
    parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist' % (plotParm.strip())
    isprintStr = getIsprintString(self.madFile, parms, filterStr)
    try:
        scanList = self.createScanInfoList(isprintStr)
    except:
        traceback.print_exc()
        return (retList)
    # loop through each scan
    scanFailureCount = 0 # keep track of how many scans fail
    for i in range(len(scanList)):
        scanCount = i - scanFailureCount
        scanInfo = scanList[i]
        try:
            startDateStr = self.getDateStrFromUT(scanInfo[1])
        except:
            # no scans may have been found
            continue
        endDateStr = self.getTimeStrFromUT(scanInfo[4])
        # create title
        
        if scanType == 'el':
            if scanInfo[3] > scanInfo[6]:
                ind = 'down'
                direction = 1
            else:
                ind = 'up'
                direction = 0
            titleStr = 'El %s scan (%s) %s-%s, az=%i' % (plotParm,
                                                         ind,
                                                         startDateStr,
                                                         endDateStr,
                                                         int(scanInfo[2]))
            timeStr = '%s-%s' % (startDateStr, endDateStr)
            metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                            scanInfo[3],
                                                                                            scanInfo[2],
                                                                                            scanInfo[4],
                                                                                            scanInfo[6],
                                                                                            scanInfo[5],
                                                                                            direction)
            retList.append((timeStr,metaStr))
            
            if scanInfo[7] == 'Gdlat':
                xLabelStr = 'Geodetic latitude'
                xGridSize = 1.0
            elif scanInfo[7] == 'Glon':
                xLabelStr = 'Longitude'
                xGridSize = 1.0
            elif scanInfo[7] == 'Gcdist':
                xLabelStr = 'Ground distance in km'
                xGridSize = 100.0
            xLabelStr = ''
            yLabelStr = 'Altitude (km)'
            if xGridSize == None:
                xGridSize = 1.0
            if yGridSize == None:
                yGridSize = 20.0
            
        else:
            if scanInfo[2] > scanInfo[5]:
                ind = 'ccw'
                direction = 1
            else:
                ind = 'cw'
                direction = 0
            titleStr = 'Az %s scan (%s) %s-%s, el=%i' % (plotParm,
                                                         ind,
                                                         startDateStr,
                                                         endDateStr,
                                                         int(scanInfo[3]))
            timeStr = '%s-%s' % (startDateStr, endDateStr)
            metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                            scanInfo[2],
                                                                                            scanInfo[3],
                                                                                            scanInfo[4],
                                                                                            scanInfo[5],
                                                                                            scanInfo[6],
                                                                                            direction)
            retList.append((timeStr,metaStr))
            xLabelStr = 'Longitude'
            yLabelStr = 'Geodetic latitude'
            xGridSize = 1.0
            yGridSize = 1.0
        name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)
        try:
            madPcolorScan(scanInfo[0],
                          xGridSize,
                          yGridSize,
                          titleStr,
                          xLabelStr,
                          yLabelStr,
                          name,
                          minColormap = minColormap,
                          maxColormap = maxColormap,
                          colorMap = cmap)
        except:
            print('Problem creating scan %s' % (str(retList[-1])))
            traceback.print_exc()
            scanFailureCount += 1
            retList.remove(retList[-1])
    return (retList)

def plotAllWedgeScans(

self, scanType, fullFilenameTemplate, plotParm, size='small', xMinimum=None, xMaximum=None, yMinimum=None, yMaximum=None, minColormap=None, maxColormap=None, colorMap=<matplotlib.colors.LinearSegmentedColormap object at 0x1169fb450>, maxNumLines=None, filterStr='', addTitle='', includeKml=False, radarName=None, radarDesc=None)

plotAllWedgeScans creates a series of az or el scans. Similar to plotAllScans, except produces fancier wedge plots with uniform scalling.

Inputs:

scanType - must be 'az' or 'el'

fullFilename - full path of file containing pcolor plot to be saved. Each image
created will have _#.png appended, with # starting at 0

plotParm - mnemonic of parameter to be plotted.

size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.

xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.

xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.

yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.

yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.

minColormap - minimum parameter value (defaults to lowest parameter value)

maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
              both minColormap and maxColormap == None, autoscaling applied.

colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet

maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation

filterStr - a filter string to pass to isprint.  Defaults to no filtering

addTitle - a string with additional title.  If empty string (the default), no additional title string.

includeKml - if True, create a corresponding kml file for every png file.  Same names
        as png files, except extension = .kml.  If False, do not.

radarName - name to label radar placemark if kml generated.

radarDesc - description text for radar placemark if kml generated

Returns - a list of tuples, where each tuple has two items: 1. time string, 2. metadata string in form for scanTab.txt. List length = number of plots created

def plotAllWedgeScans(self,
                 scanType,
                 fullFilenameTemplate,
                 plotParm,
                 size = 'small',
                 xMinimum = None,
                 xMaximum = None,
                 yMinimum = None,
                 yMaximum = None,
                 minColormap = None,
                 maxColormap = None,
                 colorMap = matplotlib.cm.jet,
                 maxNumLines = None,
                 filterStr = '',
                 addTitle = '',
                 includeKml = False,
                 radarName = None,
                 radarDesc = None):
    """plotAllWedgeScans creates a series of az or el scans.  Similar to plotAllScans, except produces
    fancier wedge plots with uniform scalling.
    Inputs:
        scanType - must be 'az' or 'el'
        fullFilename - full path of file containing pcolor plot to be saved. Each image
        created will have _#.png appended, with # starting at 0
        plotParm - mnemonic of parameter to be plotted.
        size - size of plot to save. Must be "small", "wide", or "large". Defaults to small.
        xMinimum = minumum x value.  If None (default), uses lowest x value found for each scan.
        xMaximum = maximum x value.  If None (default), uses highest x value found for each scan.
        yMinimum = minumum y value.  If None (default), uses lowest y value found for each scan.
        yMaximum = maximum y value.  If None (default), uses highest y value found for each scan.
        
        minColormap - minimum parameter value (defaults to lowest parameter value)
        maxColormap - maximum parameter value (defaults to highest parameter value).  However, if
                      both minColormap and maxColormap == None, autoscaling applied.
        colorMap - sets colormap.  It not given, defaults to matplotlib.cm.jet
        maxNumLine - max number of lines in isprintText before truncating.  If None, no truncation
        
        filterStr - a filter string to pass to isprint.  Defaults to no filtering
        
        addTitle - a string with additional title.  If empty string (the default), no additional title string.
        
        includeKml - if True, create a corresponding kml file for every png file.  Same names
                as png files, except extension = .kml.  If False, do not.
                
        radarName - name to label radar placemark if kml generated.
        
        radarDesc - description text for radar placemark if kml generated

    Returns - a list of tuples, where each tuple has two items: 1. time string,
              2. metadata string in form for scanTab.txt.  List length = number
              of plots created
"""
    
    if scanType not in ('az', 'el'):
        raise ValueError('scantype must be az or el, not %s' % (str(scanType)))
    self.scanType = scanType
    
    if scanType == 'az':
        wedgeScanType = 'az'
    retList = []
    # handle ion velocity
    if plotParm.lower() == 'vo':
        cmap = madrigal.ui.madrigalPlot.get_vo_cmap()
    else:
        cmap = matplotlib.cm.jet
    # create isprint string
    parms = 'ut1,scntyp,cycn,%s,gdalt,gdlat,glon,azm,elm,gcdist,gdlatr,gdlonr,galtr,range,az1,az2,el1,el2' % (plotParm.strip())
    filterStr += ' filter=%s,, ' % (plotParm.strip())
    isprintStr = getIsprintString(self.madFile, parms, filterStr)
    try:
        scanList = self.createWedgeScanInfoList(isprintStr)
    except:
        traceback.print_exc()
        return (retList)
    
    gdlatr, gdlonr, galtr = self._getInstLocation(isprintStr)
    
    # create limit dictionary used to keep all scans of the same type with the same limits
    limitDict = self._getLimits(scanList, xMinimum, xMaximum, yMinimum, yMaximum)
    # loop through each scan
    scanFailureCount = 0 # keep track of how many scans fail
    for i in range(len(scanList)):
        scanCount = i - scanFailureCount
        scanInfo = scanList[i]
        try:
            startDateStr = self.getDateStrFromUT(scanInfo[1])
        except:
            # no scans may have been found
            continue
        endDateStr = self.getTimeStrFromUT(scanInfo[4])
        # create title
        
        if scanType == 'el':
            if scanInfo[3] > scanInfo[6]:
                ind = 'down'
                direction = 1
            else:
                ind = 'up'
                direction = 0
            if int(scanInfo[2]) == int(scanInfo[5]):
                # no change in az
                titleStr = 'El %s scan (%s) %s-%s, az=%i %s' % (plotParm,
                                                                ind,
                                                                startDateStr,
                                                                endDateStr,
                                                                int(scanInfo[2]),
                                                                addTitle)
            else:
                # az changed
                titleStr = 'El (%i-%i) %s scan %s-%s, az=%i-%i %s' % (int(scanInfo[3]),
                                                                      int(scanInfo[6]),
                                                                      plotParm,
                                                                      startDateStr,
                                                                      endDateStr,
                                                                      int(scanInfo[2]),
                                                                      int(scanInfo[5]),
                                                                      addTitle)
                
            timeStr = '%s-%s' % (startDateStr, endDateStr)
            metaStr = 'El scan: startTime %i el %f az %f stopTime %i el %f az %f dir %i' % (scanInfo[1],
                                                                                            scanInfo[3],
                                                                                            scanInfo[2],
                                                                                            scanInfo[4],
                                                                                            scanInfo[6],
                                                                                            scanInfo[5],
                                                                                            direction)
            retList.append((timeStr,metaStr))
            
            if scanInfo[7] == 'Gdlat':
                xLabelStr = 'Geodetic latitude'
                wedgeScanType = 'el_lat'
            elif scanInfo[7] == 'Glon':
                xLabelStr = 'Longitude'
                wedgeScanType = 'el_lon'
            elif scanInfo[7] == 'Gcdist':
                xLabelStr = 'Ground distance in km'
                wedgeScanType = 'el_gcdist'
            xLabelStr = ''
            yLabelStr = 'Altitude (km)'
            
        else:
            if scanInfo[2] > scanInfo[5]:
                ind = 'ccw'
                direction = 1
            else:
                ind = 'cw'
                direction = 0
            titleStr = 'Az %s scan (%s) %s-%s, el=%i %s' % (plotParm,
                                                         ind,
                                                         startDateStr,
                                                         endDateStr,
                                                         int(scanInfo[3]),
                                                         addTitle)
            timeStr = '%s-%s' % (startDateStr, endDateStr)
            metaStr = 'Az scan: startTime %i az %f el %f stopTime %i az %f el %f dir %i' % (scanInfo[1],
                                                                                            scanInfo[2],
                                                                                            scanInfo[3],
                                                                                            scanInfo[4],
                                                                                            scanInfo[5],
                                                                                            scanInfo[6],
                                                                                            direction)
            retList.append((timeStr,metaStr))
            xLabelStr = 'Longitude'
            yLabelStr = 'Geodetic latitude'
        name = '%s_%06i.png' % (fullFilenameTemplate, scanCount)
        try:
            madPcolorWedgeScan(scanInfo[0],
                               wedgeScanType,
                               titleStr,
                               xLabelStr,
                               yLabelStr,
                               name,
                               xMinimum = limitDict[wedgeScanType][0],
                               xMaximum = limitDict[wedgeScanType][1],
                               yMinimum = limitDict[wedgeScanType][2],
                               yMaximum = limitDict[wedgeScanType][3],
                               minColormap = minColormap,
                               maxColormap = maxColormap,
                               colorMap = cmap)
            success = True
        except:
            print('Problem creating scan %s' % (str(retList[-1])))
            traceback.print_exc()
            scanFailureCount += 1
            retList.remove(retList[-1])
            success = False
            
        if includeKml and success:
            kmlName = name[:-3] + 'kml'
            try:
                madPcolorWedgeKmlScan(scanInfo[0],
                                      wedgeScanType,
                                      kmlName,
                                      minColormap = minColormap,
                                      maxColormap = maxColormap,
                                      instName = radarName,
                                      instDesc = radarDesc,
                                      instLat = gdlatr,
                                      instLon = gdlonr,
                                      instAlt = galtr,
                                      colorMap = cmap)
            except:
                print('Problem creating kml scan')
                traceback.print_exc()
                                      
            
    return (retList)
Previous: madrigal.ui   Up: madrigal.ui   Next: madrigal.ui.userData