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 +1 -1
- data/lib/gchart.rb +392 -234
- data/lib/gchart/version.rb +1 -1
- data/lib/googlecharts.rb +2 -0
- data/spec/gchart_spec.rb +38 -25
- metadata +3 -3
- data/spec/spec.opts +0 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def self.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
-
"#{
|
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
|
-
|
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
|
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
|
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
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
@
|
127
|
-
|
128
|
-
|
225
|
+
@dataset = convert_dataset(data || [])
|
226
|
+
full_data_range(@dataset) # unless axis_range
|
227
|
+
@dataset
|
228
|
+
end
|
129
229
|
end
|
130
230
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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(/\[|\{|\}
|
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
|
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=\"#{
|
161
|
-
image += " class=\"#{
|
266
|
+
image += " id=\"#{id}\"" if id
|
267
|
+
image += " class=\"#{klass}\"" if klass
|
162
268
|
image += " src=\"#{query_builder(:html)}\""
|
163
|
-
image += " width=\"#{
|
164
|
-
image += " height=\"#{
|
165
|
-
image += " alt=\"#{
|
166
|
-
image += " 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 = (
|
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(
|
208
|
-
chart_type = fill_type(
|
209
|
-
|
210
|
-
"chf=" + {'bg' => fill_for(bg_type,
|
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 =
|
216
|
-
"chco=#{
|
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 =
|
221
|
-
"chld=#{
|
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
|
336
|
+
width_and_spacing_values = case bar_width_and_spacing
|
231
337
|
when String
|
232
|
-
|
338
|
+
bar_width_and_spacing
|
233
339
|
when Array
|
234
|
-
|
340
|
+
bar_width_and_spacing.join(',')
|
235
341
|
when Hash
|
236
|
-
width
|
237
|
-
spacing
|
238
|
-
group_spacing =
|
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
|
-
|
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
|
353
|
+
markers = case range_markers
|
248
354
|
when Hash
|
249
|
-
set_range_marker(
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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
|
284
|
-
|
285
|
-
if
|
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=#{
|
394
|
+
"chdl=#{legend}"
|
289
395
|
end
|
290
|
-
|
396
|
+
|
291
397
|
end
|
292
|
-
|
398
|
+
|
293
399
|
def set_labels
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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 =
|
303
|
-
"chxt=#{
|
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
|
-
|
308
|
-
|
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
|
-
|
424
|
+
"#{index}:|#{labels.map{|label| "#{CGI::escape(label.to_s)}"}.join('|')}"
|
311
425
|
else
|
312
|
-
|
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
|
-
|
325
|
-
|
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=#{
|
446
|
+
"chtm=#{geographical_area}"
|
334
447
|
end
|
335
|
-
|
448
|
+
|
336
449
|
def set_type
|
337
|
-
case
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
373
|
-
|
374
|
-
|
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 =
|
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
|
-
|
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
|
-
|
416
|
-
|
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
|
-
|
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
|
449
|
-
|
606
|
+
set_data unless data == []
|
607
|
+
# Set the graph size
|
450
608
|
when '@width'
|
451
|
-
set_size unless
|
609
|
+
set_size unless width.nil? || height.nil?
|
452
610
|
when '@type'
|
453
611
|
set_type
|
454
612
|
when '@title'
|
455
|
-
set_title unless
|
613
|
+
set_title unless title.nil?
|
456
614
|
when '@legend'
|
457
|
-
set_legend unless
|
615
|
+
set_legend unless legend.nil?
|
458
616
|
when '@bg_color'
|
459
617
|
set_colors
|
460
618
|
when '@chart_color'
|
461
|
-
set_colors if
|
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
|
-
|
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
|
-
|
644
|
+
# Escape ampersand for html image tags
|
487
645
|
else
|
488
646
|
delimiter = '&'
|
489
647
|
end
|
490
|
-
|
491
|
-
jstize(
|
648
|
+
|
649
|
+
jstize(Gchart.url + query_params.join(delimiter))
|
492
650
|
end
|
493
|
-
|
651
|
+
|
494
652
|
end
|
data/lib/gchart/version.rb
CHANGED
data/lib/googlecharts.rb
ADDED
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
|
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(/
|
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
|
37
|
-
|
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
|
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
|
48
|
-
Gchart.line(:data => [0, 26], :max_value => false).include
|
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
|
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
|
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
|
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
|
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
|
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
|
323
|
-
Gchart.line(:bg => {:color => 'efefef', :type => 'solid'}).include
|
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
|
326
|
-
Gchart.line(:bg => {:color => 'efefef,0,ffffff,1', :type => 'gradient'}).include
|
327
|
-
Gchart.line(:bg => {:color => 'efefef', :type => 'gradient', :angle => 90}).include
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
441
|
-
Gchart.pie_3d(:title => @title, :labels => @legend, :data => @data).include
|
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
|
+
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:
|
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