Example #1
0
 def new_plot(self, data, plot_info, measurement_count):
     """ Produce all the plot output by calls to the different
     subsection methods
     """
     self.c = Color(data, self.ggs)
     self.measurement_count = sum(measurement_count)
     self._header()
     self._data(data)
     self._options(data, plot_info)
     self._end()
Example #2
0
    def __init__(self, name):
        """ Initialize a texture from a tga file
            
            Note: This is hand-coded. With a more robust renderer, we could
            probably use an image loader 
        """
        self._width: int = 0
        self._height: int = 0
        self._pixels: [Color] = []

        path = "{}{}{}{}".format('Resources', os.path.sep, name, '.tga')

        with open(path, 'rb') as fd:
            try:
                # Read file header
                # TODO worry about sign? (we should be reading uint8)
                # Note: header is of type "bytes"
                header = fd.read(18)
            except Exception as e:
                print("Couldn't load the tga {}: {}".format(path, e))
                traceback.print_exc()
                return

            useColorMap = header[0] != 0
            imageType = int(header[2])
            if useColorMap or imageType in (0, 1, 3):
                raise Exception("{} is not a color tga".format(path))
                return

            IDLength = header[0]
            self._width = int(header[13]) * 256 + int(header[12])
            self._height = int(header[15]) * 256 + int(header[14])
            pixelDepth = header[16]

            lengthImage = int(pixelDepth) * self._width * self._height // 8

            fd.seek(18 + int(IDLength))
            content = fd.read(lengthImage)

            if pixelDepth == 8:
                self._pixels = [
                    Color(
                        float(byt) / 255.0,
                        float(byt) / 255.0,
                        float(byt) / 255.0) for byt in content
                ]
            else:
                pixelBytes = pixelDepth // 8

                for i in range(0, (self._width * self._height)):
                    r = float(content[pixelBytes * i + 2]) / 255.0
                    g = float(content[pixelBytes * i + 1]) / 255.0
                    b = float(content[pixelBytes * i]) / 255.0
                    self._pixels.append(Color(r, g, b))
Example #3
0
    def new_plot(self, data, plot_info, measurement_count):
        """ Form a new plot with the given data and info """
        self.c = Color(data, self.ggs)

        self.measurement_count = sum(measurement_count)
        self._init_plot(data)
        # _plot returns True or False to indicate whether the plot is good
        if self._plot(data):
            self._zoom_and_flip(data)
            self._title_and_labels(plot_info)
        self._save(plot_info)
    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs  # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()
        self.reduction = 1
    def __init__(self, options, ggs):
        """ Description of init """
        
        self.o = options
        self.ggs = ggs
        
        # Set the image format to standard, overwite with ggs value and again
        # options value if it exits
        if self.o['image_format'] == '':
            self.image_format = self.ggs['image_format']
        else:
            self.image_format = self.o['image_format']

        # Default values for matplotlib plots (names correspond to ggs names)
        mpl_settings = {'width': 900,
                        'height': 600,
                        'title_size': '24',
                        'xtick_labelsize': '12',
                        'ytick_labelsize': '12',
                        'legend_fontsize': '10',
                        'label_fontsize': '16',
                        'linewidth': 1.0,
                        'grid': False}
        
        # Owerwrite defaults with gs values and convert to appropriate types
        for key, value in mpl_settings.items():
            try:
                mpl_settings[key] = type(value)(self.ggs['matplotlib_settings'][key])
            except KeyError:
                pass
        
        # Write some settings to pyplot
        rc_temp = {'figure.figsize': [float(mpl_settings['width'])/100,
                                      float(mpl_settings['height'])/100],
                   'axes.titlesize': mpl_settings['title_size'],
                   'xtick.labelsize': mpl_settings['xtick_labelsize'],
                   'ytick.labelsize': mpl_settings['ytick_labelsize'],
                   'legend.fontsize': mpl_settings['legend_fontsize'],
                   'axes.labelsize': mpl_settings['label_fontsize'],
                   'lines.linewidth': mpl_settings['linewidth'],
                   'axes.grid': mpl_settings['grid']
                   }
        plt.rcParams.update(rc_temp)
                                                        
        # Plotting options
        self.maxticks=15
        self.tz = timezone('Europe/Copenhagen')
        self.right_yaxis = None
        self.measurement_count = None
 
        # object to give first good color, and then random colors
        self.c = Color()
    def clear(self, color: Color = Color(0.0)):
        """ Clear the frame buffer

            By default, the color is black (0,0,0)
            But other colors can be specified
        """
        for i in range(0, self.width * self.height):
            self._colorBuffer[i] = color
            self._depthBuffer[i] = 1.0
    def __init__(self, w=0, h=0):
        ## "public"
        self.width = int(w)
        self.height = int(h)

        ## "private"
        # array of Color objects
        self._colorBuffer = [Color()] * self.width * self.height
        # array of floats (0.0 - 1.0)
        self._depthBuffer = [1.0] * self.width * self.height
Example #8
0
 def new_plot(self, data, plot_info, measurement_count):
     """ Produce all the plot output by calls to the different
     subsection methods
     """
     self.c = Color(data, self.ggs)
     self.measurement_count = sum(measurement_count)
     self._header(self.out, data, plot_info)
     self._data(self.out, data, plot_info)
     self._options(self.out, data, plot_info)
     self._end(self.out, data, plot_info)
    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()
    def __init__(self, options, ggs):
        """ Description of init """
        
        self.o = options
        self.ggs = ggs
        
        # Set the image format to standard, overwite with ggs value and again
        # options value if it exits
        if self.o['image_format'] == '':
            self.image_format = self.ggs['image_format']
        else:
            self.image_format = self.o['image_format']

        # Default values for matplotlib plots (names correspond to ggs names)
        mpl_settings = {'width': 900,
                        'height': 600,
                        'title_size': '24',
                        'xtick_labelsize': '12',
                        'ytick_labelsize': '12',
                        'legend_fontsize': '10',
                        'label_fontsize': '16',
                        'linewidth': 1.0,
                        'grid': False}
        
        # Owerwrite defaults with gs values and convert to appropriate types
        for key, value in mpl_settings.items():
            try:
                mpl_settings[key] = type(value)(self.ggs['matplotlib_settings'][key])
            except KeyError:
                pass
        
        # Write some settings to pyplot
        rc_temp = {'figure.figsize': [float(mpl_settings['width'])/100,
                                      float(mpl_settings['height'])/100],
                   'axes.titlesize': mpl_settings['title_size'],
                   'xtick.labelsize': mpl_settings['xtick_labelsize'],
                   'ytick.labelsize': mpl_settings['ytick_labelsize'],
                   'legend.fontsize': mpl_settings['legend_fontsize'],
                   'axes.labelsize': mpl_settings['label_fontsize'],
                   'lines.linewidth': mpl_settings['linewidth'],
                   'axes.grid': mpl_settings['grid']
                   }
        plt.rcParams.update(rc_temp)
                                                        
        # Plotting options
        self.maxticks=15
        self.tz = GMT1()
        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.measurement_count = None
 
        # object to give first good color, and then random colors
        self.c = Color()
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)
        
    def _header(self, out, data, plot_info):
        """ Form the header """
        out.write('g = new Dygraph(\n' +
                  self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(out, data, plot_info)
        else:
            self._data_xyplot(out, data, plot_info)

    def _data_dateplot(self, out, data, plot_info):
        plot_n = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        last_line = '\n' + self.tab + '//DATA\n'
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_n + 1)
            for item in dat['data']:
                out.write(last_line)
                this_line[0] = str(time.strftime('%Y-%m-%d %H:%M:%S',
                                                 time.localtime(int(item[0]))))
                this_line[n+1] = str(item[1])
                last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        if self.measurement_count == 0:
            out.write(last_line)
            this_line = [str(42), str(42)]
            last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        out.write(last_line.rstrip(' +\n') + ',\n\n')

    def _data_xyplot(self, out, data, plot_info):
        plot_n = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        last_line = '\n' + self.tab + '//DATA\n'
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_n + 1)
            for item in dat['data']:
                out.write(last_line)
                this_line[0] = str(item[0])
                this_line[n+1] = str(item[1])
                last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write(last_line)
            this_line = [str(42), str(42)]
            last_line = self.tab + '"' + ','.join(this_line) + '\\n" +\n'
        
        out.write(last_line.rstrip(' +\n') + ',\n\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """

        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels=[]
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))}
                    )
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], plot_info['y_right_label_addition'])
                    options.append({'y2label':_q(label)})

        # General options
        options += [{'logscale': str(self.o['left_logscale']).lower()},
                    {'connectSeparatedPoints': 'true'},
                    {'legend': _q('always')},
                    #{'lineWidth': '0'}
                    ]
     
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'], plot_info['y_left_label_addition'])
                options.append({'ylabel':_q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel':_q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [self.c.get_color_hex() for dat in data['left'] + data['right']]
        options.append({'colors':str(colors)})
        
        # Zoom
        if self.o['left_yscale_bounding'] is not None:
            options.append({'valueRange':
                                str(list(self.o['left_yscale_bounding']))})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append(
                        {'highlightSeriesOpts': [
                                {'strokeWidth': '2'},
                                {'strokeBorderWidth': '1'},
                                {'highlightCircleSize': '5'}
                                ]
                         })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                    options += [{'labelsDiv': 'document.getElementById("labels")'},
                                {'labelsSeparateLines': sep_new_lines}]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids


        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value)-1:
                    self._output_options(out, key, value, level+1, last=True)
                else:
                    self._output_options(out, key, value, level+1, last=False)
            else:
                if n == len(options)-1:
                    out.write(self.tab*(level+1) + key + ': ' + value + '\n')
                else:
                    out.write(self.tab*(level+1) + key + ': ' + value + ',\n')
                
        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')
Example #12
0
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # Colors object, will be filled in at new_plot
        self.c = None
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.c = Color(data, self.ggs)
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)
        
    def _header(self, out, data, plot_info):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            out.write('document.getElementById("warning_div").innerHTML='\
                          '\"{0}\";'.format(warning))

        # Write dygraph header
        out.write('g = new Dygraph(\n' +
                  self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data, plot_info)
        else:
            self._data_xyplot(file_, data, plot_info)
        file_.close()

    def _data_dateplot(self, out, data, plot_info):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(time.strftime(
                            '%Y-%m-%d %H:%M:%S', time.localtime(int(item[0]))
                            ))
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data, plot_info):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """

        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels=[]
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))}
                    )
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], plot_info['y_right_label_addition'])
                    options.append({'y2label':_q(label)})

        # General options
        options += [{'logscale': str(self.o['left_logscale']).lower()},
                    {'connectSeparatedPoints': 'true'},
                    {'legend': _q('always')},
                    #{'lineWidth': '0'}
                    ]
     
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'], plot_info['y_left_label_addition'])
                options.append({'ylabel':_q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel':_q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [self.c.get_color_hex() for dat in data['left'] + data['right']]
        options.append({'colors':str(colors)})
        
        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            options.append({'valueRange':
                                str(list(self.o['left_yscale_bounding']))})
        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            xzoomstring = '[{0}, {1}]'.format(self.o['xscale_bounding'][0],
                                              self.o['xscale_bounding'][1])
            options.append({'dateWindow': xzoomstring})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append(
                        {'highlightSeriesOpts': [
                                {'strokeWidth': '2'},
                                {'strokeBorderWidth': '1'},
                                {'highlightCircleSize': '5'}
                                ]
                         })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                    options += [{'labelsDiv': 'document.getElementById("labels")'},
                                {'labelsSeparateLines': sep_new_lines}]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids


        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value)-1:
                    self._output_options(out, key, value, level+1, last=True)
                else:
                    self._output_options(out, key, value, level+1, last=False)
            else:
                if n == len(options)-1:
                    out.write(self.tab*(level+1) + key + ': ' + value + '\n')
                else:
                    out.write(self.tab*(level+1) + key + ': ' + value + ',\n')
                
        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')
Example #13
0
    def __init__(self):
        """ Description of init """

        # Create the option parser for the command line options
        usage = (
            'usage: %prog [options]\n\n'
            'All options are strings. Boolean options are true when they \n'
            'contains a certain specific keywords, which is written in \n'
            'the option description in parantheses.')
        parser = OptionParser(usage=usage)

        # Add the options to the option parser
        parser.add_option('-a',
                          '--type',
                          help='Type string from '
                          'graphsettings.xml')
        parser.add_option('-b', '--idlist', help='List of id\'s to plot')
        parser.add_option('-c',
                          '--from_d',
                          help='From timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-d',
                          '--to_d',
                          help='To timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-e', '--xmin', help='X-min for zoom')
        parser.add_option('-f', '--xmax', help='X-max for zoom')
        parser.add_option('-g', '--ymin', help='Y-min for zoom')
        parser.add_option('-i', '--ymax', help='Y-max for zoom')
        parser.add_option('-j',
                          '--offset',
                          help='List of offsets for the '
                          'graphs (for plots that goes on a log scale and has '
                          'negative values)')
        parser.add_option(
            '-k',
            '--as_function_of_t',
            help='Plot the graphs as '
            'a function of temperature (boolean \'checked\'=True)')
        parser.add_option('-l',
                          '--logscale',
                          help='Use a log for the right '
                          'axis (boolean \'checked\'=True)')
        parser.add_option('-m',
                          '--shift_temp_unit',
                          help='Change between K '
                          'and C when values are plotted as a function of '
                          'temperature (boolean \'checked\'=True)')
        parser.add_option('-n',
                          '--flip_x',
                          help='Exchange min and max for the '
                          'x-axis (boolean \'checked\'=True)')
        parser.add_option('-o',
                          '--shift_be_ke',
                          help='Shift between binding '
                          'energy and kinetic energy for XPS plots (boolean '
                          '\'checked\'=True)')
        # -p is availabel from previous options
        parser.add_option(
            '-q',
            '--image_format',
            help='Image format for the '
            'figure exports, given as the figure extension. Can '
            'be svg, eps, ps, pdf and default. Default means use '
            'the one in graphsettings.xml or internal deaault.')
        parser.add_option('-r',
                          '--small_plot',
                          help='Produce a small plot '
                          '(boolean \'checked\'=1)')

        # Parse the options
        (options, args) = parser.parse_args()

        ### Process options - all options are given as string, and they need to
        ### be converted into other data types
        # Convert idlist
        self.idlist = [
            int(element) for element in options.idlist.split(',')[1:]
        ]
        # Turn the offset 'key:value,' pair string into a dictionary
        self.offsets = dict([[int(offset.split(':')[0]),
                              offset.split(':')[1]]
                             for offset in options.offset.split(',')[1:]])
        # Gather from and to in a fictionary
        self.from_to = {'from': options.from_d, 'to': options.to_d}
        # Turn several options into booleans
        self.as_function_of_t = True if options.as_function_of_t ==\
            'checked' else False
        self.shift_temp_unit = True if options.shift_temp_unit ==\
            'checked' else False
        self.logscale = True if options.logscale == 'checked' else False
        self.flip_x = True if options.flip_x == 'checked' else False
        self.shift_be_ke = True if options.shift_be_ke == 'checked' else False
        self.small_plot = True if options.small_plot == '1' else False

        ### Create database backend object
        self.db = dataBaseBackend(typed=options.type,
                                  from_to=self.from_to,
                                  id_list=self.idlist,
                                  offsets=self.offsets,
                                  as_function_of_t=self.as_function_of_t,
                                  shift_temp_unit=self.shift_temp_unit,
                                  shift_be_ke=self.shift_be_ke)

        ### Ask self.db for a measurement count
        measurement_count = self.db.get_data_count()

        # Set the image format to standard, overwite with gs value and again
        # options value if i exits
        if options.image_format:
            if options.image_format == 'default':
                if self.db.global_settings.has_key('image_format'):
                    self.image_format = self.db.global_settings['image_format']
                else:
                    self.image_format = 'png'
            else:
                self.image_format = options.image_format
        else:
            self.image_format = 'png'

        # Create a hash from the measurement_count, options and
        #self.db.global_settings
        hash = hashlib.md5()
        hash.update(
            str(options) + str(self.db.global_settings) +
            str(measurement_count))
        # self.namehash is unique for this plot and will form the filename
        self.namehash = ('/var/www/cinfdata/figures/' + hash.hexdigest() +
                         '.' + self.image_format)

        # For use in other methods
        self.options = options

        # object to give first good color, and then random colors
        self.c = Color()

        self.left_color = 'black'
        self.right_color = 'black'
Example #14
0
class Plot():
    """This class is used to generate the figures for the plots."""
    def __init__(self):
        """ Description of init """

        # Create the option parser for the command line options
        usage = (
            'usage: %prog [options]\n\n'
            'All options are strings. Boolean options are true when they \n'
            'contains a certain specific keywords, which is written in \n'
            'the option description in parantheses.')
        parser = OptionParser(usage=usage)

        # Add the options to the option parser
        parser.add_option('-a',
                          '--type',
                          help='Type string from '
                          'graphsettings.xml')
        parser.add_option('-b', '--idlist', help='List of id\'s to plot')
        parser.add_option('-c',
                          '--from_d',
                          help='From timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-d',
                          '--to_d',
                          help='To timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-e', '--xmin', help='X-min for zoom')
        parser.add_option('-f', '--xmax', help='X-max for zoom')
        parser.add_option('-g', '--ymin', help='Y-min for zoom')
        parser.add_option('-i', '--ymax', help='Y-max for zoom')
        parser.add_option('-j',
                          '--offset',
                          help='List of offsets for the '
                          'graphs (for plots that goes on a log scale and has '
                          'negative values)')
        parser.add_option(
            '-k',
            '--as_function_of_t',
            help='Plot the graphs as '
            'a function of temperature (boolean \'checked\'=True)')
        parser.add_option('-l',
                          '--logscale',
                          help='Use a log for the right '
                          'axis (boolean \'checked\'=True)')
        parser.add_option('-m',
                          '--shift_temp_unit',
                          help='Change between K '
                          'and C when values are plotted as a function of '
                          'temperature (boolean \'checked\'=True)')
        parser.add_option('-n',
                          '--flip_x',
                          help='Exchange min and max for the '
                          'x-axis (boolean \'checked\'=True)')
        parser.add_option('-o',
                          '--shift_be_ke',
                          help='Shift between binding '
                          'energy and kinetic energy for XPS plots (boolean '
                          '\'checked\'=True)')
        # -p is availabel from previous options
        parser.add_option(
            '-q',
            '--image_format',
            help='Image format for the '
            'figure exports, given as the figure extension. Can '
            'be svg, eps, ps, pdf and default. Default means use '
            'the one in graphsettings.xml or internal deaault.')
        parser.add_option('-r',
                          '--small_plot',
                          help='Produce a small plot '
                          '(boolean \'checked\'=1)')

        # Parse the options
        (options, args) = parser.parse_args()

        ### Process options - all options are given as string, and they need to
        ### be converted into other data types
        # Convert idlist
        self.idlist = [
            int(element) for element in options.idlist.split(',')[1:]
        ]
        # Turn the offset 'key:value,' pair string into a dictionary
        self.offsets = dict([[int(offset.split(':')[0]),
                              offset.split(':')[1]]
                             for offset in options.offset.split(',')[1:]])
        # Gather from and to in a fictionary
        self.from_to = {'from': options.from_d, 'to': options.to_d}
        # Turn several options into booleans
        self.as_function_of_t = True if options.as_function_of_t ==\
            'checked' else False
        self.shift_temp_unit = True if options.shift_temp_unit ==\
            'checked' else False
        self.logscale = True if options.logscale == 'checked' else False
        self.flip_x = True if options.flip_x == 'checked' else False
        self.shift_be_ke = True if options.shift_be_ke == 'checked' else False
        self.small_plot = True if options.small_plot == '1' else False

        ### Create database backend object
        self.db = dataBaseBackend(typed=options.type,
                                  from_to=self.from_to,
                                  id_list=self.idlist,
                                  offsets=self.offsets,
                                  as_function_of_t=self.as_function_of_t,
                                  shift_temp_unit=self.shift_temp_unit,
                                  shift_be_ke=self.shift_be_ke)

        ### Ask self.db for a measurement count
        measurement_count = self.db.get_data_count()

        # Set the image format to standard, overwite with gs value and again
        # options value if i exits
        if options.image_format:
            if options.image_format == 'default':
                if self.db.global_settings.has_key('image_format'):
                    self.image_format = self.db.global_settings['image_format']
                else:
                    self.image_format = 'png'
            else:
                self.image_format = options.image_format
        else:
            self.image_format = 'png'

        # Create a hash from the measurement_count, options and
        #self.db.global_settings
        hash = hashlib.md5()
        hash.update(
            str(options) + str(self.db.global_settings) +
            str(measurement_count))
        # self.namehash is unique for this plot and will form the filename
        self.namehash = ('/var/www/cinfdata/figures/' + hash.hexdigest() +
                         '.' + self.image_format)

        # For use in other methods
        self.options = options

        # object to give first good color, and then random colors
        self.c = Color()

        self.left_color = 'black'
        self.right_color = 'black'

    def main(self):
        if os.path.exists(self.namehash) and False:
            print self.namehash
        else:
            # Call a bunch of functions
            self._init_plot()
            self._plot()
            if self.left_color != 'black':
                if self.right_color != 'black':
                    self.c.color_axis(self.ax1, self.ax2, self.left_color,
                                      self.right_color)
                else:
                    self.c.color_axis(self.ax1, None, self.left_color, None)
            self._legend()
            self._zoom_and_flip()
            self._transform_and_label_axis()
            if not self.small_plot:
                self._title()
            self._grids()
            self._save()

    def _init_plot(self):
        ### Apply settings
        # Small plots
        if self.small_plot:
            # Apply default settings
            plt.rcParams.update({
                'figure.figsize': [4.5, 3.0],
                'ytick.labelsize': 'x-small',
                'xtick.labelsize': 'x-small',
                'legend.fontsize': 'x-small'
            })
            # Overwrite with values from graphsettings
            plt.rcParams.update(self.db.global_settings['rcparams_small'])
        else:
            plt.rcParams.update({
                'figure.figsize': [9.0, 6.0],
                'axes.titlesize': '24',
                'legend.fontsize': 'small'
            })
            plt.rcParams.update(self.db.global_settings['rcparams_regular'])

        self.fig = plt.figure(1)

        self.ax1 = self.fig.add_subplot(111)
        self.ax2 = None

        # Decide on the y axis type
        self.gs = self.db.global_settings
        if self.logscale:
            self.ax1.set_yscale('log')
        elif self.gs['default_yscale'] == 'log':
            self.ax1.set_yscale('log')

    def _plot(self):
        # Make plot
        data_in_plot = False
        for data in self.db.get_data():
            if len(data['data']) > 0:
                data_in_plot = data_in_plot or True
                # Speciel case for barplots
                if self.db.global_settings.has_key('default_style') and\
                        self.db.global_settings['default_style'] == 'barplot':
                    self.ax1.bar(data['data'][:, 0],
                                 data['data'][:, 1],
                                 color=self.c.get_color())
                # Normal graph styles
                else:
                    # If the graph go on the right side of the plot
                    if data['info']['on_the_right']:
                        # Initialise secondary plot if it isn't already
                        if not self.ax2:
                            self._init_second_y_axis()
                        # If info has a color (i.e. it is given in gs ordering)
                        if data['info'].has_key('color'):
                            # Set the color for the graph and axis
                            color = data['info']['color']
                            self.right_color = data['info']['color']
                        else:
                            # Else get a new color from self.c
                            color = self.c.get_color()

                        # Make the actual plot
                        self.ax2.plot(data['data'][:, 0],
                                      data['data'][:, 1],
                                      color=color,
                                      label=self._legend_item(data) + '(R)')
                    # If the graph does not go on the right side of the plot
                    else:
                        # If info has a color (i.e. it is given in gs ordering)
                        if data['info'].has_key('color'):
                            # Set the color for the graph and axis
                            color = data['info']['color']
                            self.left_color = data['info']['color']
                        else:
                            # Else get a new color from self.c
                            color = self.c.get_color()

                        # Make the actual plot
                        self.ax1.plot(data['data'][:, 0],
                                      data['data'][:, 1],
                                      color=color,
                                      label=self._legend_item(data))

        # If no data has been been put on the graph at all, explain why there
        # is none
        if not data_in_plot:
            y = 0.00032 if self.logscale or self.gs[
                'default_yscale'] == 'log' else 0.5
            self.ax1.text(0.5,
                          y,
                          'No data',
                          horizontalalignment='center',
                          verticalalignment='center',
                          color='red',
                          size=60)

    def _legend(self):
        if self.db.global_settings['default_xscale'] != 'dat':
            ax1_legends = self.ax1.get_legend_handles_labels()
            if self.ax2:
                ax2_legends = self.ax2.get_legend_handles_labels()
                for color, text in zip(ax2_legends[0], ax2_legends[1]):
                    ax1_legends[0].append(color)
                    ax1_legends[1].append(text)

            # loc for locations, 0 means 'best'. Why that isn't deafult I
            # have no idea
            self.ax1.legend(ax1_legends[0], ax1_legends[1], loc=0)

    def _zoom_and_flip(self):
        # Now we are done with the plotting, change axis if necessary
        # Get current axis limits
        self.axis = self.ax1.axis()
        if self.options.xmin != self.options.xmax:
            self.axis = (float(self.options.xmin), float(self.options.xmax)) +\
                self.axis[2:4]
        if self.options.ymin != self.options.ymax:
            self.axis = self.axis[0:2] + (float(
                self.options.ymin), float(self.options.ymax))
        if self.flip_x:
            self.axis = (self.axis[1], self.axis[0]) + self.axis[2:4]
        self.ax1.axis(self.axis)

    def _transform_and_label_axis(self):
        """ Transform X-AXIS axis and label it """

        # If it is a date plot
        if self.db.global_settings['default_xscale'] == 'dat':
            # Turn the x-axis into timemarks
            # IMPLEMENT add something to TimeMarks initialisation to take care
            # or morning_pressure
            markformat = '%H:%M' if self.small_plot else '%b-%d %H:%M'
            timemarks = TimeMarks(self.axis[0],
                                  self.axis[1],
                                  markformat=markformat)
            (old_tick_labels, new_tick_labels) = timemarks.get_time_marks()
            self.ax1.set_xticks(old_tick_labels)
            self.bbox_xlabels = self.ax1.\
                set_xticklabels(new_tick_labels, rotation=25,
                                horizontalalignment='right')
            # Make a little extra room for the rotated x marks
            #self.fig.subplots_adjust(bottom=0.12)
        elif self.options.type == 'masstime':
            gs_temp_unit = self.gs['temperature_unit']
            other_temp_unit = 'C' if gs_temp_unit == 'K' else 'K'
            cur_temp_unit = other_temp_unit if self.shift_temp_unit else\
                gs_temp_unit
            if self.as_function_of_t and not self.small_plot:
                self.ax1.set_xlabel(self.gs['t_xlabel'] + cur_temp_unit)
            elif not self.small_plot:
                self.ax1.set_xlabel(self.gs['xlabel'])
        elif self.options.type == 'xps':
            if self.shift_be_ke and not self.small_plot:
                self.ax1.set_xlabel(self.gs['alt_xlabel'])
            elif not self.small_plot:
                self.ax1.set_xlabel(self.gs['xlabel'])
        elif not self.small_plot:
            self.ax1.set_xlabel(self.gs['xlabel'])

        # Label Y-axis
        if not self.small_plot:
            self.ax1.set_ylabel(self.gs['ylabel'], color=self.left_color)
            if self.ax2:
                self.ax2.set_ylabel(self.gs['right_ylabel'],
                                    color=self.right_color)

    def _title(self):
        """ TITLE """
        # Set the title and raise it a bit
        if self.as_function_of_t:
            self.bbox_title = self.ax1.set_title(self.gs['t_title'], y=1.03)
        else:
            self.bbox_title = self.ax1.set_title(self.gs['title'], y=1.03)

    def _grids(self):
        # GRIDS
        self.ax1.grid(b=True, which='major')
        #plt.xscale('linear')
        #plt.xticks(range(0,100,10))
        #plt.x_minor_ticks(range(0,100,10))
        #plt.grid(b='on', which='minor')
        #plt.grid(b='on', which='major')

    def _save(self):
        ## Filesave
        # Save
        self.fig.savefig(self.namehash, bbox_inches='tight', pad_inches=0.03)

        # This is the magical line that plot.php opens
        # For the script to work this has to be the only print statement
        print self.namehash

    ### Here start the small helper functions that are called from the main flow

    def _init_second_y_axis(self):
        self.ax2 = self.ax1.twinx()
        if self.db.global_settings['right_yscale'] == 'log':
            self.ax2.set_yscale('log')

    def _legend_item(self, data):
        if self.db.global_settings['default_xscale'] == 'dat':
            return ''
        elif data['gs'].has_key('legend_field_name') and\
                data['info'][data['gs']['legend_field_name']]:
            return data['info']['mass_label'] + '-' + str(data['info']['id'])
        else:
            return str(data['info']['id'])
Example #15
0
class Plot():
    """This class is used to generate the figures for the plots."""
    
    def __init__(self, options, ggs):
        """ Description of init """
        
        self.o = options
        self.ggs = ggs
        
        # Set the image format to standard, overwite with ggs value and again
        # options value if it exits
        if self.o['image_format'] == '':
            self.image_format = self.ggs['image_format']
        else:
            self.image_format = self.o['image_format']

        # Default values for matplotlib plots (names correspond to ggs names)
        mpl_settings = {'width': 900,
                        'height': 600,
                        'title_size': '24',
                        'xtick_labelsize': '12',
                        'ytick_labelsize': '12',
                        'legend_fontsize': '10',
                        'label_fontsize': '16',
                        'linewidth': 1.0,
                        'grid': False}
        
        # Owerwrite defaults with gs values and convert to appropriate types
        for key, value in mpl_settings.items():
            try:
                mpl_settings[key] = type(value)(self.ggs['matplotlib_settings'][key])
            except KeyError:
                pass
        
        # Write some settings to pyplot
        rc_temp = {'figure.figsize': [float(mpl_settings['width'])/100,
                                      float(mpl_settings['height'])/100],
                   'axes.titlesize': mpl_settings['title_size'],
                   'xtick.labelsize': mpl_settings['xtick_labelsize'],
                   'ytick.labelsize': mpl_settings['ytick_labelsize'],
                   'legend.fontsize': mpl_settings['legend_fontsize'],
                   'axes.labelsize': mpl_settings['label_fontsize'],
                   'lines.linewidth': mpl_settings['linewidth'],
                   'axes.grid': mpl_settings['grid']
                   }
        plt.rcParams.update(rc_temp)
                                                        
        # Plotting options
        self.maxticks=15
        self.tz = timezone('Europe/Copenhagen')
        self.right_yaxis = None
        self.measurement_count = None
 
        # Colors object, will be filled in at new_plot
        self.c = None

    def new_plot(self, data, plot_info, measurement_count):
        """ Form a new plot with the given data and info """
        self.c = Color(data, self.ggs)

        self.measurement_count = sum(measurement_count)
        self._init_plot(data)
        # _plot returns True or False to indicate whether the plot is good
        if self._plot(data):
            self._zoom_and_flip(data)
            self._title_and_labels(plot_info)
        self._save(plot_info)

    def _init_plot(self, data):
        """ Initialize plot """
        self.fig = plt.figure(1)
        self.ax1 = self.fig.add_subplot(111)

        # We only activate the right y-axis, if there there points to put on it
        self.right_yaxis = sum([len(dat['data']) for dat in data['right']]) > 0

        if self.right_yaxis:
            self.ax2 = self.ax1.twinx()

        if self.o['left_logscale']:
            self.ax1.set_yscale('log')
        if self.right_yaxis and self.o['right_logscale']:
            self.ax2.set_yscale('log')

    def _plot(self, data):
        """ Determine the type of the plot and make the appropriate plot by use
        of the functions:
          _plot_dateplot
          _plot_xyplot
        """
        if self.ggs['default_xscale'] == 'dat':
            return self._plot_dateplot(data)
        else:
            return self._plot_xyplot(data)

    def _plot_dateplot(self, data):
        """ Make the date plot """
        # Rotate datemarks on xaxis
        self.ax1.set_xticklabels([], rotation=25, horizontalalignment='right')

        # Test for un-workable plot configurations
        error_msg = None
        # Test if there is data on the left axis
        if sum([len(dat['data']) for dat in data['left']]) == 0:
            error_msg = 'There must\nbe data on\nthe left y-axis'
        # Test if there is any data at all
        if self.measurement_count == 0:
            error_msg = 'No data'
        # No data
        if error_msg:
            y = 0.00032 if self.o['left_logscale'] is True else 0.5
            self.ax1.text(0.5, y, error_msg, horizontalalignment='center',
                          verticalalignment='center', color='red', size=60)
            return False

        # Left axis
        for dat in data['left']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax1.plot_date(mdates.epoch2num(dat['data'][:,0]),
                                   dat['data'][:,1],
                                   label=legend,
                                   xdate=True,
                                   color=self.c.get_color(),
                                   tz=self.tz,
                                   fmt='-')
        # Right axis
        if self.right_yaxis:
            for dat in data['right']:
                # Form legend
                if dat['lgs'].has_key('legend'):
                    legend = dat['lgs']['legend']
                else:
                    legend = None
                # Plot
                if len(dat['data']) > 0:
                    self.ax2.plot_date(mdates.epoch2num(dat['data'][:,0]),
                                       dat['data'][:,1],
                                       label=legend,
                                       xdate=True,
                                       color=self.c.get_color(),
                                       tz=self.tz,
                                       fmt='-')

        # Set xtick formatter (only if we have points)
        if self.measurement_count > 0:
            xlim = self.ax1.set_xlim()
            diff = max(xlim) - min(xlim)  # in days
            format_out = '%H:%M:%S'  # Default
            # Diff limit to date format translation, will pick the format
            # format of the largest limit the diff is larger than. Limits
            # are in minutes.
            formats = [
                [1.0,              '%a %H:%M'],  # Larger than 1 day
                [7.0,              '%Y-%m-%d'],  # Larger than 7 day
                [7*30.,              '%Y-%m'],  # Larger than 3 months
                ]
            for limit, format in formats:
                if diff > limit:
                    format_out = format
            fm = mdates.DateFormatter(format_out, tz=self.tz)
            self.ax1.xaxis.set_major_formatter(fm)

        # Indicate that the plot is good
        return True

    def _plot_xyplot(self, data):
        # Left axis
        for dat in data['left']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax1.plot(dat['data'][:,0],
                              dat['data'][:,1],
                              '-',
                              label=legend,
                              color=self.c.get_color(dat['lgs']['id']),
                              )
        # Right axis
        for dat in data['right']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax2.plot(dat['data'][:,0],
                              dat['data'][:,1],
                              '-',
                              label=legend,
                              color=self.c.get_color(dat['lgs']['id'])
                              )
        # No data
        if self.measurement_count == 0:
            y = 0.00032 if self.o['left_logscale'] is True else 0.5
            self.ax1.text(0.5, y, 'No data', horizontalalignment='center',
                          verticalalignment='center', color='red', size=60)

        # Indicate that the plot is good
        return True

    def _zoom_and_flip(self, data):
        """ Apply the y zooms.
        NOTE: self.ax1.axis() return a list of bounds [xmin,xmax,ymin,ymax] and
        we reuse x and replace y)
        """
        left_yscale_inferred = self.o['left_yscale_bounding']
        right_yscale_inferred = self.o['right_yscale_bounding']

        # X-axis zoom and infer y-axis zoom implications
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            # Set the x axis scaling, unsure if we should do it for ax2 as well
            self.ax1.set_xlim(self.o['xscale_bounding'])
            # With no specific left y-axis zoom, infer it from x-axis zoom
            if left_yscale_inferred is None:
                left_yscale_inferred = self._infer_y_on_x_zoom(
                    data['left'], self.o['left_logscale'])
            # With no specific right y-axis zoom, infer it from x-axis zoom
            if right_yscale_inferred is None and self.right_yaxis:
                right_yscale_inferred = self._infer_y_on_x_zoom(
                    data['right'])

        # Left axis 
        if left_yscale_inferred is not None:
            self.ax1.set_ylim(left_yscale_inferred)
        # Right axis
        if self.right_yaxis and right_yscale_inferred is not None:
            self.ax2.set_ylim(right_yscale_inferred)

        if self.o['flip_x']:
            self.ax1.set_xlim((self.ax1.set_xlim()[1],self.ax1.set_xlim()[0]))

    def _infer_y_on_x_zoom(self, list_of_data_sets, log=None):
        """Infer the implied Y axis zoom with an X axis zoom, for one y axis"""
        yscale_inferred = None
        min_candidates = []
        max_candidates = []
        for dat in list_of_data_sets:
            # Make mask that gets index for points where x is within bounds
            mask = (dat['data'][:, 0] > self.o['xscale_bounding'][0]) &\
                (dat['data'][:, 0] < self.o['xscale_bounding'][1])
            # Gets all the y values from that mask
            reduced = dat['data'][mask, 1]
            # Add min/max candidates
            if len(reduced) > 0:
                min_candidates.append(np.min(reduced))
                max_candidates.append(np.max(reduced))
        # If there are min/max candidates, set the inferred left y bounding
        if len(min_candidates) > 0 and len(max_candidates) > 0:
            min_, max_ = np.min(min_candidates), np.max(max_candidates)
            height = max_ - min_
            yscale_inferred = (min_ - height*0.05, max_ + height*0.05)
        return yscale_inferred

    def _title_and_labels(self, plot_info):
        """ Put title and labels on the plot """
        # xlabel
        if plot_info.has_key('xlabel'):
            label = plot_info['xlabel']
            if plot_info['xlabel_addition'] != '':
                label += '\n' + plot_info['xlabel_addition']
            self.ax1.set_xlabel(label)
        if self.o['xlabel'] != '':  # Manual override
            self.ax1.set_xlabel(r'{0}'.format(self.o['xlabel']))
        # Left ylabel
        if plot_info.has_key('left_ylabel'):
            label = plot_info['left_ylabel']
            if plot_info['y_left_label_addition'] != '':
                label += '\n' + plot_info['y_left_label_addition']
            self.ax1.set_ylabel(label, multialignment='center')
        if self.o['left_ylabel'] != '':  # Manual override
            self.ax1.set_ylabel(self.o['left_ylabel'], multialignment='center')
        # Right ylabel
        if self.right_yaxis and plot_info.has_key('right_ylabel'):
            label = plot_info['right_ylabel']
            if plot_info['y_right_label_addition'] != '':
                label += '\n' + plot_info['y_right_label_addition']
            self.ax2.set_ylabel(label, multialignment='center', rotation=270)
            if self.o['right_ylabel'] != '':  # Manual override
                self.ax2.set_ylabel(self.o['right_ylabel'],
                                    multialignment='center', rotation=270)
        # Title
        if plot_info.has_key('title'):
            self.ax1.set_title(plot_info['title'], y=1.03)
        if self.o['title'] != '':
            # experiment with 'r{0}'.form .. at some time
            self.ax1.set_title('{0}'.format(self.o['title']), y=1.03)

        # Legends
        if self.measurement_count > 0:
            ax1_legends = self.ax1.get_legend_handles_labels()
            if self.right_yaxis:
                ax2_legends = self.ax2.get_legend_handles_labels()
                for color, text in zip(ax2_legends[0], ax2_legends[1]):
                    ax1_legends[0].append(color)
                    ax1_legends[1].append(text)
                    
            # loc for locations, 0 means 'best'. Why that isn't deafult I
            # have no idea
            legends = self.ax1.legend(ax1_legends[0], ax1_legends[1], loc=0)

            # Make legend lines thicker
            for legend_handle in legends.legendHandles:
                legend_handle.set_linewidth(6)

    def _save(self, plot_info):
        """ Save the figure """
        # The tight method only works if there is a title (it caps of parts of
        # the axis numbers, therefore this hack, this may also become a problem
        # for the other edges of the figure if there are no labels)
        tight = ''
        if plot_info.has_key('title'):
            tight = 'tight'
        # For some wierd reason we cannot write directly to sys.stdout when it
        # is a pdf file, so therefore we use a the StringIO object workaround
        if self.o['image_format'] == 'pdf':
            import StringIO
            out = StringIO.StringIO()
            self.fig.savefig(out, bbox_inches=tight, pad_inches=0.03,
                             format=self.o['image_format'])
            sys.stdout.write(out.getvalue())
        else:
            self.fig.savefig(sys.stdout, bbox_inches=tight, pad_inches=0.03,
                             format=self.o['image_format'])
Example #16
0
    def __init__(self):
        """ Description of init """

        # Create the option parser for the command line options
        usage = ('usage: %prog [options]\n\n'
                 'All options are strings. Boolean options are true when they \n'
                 'contains a certain specific keywords, which is written in \n'
                 'the option description in parantheses.')
        parser = OptionParser(usage=usage)

        # Add the options to the option parser
        parser.add_option('-a', '--type', help='Type string from '
                          'graphsettings.xml')
        parser.add_option('-b', '--idlist', help='List of id\'s to plot')
        parser.add_option('-c', '--from_d', help='From timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-d', '--to_d', help='To timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-e', '--xmin', help='X-min for zoom')
        parser.add_option('-f', '--xmax', help='X-max for zoom')
        parser.add_option('-g', '--ymin', help='Y-min for zoom')
        parser.add_option('-i', '--ymax', help='Y-max for zoom')
        parser.add_option('-j', '--offset', help='List of offsets for the '
                          'graphs (for plots that goes on a log scale and has '
                          'negative values)')
        parser.add_option('-k', '--as_function_of_t', help='Plot the graphs as '
                          'a function of temperature (boolean \'checked\'=True)')
        parser.add_option('-l', '--logscale', help='Use a log for the right '
                          'axis (boolean \'checked\'=True)')
        parser.add_option('-m', '--shift_temp_unit', help='Change between K '
                          'and C when values are plotted as a function of '
                          'temperature (boolean \'checked\'=True)')
        parser.add_option('-n', '--flip_x', help='Exchange min and max for the '
                          'x-axis (boolean \'checked\'=True)')
        parser.add_option('-o', '--shift_be_ke', help='Shift between binding '
                          'energy and kinetic energy for XPS plots (boolean '
                          '\'checked\'=True)')
        # -p is availabel from previous options
        parser.add_option('-q', '--image_format', help='Image format for the '
                          'figure exports, given as the figure extension. Can '
                          'be svg, eps, ps, pdf and default. Default means use '
                          'the one in graphsettings.xml or internal deaault.')
        parser.add_option('-r', '--small_plot', help='Produce a small plot '
                          '(boolean \'checked\'=1)')

        # Parse the options
        (options, args) = parser.parse_args()

        ### Process options - all options are given as string, and they need to
        ### be converted into other data types
        # Convert idlist
        self.idlist = [int(element) for element in options.idlist.split(',')[1:]]
        # Turn the offset 'key:value,' pair string into a dictionary
        self.offsets =  dict([[int(offset.split(':')[0]), offset.split(':')[1]]
                              for offset in options.offset.split(',')[1:]])
        # Gather from and to in a fictionary
        self.from_to = {'from':options.from_d, 'to':options.to_d}
        # Turn several options into booleans
        self.as_function_of_t = True if options.as_function_of_t ==\
            'checked' else False
        self.shift_temp_unit = True if options.shift_temp_unit ==\
            'checked' else False
        self.logscale = True if options.logscale == 'checked' else False
        self.flip_x = True if options.flip_x == 'checked' else False
        self.shift_be_ke = True if options.shift_be_ke == 'checked' else False
        self.small_plot = True if options.small_plot == '1' else False
                
        ### Create database backend object
        self.db = dataBaseBackend(typed=options.type, from_to=self.from_to,
                                  id_list=self.idlist, offsets=self.offsets,
                                  as_function_of_t=self.as_function_of_t,
                                  shift_temp_unit=self.shift_temp_unit,
                                  shift_be_ke=self.shift_be_ke)

        ### Ask self.db for a measurement count
        measurement_count = self.db.get_data_count()

        # Set the image format to standard, overwite with gs value and again
        # options value if i exits
        if options.image_format:
            if options.image_format == 'default':
                if self.db.global_settings.has_key('image_format'):
                    self.image_format = self.db.global_settings['image_format']
                else:
                    self.image_format = 'png'
            else:
                self.image_format = options.image_format
        else:
            self.image_format = 'png'
        
        # Create a hash from the measurement_count, options and
        #self.db.global_settings
        hash = hashlib.md5()
        hash.update(str(options) + str(self.db.global_settings) +
                    str(measurement_count))
        # self.namehash is unique for this plot and will form the filename
        self.namehash = ('/var/www/cinfdata/figures/' + hash.hexdigest() + '.' +
                         self.image_format)
        
        # For use in other methods
        self.options = options
 
        # object to give first good color, and then random colors
        self.c = Color()

        self.left_color = 'black'
        self.right_color = 'black'
Example #17
0
class Plot():
    """This class is used to generate the figures for the plots."""
    
    def __init__(self):
        """ Description of init """

        # Create the option parser for the command line options
        usage = ('usage: %prog [options]\n\n'
                 'All options are strings. Boolean options are true when they \n'
                 'contains a certain specific keywords, which is written in \n'
                 'the option description in parantheses.')
        parser = OptionParser(usage=usage)

        # Add the options to the option parser
        parser.add_option('-a', '--type', help='Type string from '
                          'graphsettings.xml')
        parser.add_option('-b', '--idlist', help='List of id\'s to plot')
        parser.add_option('-c', '--from_d', help='From timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-d', '--to_d', help='To timestamp, format: '
                          'YYYY-MM-DD HH:MM')
        parser.add_option('-e', '--xmin', help='X-min for zoom')
        parser.add_option('-f', '--xmax', help='X-max for zoom')
        parser.add_option('-g', '--ymin', help='Y-min for zoom')
        parser.add_option('-i', '--ymax', help='Y-max for zoom')
        parser.add_option('-j', '--offset', help='List of offsets for the '
                          'graphs (for plots that goes on a log scale and has '
                          'negative values)')
        parser.add_option('-k', '--as_function_of_t', help='Plot the graphs as '
                          'a function of temperature (boolean \'checked\'=True)')
        parser.add_option('-l', '--logscale', help='Use a log for the right '
                          'axis (boolean \'checked\'=True)')
        parser.add_option('-m', '--shift_temp_unit', help='Change between K '
                          'and C when values are plotted as a function of '
                          'temperature (boolean \'checked\'=True)')
        parser.add_option('-n', '--flip_x', help='Exchange min and max for the '
                          'x-axis (boolean \'checked\'=True)')
        parser.add_option('-o', '--shift_be_ke', help='Shift between binding '
                          'energy and kinetic energy for XPS plots (boolean '
                          '\'checked\'=True)')
        # -p is availabel from previous options
        parser.add_option('-q', '--image_format', help='Image format for the '
                          'figure exports, given as the figure extension. Can '
                          'be svg, eps, ps, pdf and default. Default means use '
                          'the one in graphsettings.xml or internal deaault.')
        parser.add_option('-r', '--small_plot', help='Produce a small plot '
                          '(boolean \'checked\'=1)')

        # Parse the options
        (options, args) = parser.parse_args()

        ### Process options - all options are given as string, and they need to
        ### be converted into other data types
        # Convert idlist
        self.idlist = [int(element) for element in options.idlist.split(',')[1:]]
        # Turn the offset 'key:value,' pair string into a dictionary
        self.offsets =  dict([[int(offset.split(':')[0]), offset.split(':')[1]]
                              for offset in options.offset.split(',')[1:]])
        # Gather from and to in a fictionary
        self.from_to = {'from':options.from_d, 'to':options.to_d}
        # Turn several options into booleans
        self.as_function_of_t = True if options.as_function_of_t ==\
            'checked' else False
        self.shift_temp_unit = True if options.shift_temp_unit ==\
            'checked' else False
        self.logscale = True if options.logscale == 'checked' else False
        self.flip_x = True if options.flip_x == 'checked' else False
        self.shift_be_ke = True if options.shift_be_ke == 'checked' else False
        self.small_plot = True if options.small_plot == '1' else False
                
        ### Create database backend object
        self.db = dataBaseBackend(typed=options.type, from_to=self.from_to,
                                  id_list=self.idlist, offsets=self.offsets,
                                  as_function_of_t=self.as_function_of_t,
                                  shift_temp_unit=self.shift_temp_unit,
                                  shift_be_ke=self.shift_be_ke)

        ### Ask self.db for a measurement count
        measurement_count = self.db.get_data_count()

        # Set the image format to standard, overwite with gs value and again
        # options value if i exits
        if options.image_format:
            if options.image_format == 'default':
                if self.db.global_settings.has_key('image_format'):
                    self.image_format = self.db.global_settings['image_format']
                else:
                    self.image_format = 'png'
            else:
                self.image_format = options.image_format
        else:
            self.image_format = 'png'
        
        # Create a hash from the measurement_count, options and
        #self.db.global_settings
        hash = hashlib.md5()
        hash.update(str(options) + str(self.db.global_settings) +
                    str(measurement_count))
        # self.namehash is unique for this plot and will form the filename
        self.namehash = ('/var/www/cinfdata/figures/' + hash.hexdigest() + '.' +
                         self.image_format)
        
        # For use in other methods
        self.options = options
 
        # object to give first good color, and then random colors
        self.c = Color()

        self.left_color = 'black'
        self.right_color = 'black'


    def main(self):
        if os.path.exists(self.namehash) and False:
            print self.namehash
        else:
            # Call a bunch of functions
            self._init_plot()
            self._plot()
            if self.left_color != 'black':
                if self.right_color != 'black':
                    self.c.color_axis(self.ax1, self.ax2, self.left_color, self.right_color)
                else:
                    self.c.color_axis(self.ax1, None, self.left_color, None)
            self._legend()
            self._zoom_and_flip()
            self._transform_and_label_axis()
            if not self.small_plot:
                self._title()
            self._grids()
            self._save()

    def _init_plot(self):
        ### Apply settings        
        # Small plots
        if self.small_plot:
            # Apply default settings 
            plt.rcParams.update({'figure.figsize':[4.5, 3.0],
                                 'ytick.labelsize':'x-small',
                                 'xtick.labelsize':'x-small',
                                 'legend.fontsize':'x-small'})
            # Overwrite with values from graphsettings
            plt.rcParams.update(self.db.global_settings['rcparams_small'])
        else:
            plt.rcParams.update({'figure.figsize':[9.0, 6.0],
                                 'axes.titlesize':'24',
                                 'legend.fontsize':'small'})
            plt.rcParams.update(self.db.global_settings['rcparams_regular'])

        self.fig = plt.figure(1)

        self.ax1 = self.fig.add_subplot(111)
        self.ax2 = None

        # Decide on the y axis type
        self.gs = self.db.global_settings
        if self.logscale:
            self.ax1.set_yscale('log')
        elif self.gs['default_yscale'] == 'log':
            self.ax1.set_yscale('log')
    
    def _plot(self):
        # Make plot
        data_in_plot = False
        for data in self.db.get_data():
            if len(data['data']) > 0:
                data_in_plot = data_in_plot or True
                # Speciel case for barplots
                if self.db.global_settings.has_key('default_style') and\
                        self.db.global_settings['default_style'] == 'barplot':
                    self.ax1.bar(data['data'][:,0], data['data'][:,1],
                                 color=self.c.get_color())
                # Normal graph styles
                else:
                    # If the graph go on the right side of the plot
                    if data['info']['on_the_right']:
                        # Initialise secondary plot if it isn't already
                        if not self.ax2:
                            self._init_second_y_axis()
                        # If info has a color (i.e. it is given in gs ordering)
                        if data['info'].has_key('color'):
                            # Set the color for the graph and axis
                            color = data['info']['color']
                            if 'overview' in self.options.type:
                                self.right_color = data['info']['color']
                        else:
                            # Else get a new color from self.c
                            color = self.c.get_color()
                        
                        # Make the actual plot
                        self.ax2.plot(data['data'][:,0], data['data'][:,1],
                                      color=color,
                                      label=self._legend_item(data)+'(R)')
                    # If the graph does not go on the right side of the plot
                    else:
                        # If info has a color (i.e. it is given in gs ordering)
                        if data['info'].has_key('color'):
                            # Set the color for the graph and axis
                            color = data['info']['color']
                            if 'overview' in self.options.type:
                                self.left_color = data['info']['color']
                        else:
                            # Else get a new color from self.c
                            color = self.c.get_color()
                        
                        # Make the actual plot
                        self.ax1.plot(data['data'][:,0], data['data'][:,1],
                                      color=color,
                                      label=self._legend_item(data))
        
        # If no data has been been put on the graph at all, explain why there
        # is none
        if not data_in_plot:
            y = 0.00032 if self.logscale or self.gs['default_yscale'] == 'log' else 0.5
            self.ax1.text(0.5, y, 'No data', horizontalalignment='center',
                          verticalalignment='center', color='red', size=60)

    def _legend(self):
        if self.db.global_settings['default_xscale'] != 'dat':
            ax1_legends = self.ax1.get_legend_handles_labels()
            if self.ax2:
                ax2_legends = self.ax2.get_legend_handles_labels()
                for color, text in zip(ax2_legends[0], ax2_legends[1]):
                    ax1_legends[0].append(color)
                    ax1_legends[1].append(text)
                    
            # loc for locations, 0 means 'best'. Why that isn't deafult I
            # have no idea
            self.ax1.legend(ax1_legends[0], ax1_legends[1], loc=0)

    def _zoom_and_flip(self):
        # Now we are done with the plotting, change axis if necessary
        # Get current axis limits
        self.axis = self.ax1.axis()
        if self.options.xmin != self.options.xmax:
            self.axis = (float(self.options.xmin), float(self.options.xmax)) +\
                self.axis[2:4]
        if self.options.ymin != self.options.ymax:
            self.axis = self.axis[0:2] + (float(self.options.ymin),
                                          float(self.options.ymax))
        if self.flip_x:
            self.axis = (self.axis[1], self.axis[0]) + self.axis[2:4]
        self.ax1.axis(self.axis)
        
    def _transform_and_label_axis(self):
        """ Transform X-AXIS axis and label it """
        
        # If it is a date plot
        if self.db.global_settings['default_xscale'] == 'dat':
            # Turn the x-axis into timemarks
            # IMPLEMENT add something to TimeMarks initialisation to take care
            # or morning_pressure
            markformat = '%H:%M' if self.small_plot else '%b-%d %H:%M'
            timemarks = TimeMarks(self.axis[0], self.axis[1],
                                  markformat=markformat)
            (old_tick_labels, new_tick_labels) = timemarks.get_time_marks()
            self.ax1.set_xticks(old_tick_labels)
            self.bbox_xlabels = self.ax1.\
                set_xticklabels(new_tick_labels, rotation=25,
                                horizontalalignment='right')
            # Make a little extra room for the rotated x marks
            #self.fig.subplots_adjust(bottom=0.12)
        elif self.options.type == 'masstime':
            gs_temp_unit = self.gs['temperature_unit']
            other_temp_unit = 'C' if gs_temp_unit == 'K' else 'K'
            cur_temp_unit = other_temp_unit if self.shift_temp_unit else\
                gs_temp_unit
            if self.as_function_of_t and not self.small_plot:
                self.ax1.set_xlabel(self.gs['t_xlabel'] + cur_temp_unit)
            elif not self.small_plot:
                self.ax1.set_xlabel(self.gs['xlabel'])
        elif self.options.type == 'xps':
            if self.shift_be_ke and not self.small_plot:
                self.ax1.set_xlabel(self.gs['alt_xlabel'])
            elif not self.small_plot:
                self.ax1.set_xlabel(self.gs['xlabel'])
        elif not self.small_plot:
            self.ax1.set_xlabel(self.gs['xlabel'])
        
        # Label Y-axis
        if not self.small_plot:
            self.ax1.set_ylabel(self.gs['ylabel'], color=self.left_color)
            if self.ax2:
                self.ax2.set_ylabel(self.gs['right_ylabel'], color=self.right_color)

    def _title(self):
        """ TITLE """
        # Set the title and raise it a bit
        if self.as_function_of_t:
            self.bbox_title = self.ax1.set_title(
                self.gs['t_title'], y=1.03)
        else:
            self.bbox_title = self.ax1.set_title(
                self.gs['title'], y=1.03)
            
    def _grids(self):
        # GRIDS
        self.ax1.grid(b=True, which = 'major')
        #plt.xscale('linear')
        #plt.xticks(range(0,100,10))
        #plt.x_minor_ticks(range(0,100,10))
        #plt.grid(b='on', which='minor')
        #plt.grid(b='on', which='major')

    def _save(self):
        ## Filesave
        # Save
        self.fig.savefig(self.namehash, bbox_inches='tight', pad_inches=0.03)

        # This is the magical line that plot.php opens
        # For the script to work this has to be the only print statement
        print self.namehash

    ### Here start the small helper functions that are called from the main flow

    def _init_second_y_axis(self):
        self.ax2 = self.ax1.twinx()
        if self.db.global_settings['right_yscale'] == 'log':
            self.ax2.set_yscale('log')

    def _legend_item(self, data):
        if self.db.global_settings['default_xscale'] == 'dat':
            return ''
        elif data['gs'].has_key('legend_field_name') and\
                data['info'][data['gs']['legend_field_name']]:
            return data['info']['mass_label'] + '-' + str(data['info']['id'])
        else:
            return str(data['info']['id'])
Example #18
0
 def set_color_(self, color: common.Color):
     """Changes the controller's indicator to the specified color."""
     self.move_.set_leds(*color.rgb_bytes())
     self.move_.update_leds()
class Plot():
    """This class is used to generate the figures for the plots."""
    
    def __init__(self, options, ggs):
        """ Description of init """
        
        self.o = options
        self.ggs = ggs
        
        # Set the image format to standard, overwite with ggs value and again
        # options value if it exits
        if self.o['image_format'] == '':
            self.image_format = self.ggs['image_format']
        else:
            self.image_format = self.o['image_format']

        # Default values for matplotlib plots (names correspond to ggs names)
        mpl_settings = {'width': 900,
                        'height': 600,
                        'title_size': '24',
                        'xtick_labelsize': '12',
                        'ytick_labelsize': '12',
                        'legend_fontsize': '10',
                        'label_fontsize': '16',
                        'linewidth': 1.0,
                        'grid': False}
        
        # Owerwrite defaults with gs values and convert to appropriate types
        for key, value in mpl_settings.items():
            try:
                mpl_settings[key] = type(value)(self.ggs['matplotlib_settings'][key])
            except KeyError:
                pass
        
        # Write some settings to pyplot
        rc_temp = {'figure.figsize': [float(mpl_settings['width'])/100,
                                      float(mpl_settings['height'])/100],
                   'axes.titlesize': mpl_settings['title_size'],
                   'xtick.labelsize': mpl_settings['xtick_labelsize'],
                   'ytick.labelsize': mpl_settings['ytick_labelsize'],
                   'legend.fontsize': mpl_settings['legend_fontsize'],
                   'axes.labelsize': mpl_settings['label_fontsize'],
                   'lines.linewidth': mpl_settings['linewidth'],
                   'axes.grid': mpl_settings['grid']
                   }
        plt.rcParams.update(rc_temp)
                                                        
        # Plotting options
        self.maxticks=15
        self.tz = GMT1()
        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.measurement_count = None
 
        # object to give first good color, and then random colors
        self.c = Color()

    def new_plot(self, data, plot_info, measurement_count):
        """ Form a new plot with the given data and info """
        self.measurement_count = sum(measurement_count)
        self._init_plot()
        self._plot(data)
        self._zoom_and_flip()
        self._title_and_labels(plot_info)
        self._save(plot_info)

    def _init_plot(self):
        """ Initialize plot """
        self.fig = plt.figure(1)

        self.ax1 = self.fig.add_subplot(111)
        if self.right_yaxis:
            self.ax2 = self.ax1.twinx()

        if self.o['left_logscale']:
            self.ax1.set_yscale('log')
        if self.right_yaxis and self.o['right_logscale']:
            self.ax2.set_yscale('log')

    def _plot(self, data):
        """ Determine the type of the plot and make the appropriate plot by use
        of the functions:
          _plot_dateplot
          _plot_xyplot
        """
        if self.ggs['default_xscale'] == 'dat':
            self._plot_dateplot(data)
        else:
            self._plot_xyplot(data)

    def _plot_dateplot(self, data):
        """ Make the date plot """
        # Rotate datemarks on xaxis
        self.ax1.set_xticklabels([], rotation=25, horizontalalignment='right')
        # Left axis
        for dat in data['left']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax1.plot_date(mdates.epoch2num(dat['data'][:,0]),
                                   dat['data'][:,1],
                                   label=legend,
                                   xdate=True,
                                   color=self.c.get_color(),
                                   tz=self.tz,
                                   fmt='-')
        # Right axis
        for dat in data['right']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax2.plot_date(mdates.epoch2num(dat['data'][:,0]),
                                   dat['data'][:,1],
                                   label=legend,
                                   xdate=True,
                                   color=self.c.get_color(),
                                   tz=self.tz,
                                   fmt='-')
        # No data
        if self.measurement_count == 0:
            y = 0.00032 if self.o['left_logscale'] is True else 0.5
            self.ax1.text(0.5, y, 'No data', horizontalalignment='center',
                          verticalalignment='center', color='red', size=60)

    def _plot_xyplot(self, data):
        # Left axis
        for dat in data['left']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax1.plot(dat['data'][:,0],
                              dat['data'][:,1],
                              '-',
                              label=legend,
                              color=self.c.get_color(),
                              )
        # Right axis
        for dat in data['right']:
            # Form legend
            if dat['lgs'].has_key('legend'):
                legend = dat['lgs']['legend']
            else:
                legend = None
            # Plot
            if len(dat['data']) > 0:
                self.ax2.plot(dat['data'][:,0],
                              dat['data'][:,1],
                              '-',
                              label=legend,
                              color=self.c.get_color()
                              )
        # No data
        if self.measurement_count == 0:
            y = 0.00032 if self.o['left_logscale'] is True else 0.5
            self.ax1.text(0.5, y, 'No data', horizontalalignment='center',
                          verticalalignment='center', color='red', size=60)

    def _zoom_and_flip(self):
        """ Apply the y zooms.
        NOTE: self.ax1.axis() return a list of bounds [xmin,xmax,ymin,ymax] and
        we reuse x and replace y)
        """
        # Left axis 
        if self.o['left_yscale_bounding'] is not None:
            self.ax1.axis(self.ax1.axis()[0:2] + self.o['left_yscale_bounding'])
        # Right axis
        if self.right_yaxis and self.o['right_yscale_bounding'] is not None:
            self.ax2.axis(self.ax2.axis()[0:2] + self.o['right_yscale_bounding'])

        if self.o['flip_x']:
            self.ax1.axis((self.ax1.axis()[1], self.ax1.axis()[0]) + self.ax1.axis()[2:4])

    def _title_and_labels(self, plot_info):
        """ Put title and labels on the plot """
        # xlabel
        if plot_info.has_key('xlabel'):
            label = plot_info['xlabel']
            if plot_info['xlabel_addition'] != '':
                label += '\n' + plot_info['xlabel_addition']
            self.ax1.set_xlabel(label)
        if self.o['xlabel'] != '':  # Manual override
            self.ax1.set_xlabel(r'{0}'.format(self.o['xlabel']))
        # Left ylabel
        if plot_info.has_key('left_ylabel'):
            label = plot_info['left_ylabel']
            if plot_info['y_left_label_addition'] != '':
                label += '\n' + plot_info['y_left_label_addition']
            self.ax1.set_ylabel(label, multialignment='center')
        if self.o['left_ylabel'] != '':  # Manual override
            self.ax1.set_ylabel(self.o['left_ylabel'], multialignment='center')
        # Right ylabel
        if self.right_yaxis and plot_info.has_key('right_ylabel'):
            label = plot_info['right_ylabel']
            if plot_info['y_right_label_addition'] != '':
                label += '\n' + plot_info['y_right_label_addition']
            self.ax2.set_ylabel(label, multialignment='center', rotation=270)
            if self.o['right_ylabel'] != '':  # Manual override
                self.ax2.set_ylabel(self.o['right_ylabel'],
                                    multialignment='center', rotation=270)
        # Title
        if plot_info.has_key('title'):
            self.ax1.set_title(plot_info['title'], y=1.03)
        if self.o['title'] != '':
            # experiment with 'r{0}'.form .. at some time
            self.ax1.set_title('{0}'.format(self.o['title']), y=1.03)
        # Legends
        if self.measurement_count > 0:
            ax1_legends = self.ax1.get_legend_handles_labels()
            if self.right_yaxis:
                ax2_legends = self.ax2.get_legend_handles_labels()
                for color, text in zip(ax2_legends[0], ax2_legends[1]):
                    ax1_legends[0].append(color)
                    ax1_legends[1].append(text)
                    
            # loc for locations, 0 means 'best'. Why that isn't deafult I
            # have no idea
            self.ax1.legend(ax1_legends[0], ax1_legends[1], loc=0)

    def _save(self, plot_info):
        """ Save the figure """
        # The tight method only works if there is a title (it caps of parts of
        # the axis numbers, therefore this hack, this may also become a problem
        # for the other edges of the figure if there are no labels)
        tight = ''
        if plot_info.has_key('title'):
            tight = 'tight'
        # For some wierd reason we cannot write directly to sys.stdout when it
        # is a pdf file, so therefore we use a the StringIO object workaround
        if self.o['image_format'] == 'pdf':
            import StringIO
            out = StringIO.StringIO()
            self.fig.savefig(out, bbox_inches=tight, pad_inches=0.03,
                             format=self.o['image_format'])
            sys.stdout.write(out.getvalue())
        else:
            self.fig.savefig(sys.stdout, bbox_inches=tight, pad_inches=0.03,
                             format=self.o['image_format'])
Example #20
0
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""

    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        self.measurement_count = None
        # Colors object, will be filled in at new_plot
        self.c = None
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.c = Color(data, self.ggs)
        self.measurement_count = sum(measurement_count)
        self._header()
        self._data(data)
        self._options(data, plot_info)
        self._end()
        
    def _header(self):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            self.out.write('document.getElementById("warning_div").innerHTML='\
                               '\"{0}\";'.format(warning))

        # Write dygraph header
        self.out.write('g = new Dygraph(\n' +
                       self.tab + 'document.getElementById("graphdiv"),\n') 

    def _data(self, data):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        self.out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data)
        else:
            self._data_xyplot(file_, data)
        file_.close()

    def _data_dateplot(self, out, data):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(time.strftime(
                            '%Y-%m-%d %H:%M:%S', time.localtime(int(item[0]))
                            ))
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n+1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, data, plot_info):
        """Form all the options and output as JSON"""
        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left'] + data['right']:
            labels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable.
        options = {
            'labels': labels,
            'connectSeparatedPoints': True,
            'legend': 'always',
            }

        # FIXME WORK-AROUND: There is a bug in the new dygraphs, so
        # that if logscale is to be used on any of the axis, it must
        # be set to true as a top level option as well as in the axis
        # specific options:
        # https://github.com/danvk/dygraphs/issues/867
        if self.o['left_logscale'] or self.o['right_logscale']:
            options['logscale'] = True

        # Form the series specific options. Looks like:
        #"series": {
        #    "M4": {
        #        "color": "#63650b", 
        #        "axis": "y"
        #    }, 
        #    "Gas flow 1 [He] (r)": {
        #        "color": "#3b3914", 
        #        "axis": "y2"
        #    }, 
        #}
        series = {}
        for side, axis in (('left', 'y'), ('right', 'y2')):
            for dat in data[side]:
                series[dat['lgs']['legend']] = {
                    'axis': axis,
                    'color': self.c.get_color_hex(),
                }
        options['series'] = series

        # Add axes options
        self._options_axes(options, plot_info)

        # Add title, and axis labels
        self._options_title_axes_labels(options, plot_info)

        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            options['dateWindow'] = self.o['xscale_bounding']

        replacements = self._options_from_graphsettings(options)

        # Generate options, replace function placeholder and write out
        options_text = json.dumps(options, indent=4) + '\n'
        for placeholder, replacement in replacements.items():
            options_text = options_text.replace(placeholder, replacement)
        self.out.write(options_text)


    def _options_axes(self, options, plot_info):
        """Form the axes options, will modify options variable"""
        axes = {
            'x': {'drawGrid': True},
            'y': {
                'logscale': self.o['left_logscale'],
                'drawGrid': False,
                },
            }
        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            axes['y']['valueRange'] = self.o['left_yscale_bounding']

        # Add second yaxis configuration
        if self.right_yaxis:
            axes['y2'] = {
                'logscale': self.o['right_logscale'],
                'drawGrid': False,
                }
            if self.o['right_yscale_bounding'] is not None:
                axes['y2']['valueRange'] = self.o['right_yscale_bounding']

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    axes['y2']['axisLabelWidth'] = 80
                    y2label = plot_info['right_ylabel']
                else:
                    axes['y2']['axisLabelWidth'] = 100
                    y2label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'], 
                        plot_info['y_right_label_addition'],
                        )
                options['y2label'] = y2label

        options['axes'] = axes

    def _options_title_axes_labels(self, options, plot_info):
        """Add title and axis labels"""
        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options['title'] = 'NO DATA'
            else:
                options['title'] = plot_info['title']

        axes = options['axes']
        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                axes['y']['axisLabelWidth'] = 80
                ylabel = plot_info['left_ylabel']
            else:
                axes['y']['axisLabelWidth'] = 100
                ylabel = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'],
                    plot_info['y_left_label_addition'],
                    )
            options['ylabel'] = ylabel

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options['xlabel'] = plot_info['xlabel']
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'],
                        plot_info['xlabel_addition'],
                        )
                    options['xlabel'] = label
                    options.append({'xLabelHeight': '30'})

    def _options_from_graphsettings(self, options):
        """Add options from graphsettings"""
        replacements = {}
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = int(self.ggs['dygraph_settings']['roll_period'])
                options.update({'showRoller': 'true', 'rollPeriod': period})

            # grids
            for axis_name in 'xy':
                grid_settings_str = self.ggs['dygraph_settings']\
                    .get(axis_name + 'grid')
                if grid_settings_str is not None:
                    grid_settings = bool_str(grid_settings_str)
                    options['axes'][axis_name]['drawGrid'] = grid_settings

            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options['highlightSeriesOpts'] = {
                        'strokeWidth': 2,
                        'strokeBorderWidth': 1,
                        'highlightCircleSize': 5,
                        }

            # Labels modifications
            labels_options = {}
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    labels_options.update({
                            'labelsSeparateLines': True,
                            # func1 is just a placeholder that will
                            # be replaced by:
                            # document.getElementById("labels")
                            # in the serialized json, because json
                            # cannot serialize functions
                            'labelsDiv': '#func1#',
                            })
                    replacements['"#func1#"'] = \
                        'document.getElementById("labels")'

            sep_new_lines_str = self.ggs['dygraph_settings']\
                .get('labels_newline')
            if sep_new_lines_str is not None:
                labels_options['labelsSeparateLines'] = \
                    bool_str(sep_new_lines_str)
            options.update(labels_options)

        return replacements

    def _end(self):
        """ Output last line """
        self.out.write(');')
class Plot():
    """ This class is used to generate the dygraph content.

    NOTE: With the version of dygraph used at the time of writing
    version 2 (medio 2012) it was not possible to produce parametric
    plots (for as function of temperature type plots) nor was it
    possible to flip the x axis be reversing the x-axis limits. For
    the latter there is a snippet of code in a comment in the end of
    the file if it is ever made possible."""
    def __init__(self, options, ggs):
        """ Initialize variables """
        self.o = options
        self.ggs = ggs  # Global graph settings

        self.right_yaxis = len(self.o['right_plotlist']) > 0
        self.out = sys.stdout
        self.tab = '    '
        # object to give first good color, and then random colors
        self.c = Color()
        self.reduction = 1

    def new_plot(self, data, plot_info, measurement_count):
        """ Produce all the plot output by calls to the different
        subsection methods
        """
        self.measurement_count = sum(measurement_count)
        self._header(self.out, data, plot_info)
        self._data(self.out, data, plot_info)
        self._options(self.out, data, plot_info)
        self._end(self.out, data, plot_info)

    def _header(self, out, data, plot_info):
        """ Form the header """
        # Get max points from settings of any, default 10000
        #max_points = 100000
        max_points = 50000
        if self.ggs.get('dygraph_settings') is not None:
            if self.ggs['dygraph_settings'].get('max_points') is not None:
                max_points = int(self.ggs['dygraph_settings']['max_points'])
        # Calculate the data point reduction factor
        self.reduction = (self.measurement_count / max_points) + 1

        if self.reduction > 1:
            mouse_over = "This plot, which was supposed to contain {0} data "\
                "points, has been reduced to only plot every {1} point, to "\
                "reduce the total data size. The current data point limit is "\
                "{2} and it can be changed in your graphsettings.xml"
            mouse_over = mouse_over.format(self.measurement_count,
                                           self.reduction, max_points)
            warning = '<b title=\\"{0}\\">Reduced data set (hover for '\
                'details)</b>'.format(mouse_over)
            out.write('document.getElementById("warning_div").innerHTML='\
                          '\"{0}\";'.format(warning))

        # Write dygraph header
        out.write('g = new Dygraph(\n' + self.tab +
                  'document.getElementById("graphdiv"),\n')

    def _data(self, out, data, plot_info):
        """ Determine the type of the plot and call the appropriate
        _data_*** function
        """
        # Generate a random filename for the data
        filename = '../figures/{0}.csv'.format(uuid.uuid4())
        out.write('{0}"{1}",\n'.format(self.tab, filename))
        file_ = open(filename, 'w')
        if self.ggs['default_xscale'] == 'dat':
            self._data_dateplot(file_, data, plot_info)
        else:
            self._data_xyplot(file_, data, plot_info)
        file_.close()

    def _data_dateplot(self, out, data, plot_info):
        """Generate the CSV data for a dateplot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert date in column 0 and y in appropriate column
                    this_line[0] = str(
                        time.strftime('%Y-%m-%d %H:%M:%S',
                                      time.localtime(int(item[0]))))
                    this_line[n + 1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _data_xyplot(self, out, data, plot_info):
        """Generate the CSV data for a XY plot"""
        # Total number of plots
        plot_number = len(self.o['left_plotlist'] + self.o['right_plotlist'])
        # Loop over data series
        for n, dat in enumerate(data['left'] + data['right']):
            this_line = [''] * (plot_number + 1)
            # Loop over points in that series
            for index, item in enumerate(dat['data']):
                if index % self.reduction == 0:
                    # Insert x in column 0 and y in appropriate column
                    this_line[0] = str(item[0])
                    this_line[n + 1] = str(item[1])
                    out.write(','.join(this_line) + '\n')

        # Write one bogus points if there is no data
        if self.measurement_count == 0:
            out.write('42,42\n')

    def _options(self, out, data, plot_info):
        """ Form all the options and ask _output_options to print them in a
        javascript friendly way
        """
        def _q(string):
            """ _q for _quote: Utility function to add quotes to strings """
            return '\'{0}\''.format(string)

        # Form labels string
        labels = [self.ggs['xlabel'] if self.ggs.has_key('xlabel') else '']
        for dat in data['left']:
            labels.append(dat['lgs']['legend'])
        r_ylabels = []
        for dat in data['right']:
            labels.append(dat['lgs']['legend'])
            r_ylabels.append(dat['lgs']['legend'])
        # Overwrite labels if there is no data
        if self.measurement_count == 0:
            labels = ['NO DATA X', 'NO DATA Y']

        # Initiate options variable. A group of options are contained in a list
        # and the options are given as a key value pair in in a dictionary
        # containing only one item
        options = [{'labels': str(labels)}]

        # Add second yaxis configuration
        two_line_y_axis_label = False
        if self.right_yaxis:
            first_label = r_ylabels[0]
            # Add first data set to secondary axis
            # Ex: 'Containment (r)': {axis: {}}
            y2options = [{'logscale': str(self.o['right_logscale']).lower()}]
            if self.o['right_yscale_bounding'] is not None:
                y2options.append(
                    {'valueRange': str(list(self.o['right_yscale_bounding']))})
            options.append({_q(first_label): [{'axis': y2options}]})
            # Add remaining datasets to secondary axis
            for label in r_ylabels[1:]:
                # Ex: 'IG Buffer (r)': {axis: 'Containment (r)'}
                a = {_q(label): [{'axis': _q(first_label)}]}
                options.append(a)

            # Add the right y label
            if plot_info.has_key('right_ylabel'):
                if plot_info['y_right_label_addition'] == '':
                    options.append({'y2label': _q(plot_info['right_ylabel'])})
                else:
                    two_line_y_axis_label = True
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['right_ylabel'],
                        plot_info['y_right_label_addition'])
                    options.append({'y2label': _q(label)})

        # General options
        options += [
            {
                'logscale': str(self.o['left_logscale']).lower()
            },
            {
                'connectSeparatedPoints': 'true'
            },
            {
                'legend': _q('always')
            },
            #{'lineWidth': '0'}
        ]

        # Add title
        if plot_info.has_key('title'):
            if self.measurement_count == 0:
                options.append({'title': _q('NO DATA')})
            else:
                options.append({'title': _q(plot_info['title'])})

        # Add the left y label
        if plot_info.has_key('left_ylabel'):
            if plot_info['y_left_label_addition'] == '':
                options.append({'ylabel': _q(plot_info['left_ylabel'])})
            else:
                two_line_y_axis_label = True
                label = '<font size="3">{0}<br />{1}</font>'.format(
                    plot_info['left_ylabel'],
                    plot_info['y_left_label_addition'])
                options.append({'ylabel': _q(label)})

        # Set the proper space for y axis labels
        if two_line_y_axis_label:
            options.append({'yAxisLabelWidth': '100'})
        else:
            options.append({'yAxisLabelWidth': '80'})

        # Determine the labels and add them
        if plot_info.has_key('xlabel'):
            if self.ggs['default_xscale'] != 'dat':
                if plot_info['xlabel_addition'] == '':
                    options.append({'xlabel': _q(plot_info['xlabel'])})
                else:
                    label = '<font size="3">{0}<br />{1}</font>'.format(
                        plot_info['xlabel'], plot_info['xlabel_addition'])
                    options.append({'xlabel': _q(label)})
                    options.append({'xLabelHeight': '30'})

        colors = [
            self.c.get_color_hex() for dat in data['left'] + data['right']
        ]
        options.append({'colors': str(colors)})

        # Zoom, left y-scale
        if self.o['left_yscale_bounding'] is not None:
            options.append(
                {'valueRange': str(list(self.o['left_yscale_bounding']))})
        # X-scale
        if self.o['xscale_bounding'] is not None and\
                self.o['xscale_bounding'][1] > self.o['xscale_bounding'][0]:
            xzoomstring = '[{0}, {1}]'.format(self.o['xscale_bounding'][0],
                                              self.o['xscale_bounding'][1])
            options.append({'dateWindow': xzoomstring})

        grids = [{'drawXGrid': 'true'}, {'drawYGrid': 'false'}]
        # Add modifications from settings file
        if self.ggs.has_key('dygraph_settings'):
            # roller
            if self.ggs['dygraph_settings'].has_key('roll_period'):
                period = self.ggs['dygraph_settings']['roll_period']
                options += [{'showRoller': 'true'}, {'rollPeriod': period}]
            # grids
            if self.ggs['dygraph_settings'].has_key('xgrid'):
                grids[0]['drawXGrid'] = self.ggs['dygraph_settings']['xgrid']
            if self.ggs['dygraph_settings'].has_key('ygrid'):
                grids[1]['drawYGrid'] = self.ggs['dygraph_settings']['ygrid']
            # high light series
            if self.ggs['dygraph_settings'].has_key('series_highlight'):
                if self.ggs['dygraph_settings']['series_highlight'] == 'true':
                    options.append({
                        'highlightSeriesOpts': [{
                            'strokeWidth': '2'
                        }, {
                            'strokeBorderWidth': '1'
                        }, {
                            'highlightCircleSize': '5'
                        }]
                    })
            if self.ggs['dygraph_settings'].has_key('labels_side'):
                if self.ggs['dygraph_settings']['labels_side'] == 'true':
                    sep_new_lines = 'true'
                    if self.ggs['dygraph_settings'].has_key('labels_newline'):
                        sep_new_lines = self.ggs['dygraph_settings'][
                            'labels_newline']
                    options += [{
                        'labelsDiv':
                        'document.getElementById("labels")'
                    }, {
                        'labelsSeparateLines': sep_new_lines
                    }]
            elif self.ggs['dygraph_settings'].has_key('labels_newline'):
                sep_new_lines = self.ggs['dygraph_settings']['labels_newline']
                options += [{'labelsSeparateLines': sep_new_lines}]

        # Disable grids
        options += grids

        self._output_options(out, None, options, 1, last=True)

    def _output_options(self, out, name, options, level, last=False):
        """ Oh boy! I wish I had documented this when I wrote it! KN """
        if name is None:
            out.write(self.tab * level + '{\n')
        else:
            out.write(self.tab * level + name + ': {\n')

        for n, (key, value) in enumerate([op.items()[0] for op in options]):
            if isinstance(value, list):
                if n == len(value) - 1:
                    self._output_options(out, key, value, level + 1, last=True)
                else:
                    self._output_options(out,
                                         key,
                                         value,
                                         level + 1,
                                         last=False)
            else:
                if n == len(options) - 1:
                    out.write(self.tab * (level + 1) + key + ': ' + value +
                              '\n')
                else:
                    out.write(self.tab * (level + 1) + key + ': ' + value +
                              ',\n')

        if last:
            out.write(self.tab * level + '}\n')
        else:
            out.write(self.tab * level + '},\n')

    def _end(self, out, data, plot_info):
        """ Output last line """
        out.write(');')