googlecharts 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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/