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 __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))
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
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(');')
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(');')
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'
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'])
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'])
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'
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'])
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'])
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(');')