ghazel-googlecharts 1.4.0.1 → 1.4.0.2

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.
Files changed (2) hide show
  1. data/lib/gchart.rb +107 -70
  2. metadata +1 -1
data/lib/gchart.rb CHANGED
@@ -6,10 +6,11 @@ require "uri"
6
6
  require "cgi"
7
7
  require 'enumerator'
8
8
 
9
+
9
10
  class Gchart
10
11
 
11
12
  include GchartInfo
12
-
13
+
13
14
  @@url = "http://chart.apis.google.com/chart?"
14
15
  @@types = ['line', 'line_xy', 'scatter', 'bar', 'venn', 'pie', 'pie_3d', 'jstize', 'sparkline', 'meter', 'map']
15
16
  @@simple_chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
@@ -17,9 +18,11 @@ class Gchart
17
18
  @@ext_pairs = @@chars.map { |char_1| @@chars.map { |char_2| char_1 + char_2 } }.flatten
18
19
  @@file_name = 'chart.png'
19
20
 
20
- attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend, :data, :encoding, :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
21
+ attr_accessor :title, :type, :width, :height, :horizontal, :grouped, :legend,
22
+ :data, :encoding, :bar_colors, :title_color,
23
+ :title_size, :custom, :axis_with_labels, :axis_labels,
24
+ :bar_width_and_spacing, :id, :alt, :class, :range_markers,
25
+ :geographical_area, :map_colors, :country_codes, :axis_range
23
26
 
24
27
  # Support for Gchart.line(:title => 'my title', :size => '400x600')
25
28
  def self.method_missing(m, options={})
@@ -46,14 +49,11 @@ class Gchart
46
49
 
47
50
  def initialize(options={})
48
51
  @type = :line
49
- @data = []
50
52
  @width = 300
51
53
  @height = 200
52
54
  @horizontal = false
53
55
  @grouped = false
54
56
  @encoding = 'simple'
55
- self.min_value = 'auto'
56
- self.max_value = 'auto'
57
57
  # Sets the alt tag when chart is exported as image tag
58
58
  @alt = 'Google Chart'
59
59
  # Sets the CSS id selector when chart is exported as image tag
@@ -137,57 +137,94 @@ class Gchart
137
137
  def full_data_range(ds)
138
138
  return if @max_value == false
139
139
 
140
- @axis = []
141
- # TODO: text encoding should use @axis too
142
- if dimensions == 2 and @encoding != :text
143
- ds.each_with_index do |mds,index|
144
- cmds = mds.compact
145
- if cmds.empty?
146
- @axis << nil
147
- next
148
- end
149
- @axis[index%dimensions] ||= []
150
- ax = @axis[index%dimensions]
151
- ax[0] = @min_value.nil? ? [ax[0], cmds.min].compact.min : @min_value
152
- ax[1] = @max_value.nil? ? [ax[0], cmds.max].compact.max : @max_value
153
- end
154
- else
155
- if @type == :bar and not grouped
156
- @min_value = ds.compact.first.compact.min if @min_value.nil?
157
- if @max_value.nil?
158
- totals = []
159
- ds.compact.each do |mds|
160
- mds.each_with_index do |v, index|
161
- next if v.nil?
162
- totals[index] ||= 0
163
- totals[index] += v
164
- end
140
+ ds.each do |mds|
141
+ # global limits override individuals. is this preferred?
142
+ mds[:min_value] = @min_value if not @min_value.nil?
143
+ mds[:max_value] = @max_value if not @max_value.nil?
144
+
145
+ # TODO: can you have grouped stacked bars?
146
+ if @type == :bar and not grouped and mds[:data].first.is_a?(Array)
147
+ mds[:min_value] ||= mds[:data].first.to_a.compact.min
148
+ totals = []
149
+ mds[:data].each do |l|
150
+ l.each_with_index do |v, index|
151
+ next if v.nil?
152
+ totals[index] ||= 0
153
+ totals[index] += v
165
154
  end
166
- @max_value = totals.compact.max
167
155
  end
156
+ mds[:max_value] ||= totals.compact.max
168
157
  else
169
- @min_value = ds.compact.map{|mds| mds.compact.min}.min if @min_value.nil?
170
- @max_value = ds.compact.map{|mds| mds.compact.max}.max if @max_value.nil?
158
+ all = mds[:data].flatten.compact
159
+ mds[:min_value] ||= all.min
160
+ mds[:max_value] ||= all.max
171
161
  end
172
- @axis << [@min_value, @max_value]
173
162
  end
174
163
 
175
164
  if not @axis_range
176
- @axis_range = @axis.map{|axis| axis.nil? ? [] : axis}
177
- if @type != :line_xy and (@type != :bar or not @horizontal)
165
+ @axis_range = ds.map{|mds| [mds[:min_value], mds[:max_value]]}
166
+ if dimensions == 1 and (@type != :bar or not @horizontal)
178
167
  tmp = @axis_range.fetch(0, [])
179
168
  @axis_range[0] = @axis_range.fetch(1, [])
180
169
  @axis_range[1] = tmp
181
170
  end
182
171
  end
183
172
  end
184
-
185
- def dataset
186
- @dataset ||= prepare_dataset(data)
173
+
174
+ def number_visible
175
+ n = 0
176
+ axis_set.each do |mds|
177
+ return n.to_s if mds[:invisible] == true
178
+ if mds[:data].first.is_a?(Array)
179
+ n += mds[:data].length
180
+ else
181
+ n += 1
182
+ end
183
+ end
184
+ ""
185
+ end
186
+
187
+ # Turns input into an array of axis hashes, dependent on the chart type
188
+ def convert_dataset(ds)
189
+ if dimensions == 2
190
+ # valid inputs include:
191
+ # an array of >=2 arrays, or an array of >=2 hashes
192
+ ds = ds.map do |d|
193
+ d.is_a?(Hash) ? d : {:data => d}
194
+ end
195
+ elsif dimensions == 1
196
+ # valid inputs include:
197
+ # a hash, an array of data, an array of >=1 array, or an array of >=1 hash
198
+ if ds.is_a?(Hash)
199
+ ds = [ds]
200
+ elsif not ds.first.is_a?(Hash)
201
+ ds = [{:data => ds}]
202
+ end
203
+ end
204
+ ds
205
+ end
206
+
207
+ def prepare_dataset
208
+ @dataset = convert_dataset(data || [])
187
209
  full_data_range(@dataset)
210
+ end
211
+
212
+ def axis_set
188
213
  @dataset
189
214
  end
190
-
215
+
216
+ def dataset
217
+ datasets = []
218
+ @dataset.each do |d|
219
+ if d[:data].first.is_a?(Array)
220
+ datasets += d[:data]
221
+ else
222
+ datasets << d[:data]
223
+ end
224
+ end
225
+ datasets
226
+ end
227
+
191
228
  def self.jstize(string)
192
229
  string.gsub(' ', '+').gsub(/\[|\{|\}|\||\\|\^|\[|\]|\`|\]/) {|c| "%#{c[0].to_s(16).upcase}"}
193
230
  end
@@ -379,7 +416,6 @@ class Gchart
379
416
  # a passed axis_range should look like:
380
417
  # [[10,100]] or [[10,100,4]] or [[10,100], [20,300]]
381
418
  # in the second example, 4 is the interval
382
- dataset # just making sure we processed the data before
383
419
  if axis_range && axis_range.respond_to?(:each) && axis_range.first.respond_to?(:each)
384
420
  'chxr=' + axis_range.enum_for(:each_with_index).map{|range, index| [index, range[0], range[1], range[2]].compact.join(',')}.join("|")
385
421
  else
@@ -426,33 +462,34 @@ class Gchart
426
462
  'ls'
427
463
  end
428
464
  end
429
-
430
- # Wraps a single dataset inside another array to support more datasets
431
- def prepare_dataset(ds)
432
- ds = [ds] unless ds.first.is_a?(Array)
433
- ds
434
- end
435
465
 
436
466
  def encode_scaled_dataset chars, nil_char
437
- dataset.enum_with_index.map do |ds, index|
467
+ dsets = []
468
+ axis_set.each do |ds|
438
469
  if @max_value != false
439
- ax = @axis[index%dimensions]
440
- min = ax[0]
441
- range = ax[1] - min
470
+ range = ds[:max_value] - ds[:min_value]
442
471
  range = 1 if range == 0
443
472
  end
444
- ds.map do |number|
445
- if number.nil?
446
- nil_char
447
- else
448
- if not range.nil?
449
- number = chars.size * (number - min) / range
450
- number = [number, chars.size - 1].min
451
- end
452
- chars[number.to_i]
453
- end
454
- end.join
455
- end.join(',')
473
+ if not ds[:data].first.is_a?(Array)
474
+ datasets = [ds[:data]]
475
+ else
476
+ datasets = ds[:data]
477
+ end
478
+ datasets.each do |l|
479
+ dsets << l.map do |number|
480
+ if number.nil?
481
+ nil_char
482
+ else
483
+ if not range.nil?
484
+ number = chars.size * (number - ds[:min_value]) / range
485
+ number = [number, chars.size - 1].min
486
+ end
487
+ chars[number.to_i]
488
+ end
489
+ end.join
490
+ end
491
+ end
492
+ dsets.join(',')
456
493
  end
457
494
 
458
495
  # http://code.google.com/apis/chart/#simple
@@ -460,7 +497,7 @@ class Gchart
460
497
  # Allowing five pixels per data point, this is sufficient for line and bar charts up
461
498
  # to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size.
462
499
  def simple_encoding
463
- "s:" + encode_scaled_dataset(@@simple_chars, '_')
500
+ "s" + number_visible + ":" + encode_scaled_dataset(@@simple_chars, '_')
464
501
  end
465
502
 
466
503
  # http://code.google.com/apis/chart/#text
@@ -476,19 +513,19 @@ class Gchart
476
513
  # This encoding is not available for maps.
477
514
  #
478
515
  def text_encoding
479
- # TODO: text encoding should use @axis too
480
- "t:" + dataset.map{ |ds| ds.join(',') }.join('|') + "&chds=#{@min_value},#{@max_value}"
516
+ chds = axis_set.map{ |ds| "#{ds[:min_value]},#{ds[:max_value]}" }.join(",")
517
+ "t" + number_visible + ":" + dataset.map{ |ds| ds.join(',') }.join('|') + "&chds=" + chds
481
518
  end
482
519
 
483
520
  # http://code.google.com/apis/chart/#extended
484
521
  # Extended encoding has a resolution of 4,096 different values
485
522
  # and is best used for large charts where a large data range is required.
486
523
  def extended_encoding
487
- "e:" + encode_scaled_dataset(@@ext_pairs, '__')
524
+ "e" + number_visible + ":" + encode_scaled_dataset(@@ext_pairs, '__')
488
525
  end
489
526
 
490
527
  def query_builder(options="")
491
- dataset
528
+ prepare_dataset
492
529
  query_params = instance_variables.map do |var|
493
530
  case var
494
531
  when '@data'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ghazel-googlecharts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0.1
4
+ version: 1.4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Aimonetti