Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit ba6802dd4ec08c5f7f65dee6ee834b08de3b1e51 @willf committed Apr 9, 2011
Showing with 494 additions and 0 deletions.
  1. +20 −0 MIT-LICENSE
  2. +12 −0 README.rdoc
  3. +11 −0 Rakefile
  4. +62 −0 examples/examples.html
  5. +86 −0 examples/examples.rb
  6. +178 −0 lib/google_chart.rb
  7. +115 −0 test/google_chart.spec
  8. +10 −0 test/test_helper.rb
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Will Fitzgerald
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,12 @@
+= RUBY_GOOGLE_CHARTS -- Ruby interface to Google Charts API
+
+This is Ruby code to access the Google Chart API
+
+http://code.google.com/apis/chart/
+
+Examples can be found in examples/examples.rb (with output at example.html)
+
+No strong library requirements -- uses Erb for examples, and RSpec for
+unit tests.
+
+
@@ -0,0 +1,11 @@
+# -*- ruby -*-
+
+require 'rubygems'
+
+task :default => [:spec]
+
+task :spec do
+ ruby "test/google_chart.spec"
+end
+
+# vim: syntax=Ruby
@@ -0,0 +1,62 @@
+
+ <html>
+ <head><title>Charts!</title></head>
+ <body>
+ <h1>9 Google Chart Examples</h1>
+
+ <h3>Test Chart Two Data lines|(Given encoding)
+ <p><a href="http://chart.apis.google.com/chart?cht=lc&chs=500x200&chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&chl=Test+Chart+Two+Data+lines%7C%28Given+encoding%29">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=lc&amp;chs=500x200&amp;chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&amp;chl=Test+Chart+Two+Data+lines%7C%28Given+encoding%29" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>Test Chart Two Data lines|(Guess encoding)
+ <p><a href="http://chart.apis.google.com/chart?cht=lc&chs=500x200&chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&chl=Test+Chart+Two+Data+lines%7C%28Guess+encoding%29">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=lc&amp;chs=500x200&amp;chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&amp;chl=Test+Chart+Two+Data+lines%7C%28Guess+encoding%29" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>The Price of Tea|Foot Size
+ <p><a href="http://chart.apis.google.com/chart?cht=lxy&chs=500x200&chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&chl=The+Price+of+Tea%7CFoot+Size">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=lxy&amp;chs=500x200&amp;chd=t:0,30,60,70,90,95,100|20,30,40,50,60,70,80&amp;chl=The+Price+of+Tea%7CFoot+Size" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>The Division of Labor
+ <p><a href="http://chart.apis.google.com/chart?cht=p&chtt=The+Division+of+Labor&chs=500x200&chd=t:25,25,30,10,5&chl=You%7CMe%7CThem%7CUs%7CIt">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=p&amp;chtt=The+Division+of+Labor&amp;chs=500x200&amp;chd=t:25,25,30,10,5&amp;chl=You%7CMe%7CThem%7CUs%7CIt" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>The Division of Labor
+ <p><a href="http://chart.apis.google.com/chart?cht=p3&chtt=The+Division+of+Labor&chs=500x200&chd=t:25,25,30,10,5&chl=You%7CMe%7CThem%7CUs%7CIt">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=p3&amp;chtt=The+Division+of+Labor&amp;chs=500x200&amp;chd=t:25,25,30,10,5&amp;chl=You%7CMe%7CThem%7CUs%7CIt" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>Chart 6
+ <p><a href="http://chart.apis.google.com/chart?cht=v&chs=300x300&chdl=Yours%7CMine%7COurs&chd=t:100,80,60,30,30,30,10">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=v&amp;chs=300x300&amp;chdl=Yours%7CMine%7COurs&amp;chd=t:100,80,60,30,30,30,10" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>Pct who understand Charts
+ <p><a href="http://chart.apis.google.com/chart?cht=bvs&chtt=Pct+who+understand+Charts&chs=300x200&chd=t:100,80,60,30,30,60,80,75&chl=00%7C01%7C02%7C03%7C04%7C05%7C06%7C07">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=bvs&amp;chtt=Pct+who+understand+Charts&amp;chs=300x200&amp;chd=t:100,80,60,30,30,60,80,75&amp;chl=00%7C01%7C02%7C03%7C04%7C05%7C06%7C07" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>Random Scatter Plot
+ <p><a href="http://chart.apis.google.com/chart?chm=20&cht=s&chtt=Random+Scatter+Plot&chs=300x300&chd=s:F4yN2F7ZGdC4QBCwxexw,ZgwNcmBuL2CdMBZ5RBKe,RMxq8jGHDx5haEulr4DU&chl=The+Price+of+Tea%7CFoot+Size">link</a><br />
+ <img src="http://chart.apis.google.com/chart?chm=20&amp;cht=s&amp;chtt=Random+Scatter+Plot&amp;chs=300x300&amp;chd=s:F4yN2F7ZGdC4QBCwxexw,ZgwNcmBuL2CdMBZ5RBKe,RMxq8jGHDx5haEulr4DU&amp;chl=The+Price+of+Tea%7CFoot+Size" alt="Chart"/>
+ </p>
+ <hr />
+
+ <h3>NASA Budget per year
+ <p><a href="http://chart.apis.google.com/chart?cht=bvs&chtt=NASA+Budget+per+year&chs=1000x200&chd=s:AACDGMUYaYUROMLKJJJJIIIJJJKJJJLNOPPPONNONMMMNNNNNN&chbh=16&chf=bg%2Cs%2Cefefef&chl=%2758%7C%2759%7C%2760%7C%2761%7C%2762%7C%2763%7C%2764%7C%2765%7C%2766%7C%2767%7C%2768%7C%2769%7C%2770%7C%2771%7C%2772%7C%2773%7C%2774%7C%2775%7C%2776%7C%2777%7C%2778%7C%2779%7C%2780%7C%2781%7C%2782%7C%2783%7C%2784%7C%2785%7C%2786%7C%2787%7C%2788%7C%2789%7C%2790%7C%2791%7C%2792%7C%2793%7C%2794%7C%2795%7C%2796%7C%2797%7C%2798%7C%2799%7C%2700%7C%2701%7C%2702%7C%2703%7C%2704%7C%2705%7C%2706%7C%2707">link</a><br />
+ <img src="http://chart.apis.google.com/chart?cht=bvs&amp;chtt=NASA+Budget+per+year&amp;chs=1000x200&amp;chd=s:AACDGMUYaYUROMLKJJJJIIIJJJKJJJLNOPPPONNONMMMNNNNNN&amp;chbh=16&amp;chf=bg%2Cs%2Cefefef&amp;chl=%2758%7C%2759%7C%2760%7C%2761%7C%2762%7C%2763%7C%2764%7C%2765%7C%2766%7C%2767%7C%2768%7C%2769%7C%2770%7C%2771%7C%2772%7C%2773%7C%2774%7C%2775%7C%2776%7C%2777%7C%2778%7C%2779%7C%2780%7C%2781%7C%2782%7C%2783%7C%2784%7C%2785%7C%2786%7C%2787%7C%2788%7C%2789%7C%2790%7C%2791%7C%2792%7C%2793%7C%2794%7C%2795%7C%2796%7C%2797%7C%2798%7C%2799%7C%2700%7C%2701%7C%2702%7C%2703%7C%2704%7C%2705%7C%2706%7C%2707" alt="Chart"/>
+ </p>
+ <hr />
+
+ </body>
+ </html>
@@ -0,0 +1,86 @@
+require 'rubygems'
+require 'erb'
+
+require File.dirname(__FILE__) + '/../lib/google_chart'
+include WillWhim
+
+@charts = []
+c = GoogleChart.new(:lc, '500x200')
+c.add_data [[0,30,60,70,90,95,100],[20,30,40,50,60,70,80]], :text
+c[:chl]='Test Chart Two Data lines|(Given encoding)'
+@charts << c
+
+c = GoogleChart.new(:lc, '500x200')
+c.add_data [[0,30,60,70,90,95,100],[20,30,40,50,60,70,80]]
+c[:chl]='Test Chart Two Data lines|(Guess encoding)'
+@charts << c
+
+c = GoogleChart.new(:lxy, '500x200')
+c.add_data [[0,30,60,70,90,95,100],[20,30,40,50,60,70,80]], :text
+c[:chl]= 'The Price of Tea|Foot Size'
+@charts << c
+
+c = GoogleChart.new(:p, '500x200')
+c.add_data [25, 25, 30, 10, 5], :text
+c[:chl]= 'You|Me|Them|Us|It'
+c[:chtt]='The Division of Labor'
+@charts << c
+
+
+c = GoogleChart.new(:p3, '500x200')
+c.add_data [25, 25, 30, 10, 5], :text
+c[:chl]= 'You|Me|Them|Us|It'
+c[:chtt]='The Division of Labor'
+@charts << c
+def nrand(n,max)
+ r = []
+ n.times{ r<<rand(max)+1}
+ r
+end
+
+c = GoogleChart.new(:v, '300x300')
+c.add_data [100,80,60,30,30,30,10], :text
+c[:chdl] = 'Yours|Mine|Ours'
+@charts << c
+
+c = GoogleChart.new(:bvs, '300x200')
+c.add_data [100,80,60,30,30,60,80,75], :text
+c[:chtt] = 'Pct who understand Charts'
+c[:chl] = '00|01|02|03|04|05|06|07'
+@charts << c
+
+c = GoogleChart.new(:s, '300x300')
+c.add_data [nrand(20,61), nrand(20,61), nrand(20,61)], :simple
+c[:chl]= 'The Price of Tea|Foot Size'
+c[:chtt]='Random Scatter Plot'
+c[:chm]=20
+@charts << c
+
+years = [1958,1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007]
+budget = [0.488,0.781,2.145,3.879,6.554,12.767,20.587,24.795,26.820,24.798,20.664,17.537,14.616,12.356,11.787,10.910,9.790,9.111,9.356,9.297,8.798,8.540,8.966,9.089,9.436,9.973,10.050,9.996,9.960,9.940,11.540,13.506,14.714,15.735,15.310,15.301,14.351,13.692,13.882,14.067,13.193,12.999,12.618,12.884,13.305,13.158,13.452,13.201,13.111,13.007]
+c = GoogleChart.new(:bvs,'1000x200')
+c.add_data(budget)
+c[:chbh] = (1000/years.size)-4
+c[:chtt] = 'NASA Budget per year'
+c[:chf]= 'bg,s,efefef'
+c[:chl]= years.collect{|year| "'"+year.to_s[2,2]}.join('|')
+@charts << c
+
+template = %{
+ <html>
+ <head><title>Charts!</title></head>
+ <body>
+ <h1><%= @charts.size %> Google Chart Examples</h1>
+ <% @charts.each_with_index do |chart,i| %>
+ <h3><%= chart[:chtt] or chart[:chl] or 'Chart ' + (i+1).to_s %>
+ <p><a href="<%= chart.to_url %>">link</a><br />
+ <%= chart.to_img %>
+ </p>
+ <hr />
+ <% end %>
+ </body>
+ </html>
+ }.gsub(/^ /, '')
+
+rhtml = ERB.new(template)
+rhtml.run()
@@ -0,0 +1,178 @@
+# stdlib requires
+require 'rubygems'
+
+# 3rd party rubygem requires
+require 'htmlentities'
+require 'open-uri'
+require 'cgi'
+
+
+$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
+
+module WillWhim
+ class GoogleChart
+ VERSION = '1.0.0'
+
+ CHART_ENDPOINT = 'http://chart.apis.google.com/chart'
+
+ SIMPLE_CODES = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split('')
+ EXTENDED_CODES = SIMPLE_CODES + '-.'.split('')
+ MAX_SIMPLE = SIMPLE_CODES.size-1
+ MAX_TEXT = 100.0
+ EXTENDED_SIZE = EXTENDED_CODES.size
+ MAX_EXTENDED = (EXTENDED_SIZE**2)-1
+
+ def self.simple_encoded(data)
+ 's:' + self.simple_encoded_r(data)
+ end
+
+ def self.simple_encoded_r(data)
+ if data.first.is_a? Array
+ data.map {|datum| self.simple_encoded_r(datum)}.join(',')
+ else
+ data.map do |datum|
+ if (datum < 0) or (datum > MAX_SIMPLE)
+ '_'
+ else
+ SIMPLE_CODES[datum]
+ end
+ end.join('')
+ end
+ end
+
+ def self.text_encoded(data)
+ 't:' + self.text_encoded_r(data)
+ end
+
+ def self.text_encoded_r(data)
+ if data.first.is_a? Array
+ data.map {|datum| self.text_encoded_r(datum)}.join('|')
+ else
+ data.map do |datum|
+ if (datum < 0) or (datum > MAX_TEXT)
+ '-1'
+ else
+ (datum.is_a? Integer) ? datum.to_s : sprintf("%0.1f",datum)
+ end
+ end.join(',')
+ end
+ end
+
+ def self.extended_encoded(data)
+ 'e:' + self.extended_encoded_r(data)
+ end
+
+
+ def self.extended_encoded_r(data)
+ if data.first.is_a? Array
+ data.map {|datum| self.extended_encoded_r(datum)}.join(',')
+ else
+ data.map do |datum|
+ if (datum < 0) or (datum > MAX_EXTENDED)
+ '__'
+ else
+ datum.divmod(EXTENDED_SIZE).map {|i| EXTENDED_CODES[i]}.join('')
+ end
+ end.join('')
+ end
+ end
+
+ # [100, 200, 100, 200] 100, 200, 50 -> [0, 50, 0, 50]
+ # (n-min) = [0, 100, 0, 100]
+ # * 50/100 = [0, 50, 0, 50]
+ # [100, 120, 180] 100, 180, 100 -> [0, ,100] range = 80 120
+ # [100, 200] newmin: 50 -> 50, 150; 0 -> 0, 100
+ def self.translate(data, min, newmin)
+ return data.map {|r| GoogleChart.translate(r, min, newmin)} if data.first.is_a?(Array)
+ diff = min-newmin
+ data.map{|datum| datum - diff}
+ end
+
+ def self.scale(data, range, newrange)
+ return data.map {|r| GoogleChart.enbiggen(r, min, newmin)} if data.first.is_a?(Array)
+ diff = Float(newrange)/range
+ data.map{|datum| datum * diff}
+ end
+
+ def self.range(data)
+ flat = data.flatten
+ min = flat.min
+ max = flat.max
+ [min, max, (max-min).abs]
+ end
+
+ def self.fit(data, min, max, range, new_min, new_max, new_range)
+ return data if ((range <= new_range) and (max <= new_max)) # no fitting needed
+ return GoogleChart.translate(data, min, new_min) if (range <= new_range) # just translate
+ return GoogleChart.scale(GoogleChart.translate(data, min, new_min), range, new_range) # translate & scale
+ end
+
+ def self.guess_encoded(data)
+ min,max,range = self.range(data)
+ return GoogleChart.simple_encoded(GoogleChart.fit(data, min, max, range, 0, MAX_SIMPLE, MAX_SIMPLE)) if range <= MAX_SIMPLE
+ return GoogleChart.text_encoded(GoogleChart.fit(data, min, max, range, 0, MAX_TEXT, MAX_TEXT)) if range <= MAX_TEXT
+ GoogleChart.extended_encoded(GoogleChart.fit(data,min, max, range, 0, MAX_EXTENDED, MAX_EXTENDED)) # otherwise
+ end
+
+ def encode_key_value(key, data)
+ case key
+ when :chd: GoogleChart::encode_data(data, data_encoding)
+ else CGI.escape(data.to_s)
+ end
+ end
+
+ def self.encode_data(data, encoding=:guess)
+ case encoding
+ when :none, :identity: data
+ when :guess,false: GoogleChart::guess_encoded(data)
+ when :simple, :s: GoogleChart::simple_encoded(data)
+ when :text, :t: GoogleChart::text_encoded(data)
+ when :extended, :e: GoogleChart::extended_encoded(data)
+ else
+ raise TypeError, "invalid data encoding: #{encoding.inspect}"
+ end
+ end
+
+ attr_accessor :options
+ attr_accessor :data_encoding
+
+ def initialize(chart_type, size)
+ @options = Hash.new
+ @options[:cht] = chart_type
+ @options[:chs] = size
+ end
+
+ def [](key)
+ @options[key]
+ end
+
+ def []=(key,value)
+ @options[key]=value
+ end
+
+
+ def to_url
+ coder = HTMLEntities.new
+ CHART_ENDPOINT + "?" + @options.map{|k,v| "#{k}=#{encode_key_value(k, v)}"}.join('&')
+ end
+
+ alias to_uri to_url
+
+ def to_img(alt=false)
+ alt ||= (@options[:t] or 'Chart')
+ coder = HTMLEntities.new
+ "<img src=\"" + CHART_ENDPOINT + "?" + @options.map{|k,v| "#{k}=#{encode_key_value(k, v)}"}.join('&amp;') + "\" alt=\"#{coder.encode(alt)}\"/>"
+ end
+
+ def add_data(data, encoding=:guess)
+ @data_encoding = encoding
+ @options[:chd] = data
+ self
+ end
+
+ def to_file(path)
+ File.open(path, 'w+').write(open(to_url).read)
+ end
+ end
+
+end
Oops, something went wrong.

0 comments on commit ba6802d

Please sign in to comment.