mokolabs-googlecharts 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/gchart.rb ADDED
@@ -0,0 +1,432 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'gchart/version'
3
+ require "open-uri"
4
+ require "uri"
5
+
6
+ class Gchart
7
+
8
+ include GchartInfo
9
+
10
+ @@url = "http://chart.apis.google.com/chart?"
11
+ @@types = ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter']
12
+ @@simple_chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
13
+ @@chars = @@simple_chars + ['-', '.']
14
+ @@ext_pairs = @@chars.map { |char_1| @@chars.map { |char_2| char_1 + char_2 } }.flatten
15
+ @@file_name = 'chart.png'
16
+
17
+ attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend, :data, :encoding, :max_value, :bar_colors,
18
+ :title_color, :title_size, :custom, :axis_with_labels, :axis_labels, :bar_width_and_spacing
19
+
20
+ class << self
21
+ # Support for Gchart.line(:title => 'my title', :size => '400x600')
22
+ def method_missing(m, options={})
23
+ # Extract the format and optional filename, then clean the hash
24
+ format = options[:format] || 'url'
25
+ @@file_name = options[:filename] unless options[:filename].nil?
26
+ options.delete(:format)
27
+ options.delete(:filename)
28
+ # create the chart and return it in the format asked for
29
+ if @@types.include?(m.to_s)
30
+ chart = new(options.merge!({:type => m}))
31
+ chart.send(format)
32
+ elsif m.to_s == 'version'
33
+ Gchart::VERSION::STRING
34
+ else
35
+ "#{m} is not a supported chart format, please use one of the following: #{supported_types}."
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ def initialize(options={})
42
+ @type = :line
43
+ @data = []
44
+ @width = 300
45
+ @height = 200
46
+ @horizontal = false
47
+ @grouped = false
48
+ @encoding = 'simple'
49
+ @max_value = 'auto'
50
+ @alt = 'Google Chart'
51
+ @id = false
52
+ @class = false
53
+
54
+ # set the options value if definable
55
+ options.each do |attribute, value|
56
+ send("#{attribute.to_s}=", value) if self.respond_to?("#{attribute}=")
57
+ end
58
+ end
59
+
60
+ def self.supported_types
61
+ @@types.join(' ')
62
+ end
63
+
64
+ # Defines the Graph size using the following format:
65
+ # width X height
66
+ def size=(size='300x200')
67
+ @width, @height = size.split("x").map { |dimension| dimension.to_i }
68
+ end
69
+
70
+ def size
71
+ "#{@width}x#{@height}"
72
+ end
73
+
74
+ # Sets the alt tag when chart is exported as image tag
75
+ def alt=(alt='Google Chart')
76
+ @alt = alt
77
+ end
78
+
79
+ def alt
80
+ @alt
81
+ end
82
+
83
+ # Sets the CSS id selector when chart is exported as image tag
84
+ def id=(id=false)
85
+ @id = id
86
+ end
87
+
88
+ def id
89
+ @id
90
+ end
91
+
92
+ # Sets the CSS class selector when chart is exported as image tag
93
+ def class=(klass=false)
94
+ @class = klass
95
+ end
96
+
97
+ def class
98
+ @class
99
+ end
100
+
101
+ # Sets the orientation of a bar graph
102
+ def orientation=(orientation='h')
103
+ if orientation == 'h' || orientation == 'horizontal'
104
+ self.horizontal = true
105
+ elsif orientation == 'v' || orientation == 'vertical'
106
+ self.horizontal = false
107
+ end
108
+ end
109
+
110
+ # Sets the bar graph presentation (stacked or grouped)
111
+ def stacked=(option=true)
112
+ @grouped = option ? false : true
113
+ end
114
+
115
+ def bg=(options)
116
+ if options.is_a?(String)
117
+ @bg_color = options
118
+ elsif options.is_a?(Hash)
119
+ @bg_color = options[:color]
120
+ @bg_type = options[:type]
121
+ @bg_angle = options[:angle]
122
+ end
123
+ end
124
+
125
+ def graph_bg=(options)
126
+ if options.is_a?(String)
127
+ @chart_color = options
128
+ elsif options.is_a?(Hash)
129
+ @chart_color = options[:color]
130
+ @chart_type = options[:type]
131
+ @chart_angle = options[:angle]
132
+ end
133
+ end
134
+
135
+ def self.jstize(string)
136
+ string.gsub(' ', '+').gsub(/\[|\{|\}|\||\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s(16).upcase}"}
137
+ end
138
+ # load all the custom aliases
139
+ require 'gchart/aliases'
140
+
141
+ protected
142
+
143
+ # Returns the chart's generated PNG as a blob. (borrowed from John's gchart.rubyforge.org)
144
+ def fetch
145
+ open(query_builder) { |io| io.read }
146
+ end
147
+
148
+ # Writes the chart's generated PNG to a file. (borrowed from John's gchart.rubyforge.org)
149
+ def write(io_or_file=@@file_name)
150
+ return io_or_file.write(fetch) if io_or_file.respond_to?(:write)
151
+ open(io_or_file, "w+") { |io| io.write(fetch) }
152
+ end
153
+
154
+ # Format
155
+
156
+ def image_tag
157
+ image = "<img"
158
+ image += " id=\"#{@id}\"" if @id
159
+ image += " class=\"#{@class}\"" if @class
160
+ image += " src=\"#{query_builder}\""
161
+ image += " width=\"#{@width}\""
162
+ image += " height=\"#{@height}\""
163
+ image += " alt=\"#{@alt}\""
164
+ image += " title=\"#{@title}\"" if @title
165
+ image += " />"
166
+ end
167
+
168
+ alias_method :img_tag, :image_tag
169
+
170
+ def url
171
+ query_builder
172
+ end
173
+
174
+ def file
175
+ write
176
+ end
177
+
178
+ #
179
+ def jstize(string)
180
+ Gchart.jstize(string)
181
+ end
182
+
183
+ private
184
+
185
+ # The title size cannot be set without specifying a color.
186
+ # A dark key will be used for the title color if no color is specified
187
+ def set_title
188
+ title_params = "chtt=#{title}"
189
+ unless (title_color.nil? && title_size.nil? )
190
+ title_params << "&chts=" + (color, size = (@title_color || '454545'), @title_size).compact.join(',')
191
+ end
192
+ title_params
193
+ end
194
+
195
+ def set_size
196
+ "chs=#{size}"
197
+ end
198
+
199
+ def set_data
200
+ data = send("#{@encoding}_encoding", @data)
201
+ "chd=#{data}"
202
+ end
203
+
204
+ def set_colors
205
+ bg_type = fill_type(@bg_type) || 's' if @bg_color
206
+ chart_type = fill_type(@chart_type) || 's' if @chart_color
207
+
208
+ "chf=" + {'bg' => fill_for(bg_type, @bg_color, @bg_angle), 'c' => fill_for(chart_type, @chart_color, @chart_angle)}.map{|k,v| "#{k},#{v}" unless v.nil?}.compact.join('|')
209
+ end
210
+
211
+ # set bar, line colors
212
+ def set_bar_colors
213
+ @bar_colors = @bar_colors.join(',') if @bar_colors.is_a?(Array)
214
+ "chco=#{@bar_colors}"
215
+ end
216
+
217
+ # set bar spacing
218
+ # chbh=
219
+ # <bar width in pixels>,
220
+ # <optional space between bars in a group>,
221
+ # <optional space between groups>
222
+ def set_bar_width_and_spacing
223
+ width_and_spacing_values = case @bar_width_and_spacing
224
+ when String
225
+ @bar_width_and_spacing
226
+ when Array
227
+ @bar_width_and_spacing.join(',')
228
+ when Hash
229
+ width = @bar_width_and_spacing[:width] || 23
230
+ spacing = @bar_width_and_spacing[:spacing] || 4
231
+ group_spacing = @bar_width_and_spacing[:group_spacing] || 8
232
+ [width,spacing,group_spacing].join(',')
233
+ else
234
+ @bar_width_and_spacing.to_s
235
+ end
236
+ "chbh=#{width_and_spacing_values}"
237
+ end
238
+
239
+ def fill_for(type=nil, color='', angle=nil)
240
+ unless type.nil?
241
+ case type
242
+ when 'lg'
243
+ angle ||= 0
244
+ color = "#{color},0,ffffff,1" if color.split(',').size == 1
245
+ "#{type},#{angle},#{color}"
246
+ when 'ls'
247
+ angle ||= 90
248
+ color = "#{color},0.2,ffffff,0.2" if color.split(',').size == 1
249
+ "#{type},#{angle},#{color}"
250
+ else
251
+ "#{type},#{color}"
252
+ end
253
+ end
254
+ end
255
+
256
+ # A chart can have one or many legends.
257
+ # Gchart.line(:legend => 'label')
258
+ # or
259
+ # Gchart.line(:legend => ['first label', 'last label'])
260
+ def set_legend
261
+ return set_labels if @type == :pie || @type == :pie_3d
262
+
263
+ if @legend.is_a?(Array)
264
+ "chdl=#{@legend.map{|label| "#{label}"}.join('|')}"
265
+ else
266
+ "chdl=#{@legend}"
267
+ end
268
+
269
+ end
270
+
271
+ def set_labels
272
+ if @legend.is_a?(Array)
273
+ "chl=#{@legend.map{|label| "#{label}"}.join('|')}"
274
+ else
275
+ "chl=#{@legend}"
276
+ end
277
+ end
278
+
279
+ def set_axis_with_labels
280
+ @axis_with_labels = @axis_with_labels.join(',') if @axis_with_labels.is_a?(Array)
281
+ "chxt=#{@axis_with_labels}"
282
+ end
283
+
284
+ def set_axis_labels
285
+ labels_arr = []
286
+ axis_labels.each_with_index do |labels,index|
287
+ if labels.is_a?(Array)
288
+ labels_arr << "#{index}:|#{labels.join('|')}"
289
+ else
290
+ labels_arr << "#{index}:|#{labels}"
291
+ end
292
+ end
293
+ "chxl=#{labels_arr.join('|')}"
294
+ end
295
+
296
+ def set_type
297
+ case @type
298
+ when :line
299
+ "cht=lc"
300
+ when :line_xy
301
+ "cht=lxy"
302
+ when :bar
303
+ "cht=b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
304
+ when :pie_3d
305
+ "cht=p3"
306
+ when :pie
307
+ "cht=p"
308
+ when :venn
309
+ "cht=v"
310
+ when :scatter
311
+ "cht=s"
312
+ when :sparkline
313
+ "cht=ls"
314
+ when :meter
315
+ "cht=gom"
316
+ end
317
+ end
318
+
319
+ def fill_type(type)
320
+ case type
321
+ when 'solid'
322
+ 's'
323
+ when 'gradient'
324
+ 'lg'
325
+ when 'stripes'
326
+ 'ls'
327
+ end
328
+ end
329
+
330
+ # Wraps a single dataset inside another array to support more datasets
331
+ def prepare_dataset(ds)
332
+ ds = [ds] unless ds.first.is_a?(Array)
333
+ ds
334
+ end
335
+
336
+ def convert_to_simple_value(number)
337
+ if number.nil?
338
+ "_"
339
+ else
340
+ value = @@simple_chars[number.to_i]
341
+ value.nil? ? "_" : value
342
+ end
343
+ end
344
+
345
+ # http://code.google.com/apis/chart/#simple
346
+ # Simple encoding has a resolution of 62 different values.
347
+ # Allowing five pixels per data point, this is sufficient for line and bar charts up
348
+ # to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size.
349
+ def simple_encoding(dataset=[])
350
+ dataset = prepare_dataset(dataset)
351
+ @max_value = dataset.map{|ds| ds.max}.max if @max_value == 'auto'
352
+
353
+ if @max_value == false || @max_value == 'false' || @max_value == :false || @max_value == 0
354
+ "s:" + dataset.map { |ds| ds.map { |number| convert_to_simple_value(number) }.join }.join(',')
355
+ else
356
+ "s:" + dataset.map { |ds| ds.map { |number| convert_to_simple_value( (@@simple_chars.size - 1) * number / @max_value) }.join }.join(',')
357
+ end
358
+
359
+ end
360
+
361
+ # http://code.google.com/apis/chart/#text
362
+ # Text encoding has a resolution of 1,000 different values,
363
+ # using floating point numbers between 0.0 and 100.0. Allowing five pixels per data point,
364
+ # integers (1.0, 2.0, and so on) are sufficient for line and bar charts up to about 500 pixels.
365
+ # Include a single decimal place (35.7 for example) if you require higher resolution.
366
+ # Text encoding is suitable for all other types of chart regardless of size.
367
+ def text_encoding(dataset=[])
368
+ dataset = prepare_dataset(dataset)
369
+ "t:" + dataset.map{ |ds| ds.join(',') }.join('|')
370
+ end
371
+
372
+ def convert_to_extended_value(number)
373
+ if number.nil?
374
+ '__'
375
+ else
376
+ value = @@ext_pairs[number.to_i]
377
+ value.nil? ? "__" : value
378
+ end
379
+ end
380
+
381
+ # http://code.google.com/apis/chart/#extended
382
+ # Extended encoding has a resolution of 4,096 different values
383
+ # and is best used for large charts where a large data range is required.
384
+ def extended_encoding(dataset=[])
385
+
386
+ dataset = prepare_dataset(dataset)
387
+ @max_value = dataset.map{|ds| ds.max}.max if @max_value == 'auto'
388
+
389
+ if @max_value == false || @max_value == 'false' || @max_value == :false
390
+ "e:" + dataset.map { |ds| ds.map { |number| convert_to_extended_value(number)}.join }.join(',')
391
+ else
392
+ "e:" + dataset.map { |ds| ds.map { |number| convert_to_extended_value( (@@ext_pairs.size - 1) * number / @max_value) }.join }.join(',')
393
+ end
394
+
395
+ end
396
+
397
+
398
+ def query_builder
399
+ query_params = instance_variables.map do |var|
400
+ case var
401
+ # Set the graph size
402
+ when '@width'
403
+ set_size unless @width.nil? || @height.nil?
404
+ when '@type'
405
+ set_type
406
+ when '@title'
407
+ set_title unless @title.nil?
408
+ when '@legend'
409
+ set_legend unless @legend.nil?
410
+ when '@bg_color'
411
+ set_colors
412
+ when '@chart_color'
413
+ set_colors if @bg_color.nil?
414
+ when '@data'
415
+ set_data unless @data == []
416
+ when '@bar_colors'
417
+ set_bar_colors
418
+ when '@bar_width_and_spacing'
419
+ set_bar_width_and_spacing
420
+ when '@axis_with_labels'
421
+ set_axis_with_labels
422
+ when '@axis_labels'
423
+ set_axis_labels
424
+ when '@custom'
425
+ @custom
426
+ end
427
+ end.compact
428
+
429
+ jstize(@@url + query_params.join('&'))
430
+ end
431
+
432
+ end