From ad18e3127708fb259454da423dbb11b5f45ab4d1 Mon Sep 17 00:00:00 2001 From: jaraco Date: Sun, 10 Feb 2008 16:43:52 +0000 Subject: [PATCH] Removed file/folder --- Plot.py | 518 ----------------------------------- TimeSeries.py | 181 ------------- __init__.py | 728 -------------------------------------------------- build.py | 23 -- testing.py | 44 --- testing.rb | 45 ---- 6 files changed, 1539 deletions(-) delete mode 100644 Plot.py delete mode 100644 TimeSeries.py delete mode 100644 __init__.py delete mode 100644 build.py delete mode 100644 testing.py delete mode 100644 testing.rb diff --git a/Plot.py b/Plot.py deleted file mode 100644 index ba6ffdf..0000000 --- a/Plot.py +++ /dev/null @@ -1,518 +0,0 @@ -#!/usr/bin/env python -import SVG -from itertools import izip, count, chain - -def get_pairs( i ): - i = iter( i ) - while True: yield i.next(), i.next() - -def float_range( start = 0, stop = None, step = 1 ): - "Much like the built-in function range, but accepts floats" - while start < stop: - yield float( start ) - start += step - -class Plot( SVG.Graph ): - """=== For creating SVG plots of scalar data - - = Synopsis - - require 'SVG/Graph/Plot' - - # Data sets are x,y pairs - # Note that multiple data sets can differ in length, and that the - # data in the datasets needn't be in order; they will be ordered - # by the plot along the X-axis. - projection = [ - 6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13, - 7, 9 - ] - actual = [ - 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12, - 15, 6, 4, 17, 2, 12 - ] - - graph = SVG::Graph::Plot.new({ - :height => 500, - :width => 300, - :key => true, - :scale_x_integers => true, - :scale_y_integerrs => true, - }) - - graph.add_data({ - :data => projection - :title => 'Projected', - }) - - graph.add_data({ - :data => actual, - :title => 'Actual', - }) - - print graph.burn() - - = Description - - Produces a graph of scalar data. - - This object aims to allow you to easily create high quality - SVG[http://www.w3c.org/tr/svg] scalar plots. You can either use the - default style sheet or supply your own. Either way there are many options - which can be configured to give you control over how the graph is - generated - with or without a key, data elements at each point, title, - subtitle etc. - - = Examples - - http://www.germane-software/repositories/public/SVG/test/plot.rb - - = Notes - - The default stylesheet handles upto 10 data sets, if you - use more you must create your own stylesheet and add the - additional settings for the extra data sets. You will know - if you go over 10 data sets as they will have no style and - be in black. - - Unlike the other types of charts, data sets must contain x,y pairs: - - [ 1, 2 ] # A data set with 1 point: (1,2) - [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6) - - = See also - - * SVG::Graph::Graph - * SVG::Graph::BarHorizontal - * SVG::Graph::Bar - * SVG::Graph::Line - * SVG::Graph::Pie - * SVG::Graph::TimeSeries - - == Author - - Sean E. Russell - - Copyright 2004 Sean E. Russell - This software is available under the Ruby license[LICENSE.txt]""" - - top_align = right_align = top_font = right_font = 1 - - - """Determines the scaling for the Y axis divisions. - - graph.scale_y_divisions = 0.5 - - would cause the graph to attempt to generate labels stepped by 0.5; EG: - 0, 0.5, 1, 1.5, 2, ...""" - scale_y_divisions = None - "Make the X axis labels integers" - scale_x_integers = False - "Make the Y axis labels integers" - scale_y_integers = False - "Fill the area under the line" - area_fill = False - """Show a small circle on the graph where the line - goes from one point to the next.""" - show_data_points = True - "Indicate whether the lines should be drawn between points" - draw_lines_between_points = True - "Set the minimum value of the X axis" - min_x_value = None - "Set the minimum value of the Y axis" - min_y_value = None - "Set the maximum value of the X axis" - max_x_value = None - "Set the maximum value of the Y axis" - max_y_value = None - - stacked = False - - @apply - def scale_x_divisions(): - doc = """Determines the scaling for the X axis divisions. - - graph.scale_x_divisions = 2 - - would cause the graph to attempt to generate labels stepped by 2; EG: - 0,2,4,6,8...""" - def fget( self ): - return getattr( self, '_scale_x_divisions', None ) - def fset( self, val ): - self._scale_x_divisions = val - return property(**locals()) - - def validate_data( self, data ): - if len( data['data'] ) % 2 != 0: - raise "Expecting x,y pairs for data points for %s." % self.__class__.__name__ - - def process_data( self, data ): - pairs = list( get_pairs( data['data'] ) ) - pairs.sort() - data['data'] = zip( *pairs ) - - def calculate_left_margin( self ): - super( Plot, self ).calculate_left_margin() - label_left = len( str( self.get_x_labels()[0] ) ) / 2 * self.font_size * 0.6 - self.border_left = max( label_left, self.border_left ) - - def calculate_right_margin( self ): - super( Plot, self ).calculate_right_margin() - label_right = len( str( self.get_x_labels()[-1] ) ) / 2 * self.font_size * 0.6 - self.border_right = max( label_right, self.border_right ) - - def data_max( self, axis ): - data_index = getattr( self, '%s_data_index' % axis ) - max_value = max( chain( *map( lambda set: set['data'][data_index], self.data ) ) ) - # above is same as - #max_value = max( map( lambda set: max( set['data'][data_index] ), self.data ) ) - spec_max = getattr( self, 'max_%s_value' % axis ) - max_value = max( max_value, spec_max ) - return max_value - - def data_min( self, axis ): - data_index = getattr( self, '%s_data_index' % axis ) - min_value = min( chain( *map( lambda set: set['data'][data_index], self.data ) ) ) - spec_min = getattr( self, 'min_%s_value' % axis ) - if spec_min is not None: - min_value = min( min_value, spec_min ) - return min_value - - x_data_index = 0 - y_data_index = 1 - def data_range( self, axis ): - side = { 'x': 'right', 'y': 'top' }[axis] - - min_value = self.data_min( axis ) - max_value = self.data_max( axis ) - range = max_value - min_value - - side_pad = range / 20.0 or 10 - scale_range = ( max_value + side_pad ) - min_value - - scale_division = getattr( self, 'scale_%s_divisions' % axis ) or ( scale_range / 10.0 ) - - if getattr( self, 'scale_%s_integers' % axis ): - scale_division = scale_division.round() or 1 - - return min_value, max_value, scale_division - - def x_range( self ): return self.data_range( 'x' ) - def y_range( self ): return self.data_range( 'y' ) - - def get_data_values( self, axis ): - min_value, max_value, scale_division = self.data_range( axis ) - return tuple( float_range( *self.data_range( axis ) ) ) - - def get_x_values( self ): return self.get_data_values( 'x' ) - def get_y_values( self ): return self.get_data_values( 'y' ) - - def get_x_labels( self ): - return map( str, self.get_x_values() ) - def get_y_labels( self ): - return map( str, self.get_y_values() ) - - def field_size( self, axis ): - size = { 'x': 'width', 'y': 'height' }[axis] - side = { 'x': 'right', 'y': 'top' }[axis] - values = getattr( self, 'get_%s_values' % axis )() - max_d = self.data_max( axis ) - dx = float( max_d - values[-1] ) / ( values[-1] - values[-2] ) - graph_size = getattr( self, 'graph_%s' % size ) - side_font = getattr( self, '%s_font' % side ) - side_align = getattr( self, '%s_align' % side ) - result = ( float( graph_size ) - self.font_size*2*side_font ) / \ - ( len( values ) + dx - side_align ) - return result - - def field_width( self ): return self.field_size( 'x' ) - def field_height( self ): return self.field_size( 'y' ) - - def draw_data( self ): - self.load_transform_parameters() - for line, data in izip( count(1), self.data ): - x_start, y_start = self.transform_output_coordinates( - ( data['data'][self.x_data_index][0], - data['data'][self.y_data_index][0] ) - ) - data_points = zip( *data['data'] ) - graph_points = self.get_graph_points( data_points ) - lpath = self.get_lpath( graph_points ) - if self.area_fill: - graph_height = self.graph_height - path = self._create_element( 'path', { - 'd': 'M%(x_start)f %(graph_height)f %(lpath)s V%(graph_height)f Z' % vars(), - 'class': 'fill%(line)d' % vars() } ) - self.graph.appendChild( path ) - if self.draw_lines_between_points: - path = self._create_element( 'path', { - 'd': 'M%(x_start)f %(y_start)f %(lpath)s' % vars(), - 'class': 'line%(line)d' % vars() } ) - self.graph.appendChild( path ) - self.draw_data_points( line, data_points, graph_points ) - self._draw_constant_lines( ) - del self.__transform_parameters - - def add_constant_line( self, value, label = None, style = None ): - self.constant_lines = getattr( self, 'constant_lines', [] ) - self.constant_lines.append( ( value, label, style ) ) - - def _draw_constant_lines( self ): - if hasattr( self, 'constant_lines' ): - map( self.__draw_constant_line, self.constant_lines ) - - def __draw_constant_line( self, ( value, label, style ) ): - "Draw a constant line on the y-axis with the label" - start = self.transform_output_coordinates( ( 0, value ) )[1] - stop = self.graph_width - path = self._create_element( 'path', { - 'd': 'M 0 %(start)s h%(stop)s' % vars(), - 'class': 'constantLine' } ) - if style: - path['style'] = style - self.graph.appendChild( path ) - text = self._create_element( 'text', { - 'x': str( 2 ), - 'y': str( start - 2 ), - 'class': 'constantLine' } ) - text.appendChild( self._doc.createTextNode( label ) ) - self.graph.appendChild( text ) - - def load_transform_parameters( self ): - "Cache the parameters necessary to transform x & y coordinates" - x_min, x_max, x_div = self.x_range() - y_min, y_max, y_div = self.y_range() - x_step = ( float( self.graph_width ) - self.font_size*2 ) / \ - ( x_max - x_min ) - y_step = ( float( self.graph_height ) - self.font_size*2 ) / \ - ( y_max - y_min ) - self.__transform_parameters = dict( vars() ) - del self.__transform_parameters['self'] - - def get_graph_points( self, data_points ): - return map( self.transform_output_coordinates, data_points ) - - def get_lpath( self, points ): - points = map( lambda p: "%f %f" % p, points ) - return 'L' + ' '.join( points ) - - def transform_output_coordinates( self, (x,y) ): - x_min = self.__transform_parameters['x_min'] - x_step = self.__transform_parameters['x_step'] - y_min = self.__transform_parameters['y_min'] - y_step = self.__transform_parameters['y_step'] - #locals().update( self.__transform_parameters ) - #vars().update( self.__transform_parameters ) - x = ( x - x_min ) * x_step - y = self.graph_height - ( y - y_min ) * y_step - return x,y - - def draw_data_points( self, line, data_points, graph_points ): - if not self.show_data_points \ - and not self.show_data_values: return - for ((dx,dy),(gx,gy)) in izip( data_points, graph_points ): - if self.show_data_points: - circle = self._create_element( 'circle', { - 'cx': str( gx ), - 'cy': str( gy ), - 'r': '2.5', - 'class': 'dataPoint%(line)s' % vars() } ) - self.graph.appendChild( circle ) - if self.show_data_values: - self.add_popup( gx, gy, self.format( dx, dy ) ) - self.make_datapoint_text( gx, gy-6, dy ) - - def format( self, x, y ): - return '(%0.2f, %0.2f)' % (x,y) - - def get_css( self ): - return """/* default line styles */ -.line1{ - fill: none; - stroke: #ff0000; - stroke-width: 1px; -} -.line2{ - fill: none; - stroke: #0000ff; - stroke-width: 1px; -} -.line3{ - fill: none; - stroke: #00ff00; - stroke-width: 1px; -} -.line4{ - fill: none; - stroke: #ffcc00; - stroke-width: 1px; -} -.line5{ - fill: none; - stroke: #00ccff; - stroke-width: 1px; -} -.line6{ - fill: none; - stroke: #ff00ff; - stroke-width: 1px; -} -.line7{ - fill: none; - stroke: #00ffff; - stroke-width: 1px; -} -.line8{ - fill: none; - stroke: #ffff00; - stroke-width: 1px; -} -.line9{ - fill: none; - stroke: #ccc6666; - stroke-width: 1px; -} -.line10{ - fill: none; - stroke: #663399; - stroke-width: 1px; -} -.line11{ - fill: none; - stroke: #339900; - stroke-width: 1px; -} -.line12{ - fill: none; - stroke: #9966FF; - stroke-width: 1px; -} -/* default fill styles */ -.fill1{ - fill: #cc0000; - fill-opacity: 0.2; - stroke: none; -} -.fill2{ - fill: #0000cc; - fill-opacity: 0.2; - stroke: none; -} -.fill3{ - fill: #00cc00; - fill-opacity: 0.2; - stroke: none; -} -.fill4{ - fill: #ffcc00; - fill-opacity: 0.2; - stroke: none; -} -.fill5{ - fill: #00ccff; - fill-opacity: 0.2; - stroke: none; -} -.fill6{ - fill: #ff00ff; - fill-opacity: 0.2; - stroke: none; -} -.fill7{ - fill: #00ffff; - fill-opacity: 0.2; - stroke: none; -} -.fill8{ - fill: #ffff00; - fill-opacity: 0.2; - stroke: none; -} -.fill9{ - fill: #cc6666; - fill-opacity: 0.2; - stroke: none; -} -.fill10{ - fill: #663399; - fill-opacity: 0.2; - stroke: none; -} -.fill11{ - fill: #339900; - fill-opacity: 0.2; - stroke: none; -} -.fill12{ - fill: #9966FF; - fill-opacity: 0.2; - stroke: none; -} -/* default line styles */ -.key1,.dataPoint1{ - fill: #ff0000; - stroke: none; - stroke-width: 1px; -} -.key2,.dataPoint2{ - fill: #0000ff; - stroke: none; - stroke-width: 1px; -} -.key3,.dataPoint3{ - fill: #00ff00; - stroke: none; - stroke-width: 1px; -} -.key4,.dataPoint4{ - fill: #ffcc00; - stroke: none; - stroke-width: 1px; -} -.key5,.dataPoint5{ - fill: #00ccff; - stroke: none; - stroke-width: 1px; -} -.key6,.dataPoint6{ - fill: #ff00ff; - stroke: none; - stroke-width: 1px; -} -.key7,.dataPoint7{ - fill: #00ffff; - stroke: none; - stroke-width: 1px; -} -.key8,.dataPoint8{ - fill: #ffff00; - stroke: none; - stroke-width: 1px; -} -.key9,.dataPoint9{ - fill: #cc6666; - stroke: none; - stroke-width: 1px; -} -.key10,.dataPoint10{ - fill: #663399; - stroke: none; - stroke-width: 1px; -} -.key11,.dataPoint11{ - fill: #339900; - stroke: none; - stroke-width: 1px; -} -.key12,.dataPoint12{ - fill: #9966FF; - stroke: none; - stroke-width: 1px; -} -.constantLine{ - color: navy; - stroke: navy; - stroke-width: 1px; - stroke-dasharray: 9 1 1; -} -""" \ No newline at end of file diff --git a/TimeSeries.py b/TimeSeries.py deleted file mode 100644 index 5b390fb..0000000 --- a/TimeSeries.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python -import SVG -import re -#requires python date-util from http://labix.org/python-dateutil -from dateutil.parser import parse -from dateutil.relativedelta import relativedelta -from time import mktime -import datetime -fromtimestamp = datetime.datetime.fromtimestamp - -class Plot( SVG.Plot.Plot ): - """=== For creating SVG plots of scalar temporal data - - = Synopsis - - import SVG.TimeSeries - - # Data sets are x,y pairs - data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11, - "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13] - data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4, - "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6, - "5/1/84", 17, "10/1/80", 12] - - graph = SVG::Graph::TimeSeries.new( { - :width => 640, - :height => 480, - :graph_title => title, - :show_graph_title => true, - :no_css => true, - :key => true, - :scale_x_integers => true, - :scale_y_integers => true, - :min_x_value => 0, - :min_y_value => 0, - :show_data_labels => true, - :show_x_guidelines => true, - :show_x_title => true, - :x_title => "Time", - :show_y_title => true, - :y_title => "Ice Cream Cones", - :y_title_text_direction => :bt, - :stagger_x_labels => true, - :x_label_format => "%m/%d/%y", - }) - - graph.add_data({ - :data => projection - :title => 'Projected', - }) - - graph.add_data({ - :data => actual, - :title => 'Actual', - }) - - print graph.burn() - - = Description - - Produces a graph of temporal scalar data. - - = Examples - - http://www.germane-software/repositories/public/SVG/test/timeseries.rb - - = Notes - - The default stylesheet handles upto 10 data sets, if you - use more you must create your own stylesheet and add the - additional settings for the extra data sets. You will know - if you go over 10 data sets as they will have no style and - be in black. - - Unlike the other types of charts, data sets must contain x,y pairs: - - [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) - [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and - # ("14:20",6) - - Note that multiple data sets within the same chart can differ in length, - and that the data in the datasets needn't be in order; they will be ordered - by the plot along the X-axis. - - The dates must be parseable by ParseDate, but otherwise can be - any order of magnitude (seconds within the hour, or years) - - = See also - - * SVG::Graph::Graph - * SVG::Graph::BarHorizontal - * SVG::Graph::Bar - * SVG::Graph::Line - * SVG::Graph::Pie - * SVG::Graph::Plot - - == Author - - Sean E. Russell - - Copyright 2004 Sean E. Russell - This software is available under the Ruby license[LICENSE.txt] -""" - popup_format = x_label_format = '%Y-%m-%d %H:%M:%S' - __doc_popup_format_ = "The formatting usped for the popups. See x_label_format" - __doc_x_label_format_ = "The format string used to format the X axis labels. See strftime." - - timescale_divisions = None - __doc_timescale_divisions_ = """Use this to set the spacing between dates on the axis. The value - must be of the form - "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?" - - EG: - - graph.timescale_divisions = "2 weeks" - - will cause the chart to try to divide the X axis up into segments of - two week periods.""" - - def add_data( self, data ): - """Add data to the plot. - d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) - d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and - # ("14:20",6) - graph.add_data( - :data => d1, - :title => 'One' - ) - graph.add_data( - :data => d2, - :title => 'Two' - ) - - Note that the data must be in time,value pairs, and that the date format - may be any date that is parseable by ParseDate.""" - super( Plot, self ).add_data( data ) - - def process_data( self, data ): - super( Plot, self ).process_data( data ) - # the date should be in the first element, so parse it out - data['data'][0] = map( self.parse_date, data['data'][0] ) - - _min_x_value = SVG.Plot.Plot.min_x_value - def get_min_x_value( self ): - return self._min_x_value - def set_min_x_value( self, date ): - self._min_x_value = self.parse_date( date ) - min_x_value = property( get_min_x_value, set_min_x_value ) - - def format( self, x, y ): - return fromtimestamp( x ).strftime( self.popup_format ) - - def get_x_labels( self ): - return map( lambda t: fromtimestamp( t ).strftime( self.x_label_format ), self.get_x_values() ) - - def get_x_values( self ): - result = self.get_x_timescale_division_values() - if result: return result - return tuple( SVG.Plot.float_range( *self.x_range() ) ) - - def get_x_timescale_division_values( self ): - if not self.timescale_divisions: return - min, max, scale_division = self.x_range() - m = re.match( '(?P\d+) ?(?Pdays|weeks|months|years|hours|minutes|seconds)?', self.timescale_divisions ) - # copy amount and division_units into the local namespace - division_units = m.groupdict()['division_units'] or 'days' - amount = int( m.groupdict()['amount'] ) - if not amount: return - delta = relativedelta( **{ division_units: amount } ) - result = tuple( self.get_time_range( min, max, delta ) ) - return result - - def get_time_range( self, start, stop, delta ): - start, stop = map( fromtimestamp, (start, stop ) ) - current = start - while current <= stop: - yield mktime( current.timetuple() ) - current += delta - - def parse_date( self, date_string ): - return mktime( parse( date_string ).timetuple() ) \ No newline at end of file diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 5da88b0..0000000 --- a/__init__.py +++ /dev/null @@ -1,728 +0,0 @@ -# -*- coding: UTF-8 -*- - -__all__ = ( 'Plot', 'TimeSeries' ) - -from xml.dom import minidom as dom -from operator import itemgetter -from itertools import islice - -try: - import zlib - __have_zlib = True -except ImportError: - __have_zlib = False - -def sort_multiple( arrays ): - "sort multiple lists (of equal size) using the first list for the sort keys" - tuples = zip( *arrays ) - tuples.sort() - return zip( *tuples ) - -class Graph( object ): - """=== Base object for generating SVG Graphs - -== Synopsis - -This class is only used as a superclass of specialized charts. Do not -attempt to use this class directly, unless creating a new chart type. - -For examples of how to subclass this class, see the existing specific -subclasses, such as SVG.Pie. - -== Examples - -For examples of how to use this package, see either the test files, or -the documentation for the specific class you want to use. - -* file:test/plot.rb -* file:test/single.rb -* file:test/test.rb -* file:test/timeseries.rb - -== Description - -This package should be used as a base for creating SVG graphs. - -== Acknowledgements - -Sean E. Russel for creating the SVG::Graph Ruby package from which this -Python port is derived. - -Leo Lapworth for creating the SVG::TT::Graph package which the Ruby -port is based on. - -Stephen Morgan for creating the TT template and SVG. - -== See - -* SVG.BarHorizontal -* SVG.Bar -* SVG.Line -* SVG.Pie -* SVG.Plot -* SVG.TimeSeries - -== Author - -Jason R. Coombs - -Copyright 2005 Sandia National Laboratories -""" - width= 500 - height= 300 - show_x_guidelines= False - show_y_guidelines= True - show_data_values= True - min_scale_value= 0 - show_x_labels= True - stagger_x_labels= False - rotate_x_labels= False - step_x_labels= 1 - step_include_first_x_label= True - show_y_labels= True - rotate_y_labels= False - stagger_y_labels= False - step_include_first_y_label= True - step_y_labels= 1 - scale_integers= False - show_x_title= False - x_title= 'X Field names' - show_y_title= False - y_title_text_direction= 'bt' # 'bt' for bottom to top; 'tb' for top to bottom - y_title= 'Y Scale' - show_graph_title= False - graph_title= 'Graph Title' - show_graph_subtitle= False - graph_subtitle= 'Graph Subtitle' - key= True - key_position= 'right' # 'bottom' or 'right', - - font_size= 12 - title_font_size= 16 - subtitle_font_size= 14 - x_label_font_size= 12 - x_title_font_size= 14 - y_label_font_size= 12 - y_title_font_size= 14 - key_font_size= 10 - - no_css= False - add_popups= False - - top_align = top_font = right_align = right_font = 0 - - def __init__( self, config = {} ): - """Initialize the graph object with the graph settings. You won't - instantiate this class directly; see the subclass for options.""" - self.load_config( config ) - self.clear_data() - - def load_config( self, config ): - self.__dict__.update( config ) - - def add_data( self, conf ): - """This method allows you do add data to the graph object. - It can be called several times to add more data sets in. - - >>> data_sales_02 = [12, 45, 21] - >>> graph.add_data({ - ... 'data': data_sales_02, - ... 'title': 'Sales 2002' - ... }) - """ - self.validate_data( conf ) - self.process_data( conf ) - self.data.append( conf ) - - def validate_data( self, data ): - try: - assert( isinstance( conf['data'], ( tuple, list ) ) ) - except TypeError, e: - raise TypeError, "conf should be dictionary with 'data' and other items" - except AssertionError: - if not hasattr( conf['data'], '__iter__' ): - raise TypeError, "conf['data'] should be tuple or list or iterable" - - def process_data( data ): pass - - def clear_data( self ): - """This method removes all data from the object so that you can - reuse it to create a new graph but with the same config options. - - >>> graph.clear_data()""" - self.data = [] - - def burn( self ): - """This method processes the template with the data and - config which has been set and returns the resulting SVG. - - This method will croak unless at least one data set has - been added to the graph object. - - Ex: graph.burn()""" - if not self.data: raise ValueError( "No data available" ) - - if hasattr( self, 'calculations' ): self.calculations() - - self.start_svg() - self.calculate_graph_dimensions() - self.foreground = self._create_element( "g" ) - self.draw_graph() - self.draw_titles() - self.draw_legend() - self.draw_data() - self.graph.appendChild( self.foreground ) - self.style() - - data = self._doc.toprettyxml() - - if hasattr( self, 'compress' ) and self.compress: - if __have_zlib: - data = zlib.compress( data ) - else: - data += '' - - return data - - KEY_BOX_SIZE = 12 - - def calculate_left_margin( self ): - """Override this (and call super) to change the margin to the left - of the plot area. Results in border_left being set.""" - bl = 7 - # Check for Y labels - if self.rotate_y_labels: - max_y_label_height_px = self.y_label_font_size - else: - label_lengths = map( len, self.get_y_labels() ) - max_y_label_len = max( label_lengths ) - max_y_label_height_px = 0.6 * max_y_label_len * self.y_label_font_size - if self.show_y_labels: bl += max_y_label_height_px - if self.stagger_y_labels: bl += max_y_label_height_px + 10 - if self.show_y_title: bl += self.y_title_font_size + 5 - self.border_left = bl - - def max_y_label_width_px( self ): - """Calculates the width of the widest Y label. This will be the - character height if the Y labels are rotated.""" - if self.rotate_y_labels: - return self.font_size - - def calculate_right_margin( self ): - """Override this (and call super) to change the margin to the right - of the plot area. Results in border_right being set.""" - br = 7 - if self.key and self.key_position == 'right': - max_key_len = max( map( len, self.keys() ) ) - br += max_key_len * self.key_font_size * 0.6 - br += self.KEY_BOX_SIZE - br += 10 # Some padding around the box - self.border_right = br - - def calculate_top_margin( self ): - """Override this (and call super) to change the margin to the top - of the plot area. Results in border_top being set.""" - self.border_top = 5 - if self.show_graph_title: self.border_top += self.title_font_size - self.border_top += 5 - if self.show_graph_subtitle: self.border_top += self.subtitle_font_size - - def add_popup( self, x, y, label ): - "Adds pop-up point information to a graph." - txt_width = len( label ) * self.font_size * 0.6 + 10 - tx = x + [5,-5][int(x+txt_width > self.width)] - t = self._create_element( 'text' ) - anchor = ['start', 'end'][x+txt_width > self.width] - style = 'fill: #000; text-anchor: %s;' % anchor - id = 'label-%s' % label - attributes = { 'x': str( tx ), - 'y': str( y - self.font_size ), - 'visibility': 'hidden', - 'style': style, - 'text': label, - 'id': id - } - map( lambda a: t.setAttribute( *a ), attributes.items() ) - self.foreground.appendChild( t ) - - visibility = "document.getElementById(%s).setAttribute('visibility', %%s )" % id - t = self._create_element( 'circle' ) - attributes = { 'cx': str( x ), - 'cy': str( y ), - 'r': 10, - 'style': 'opacity: 0;', - 'onmouseover': visibility % 'visible', - 'onmouseout': visibility % 'hidden', - } - map( lambda a: t.setAttribute( *a ), attributes.items() ) - - def calculate_bottom_margin( self ): - """Override this (and call super) to change the margin to the bottom - of the plot area. Results in border_bottom being set.""" - bb = 7 - if self.key and self.key_position == 'bottom': - bb += len( self.data ) * ( self.font_size + 5 ) - bb += 10 - if self.show_x_labels: - max_x_label_height_px = self.x_label_font_size - if self.rotate_x_labels: - label_lengths = map( len, self.get_x_labels() ) - max_x_label_len = reduce( max, label_lengths ) - max_x_label_height_px *= 0.6 * max_x_label_len - bb += max_x_label_height_px - if self.stagger_x_labels: bb += max_x_label_height_px + 10 - if self.show_x_title: bb += self.x_title_font_size + 5 - self.border_bottom = bb - - def draw_graph( self ): - transform = 'translate ( %s %s )' % ( self.border_left, self.border_top ) - self.graph = self._create_element( 'g', { 'transform': transform } ) - self.root.appendChild( self.graph ) - - self.graph.appendChild( self._create_element( 'rect', { - 'x': '0', - 'y': '0', - 'width': str( self.graph_width ), - 'height': str( self.graph_height ), - 'class': 'graphBackground' - } ) ) - - #Axis - self.graph.appendChild( self._create_element( 'path', { - 'd': 'M 0 0 v%s' % self.graph_height, - 'class': 'axis', - 'id': 'xAxis' - } ) ) - self.graph.appendChild( self._create_element( 'path', { - 'd': 'M 0 %s h%s' % ( self.graph_height, self.graph_width ), - 'class': 'axis', - 'id': 'yAxis' - } ) ) - - self.draw_x_labels() - self.draw_y_labels() - - def x_label_offset( self, width ): - """Where in the X area the label is drawn - Centered in the field, should be width/2. Start, 0.""" - return 0 - - def make_datapoint_text( self, x, y, value, style='' ): - if self.show_data_values: - e = self._create_element( 'text', { - 'x': str( x ), - 'y': str( y ), - 'class': 'dataPointLabel', - 'style': '%(style)s stroke: #fff; stroke-width: 2;' % vars(), - } ) - e.appendChild( self._doc.createTextNode( str( value ) ) ) - self.foreground.appendChild( e ) - e = self._create_element( 'text', { - 'x': str( x ), - 'y': str( y ), - 'class': 'dataPointLabel' } ) - e.appendChild( self._doc.createTextNode( str( value ) ) ) - if style: e.setAttribute( 'style', style ) - self.foreground.appendChild( e ) - - def draw_x_labels( self ): - "Draw the X axis labels" - if self.show_x_labels: - labels = self.get_x_labels() - count = len( labels ) - - labels = enumerate( iter( labels ) ) - start = int( not self.step_include_first_x_label ) - labels = islice( labels, start, None, self.step_x_labels ) - map( self.draw_x_label, labels ) - self.draw_x_guidelines( self.field_width(), count ) - - def draw_x_label( self, label ): - label_width = self.field_width() - index, label = label - text = self._create_element( 'text', { 'class': 'xAxisLabels' } ) - text.appendChild( self._doc.createTextNode( label ) ) - self.graph.appendChild( text ) - - x = index * label_width + self.x_label_offset( label_width ) - y = self.graph_height + self.x_label_font_size + 3 - t = 0 - ( self.font_size / 2 ) - - if self.stagger_x_labels and (index % 2 ): - stagger = self.x_label_font_size + 5 - y += stagger - graph_height = self.graph_height - path = self._create_element( 'path', { - 'd': 'M%(x)f %(graph_height)f v%(stagger)d' % vars(), - 'class': 'staggerGuideLine' - } ) - self.graph.appendChild( path ) - - text.setAttribute( 'x', str( x ) ) - text.setAttribute( 'y', str( y ) ) - - if self.rotate_x_labels: - transform = 'rotate( 90 %d %d ) translate( 0 -%d )' % \ - ( x, y-self.x_label_font_size, x_label_font_size/4 ) - text.setAttribute( 'transform', transform ) - text.setAttribute( 'style', 'text-anchor: start' ) - else: - text.setAttribute( 'style', 'text-anchor: middle' ) - - def y_label_offset( self, height ): - """Where in the Y area the label is drawn - Centered in the field, should be width/2. Start, 0.""" - return 0 - - def get_field_width( self ): - return float( self.graph_width - self.font_size*2*self.right_font ) / \ - ( len( self.get_x_labels() ) - self.right_align ) - field_width = property( get_field_width ) - - def get_field_height( self ): - return float( self.graph_height - self.font_size*2*self.top_font ) / \ - ( len( self.get_y_labels() ) - self.top_align ) - field_height = property( get_field_height ) - - def draw_y_labels( self ): - "Draw the Y axis labels" - if self.show_y_labels: - labels = self.get_y_labels() - count = len( labels ) - - labels = enumerate( iter( labels ) ) - start = int( not self.step_include_first_y_label ) - labels = islice( labels, start, None, self.step_y_labels ) - map( self.draw_y_label, labels ) - self.draw_y_guidelines( self.field_height(), count ) - - def get_y_offset( self ): - #result = self.graph_height + self.y_label_offset( label_height ) - result = self.graph_height + self.y_label_offset( self.field_height() ) - if not self.rotate_y_labels: result += self.font_size/1.2 - return result - y_offset = property( get_y_offset ) - - def draw_y_label( self, label ): - label_height = self.field_height() - index, label = label - text = self._create_element( 'text', { 'class': 'yAxisLabels' } ) - text.appendChild( self._doc.createTextNode( label ) ) - self.graph.appendChild( text ) - - y = self.y_offset - ( label_height * index ) - x = {True: 0, False:-3}[self.rotate_y_labels] - - if self.stagger_y_labels and (index % 2 ): - stagger = self.y_label_font_size + 5 - x -= stagger - path = self._create_element( 'path', { - 'd': 'M%(x)f %(y)f h%(stagger)d' % vars(), - 'class': 'staggerGuideLine' - } ) - self.graph.appendChild( path ) - - text.setAttribute( 'x', str( x ) ) - text.setAttribute( 'y', str( y ) ) - - if self.rotate_y_labels: - transform = 'translate( -%d 0 ) rotate ( 90 %d %d )' % \ - ( self.font_size, x, y ) - text.setAttribute( 'transform', transform ) - text.setAttribute( 'style', 'text-anchor: middle' ) - else: - text.setAttribute( 'y', str( y - self.y_label_font_size/2 ) ) - text.setAttribute( 'style', 'text-anchor: end' ) - - def draw_x_guidelines( self, label_height, count ): - "Draw the X-axis guidelines" - if not self.show_x_guidelines: return - # skip the first one - for count in range(1,count): - start = label_height*count - stop = self.graph_height - path = self._create_element( 'path', { - 'd': 'M %(start)s 0 v%(stop)s' % vars(), - 'class': 'guideLines' } ) - self.graph.appendChild( path ) - - - def draw_y_guidelines( self, label_height, count ): - "Draw the Y-axis guidelines" - if not self.show_y_guidelines: return - for count in range( 1, count ): - start = self.graph_height - label_height*count - stop = self.graph_width - path = self._create_element( 'path', { - 'd': 'M 0 %(start)s h%(stop)s' % vars(), - 'class': 'guideLines' } ) - self.graph.appendChild( path ) - - def draw_titles( self ): - "Draws the graph title and subtitle" - if self.show_graph_title: self.draw_graph_title() - if self.show_graph_subtitle: self.draw_graph_subtitle() - if self.show_x_title: self.draw_x_title() - if self.show_y_title: self.draw_y_title() - - def draw_graph_title( self ): - text = self._create_element( 'text', { - 'x': str( self.width / 2 ), - 'y': str( self.title_font_size ), - 'class': 'mainTitle' } ) - text.appendChild( self._doc.createTextNode( self.graph_title ) ) - self.root.appendChild( text ) - - def draw_graph_subtitle( self ): - raise NotImplementedError - - def draw_x_title( self ): - raise NotImplementedError - - def draw_y_title( self ): - x = self.y_title_font_size - if self.y_title_text_direction=='bt': - x += 3 - rotate = -90 - else: - x -= 3 - rotate = 90 - y = self.height / 2 - text = self._create_element( 'text', { - 'x': str( x ), - 'y': str( y ), - 'class': 'yAxisTitle', - } ) - text.appendChild( self._doc.createTextNode( self.y_title ) ) - text.setAttribute( 'transform', 'rotate( %(rotate)d, %(x)s, %(y)s )' % vars() ) - self.root.appendChild( text ) - - def keys( self ): - return map( itemgetter( 'title' ), self.data ) - - def draw_legend( self ): - if self.key: - group = self._create_element( 'g' ) - self.root.appendChild( group ) - - for key_count, key_name in enumerate( self.keys() ): - y_offset = ( self.KEY_BOX_SIZE * key_count ) + (key_count * 5 ) - rect = self._create_element( 'rect', { - 'x': '0', - 'y': str( y_offset ), - 'width': str( self.KEY_BOX_SIZE ), - 'height': str( self.KEY_BOX_SIZE ), - 'class': 'key%s' % (key_count + 1), - } ) - group.appendChild( rect ) - text = self._create_element( 'text', { - 'x': str( self.KEY_BOX_SIZE + 5 ), - 'y': str( y_offset + self.KEY_BOX_SIZE ), - 'class': 'keyText' } ) - text.appendChild( self._doc.createTextNode( key_name ) ) - group.appendChild( text ) - - if self.key_position == 'right': - x_offset = self.graph_width + self.border_left + 10 - y_offset = self.border_top + 20 - if self.key_position == 'bottom': - raise NotImplementedError - group.setAttribute( 'transform', 'translate(%(x_offset)d %(y_offset)d)' % vars() ) - - def style( self ): - "hard code the styles into the xml if style sheets are not used." - if self.no_css: - styles = self.parse_css() - for node in xpath.Evaluate( '//*[@class]', self.root ): - cl = node.getAttribute( 'class' ) - style = styles[ cl ] - if node.hasAttribute( 'style' ): - style += node.getAtrtibute( 'style' ) - node.setAttribute( 'style', style ) - - def parse_css( self ): - """Take a .css file (classes only please) and parse it into a dictionary - of class/style pairs.""" - css = self.get_style() - result = {} - for match in re.finditer( '^(?\.(\w+)(?:\s*,\s*\.\w+)*)\s*\{(?[^}]+)\}' ): - names = match.group_dict()['names'] - # apperantly, we're only interested in class names - names = filter( None, re.split( '\s*,?\s*\.' ) ) - content = match.group_dict()['content'] - # convert all whitespace to - content = re.sub( '\s+', ' ', content ) - for name in names: - result[name] = ';'.join( result[name], content ) - return result - - def add_defs( self, defs ): - "Override and place code to add defs here" - pass - - def start_svg( self ): - "Base SVG Document Creation" - impl = dom.getDOMImplementation() - #dt = impl.createDocumentType( 'svg', 'PUBLIC' - self._doc = impl.createDocument( None, 'svg', None ) - self.root = self._doc.documentElement - if hasattr( self, 'style_sheet' ): - pi = self._doc.createProcessingInstruction( 'xml-stylesheet', - 'href="%s" type="text/css"' % self.style_sheet ) - attributes = { - 'width': str( self.width ), - 'height': str( self.height ), - 'viewBox': '0 0 %s %s' % ( self.width, self.height ), - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'xmlns:a3': 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/', - 'a3:scriptImplementation': 'Adobe' } - map( lambda a: self.root.setAttribute( *a ), attributes.items() ) - self.root.appendChild( self._doc.createComment( ' Created with SVG.Graph ' ) ) - self.root.appendChild( self._doc.createComment( ' SVG.Graph by Jason R. Coombs ' ) ) - self.root.appendChild( self._doc.createComment( ' Based on SVG::Graph by Sean E. Russel ' ) ) - self.root.appendChild( self._doc.createComment( ' Based on Perl SVG:TT:Graph by Leo Lapworth & Stephan Morgan ' ) ) - self.root.appendChild( self._doc.createComment( ' '+'/'*66 ) ) - - defs = self._create_element( 'defs' ) - self.add_defs( defs ) - self.root.appendChild( defs ) - if not hasattr( self, 'style_sheet' ) and not self.no_css: - self.root.appendChild( self._doc.createComment( ' include default stylesheet if none specified ' ) ) - style = self._create_element( 'style', { 'type': 'text/css' } ) - defs.appendChild( style ) - style_data = self._doc.createCDATASection( self.get_style() ) - style.appendChild( style_data ) - - self.root.appendChild( self._doc.createComment( 'SVG Background' ) ) - rect = self._create_element( 'rect', { - 'width': str( self.width ), - 'height': str( self.height ), - 'x': '0', - 'y': '0', - 'class': 'svgBackground' } ) - self.root.appendChild( rect ) - - def calculate_graph_dimensions( self ): - self.calculate_left_margin() - self.calculate_right_margin() - self.calculate_bottom_margin() - self.calculate_top_margin() - self.graph_width = self.width - self.border_left - self.border_right - self.graph_height = self.height - self.border_top - self.border_bottom - - def get_style( self ): - result = """/* Copy from here for external style sheet */ -.svgBackground{ - fill:#ffffff; -} -.graphBackground{ - fill:#f0f0f0; -} - -/* graphs titles */ -.mainTitle{ - text-anchor: middle; - fill: #000000; - font-size: %(title_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} -.subTitle{ - text-anchor: middle; - fill: #999999; - font-size: %(subtitle_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.axis{ - stroke: #000000; - stroke-width: 1px; -} - -.guideLines{ - stroke: #666666; - stroke-width: 1px; - stroke-dasharray: 5 5; -} - -.xAxisLabels{ - text-anchor: middle; - fill: #000000; - font-size: %(x_label_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.yAxisLabels{ - text-anchor: end; - fill: #000000; - font-size: %(y_label_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.xAxisTitle{ - text-anchor: middle; - fill: #ff0000; - font-size: %(x_title_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.yAxisTitle{ - fill: #ff0000; - text-anchor: middle; - font-size: %(y_title_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.dataPointLabel{ - fill: #000000; - text-anchor:middle; - font-size: 10px; - font-family: "Arial", sans-serif; - font-weight: normal; -} - -.staggerGuideLine{ - fill: none; - stroke: #000000; - stroke-width: 0.5px; -} - -%%s - -.keyText{ - fill: #000000; - text-anchor:start; - font-size: %(key_font_size)dpx; - font-family: "Arial", sans-serif; - font-weight: normal; -} -/* End copy for external style sheet */ -""" % class_dict( self ) - result = result % self.get_css() - return result - - def _create_element( self, nodeName, attributes={} ): - "Create an XML node and set the attributes from a dict" - node = self._doc.createElement( nodeName ) - map( lambda a: node.setAttribute( *a ), attributes.items() ) - return node - -class class_dict( object ): - "Emulates a dictionary, but retrieves class attributes" - def __init__( self, obj ): - self.__obj__ = obj - - def __getitem__( self, item ): - return getattr( self.__obj__, item ) - - def keys( self ): - # dir returns a good guess of what attributes might be available - return dir( self.__obj__ ) - -import Plot, TimeSeries diff --git a/build.py b/build.py deleted file mode 100644 index 00686e0..0000000 --- a/build.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: UTF-8 -*- - -""" Setup script for building SVG distribution - -Copyright © 2005 Jason R. Coombs -""" - -from distutils.core import setup - -__author__ = 'Jason R. Coombs ' -__version__ = '$Rev: $'[6:-2] -__svnauthor__ = '$Author: $'[9:-2] -__date__ = '$Date: $'[7:-2] - -setup ( name = 'SVGChart', - version = '1.0.1', - description = 'Python SVG Charting Support', - author = 'Jason R. Coombs', - author_email = 'jaraco@sandia.gov', - packages = ['SVG'], - package_dir = { 'SVG':'.' }, - script_args = ( 'bdist_wininst', ) - ) diff --git a/testing.py b/testing.py deleted file mode 100644 index 29728e0..0000000 --- a/testing.py +++ /dev/null @@ -1,44 +0,0 @@ -import sys, os -#sys.path.insert( 0, 'c:\documents and settings\jaraco\my documents\projects\jaraco' ) -import SVG -from SVG import Plot -reload( SVG ) -reload( Plot ) -g = Plot.Plot( { - 'min_x_value': 0, - 'min_y_value': 0, - 'area_fill': True, - 'stagger_x_labels': True, - 'stagger_y_labels': True, - 'show_x_guidelines': True - }) -g.add_data( { 'data': [ 1, 25, 2, 30, 3, 45 ], 'title': 'foo' } ) -g.add_data( { 'data': [ 1,30, 2, 31, 3, 40 ], 'title': 'foo2' } ) -g.add_data( { 'data': [ .5,35, 1, 20, 3, 10.5 ], 'title': 'foo2' } ) -res = g.burn() -f = open( r'c:\sample.svg', 'w' ) -f.write( res ) -f.close() - -from SVG import TimeSeries -reload( TimeSeries ) - -g = TimeSeries.Plot( { } ) - -g.timescale_divisions = '4 hours' -g.stagger_x_labels = True -g.x_label_format = '%d-%b' -#g.max_y_value = 200 - -g.add_data( { 'data': [ '2005-12-21T00:00:00', 20, '2005-12-22T00:00:00', 21 ], 'title': 'foo' } ) - -res = g.burn() -print g.field_width -print g.font_size -print g.right_font -print g.right_align -print g.get_x_labels() - -f = open( r'c:\timeseries.py.svg', 'w' ) -f.write( res ) -f.close() diff --git a/testing.rb b/testing.rb deleted file mode 100644 index 2f12486..0000000 --- a/testing.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'SVG/Graph/Graph' -require 'SVG/Graph/Plot' - -graph = SVG::Graph::Plot.new( { - :min_x_value=>0, - :min_y_value=>0, - :area_fill=> true, - :stagger_x_labels=>true, - :stagger_y_labels=>true -}) - -#data1 = [ 1,25, 2,30, 3,45 ] - -graph.add_data( { :data=>[ 1,25, 2,30, 3,45 ], :title=>'foo' } ) - -graph.add_data( { :data=>[ 1,30, 2, 31, 3, 40 ], :title=>'foo2' } ) - -res = graph.burn() - -f = File.new( 'c:\ruby.svg', 'w' ) -f.write( res ) -f.close() - -require 'SVG/Graph/TimeSeries' - -g = SVG::Graph::TimeSeries.new( { - :timescale_divisions => '4 hours', - :stagger_x_labels => true, - :x_label_format => '%d-%b', - } ) -g.add_data( { :data=> [ '2005-12-21T00:00:00', 20, '2005-12-22T00:00:00', 21 ], :title=> 'foo' } ) - -res = g.burn() -print g.field_width -print "\n" -print g.inspect -print g.get_x_labels.length -print "\n" -print g.right_align -print "\n" -print g.get_x_labels -print "\n" -f = File.new( 'c:\timeseries.rb.svg', 'w' ) -f.write( res ) -f.close() \ No newline at end of file