32
33
reo_colour = re.compile('^([A-Fa-f0-9]{2,2}){3,4}$')
34
36
def _check_colour(colour):
35
37
if not reo_colour.match(colour):
36
38
raise InvalidParametersException('Colours need to be in ' \
40
42
# Exception Classes
41
43
# -----------------------------------------------------------------------------
43
46
class PyGoogleChartException(Exception):
46
50
class DataOutOfRangeException(PyGoogleChartException):
49
54
class UnknownDataTypeException(PyGoogleChartException):
52
58
class NoDataGivenException(PyGoogleChartException):
55
62
class InvalidParametersException(PyGoogleChartException):
66
class BadContentTypeException(PyGoogleChartException):
59
71
# -----------------------------------------------------------------------------
61
74
class Data(object):
62
76
def __init__(self, data):
63
77
assert(type(self) != Data) # This is an abstract class
66
81
class SimpleData(Data):
67
82
enc_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
68
84
def __repr__(self):
70
86
for data in self.data:
96
115
raise DataOutOfRangeException()
97
116
encoded_data.append(','.join(sub_data))
98
117
return 'chd=t:' + '|'.join(encoded_data)
103
124
class ExtendedData(Data):
105
126
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'
106
128
def __repr__(self):
107
129
encoded_data = []
108
130
enc_size = len(ExtendedData.enc_map)
130
153
# -----------------------------------------------------------------------------
132
156
class Axis(object):
137
161
TYPES = (BOTTOM, TOP, LEFT, RIGHT)
138
163
def __init__(self, axis, **kw):
139
164
assert(axis in Axis.TYPES)
140
165
self.has_style = False
141
166
self.index = None
142
167
self.positions = None
143
169
def set_index(self, index):
144
170
self.index = index
145
172
def set_positions(self, positions):
146
173
self.positions = positions
147
175
def set_style(self, colour, font_size=None, alignment=None):
148
176
_check_colour(colour)
149
177
self.colour = colour
150
178
self.font_size = font_size
151
179
self.alignment = alignment
152
180
self.has_style = True
153
182
def style_to_url(self):
155
184
bits.append(str(self.index))
159
188
if self.alignment is not None:
160
189
bits.append(str(self.alignment))
161
190
return ','.join(bits)
162
192
def positions_to_url(self):
164
194
bits.append(str(self.index))
165
bits += [ str(a) for a in self.positions ]
195
bits += [str(a) for a in self.positions]
166
196
return ','.join(bits)
168
199
class LabelAxis(Axis):
169
201
def __init__(self, axis, values, **kwargs):
170
202
Axis.__init__(self, axis, **kwargs)
171
self.values = [ str(a) for a in values ]
203
self.values = [str(a) for a in values]
172
205
def __repr__(self):
173
206
return '%i:|%s' % (self.index, '|'.join(self.values))
175
209
class RangeAxis(Axis):
176
211
def __init__(self, axis, low, high, **kwargs):
177
212
Axis.__init__(self, axis, **kwargs)
180
216
def __repr__(self):
181
217
return '%i,%s,%s' % (self.index, self.low, self.high)
184
220
# -----------------------------------------------------------------------------
186
223
class Chart(object):
224
"""Abstract class for all chart types.
226
width are height specify the dimensions of the image. title sets the title
227
of the chart. legend requires a list that corresponds to datasets.
188
230
BASE_URL = 'http://chart.apis.google.com/chart?'
189
231
BACKGROUND = 'bg'
252
294
# -------------------------------------------------------------------------
253
296
def download(self, file_name):
297
opener = urllib2.urlopen(self.get_url())
299
if opener.headers['content-type'] != 'image/png':
300
raise BadContentTypeException('Server responded with a ' \
301
'content-type of %s' % opener.headers['content-type'])
254
303
open(file_name, 'wb').write(urllib.urlopen(self.get_url()).read())
256
305
# Simple settings
418
468
# -------------------------------------------------------------------------
420
470
def markers_to_url(self):
421
return 'chm=%s' % '|'.join([ ','.join(a) for a in self.markers ])
471
return 'chm=%s' % '|'.join([','.join(a) for a in self.markers])
423
473
def add_marker(self, index, point, marker_type, colour, size):
424
474
self.markers.append((marker_type, colour, str(index), str(point), \
437
487
def add_fill_simple(self, colour):
438
488
self.markers.append(('B', colour, '1', '1', '1'))
440
491
class ScatterChart(Chart):
441
493
def __init__(self, *args, **kwargs):
442
494
Chart.__init__(self, *args, **kwargs)
443
496
def type_to_url(self):
447
500
class LineChart(Chart):
448
502
def __init__(self, *args, **kwargs):
503
assert(type(self) != LineChart) # This is an abstract class
449
504
Chart.__init__(self, *args, **kwargs)
450
505
self.line_styles = {}
452
508
def set_line_style(self, index, thickness=1, line_segment=None, \
453
509
blank_segment=None):
457
513
value.append(str(line_segment))
458
514
value.append(str(blank_segment))
459
515
self.line_styles[index] = value
460
517
def set_grid(self, x_step, y_step, line_segment=1, \
461
518
blank_segment=0):
462
519
self.grid = '%s,%s,%s,%s' % (x_step, y_step, line_segment, \
464
522
def get_url_bits(self):
465
523
url_bits = Chart.get_url_bits(self)
466
524
if self.line_styles:
477
535
url_bits.append('chg=%s' % self.grid)
480
539
class SimpleLineChart(LineChart):
481
541
def type_to_url(self):
484
545
class XYLineChart(LineChart):
485
547
def type_to_url(self):
488
551
class BarChart(Chart):
489
553
def __init__(self, *args, **kwargs):
490
554
assert(type(self) != BarChart) # This is an abstract class
491
555
Chart.__init__(self, *args, **kwargs)
492
556
self.bar_width = None
493
558
def set_bar_width(self, bar_width):
494
559
self.bar_width = bar_width
495
561
def get_url_bits(self):
496
562
url_bits = Chart.get_url_bits(self)
497
563
url_bits.append('chbh=%i' % self.bar_width)
500
567
class StackedHorizontalBarChart(BarChart):
501
569
def type_to_url(self):
504
573
class StackedVerticalBarChart(BarChart):
505
575
def type_to_url(self):
508
579
class GroupedBarChart(BarChart):
509
581
def __init__(self, *args, **kwargs):
510
582
assert(type(self) != GroupedBarChart) # This is an abstract class
511
583
BarChart.__init__(self, *args, **kwargs)
512
584
self.bar_spacing = None
513
586
def set_bar_spacing(self, spacing):
514
587
self.bar_spacing = spacing
515
589
def get_url_bits(self):
516
590
# Skip 'BarChart.get_url_bits' and call Chart directly so the parent
517
591
# doesn't add "chbh" before we do.
525
599
url_bits.append('chbh=%i' % self.bar_width)
528
603
class GroupedHorizontalBarChart(GroupedBarChart):
529
605
def type_to_url(self):
532
609
class GroupedVerticalBarChart(GroupedBarChart):
533
611
def type_to_url(self):
536
615
class PieChart(Chart):
537
617
def __init__(self, *args, **kwargs):
538
618
assert(type(self) != PieChart) # This is an abstract class
539
619
Chart.__init__(self, *args, **kwargs)
540
620
self.pie_labels = []
541
622
def set_pie_labels(self, labels):
542
623
self.pie_labels = labels
543
625
def get_url_bits(self):
544
626
url_bits = Chart.get_url_bits(self)
545
627
if self.pie_labels:
546
628
url_bits.append('chl=%s' % '|'.join(self.pie_labels))
549
632
class PieChart2D(PieChart):
550
634
def type_to_url(self):
553
638
class PieChart3D(PieChart):
554
640
def type_to_url(self):
557
644
class VennChart(Chart):
558
646
def type_to_url(self):
562
651
chart = GroupedVerticalBarChart(320, 200)
563
652
chart = PieChart2D(320, 200)
564
653
chart = ScatterChart(320, 200)
565
654
chart = SimpleLineChart(320, 200)
566
sine_data = [ math.sin(float(a) / 10) * 2000 + 2000 for a in xrange(100) ]
567
random_data = [ a * random.random() * 30 for a in xrange(40) ]
568
random_data2 = [ random.random() * 4000 for a in xrange(10) ]
655
sine_data = [math.sin(float(a) / 10) * 2000 + 2000 for a in xrange(100)]
656
random_data = [a * random.random() * 30 for a in xrange(40)]
657
random_data2 = [random.random() * 4000 for a in xrange(10)]
569
658
# chart.set_bar_width(50)
570
659
# chart.set_bar_spacing(0)
571
660
chart.add_data(sine_data)
600
689
chart.add_fill_simple('303030A0')
603
chart = SimpleLineChart(320, 200)
604
data = [ 1, 5, 30, 10, 25 ]
606
chart.set_title('Hello World!')
607
chart.set_axis_range(Axis.LEFT, 0, 10)
608
print chart.get_url()
609
chart.download('hello.png')
691
chart.download('test.png')
611
693
url = chart.get_url()