Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
willf committed Apr 9, 2011
0 parents commit ba6802d
Show file tree
Hide file tree
Showing 8 changed files with 494 additions and 0 deletions.
20 changes: 20 additions & 0 deletions MIT-LICENSE
@@ -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.
12 changes: 12 additions & 0 deletions README.rdoc
@@ -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.


11 changes: 11 additions & 0 deletions Rakefile
@@ -0,0 +1,11 @@
# -*- ruby -*-

require 'rubygems'

task :default => [:spec]

task :spec do
ruby "test/google_chart.spec"
end

# vim: syntax=Ruby
62 changes: 62 additions & 0 deletions examples/examples.html
@@ -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>
86 changes: 86 additions & 0 deletions examples/examples.rb
@@ -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()
178 changes: 178 additions & 0 deletions lib/google_chart.rb
@@ -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

0 comments on commit ba6802d

Please sign in to comment.