Sinatraでグラフ画像を表示
SinatraAdventCalendar11日目の記事です(http://www.adventar.org/calendars/262)
昨日はRSSFeed(http://advent.nzwsch.com/rss-feed)でした.
今回は,sinatraで遊んでみたような内容です.
力を抜いて読んでください.
はじめに
今年の初頭あたりにipodに入れていた歩数計のデータをウェブブラウザ上からグラフ表示してみたのですが,当時,僕はフレームワークを使ったことがありませんでした.内容としてはpythonからipodに入っていた歩数計データを読みだして,matplotlibでグラフ生成したあと,htmlに読み込ませるというものです.
フレームワークを使えばもっと素早く簡単にできるだろうと考えて,今回RubyのSinatraでウェブ上にグラフ画像を表示してみました.
内容は同じでは面白みがないので,東京電力の提供している電力使用量をグラフ化するコードにしました. 一年間の温度変化などでも良かったのですが,更新頻度の高いデータのほうが動的なアプリケーションとして面白いと判断したため このような題材を選びました.
他には二酸化炭素などの公害の原因となる物質の量をグラフ化してみようと考えましたが,きっちりとWebAPI化が進んでいるようで, 面白みが無いと判断して今回はやめました.
処理の内容
ユーザが訪れるたびにcsvファイルをダウンロードしてGruffというライブラリでグラフ画像を生成し,ウェブ上に表示するというものです.
一応,csvダウンロード先のウェブサイトへの負担を考えて,5分立たなければダウンロードできない仕組みとしてます.
図:処理のフローチャート
各ファイルの構成は次のようになっています
. ├── grufftest.rb ├── juyo-j.csv ├── public │ └── PowerUsageGraph.png └── views ├── index.erb └── layout.erb
必要なライブラリの解説
まずはsinatraですね.こちらは必須です.
gem install sinatra
でインストールが可能です.
今回はgruffというRubyで見た目重視のグラフライブラリを使用してみました.
matplotlibはGnuPlotのように見やすいのですが,飾り気がないので面白みがありません(グラフに本来求める要素ではないですからね).
こちらのライブラリはgem install gruff
でインストールできます.
gruffの使い方は簡単で,公式サイト(http://nubyonrails.com/pages/gruff)に沿えばすぐに使い方がわかると思います.
以下にサンプルのコードを示します
require 'rubygems' require 'gruff' g = Gruff::Line.new g.title = "My Graph" g.data("Apples", [1, 2, 3, 4, 4, 3]) g.data("Oranges", [4, 8, 7, 9, 8, 9]) g.data("Watermelon", [2, 3, 1, 5, 6, 8]) g.data("Peaches", [9, 9, 10, 8, 7, 9]) g.labels = {0 => '2003', 2 => '2004', 4 => '2005'} g.write('my_fruity_graph.png')
g.dataには項目名と数値リテラルの配列を渡し,g.labelsにはハッシュで横軸の項目を入れているんですね. そこまで整ったらg.writeで保存したいグラフ画像名を入れて保存です.
コード
ではさっそくコードを載せます.
main.rb
require 'gruff' require 'csv' require 'sinatra' require 'date' def graphgenerate(y,x_name,y_name,graphname) g = Gruff::Line.new g.title = graphname g.data(graphname,y) g.labels = {0 => '0',12 => '1',24 => '2',36 => '3',48 => '4',60 => '5',72 => '6',84 => '7',96 => '8',108 => '9',120 => '10',132 => '11',144 => '12',156 => '13',168 => '14',180 => '15',192 => '16',204 => '17',216 => '18',228 => '19',240 => '20',252 => '21',264 => '22',276 => '23'} g.x_axis_label = x_name g.y_axis_label = y_name g.write('./public/'+graphname) end def filedownload(url,filename) system('wget '+url+' -O'+ filename) system('nkf -w --overwrite '+ filename) end def fileanalysis filedownload('http://www.tepco.co.jp/forecast/html/images/juyo-j.csv','juyo-j.csv') powerdata=[] count = 0 datatime="" CSV.foreach("./juyo-j.csv","r") do |data| if(count==0) then datatime=data[0] end if(count>46) then powerdata << data[2].to_f*10000/10**6#kWはわかりにくいのでMW end count=count+1 end graphgenerate(powerdata,'Hour','Power Usage[MW]','PowerUsageGraph.png') return datatime end get '/' do if((DateTime.now.to_time-File.stat("./juyo-j.csv").mtime.to_time).to_i > 300) then @graphdatetime=fileanalysis end @graphimage="PowerUsageGraph.png" erb :index end
index.erb
<h1>本日の電力使用状況(東京電力)</h1> <p>参照:http://www.tepco.co.jp/forecast/html/images/juyo-j.csv</p> <img src=<%= @graphimage %>> <h2><%= @graphdatetime %></h2>
layout.erb
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>本日の電力使用量</title> </head> <body> <%= yield %> </body> </html>
簡単な解説
次のような3つのメソッドの呼び出しで動作を実現しています.
・graphgenerate(y,x_name,y_name,graphname)
・filedownload(url,filename)
・fileanalysis
・graphgenerateですが,こちらでグラフ画像を生成しています. サンプルにあるものをいじっただけです.
・filedownloadはwgetコマンドをrubyの上から実行し,ダウンロードしてきたcsvファイルをutf-8に変換しています. rubyの上でも実現できるかもしれませんが,てっとり早い方法なのでこのような処理をしています.
・fileanalysisは上二つのメソッドをまとめたものです. get '/' do以下のメソッドで記述しても良いですが,長くなったのでこのようなメソッドにまとめました. CSVを読み込んでいるところではcountが0であるときと,47以上のときに実行されるif文があります.これは更新日時が1行目にあり,グラフ化したいデータが47行目にあるためです.Rubyではこのあたりもっと簡潔に書けそうですが何かないでしょうか?
なお,生成した画像はpublicディレクトリに移さなければerb側で表示ができませんのでおいておきました.
erbの配置については,前のブログに書いたとおりです.よろしければ参考にしてください (http://kuroneko0208.hatenablog.com/entries/2013/11/26).
表示結果
localhost:4567にアクセスすると次のように表示されます.
まとめ
sinatraを使って東京電力の提供している電力使用量をグラフを表示してみました.
グラフ生成にはGruffというビジュアル重視のグラフ生成ライブラリを使用することで,グラフ画像の生成を行ないます.
グラフ画像はpublicにおかなければ反映されないのでおく必要があります.