googlecharts 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0
1
+ 1.5.0
data/lib/gchart.rb CHANGED
@@ -7,79 +7,108 @@ require "cgi"
7
7
  require 'enumerator'
8
8
 
9
9
  class Gchart
10
-
11
10
  include GchartInfo
12
-
13
- @@url = "http://chart.apis.google.com/chart?"
14
- @@types = ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter', 'map']
15
- @@simple_chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
16
- @@chars = @@simple_chars + ['-', '.']
17
- @@ext_pairs = @@chars.map { |char_1| @@chars.map { |char_2| char_1 + char_2 } }.flatten
18
- @@file_name = 'chart.png'
19
-
20
- attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend, :data, :encoding, :min_value, :max_value, :bar_colors,
21
- :title_color, :title_size, :custom, :axis_with_labels, :axis_labels, :bar_width_and_spacing, :id, :alt, :class,
22
- :range_markers, :geographical_area, :map_colors, :country_codes, :axis_range
23
-
24
- # Support for Gchart.line(:title => 'my title', :size => '400x600')
25
- def self.method_missing(m, options={})
26
- # Start with theme defaults if a theme is set
27
- theme = options[:theme]
28
- options = theme ? Chart::Theme.load(theme).to_options.merge(options) : options
29
- # Extract the format and optional filename, then clean the hash
30
- format = options[:format] || 'url'
31
- @@file_name = options[:filename] unless options[:filename].nil?
32
- options.delete(:format)
33
- options.delete(:filename)
34
- #update map_colors to be bar_colors
35
- options.update(:bar_colors => options[:map_colors]) if options.has_key?(:map_colors)
36
- # create the chart and return it in the format asked for
37
- if @@types.include?(m.to_s)
38
- chart = new(options.merge!({:type => m}))
11
+
12
+ def self.url
13
+ "http://chart.apis.google.com/chart?"
14
+ end
15
+
16
+ def self.types
17
+ @types ||= ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter', 'map']
18
+ end
19
+
20
+ def self.simple_chars
21
+ @simple_chars ||= ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
22
+ end
23
+
24
+ def self.chars
25
+ @chars ||= simple_chars + ['-', '.']
26
+ end
27
+
28
+ def self.ext_pairs
29
+ @ext_pairs ||= chars.map { |char_1| chars.map { |char_2| char_1 + char_2 } }.flatten
30
+ end
31
+
32
+ def self.default_filename
33
+ 'chart.png'
34
+ end
35
+
36
+ attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend, :data, :encoding, :bar_colors,
37
+ :title_color, :title_size, :custom, :axis_with_labels, :axis_labels, :bar_width_and_spacing, :id, :alt, :klass,
38
+ :range_markers, :geographical_area, :map_colors, :country_codes, :axis_range, :filename, :min, :max, :colors
39
+
40
+ attr_accessor :bg_type, :bg_color, :bg_angle, :chart_type, :chart_color, :chart_angle, :axis_range
41
+
42
+ attr_accessor :min_value, :max_value
43
+
44
+ types.each do |type|
45
+ instance_eval <<-DYNCLASSMETH
46
+ def #{type}(options = {})
47
+ # Start with theme defaults if a theme is set
48
+ theme = options[:theme]
49
+ options = theme ? Chart::Theme.load(theme).to_options.merge(options) : options
50
+ # # Extract the format and optional filename, then clean the hash
51
+ format = options[:format] || 'url'
52
+ options[:filename] ||= Gchart.default_filename
53
+ options.delete(:format)
54
+ #update map_colors to become bar_colors
55
+ options.update(:bar_colors => options[:map_colors]) if options.has_key?(:map_colors)
56
+ chart = new(options.merge!({:type => "#{type}"}))
39
57
  chart.send(format)
40
- elsif m.to_s == 'version'
41
- Gchart::VERSION::STRING
42
- else
43
- "#{m} is not a supported chart format, please use one of the following: #{supported_types}."
44
- end
58
+ end
59
+ DYNCLASSMETH
45
60
  end
46
-
61
+
62
+ def version
63
+ Gchart::VERSION::STRING
64
+ end
65
+
66
+ def self.method_missing(m, options={})
67
+ raise NoMethodError, "#{m} is not a supported chart format, please use one of the following: #{supported_types}."
68
+ end
69
+
47
70
  def initialize(options={})
48
- @type = :line
49
- @data = []
50
- @width = 300
51
- @height = 200
52
- @horizontal = false
53
- @grouped = false
54
- @encoding = 'simple'
55
- @max_value = 'auto'
56
- # Sets the alt tag when chart is exported as image tag
57
- @alt = 'Google Chart'
58
- # Sets the CSS id selector when chart is exported as image tag
59
- @id = false
60
- # Sets the CSS class selector when chart is exported as image tag
61
- @class = false
62
-
63
- # set the options value if definable
64
- options.each do |attribute, value|
65
- send("#{attribute.to_s}=", value) if self.respond_to?("#{attribute}=")
66
- end
71
+ @type = options[:type] || 'line'
72
+ @data = []
73
+ @width = 300
74
+ @height = 200
75
+ @horizontal = false
76
+ @grouped = false
77
+ @encoding = 'simple'
78
+ # @max_value = 'auto'
79
+ # @min_value defaults to nil meaning zero
80
+ @filename = options[:filename]
81
+ # Sets the alt tag when chart is exported as image tag
82
+ @alt = 'Google Chart'
83
+ # Sets the CSS id selector when chart is exported as image tag
84
+ @id = false
85
+ # Sets the CSS class selector when chart is exported as image tag
86
+ @klass = options[:class] || false
87
+ # set the options value if definable
88
+ options.each do |attribute, value|
89
+ send("#{attribute}=", value) if self.respond_to?("#{attribute}=")
90
+ end
67
91
  end
68
-
92
+
69
93
  def self.supported_types
70
- @@types.join(' ')
94
+ Gchart.types.join(' ')
71
95
  end
72
-
96
+
73
97
  # Defines the Graph size using the following format:
74
98
  # width X height
75
99
  def size=(size='300x200')
76
100
  @width, @height = size.split("x").map { |dimension| dimension.to_i }
77
101
  end
78
-
102
+
79
103
  def size
80
- "#{@width}x#{@height}"
104
+ "#{width}x#{height}"
81
105
  end
82
106
 
107
+ def dimensions
108
+ # TODO: maybe others?
109
+ [:line_xy, :scatter].include?(type) ? 2 : 1
110
+ end
111
+
83
112
  # Sets the orientation of a bar graph
84
113
  def orientation=(orientation='h')
85
114
  if orientation == 'h' || orientation == 'horizontal'
@@ -88,276 +117,360 @@ class Gchart
88
117
  self.horizontal = false
89
118
  end
90
119
  end
91
-
120
+
92
121
  # Sets the bar graph presentation (stacked or grouped)
93
122
  def stacked=(option=true)
94
- @grouped = option ? false : true
123
+ @grouped = option ? false : true
95
124
  end
96
-
125
+
97
126
  def bg=(options)
98
127
  if options.is_a?(String)
99
128
  @bg_color = options
100
129
  elsif options.is_a?(Hash)
101
130
  @bg_color = options[:color]
102
- @bg_type = options[:type]
131
+ @bg_type = options[:type]
103
132
  @bg_angle = options[:angle]
104
133
  end
105
134
  end
106
-
135
+
107
136
  def graph_bg=(options)
108
137
  if options.is_a?(String)
109
138
  @chart_color = options
110
139
  elsif options.is_a?(Hash)
111
140
  @chart_color = options[:color]
112
- @chart_type = options[:type]
141
+ @chart_type = options[:type]
113
142
  @chart_angle = options[:angle]
114
143
  end
115
144
  end
116
145
 
146
+ def max_value=(max_v)
147
+ if max_v =~ /false/
148
+ @max_value = false
149
+ else
150
+ @max_value = max_v
151
+ end
152
+ end
153
+
154
+ def min_value=(min_v)
155
+ if min_v =~ /false/
156
+ @min_value = false
157
+ else
158
+ @min_value = min_v
159
+ end
160
+ end
161
+
117
162
  # returns the full data range as an array
118
163
  # it also sets the data range if not defined
119
164
  def full_data_range(ds)
120
- return [@min, @max] unless (@min.nil? || @max.nil?)
121
- @max = (max_value.nil? || max_value == 'auto') ? ds.compact.map{|mds| mds.compact.max}.max : max_value
122
- if (min_value.nil? || min_value == 'auto')
123
- min_ds_value = ds.compact.map{|mds| mds.compact.min}.min || 0
124
- @min = (min_ds_value < 0) ? min_ds_value : 0
165
+ return if max_value == false
166
+
167
+ ds.each_with_index do |mds, mds_index|
168
+ mds[:min_value] ||= min_value
169
+ mds[:max_value] ||= max_value
170
+
171
+ if mds_index == 0 && type.to_s == 'bar'
172
+ # TODO: unless you specify a zero line (using chp or chds),
173
+ # the min_value of a bar chart is always 0.
174
+ #mds[:min_value] ||= mds[:data].first.to_a.compact.min
175
+ mds[:min_value] ||= 0
176
+ end
177
+ if (mds_index == 0 && type.to_s == 'bar' &&
178
+ !grouped && mds[:data].first.is_a?(Array))
179
+ totals = []
180
+ mds[:data].each do |l|
181
+ l.each_with_index do |v, index|
182
+ next if v.nil?
183
+ totals[index] ||= 0
184
+ totals[index] += v
185
+ end
186
+ end
187
+ mds[:max_value] ||= totals.compact.max
188
+ else
189
+ all = mds[:data].flatten.compact
190
+ # default min value should be 0 unless set to auto
191
+ if mds[:min_value] == 'auto'
192
+ mds[:min_value] = all.min
193
+ else
194
+ min = all.min
195
+ mds[:min_value] ||= (min && min < 0 ? min : 0)
196
+ end
197
+ mds[:max_value] ||= all.max
198
+ end
199
+ end
200
+
201
+ unless axis_range
202
+ @axis_range = ds.map{|mds| [mds[:min_value], mds[:max_value]]}
203
+ if dimensions == 1 && (type.to_s != 'bar' || horizontal)
204
+ tmp = axis_range.fetch(0, [])
205
+ @axis_range[0] = axis_range.fetch(1, [])
206
+ @axis_range[1] = tmp
207
+ end
208
+ end
209
+ # return [min, max] unless (min.nil? || max.nil?)
210
+ # @max = (max_value.nil? || max_value == 'auto') ? ds.compact.map{|mds| mds.compact.max}.max : max_value
211
+ #
212
+ # if min_value.nil?
213
+ # min_ds_value = ds.compact.map{|mds| mds.compact.min}.min || 0
214
+ # @min = (min_ds_value < 0) ? min_ds_value : 0
215
+ # else
216
+ # @min = min_value == 'auto' ? ds.compact.map{|mds| mds.compact.min}.min || 0 : min_value
217
+ # end
218
+ # @axis_range = [[min,max]]
219
+ end
220
+
221
+ def dataset
222
+ if @dataset
223
+ @dataset
125
224
  else
126
- @min = min_value
127
- end
128
- @axis_range = [[@min,@max]]
225
+ @dataset = convert_dataset(data || [])
226
+ full_data_range(@dataset) # unless axis_range
227
+ @dataset
228
+ end
129
229
  end
130
230
 
131
- def dataset
132
- @dataset ||= prepare_dataset(data)
133
- full_data_range(@dataset) unless @axis_range
134
- @dataset
231
+ # Sets of data to handle multiple sets
232
+ def datasets
233
+ datasets = []
234
+ dataset.each do |d|
235
+ if d[:data].first.is_a?(Array)
236
+ datasets += d[:data]
237
+ else
238
+ datasets << d[:data]
239
+ end
240
+ end
241
+ datasets
135
242
  end
136
243
 
137
244
  def self.jstize(string)
138
- string.gsub(' ', '+').gsub(/\[|\{|\}|\||\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s(16).upcase}"}
245
+ string.gsub(' ', '+').gsub(/\[|\{|\}|\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s.upcase}"}
139
246
  end
140
247
  # load all the custom aliases
141
248
  require 'gchart/aliases'
142
-
143
- protected
144
-
249
+
145
250
  # Returns the chart's generated PNG as a blob. (borrowed from John's gchart.rubyforge.org)
146
251
  def fetch
147
252
  open(query_builder) { |io| io.read }
148
253
  end
149
254
 
150
255
  # Writes the chart's generated PNG to a file. (borrowed from John's gchart.rubyforge.org)
151
- def write(io_or_file=@@file_name)
256
+ def write
257
+ io_or_file = filename || Gchart.default_filename
152
258
  return io_or_file.write(fetch) if io_or_file.respond_to?(:write)
153
259
  open(io_or_file, "w+") { |io| io.write(fetch) }
154
260
  end
155
-
261
+
156
262
  # Format
157
-
263
+
158
264
  def image_tag
159
265
  image = "<img"
160
- image += " id=\"#{@id}\"" if @id
161
- image += " class=\"#{@class}\"" if @class
266
+ image += " id=\"#{id}\"" if id
267
+ image += " class=\"#{klass}\"" if klass
162
268
  image += " src=\"#{query_builder(:html)}\""
163
- image += " width=\"#{@width}\""
164
- image += " height=\"#{@height}\""
165
- image += " alt=\"#{@alt}\""
166
- image += " title=\"#{@title}\"" if @title
269
+ image += " width=\"#{width}\""
270
+ image += " height=\"#{height}\""
271
+ image += " alt=\"#{alt}\""
272
+ image += " title=\"#{title}\"" if title
167
273
  image += " />"
168
274
  end
169
-
275
+
170
276
  alias_method :img_tag, :image_tag
171
-
277
+
172
278
  def url
173
279
  query_builder
174
280
  end
175
-
281
+
176
282
  def file
177
283
  write
178
284
  end
179
-
285
+
180
286
  #
181
287
  def jstize(string)
182
288
  Gchart.jstize(string)
183
289
  end
184
-
290
+
185
291
  private
186
-
292
+
187
293
  # The title size cannot be set without specifying a color.
188
294
  # A dark key will be used for the title color if no color is specified
189
295
  def set_title
190
296
  title_params = "chtt=#{title}"
191
297
  unless (title_color.nil? && title_size.nil? )
192
- title_params << "&chts=" + (color, size = (@title_color || '454545'), @title_size).compact.join(',')
298
+ title_params << "&chts=" + (color, size = (title_color || '454545'), title_size).compact.join(',')
193
299
  end
194
300
  title_params
195
301
  end
196
-
302
+
197
303
  def set_size
198
304
  "chs=#{size}"
199
305
  end
200
-
306
+
201
307
  def set_data
202
308
  data = send("#{@encoding}_encoding")
203
309
  "chd=#{data}"
204
310
  end
205
-
311
+
206
312
  def set_colors
207
- bg_type = fill_type(@bg_type) || 's' if @bg_color
208
- chart_type = fill_type(@chart_type) || 's' if @chart_color
209
-
210
- "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('|')
313
+ @bg_type = fill_type(bg_type) || 's' if bg_color
314
+ @chart_type = fill_type(chart_type) || 's' if chart_color
315
+
316
+ "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('|')
211
317
  end
212
-
318
+
213
319
  # set bar, line colors
214
320
  def set_bar_colors
215
- @bar_colors = @bar_colors.join(',') if @bar_colors.is_a?(Array)
216
- "chco=#{@bar_colors}"
321
+ @bar_colors = bar_colors.join(',') if bar_colors.is_a?(Array)
322
+ "chco=#{bar_colors}"
217
323
  end
218
-
324
+
219
325
  def set_country_codes
220
- @country_codes = @country_codes.join() if @country_codes.is_a?(Array)
221
- "chld=#{@country_codes}"
326
+ @country_codes = country_codes.join() if country_codes.is_a?(Array)
327
+ "chld=#{country_codes}"
222
328
  end
223
-
329
+
224
330
  # set bar spacing
225
331
  # chbh=
226
332
  # <bar width in pixels>,
227
333
  # <optional space between bars in a group>,
228
334
  # <optional space between groups>
229
335
  def set_bar_width_and_spacing
230
- width_and_spacing_values = case @bar_width_and_spacing
336
+ width_and_spacing_values = case bar_width_and_spacing
231
337
  when String
232
- @bar_width_and_spacing
338
+ bar_width_and_spacing
233
339
  when Array
234
- @bar_width_and_spacing.join(',')
340
+ bar_width_and_spacing.join(',')
235
341
  when Hash
236
- width = @bar_width_and_spacing[:width] || 23
237
- spacing = @bar_width_and_spacing[:spacing] || 4
238
- group_spacing = @bar_width_and_spacing[:group_spacing] || 8
342
+ width = bar_width_and_spacing[:width] || 23
343
+ spacing = bar_width_and_spacing[:spacing] || 4
344
+ group_spacing = bar_width_and_spacing[:group_spacing] || 8
239
345
  [width,spacing,group_spacing].join(',')
240
346
  else
241
- @bar_width_and_spacing.to_s
347
+ bar_width_and_spacing.to_s
242
348
  end
243
349
  "chbh=#{width_and_spacing_values}"
244
350
  end
245
-
351
+
246
352
  def set_range_markers
247
- markers = case @range_markers
353
+ markers = case range_markers
248
354
  when Hash
249
- set_range_marker(@range_markers)
355
+ set_range_marker(range_markers)
250
356
  when Array
251
357
  range_markers.collect{|marker| set_range_marker(marker)}.join('|')
252
358
  end
253
359
  "chm=#{markers}"
254
360
  end
255
-
361
+
256
362
  def set_range_marker(options)
257
363
  orientation = ['vertical', 'Vertical', 'V', 'v', 'R'].include?(options[:orientation]) ? 'R' : 'r'
258
364
  "#{orientation},#{options[:color]},0,#{options[:start_position]},#{options[:stop_position]}#{',1' if options[:overlaid?]}"
259
365
  end
260
-
366
+
261
367
  def fill_for(type=nil, color='', angle=nil)
262
368
  unless type.nil?
263
369
  case type
264
- when 'lg'
265
- angle ||= 0
266
- color = "#{color},0,ffffff,1" if color.split(',').size == 1
267
- "#{type},#{angle},#{color}"
268
- when 'ls'
269
- angle ||= 90
270
- color = "#{color},0.2,ffffff,0.2" if color.split(',').size == 1
271
- "#{type},#{angle},#{color}"
272
- else
273
- "#{type},#{color}"
274
- end
370
+ when 'lg'
371
+ angle ||= 0
372
+ color = "#{color},0,ffffff,1" if color.split(',').size == 1
373
+ "#{type},#{angle},#{color}"
374
+ when 'ls'
375
+ angle ||= 90
376
+ color = "#{color},0.2,ffffff,0.2" if color.split(',').size == 1
377
+ "#{type},#{angle},#{color}"
378
+ else
379
+ "#{type},#{color}"
380
+ end
275
381
  end
276
382
  end
277
-
383
+
278
384
  # A chart can have one or many legends.
279
385
  # Gchart.line(:legend => 'label')
280
386
  # or
281
387
  # Gchart.line(:legend => ['first label', 'last label'])
282
388
  def set_legend
283
- return set_labels if @type == :pie || @type == :pie_3d || @type == :meter
284
-
285
- if @legend.is_a?(Array)
286
- "chdl=#{@legend.map{|label| "#{CGI::escape(label)}"}.join('|')}"
389
+ return set_labels if type.to_s =~ /pie|pie_3d|meter/
390
+
391
+ if legend.is_a?(Array)
392
+ "chdl=#{@legend.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
287
393
  else
288
- "chdl=#{@legend}"
394
+ "chdl=#{legend}"
289
395
  end
290
-
396
+
291
397
  end
292
-
398
+
293
399
  def set_labels
294
- if @legend.is_a?(Array)
295
- "chl=#{@legend.map{|label| "#{label}"}.join('|')}"
296
- else
297
- "chl=#{@legend}"
298
- end
400
+ if legend.is_a?(Array)
401
+ "chl=#{@legend.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
402
+ else
403
+ "chl=#{@legend}"
404
+ end
299
405
  end
300
-
406
+
301
407
  def set_axis_with_labels
302
- @axis_with_labels = @axis_with_labels.join(',') if @axis_with_labels.is_a?(Array)
303
- "chxt=#{@axis_with_labels}"
408
+ @axis_with_labels = axis_with_labels.join(',') if @axis_with_labels.is_a?(Array)
409
+ "chxt=#{axis_with_labels}"
304
410
  end
305
-
411
+
306
412
  def set_axis_labels
307
- labels_arr = []
308
- axis_labels.each_with_index do |labels,index|
413
+ if axis_labels.is_a?(Array)
414
+ if RUBY_VERSION.to_f < 1.9
415
+ labels_arr = axis_labels.enum_with_index.map{|labels,index| [index,labels]}
416
+ else
417
+ labels_arr = axis_labels.map.with_index.map{|labels,index| [index,labels]}
418
+ end
419
+ elsif axis_labels.is_a?(Hash)
420
+ labels_arr = axis_labels.to_a
421
+ end
422
+ labels_arr.map! do |index,labels|
309
423
  if labels.is_a?(Array)
310
- labels_arr << "#{index}:|#{labels.join('|')}"
424
+ "#{index}:|#{labels.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
311
425
  else
312
- labels_arr << "#{index}:|#{labels}"
426
+ "#{index}:|#{labels}"
313
427
  end
314
428
  end
315
429
  "chxl=#{labels_arr.join('|')}"
316
430
  end
317
-
431
+
318
432
  # http://code.google.com/apis/chart/labels.html#axis_range
319
433
  # Specify a range for axis labels
320
434
  def set_axis_range
321
435
  # a passed axis_range should look like:
322
436
  # [[10,100]] or [[10,100,4]] or [[10,100], [20,300]]
323
437
  # in the second example, 4 is the interval
324
- dataset # just making sure we processed the data before
325
- if axis_range && axis_range.respond_to?(:each) && axis_range.first.respond_to?(:each)
326
- 'chxr=' + axis_range.enum_for(:each_with_index).map{|range, index| [index, range[0], range[1], range[2]].compact.join(',')}.join("|")
438
+ if datasets && datasets.respond_to?(:each) && datasets.first.respond_to?(:each)
439
+ 'chxr=' + datasets.enum_for(:each_with_index).map{|range, index| [index, (min_value || range[0]), range[1], (max_value || range[2])].compact.uniq.join(',')}.join("|")
327
440
  else
328
441
  nil
329
442
  end
330
443
  end
331
-
444
+
332
445
  def set_geographical_area
333
- "chtm=#{@geographical_area}"
446
+ "chtm=#{geographical_area}"
334
447
  end
335
-
448
+
336
449
  def set_type
337
- case @type
338
- when :line
339
- "cht=lc"
340
- when :line_xy
341
- "cht=lxy"
342
- when :bar
343
- "cht=b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
344
- when :pie_3d
345
- "cht=p3"
346
- when :pie
347
- "cht=p"
348
- when :venn
349
- "cht=v"
350
- when :scatter
351
- "cht=s"
352
- when :sparkline
353
- "cht=ls"
354
- when :meter
355
- "cht=gom"
356
- when :map
357
- "cht=t"
358
- end
450
+ case type.to_s
451
+ when 'line'
452
+ "cht=lc"
453
+ when 'line_xy'
454
+ "cht=lxy"
455
+ when 'bar'
456
+ "cht=b" + (horizontal? ? "h" : "v") + (grouped? ? "g" : "s")
457
+ when 'pie_3d'
458
+ "cht=p3"
459
+ when 'pie'
460
+ "cht=p"
461
+ when 'venn'
462
+ "cht=v"
463
+ when 'scatter'
464
+ "cht=s"
465
+ when 'sparkline'
466
+ "cht=ls"
467
+ when 'meter'
468
+ "cht=gom"
469
+ when 'map'
470
+ "cht=t"
471
+ end
359
472
  end
360
-
473
+
361
474
  def fill_type(type)
362
475
  case type
363
476
  when 'solid'
@@ -369,36 +482,99 @@ class Gchart
369
482
  end
370
483
  end
371
484
 
372
- # Wraps a single dataset inside another array to support more datasets
373
- def prepare_dataset(ds)
374
- ds = [ds] unless ds.first.is_a?(Array)
485
+ def number_visible
486
+ n = 0
487
+ dataset.each do |mds|
488
+ return n.to_s if mds[:invisible] == true
489
+ if mds[:data].first.is_a?(Array)
490
+ n += mds[:data].length
491
+ else
492
+ n += 1
493
+ end
494
+ end
495
+ ""
496
+ end
497
+
498
+ # Turns input into an array of axis hashes, dependent on the chart type
499
+ def convert_dataset(ds)
500
+ if dimensions == 2
501
+ # valid inputs include:
502
+ # an array of >=2 arrays, or an array of >=2 hashes
503
+ ds = ds.map do |d|
504
+ d.is_a?(Hash) ? d : {:data => d}
505
+ end
506
+ elsif dimensions == 1
507
+ # valid inputs include:
508
+ # a hash, an array of data, an array of >=1 array, or an array of >=1 hash
509
+ if ds.is_a?(Hash)
510
+ ds = [ds]
511
+ elsif not ds.first.is_a?(Hash)
512
+ ds = [{:data => ds}]
513
+ end
514
+ end
375
515
  ds
376
516
  end
377
517
 
518
+ # just an alias
519
+ def axis_set
520
+ dataset
521
+ end
522
+
378
523
  def convert_to_simple_value(number)
379
524
  if number.nil?
380
525
  "_"
381
526
  else
382
- value = @@simple_chars[number.to_i]
527
+ value = Gchart.simple_chars[number.to_i]
383
528
  value.nil? ? "_" : value
384
529
  end
385
530
  end
386
531
 
532
+ def convert_to_extended_value(number)
533
+ if number.nil?
534
+ '__'
535
+ else
536
+ value = Gchart.ext_pairs[number.to_i]
537
+ value.nil? ? "__" : value
538
+ end
539
+ end
540
+
541
+ def encode_scaled_dataset(chars, nil_char)
542
+ dsets = []
543
+ dataset.each do |ds|
544
+ if max_value != false
545
+ range = ds[:max_value] - ds[:min_value]
546
+ range = 1 if range == 0
547
+ end
548
+ unless ds[:data].first.is_a?(Array)
549
+ ldatasets = [ds[:data]]
550
+ else
551
+ ldatasets = ds[:data]
552
+ end
553
+ ldatasets.each do |l|
554
+ dsets << l.map do |number|
555
+ if number.nil?
556
+ nil_char
557
+ else
558
+ unless range.nil? || range.zero?
559
+ number = chars.size * (number - ds[:min_value]) / range.to_f
560
+ number = [number, chars.size - 1].min
561
+ end
562
+ chars[number.to_i]
563
+ end
564
+ end.join
565
+ end
566
+ end
567
+ dsets.join(',')
568
+ end
569
+
387
570
  # http://code.google.com/apis/chart/#simple
388
571
  # Simple encoding has a resolution of 62 different values.
389
572
  # Allowing five pixels per data point, this is sufficient for line and bar charts up
390
573
  # to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size.
391
574
  def simple_encoding
392
- @max_value = dataset.compact.map{|ds| ds.compact.max}.max if @max_value == 'auto'
393
-
394
- if @max_value == false || @max_value == 'false' || @max_value == :false || @max_value == 0
395
- "s:" + dataset.map { |ds| ds.map { |number| number.nil? ? '_' : convert_to_simple_value(number) }.join }.join(',')
396
- else
397
- "s:" + dataset.map { |ds| ds.map { |number| number.nil? ? '_' : convert_to_simple_value( (@@simple_chars.size - 1) * number / @max_value) }.join }.join(',')
398
- end
399
-
575
+ "s" + number_visible + ":" + encode_scaled_dataset(Gchart.simple_chars, '_')
400
576
  end
401
-
577
+
402
578
  # http://code.google.com/apis/chart/#text
403
579
  # Text encoding with data scaling lets you specify arbitrary positive or
404
580
  # negative floating point numbers, in combination with a scaling parameter
@@ -412,61 +588,41 @@ class Gchart
412
588
  # This encoding is not available for maps.
413
589
  #
414
590
  def text_encoding
415
- "t:" + dataset.map{ |ds| ds.join(',') }.join('|') + "&chds=#{@min},#{@max}"
416
- end
417
-
418
- def convert_to_extended_value(number)
419
- if number.nil?
420
- '__'
421
- else
422
- value = @@ext_pairs[number.to_i]
423
- value.nil? ? "__" : value
424
- end
591
+ chds = dataset.map{|ds| "#{ds[:min_value]},#{ds[:max_value]}" }.join(",")
592
+ "t" + number_visible + ":" + datasets.map{ |ds| ds.join(',') }.join('|') + "&chds=" + chds
425
593
  end
426
594
 
427
-
428
595
  # http://code.google.com/apis/chart/#extended
429
596
  # Extended encoding has a resolution of 4,096 different values
430
597
  # and is best used for large charts where a large data range is required.
431
598
  def extended_encoding
432
- @max_value = dataset.compact.map{|ds| ds.compact.max}.max if @max_value == 'auto'
433
-
434
- if @max_value == false || @max_value == 'false' || @max_value == :false || @max_value == 0
435
- "e:" + dataset.map { |ds| ds.map { |number| number.nil? ? '__' : convert_to_extended_value(number)}.join }.join(',')
436
- else
437
- "e:" + dataset.map { |ds| ds.map { |number| number.nil? ? '__' : convert_to_extended_value( (@@ext_pairs.size - 1) * number / @max_value) }.join }.join(',')
438
- end
439
-
599
+ "e" + number_visible + ":" + encode_scaled_dataset(Gchart.ext_pairs, '__')
440
600
  end
441
-
442
-
601
+
443
602
  def query_builder(options="")
444
- dataset
445
603
  query_params = instance_variables.sort.map do |var|
446
- case var
604
+ case var.to_s
447
605
  when '@data'
448
- set_data unless @data == []
449
- # Set the graph size
606
+ set_data unless data == []
607
+ # Set the graph size
450
608
  when '@width'
451
- set_size unless @width.nil? || @height.nil?
609
+ set_size unless width.nil? || height.nil?
452
610
  when '@type'
453
611
  set_type
454
612
  when '@title'
455
- set_title unless @title.nil?
613
+ set_title unless title.nil?
456
614
  when '@legend'
457
- set_legend unless @legend.nil?
615
+ set_legend unless legend.nil?
458
616
  when '@bg_color'
459
617
  set_colors
460
618
  when '@chart_color'
461
- set_colors if @bg_color.nil?
619
+ set_colors if bg_color.nil?
462
620
  when '@bar_colors'
463
621
  set_bar_colors
464
622
  when '@bar_width_and_spacing'
465
623
  set_bar_width_and_spacing
466
624
  when '@axis_with_labels'
467
625
  set_axis_with_labels
468
- when '@axis_range'
469
- set_axis_range if dataset
470
626
  when '@axis_labels'
471
627
  set_axis_labels
472
628
  when '@range_markers'
@@ -476,19 +632,21 @@ class Gchart
476
632
  when '@country_codes'
477
633
  set_country_codes
478
634
  when '@custom'
479
- @custom
635
+ custom
480
636
  end
481
637
  end.compact
482
-
638
+
639
+ query_params << set_axis_range
640
+
483
641
  # Use ampersand as default delimiter
484
642
  unless options == :html
485
643
  delimiter = '&'
486
- # Escape ampersand for html image tags
644
+ # Escape ampersand for html image tags
487
645
  else
488
646
  delimiter = '&amp;'
489
647
  end
490
-
491
- jstize(@@url + query_params.join(delimiter))
648
+
649
+ jstize(Gchart.url + query_params.join(delimiter))
492
650
  end
493
-
651
+
494
652
  end
@@ -1,7 +1,7 @@
1
1
  module GchartInfo #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 4
4
+ MINOR = 5
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -0,0 +1,2 @@
1
+ require 'gchart'
2
+ Googlecharts = Gchart unless Object.const_defined? 'Googlechart'
data/spec/gchart_spec.rb CHANGED
@@ -16,7 +16,7 @@ describe "generating a default Gchart" do
16
16
  end
17
17
 
18
18
  it "should have a default size" do
19
- @chart.include?('chs=300x200').should be_true
19
+ @chart.should include('chs=300x200')
20
20
  end
21
21
 
22
22
  it "should be able to have a custom size" do
@@ -25,7 +25,7 @@ describe "generating a default Gchart" do
25
25
  end
26
26
 
27
27
  it "should have query parameters in predictable order" do
28
- Gchart.line(:axis_with_labels => 'x,y,r', :size => '400x600').should match(/chxr=.+chxt=.+cht=.+chs=/)
28
+ Gchart.line(:axis_with_labels => 'x,y,r', :size => '400x600').should match(/chxt=.+cht=.+chs=/)
29
29
  end
30
30
 
31
31
  it "should have a type" do
@@ -33,19 +33,23 @@ describe "generating a default Gchart" do
33
33
  end
34
34
 
35
35
  it 'should use theme defaults if theme is set' do
36
- Gchart.line(:theme=>:test).include?('chco=6886B4,FDD84E').should be_true
37
- Gchart.line(:theme=>:test).include?(Gchart.jstize('chf=c,s,FFFFFF|bg,s,FFFFFF')).should be_true
36
+ Gchart.line(:theme=>:test).should include('chco=6886B4,FDD84E')
37
+ if RUBY_VERSION.to_f < 1.9
38
+ Gchart.line(:theme=>:test).should include(Gchart.jstize('chf=c,s,FFFFFF|bg,s,FFFFFF'))
39
+ else
40
+ Gchart.line(:theme=>:test).should include(Gchart.jstize('chf=bg,s,FFFFFF|c,s,FFFFFF'))
41
+ end
38
42
  end
39
43
 
40
44
  it "should use the simple encoding by default with auto max value" do
41
45
  # 9 is the max value in simple encoding, 26 being our max value the 2nd encoded value should be 9
42
- Gchart.line(:data => [0, 26]).include?('chd=s:A9').should be_true
46
+ Gchart.line(:data => [0, 26]).should include('chd=s:A9')
43
47
  Gchart.line(:data => [0, 26], :max_value => 26).should == Gchart.line(:data => [0, 26])
44
48
  end
45
49
 
46
50
  it "should support simple encoding with and without max_value" do
47
- Gchart.line(:data => [0, 26], :max_value => 26).include?('chd=s:A9').should be_true
48
- Gchart.line(:data => [0, 26], :max_value => false).include?('chd=s:Aa').should be_true
51
+ Gchart.line(:data => [0, 26], :max_value => 26).should include('chd=s:A9')
52
+ Gchart.line(:data => [0, 26], :max_value => false).should include('chd=s:Aa')
49
53
  end
50
54
 
51
55
  it "should support the extended encoding and encode properly" do
@@ -57,21 +61,21 @@ describe "generating a default Gchart" do
57
61
  end
58
62
 
59
63
  it "should auto set the max value for extended encoding" do
60
- Gchart.line(:data => [0, 25], :encoding => 'extended', :max_value => false).include?('chd=e:AAAZ').should be_true
64
+ Gchart.line(:data => [0, 25], :encoding => 'extended', :max_value => false).should include('chd=e:AAAZ')
61
65
  # Extended encoding max value is '..'
62
66
  Gchart.line(:data => [0, 25], :encoding => 'extended').include?('chd=e:AA..').should be_true
63
67
  end
64
68
 
65
69
  it "should be able to have data with text encoding" do
66
- Gchart.line(:data => [10, 5.2, 4, 45, 78], :encoding => 'text').include?('chd=t:10,5.2,4,45,78').should be_true
70
+ Gchart.line(:data => [10, 5.2, 4, 45, 78], :encoding => 'text').should include('chd=t:10,5.2,4,45,78')
67
71
  end
68
72
 
69
73
  it "should handle max and min values with text encoding" do
70
- Gchart.line(:data => [10, 5.2, 4, 45, 78], :encoding => 'text').include?('chds=0,78').should be_true
74
+ Gchart.line(:data => [10, 5.2, 4, 45, 78], :encoding => 'text').should include('chds=0,78')
71
75
  end
72
76
 
73
77
  it "should automatically handle negative values with proper max/min limits when using text encoding" do
74
- Gchart.line(:data => [-10, 5.2, 4, 45, 78], :encoding => 'text').include?('chds=-10,78').should be_true
78
+ Gchart.line(:data => [-10, 5.2, 4, 45, 78], :encoding => 'text').should include('chds=-10,78')
75
79
  end
76
80
 
77
81
  it "should handle negative values with manual max/min limits when using text encoding" do
@@ -159,7 +163,8 @@ describe "generating different type of charts" do
159
163
  end
160
164
 
161
165
  it "should not support other types" do
162
- Gchart.sexy.should == "sexy is not a supported chart format, please use one of the following: #{Gchart.supported_types}."
166
+ lambda{Gchart.sexy}.should raise_error
167
+ # msg => "sexy is not a supported chart format, please use one of the following: #{Gchart.supported_types}."
163
168
  end
164
169
 
165
170
  end
@@ -319,18 +324,18 @@ describe "a line chart" do
319
324
  end
320
325
 
321
326
  it "should be able to set the background fill" do
322
- Gchart.line(:bg => 'efefef').include?("chf=bg,s,efefef").should be_true
323
- Gchart.line(:bg => {:color => 'efefef', :type => 'solid'}).include?("chf=bg,s,efefef").should be_true
327
+ Gchart.line(:bg => 'efefef').should include("chf=bg,s,efefef")
328
+ Gchart.line(:bg => {:color => 'efefef', :type => 'solid'}).should include("chf=bg,s,efefef")
324
329
 
325
- Gchart.line(:bg => {:color => 'efefef', :type => 'gradient'}).include?("chf=bg,lg,0,efefef,0,ffffff,1").should be_true
326
- Gchart.line(:bg => {:color => 'efefef,0,ffffff,1', :type => 'gradient'}).include?("chf=bg,lg,0,efefef,0,ffffff,1").should be_true
327
- Gchart.line(:bg => {:color => 'efefef', :type => 'gradient', :angle => 90}).include?("chf=bg,lg,90,efefef,0,ffffff,1").should be_true
330
+ Gchart.line(:bg => {:color => 'efefef', :type => 'gradient'}).should include("chf=bg,lg,0,efefef,0,ffffff,1")
331
+ Gchart.line(:bg => {:color => 'efefef,0,ffffff,1', :type => 'gradient'}).should include("chf=bg,lg,0,efefef,0,ffffff,1")
332
+ Gchart.line(:bg => {:color => 'efefef', :type => 'gradient', :angle => 90}).should include("chf=bg,lg,90,efefef,0,ffffff,1")
328
333
 
329
- Gchart.line(:bg => {:color => 'efefef', :type => 'stripes'}).include?("chf=bg,ls,90,efefef,0.2,ffffff,0.2").should be_true
334
+ Gchart.line(:bg => {:color => 'efefef', :type => 'stripes'}).should include("chf=bg,ls,90,efefef,0.2,ffffff,0.2")
330
335
  end
331
336
 
332
337
  it "should be able to set a graph fill" do
333
- Gchart.line(:graph_bg => 'efefef').include?("chf=c,s,efefef").should be_true
338
+ Gchart.line(:graph_bg => 'efefef').should include("chf=c,s,efefef")
334
339
  Gchart.line(:graph_bg => {:color => 'efefef', :type => 'solid'}).include?("chf=c,s,efefef").should be_true
335
340
  Gchart.line(:graph_bg => {:color => 'efefef', :type => 'gradient'}).include?("chf=c,lg,0,efefef,0,ffffff,1").should be_true
336
341
  Gchart.line(:graph_bg => {:color => 'efefef,0,ffffff,1', :type => 'gradient'}).include?("chf=c,lg,0,efefef,0,ffffff,1").should be_true
@@ -340,7 +345,11 @@ describe "a line chart" do
340
345
  it "should be able to set both a graph and a background fill" do
341
346
  Gchart.line(:bg => 'efefef', :graph_bg => '76A4FB').include?("bg,s,efefef").should be_true
342
347
  Gchart.line(:bg => 'efefef', :graph_bg => '76A4FB').include?("c,s,76A4FB").should be_true
343
- Gchart.line(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=c,s,76A4FB|bg,s,efefef")).should be_true
348
+ if RUBY_VERSION.to_f < 1.9
349
+ Gchart.line(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=c,s,76A4FB|bg,s,efefef")).should be_true
350
+ else
351
+ Gchart.line(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=bg,s,efefef|c,s,76A4FB")).should be_true
352
+ end
344
353
  end
345
354
 
346
355
  it "should be able to have different line colors" do
@@ -349,7 +358,7 @@ describe "a line chart" do
349
358
  end
350
359
 
351
360
  it "should be able to render a graph where all the data values are 0" do
352
- Gchart.line(:data => [0, 0, 0]).include?("chd=s:AAA").should be_true
361
+ Gchart.line(:data => [0, 0, 0]).should include("chd=s:AAA")
353
362
  end
354
363
 
355
364
  end
@@ -408,7 +417,11 @@ describe "a sparkline chart" do
408
417
  it "should be able to set both a graph and a background fill" do
409
418
  Gchart.sparkline(:bg => 'efefef', :graph_bg => '76A4FB').include?("bg,s,efefef").should be_true
410
419
  Gchart.sparkline(:bg => 'efefef', :graph_bg => '76A4FB').include?("c,s,76A4FB").should be_true
411
- Gchart.sparkline(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=c,s,76A4FB|bg,s,efefef")).should be_true
420
+ if RUBY_VERSION.to_f < 1.9
421
+ Gchart.sparkline(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=c,s,76A4FB|bg,s,efefef")).should be_true
422
+ else
423
+ Gchart.sparkline(:bg => 'efefef', :graph_bg => '76A4FB').include?(Gchart.jstize("chf=bg,s,efefef|c,s,76A4FB")).should be_true
424
+ end
412
425
  end
413
426
 
414
427
  it "should be able to have different line colors" do
@@ -437,8 +450,8 @@ describe "a 3d pie chart" do
437
450
  end
438
451
 
439
452
  it "should be able to set labels by using the legend or labesl accessor" do
440
- Gchart.pie_3d(:title => @title, :legend => @legend, :data => @data).include?("chl=#{@jstized_legend}").should be_true
441
- Gchart.pie_3d(:title => @title, :labels => @legend, :data => @data).include?("chl=#{@jstized_legend}").should be_true
453
+ Gchart.pie_3d(:title => @title, :legend => @legend, :data => @data).should include("chl=#{@jstized_legend}")
454
+ Gchart.pie_3d(:title => @title, :labels => @legend, :data => @data).should include("chl=#{@jstized_legend}")
442
455
  Gchart.pie_3d(:title => @title, :labels => @legend, :data => @data).should == Gchart.pie_3d(:title => @title, :legend => @legend, :data => @data)
443
456
  end
444
457
 
@@ -559,7 +572,7 @@ describe 'exporting a chart' do
559
572
  File.delete('foo.png') if File.exist?('foo.png')
560
573
  Gchart.line(:size => '400x200',
561
574
  :data => [1,2,3,4,5],
562
- :axis_labels => [[1,2,3,4, 5], %w[foo bar]],
575
+ # :axis_labels => [[1,2,3,4, 5], %w[foo bar]],
563
576
  :axis_with_labels => 'x,r',
564
577
  :format => "file",
565
578
  :filename => "foo.png"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googlecharts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Aimonetti
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-30 00:00:00 -07:00
12
+ date: 2010-01-22 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -39,6 +39,7 @@ files:
39
39
  - lib/gchart/aliases.rb
40
40
  - lib/gchart/theme.rb
41
41
  - lib/gchart/version.rb
42
+ - lib/googlecharts.rb
42
43
  - lib/themes.yml
43
44
  - script/destroy
44
45
  - script/generate
@@ -47,7 +48,6 @@ files:
47
48
  - spec/fixtures/another_test_theme.yml
48
49
  - spec/fixtures/test_theme.yml
49
50
  - spec/gchart_spec.rb
50
- - spec/spec.opts
51
51
  - spec/spec_helper.rb
52
52
  - spec/theme_spec.rb
53
53
  - tasks/deployment.rake
data/spec/spec.opts DELETED
@@ -1,4 +0,0 @@
1
- spec/
2
- spec/gchart_spec.rb
3
- spec/
4
- spec/