Google Chart APIを使って動的にグラフを生成する

/web/javascript

Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。

Note: Chart APIは現在ではGoogle Chartとして整理されており、この記事で利用されているImage Chart APIは旧仕様として削除対象になっています。実際に削除はされていないので今のところ利用できていますが、2015年中には消えてしまいそうです。(2015-08-31)

※グラフを表示するためには、JavaScriptを有効にしてください。

CGI側でデータを用意してやって、JavaScriptでうにうにするみたいな。値は吐けるんだからTTとか使ってれば直接埋め込んでも良いような気がしますが、JS挟んだほうがメンテしやすいので!

とはいえ未完成(IEのみ)。ここまでで1時間弱。70%くらい? まあ原因ははっきりしているのでこのままでも使えはしますが。う~ん(ーー; 一緒に悩んで、暇な人!

IE対応しました~。

HTML

  1. <p id="IMG1">※グラフを表示するためには、JavaScriptを有効にしてください。</p>

HTML側はこれだけ。グラフの挿入先になるタグです。JS無効時のアラートも兼ねたお手軽設計。ホントはこれも生成しちゃっても良いんだけど、まあそこまで拘るひつこともないか、とかとか。

JavaScript

  1. var chd = [
  2. [% IF enq1_1 != '' %][% data.q1_1 || 0 %],[% END %]
  3. [% IF enq1_2 != '' %][% data.q1_2 || 0 %],[% END %]
  4. [% IF enq1_3 != '' %][% data.q1_3 || 0 %],[% END %]
  5. [% IF enq1_4 != '' %][% data.q1_4 || 0 %],[% END %]
  6. [% IF enq1_5 != '' %][% data.q1_5 || 0 %],[% END %]
  7. [% IF enq1_6 != '' %][% data.q1_6 || 0 %],[% END %]
  8. [% IF enq1_7 != '' %][% data.q1_7 || 0 %],[% END %]
  9. [% IF enq1_8 != '' %][% data.q1_8 || 0 %],[% END %]
  10. [% IF enq1_9 != '' %][% data.q1_9 || 0 %],[% END %]
  11. [% IF enq1_10 != '' %][% data.q1_10 || 0 %],[% END %]
  12. ];
  13. var chl = [
  14. [% IF enq1_1 != '' %]1,[% END %]
  15. [% IF enq1_2 != '' %]2,[% END %]
  16. [% IF enq1_3 != '' %]3,[% END %]
  17. [% IF enq1_4 != '' %]4,[% END %]
  18. [% IF enq1_5 != '' %]5,[% END %]
  19. [% IF enq1_6 != '' %]6,[% END %]
  20. [% IF enq1_7 != '' %]7,[% END %]
  21. [% IF enq1_8 != '' %]8,[% END %]
  22. [% IF enq1_9 != '' %]9,[% END %]
  23. [% IF enq1_10 != '' %]10,[% END %]
  24. ];
  25.  
  26. function createImg(chd,chl){
  27. var max_value = Math.max.apply(null,chd);(apply()はIE未実装?)
  28. var max_value = 2;
  29. for(var i=0;i<chd.length;i++){
  30. if(max_value < chd[i]){max_value = chd[i]}
  31. }
  32. //IEの空配列を削除
  33. for(var i=0;i<chd.length;i++){
  34. if(isNaN(chd[i])){ chd.splice(i,1); }
  35. }
  36. var max_value = Math.max.apply(null,chd);
  37. var mid_value = Math.round(max_value / 2);
  38. if(max_value < 2) max_vlue = 2;
  39.  
  40. //値をchart用に百分率化
  41. for(var i=0;i<chd.length;i++){
  42. chd[i] = Math.round(chd[i] / max_value * 100);
  43. if(isNaN(chd[i])){ chd.splice(i,1); }
  44. }
  45.  
  46. //値をsrc用に連結
  47. var str_chd = chd.join(',');
  48. var str_chl = chl.join('|');
  49.  
  50. // 棒グラフ
  51. var graph1 = new Image();
  52. graph1.src = 'http://chart.apis.google.com/chart?cht=bvs';
  53. graph1.src += '&chs=240x125';
  54. graph1.src += '&chxt=x,y';
  55. graph1.src += '&chxl=0:|' + str_chl + '|1:|0|' + mid_value + '|' + max_value;
  56. graph1.src += '&chco=FFC266';
  57. graph1.src += '&chd=t:' + str_chd;
  58.  
  59. // 円グラフ
  60. var graph2 = new Image();
  61. graph2.src = 'http://chart.apis.google.com/chart?cht=p';
  62. graph2.src += '&chs=240x125';
  63. graph2.src += '&chl=' + str_chl;
  64. graph2.src += '&chd=t:' + str_chd;
  65.  
  66. $('IMG1').removeChild($('IMG1').firstChild);
  67. $('IMG1').appendChild(graph1);
  68. $('IMG1').appendChild(graph2);
  69. }
  70.  
  71. createImg(chd,chl);

データを用意し、最大値と中央値を算出し、データをグラフ用の百分率にして、Imageオブジェクト作ってsrc属性をうにうに追記しつつ、挿入先のテキスト消してからインサート!

24行目までがデータ部分。普通に配列。[% %]みたいなのは元コードのTemplate Toolkitの名残ですが、JSON吐けるならそれでも良いでしょう。

34/43行目はIE対策です。書き方てきとーだからってのもありますが、データ作るときに後ろにカンマ付けてるので、IEだと空の配列が1個できちゃいます。なのでNaNを検出して配列から消してる感じ。

あとは特に言うことはないかな。66~68行目の$()関数は自前です。組み込みじゃありませんよ!

  1. function $(id){
  2. return document.getElementById(id);
  3. }

document.getElementById('IMG1') と同じ。getElement~といちいち書くのも面倒なので。IEだと高速化も見込めたような? てかprototype.js以降たびたび見かけますね。

課題

問題なのは27行目。Math.max()に配列つっこんで最大値を取ってるわけですが、IEはapply()未実装

まあ適当な数を入れるなり直接書くなりすれば動きはするんですけどね。値が百分率なんでMaxずれると微妙だよなあ ...てなところです。ここさえどうにかできればcreateImg()を外部JSに移せるんだけども。なんか良い案はないものか?

解法その1

コメント欄より、chihiroさん案。オリジナルはArrayオブジェクト拡張だったけど、わかりやすくcreateImg()内で直接回しても良し。

  1. var max_value = 2;
  2. for(var i=0;i<chd.length;i++){
  3. if(max_value < chd[i]){max_value = chd[i]}
  4. }
  5. var mid_value = Math.round(max_value / 2);

解法その2

IEでapply()使えたよ! エラーチェックすべきだったという話(汗) しかし記事にして良かったー。chihiroさん度々ありがとう!

関連記事

Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。

6 Comments

chihiro wrote:
Arrayにmaxメソッドを実装してみるのはどうでしょうか。
配列に数字以外が入ると動かなくなりますが…
Array.prototype.max = function(){
max = this[0];
for(i=0;i<this.length;i++){
if(max < this[i]){max = this[i]}
}
return max;
}

上記宣言後、27行目は以下に置換。
var max_value = chd.max(); 2008–10–10 07:50
Sig. wrote:
おー、お久しぶりです。

なるほどprototype拡張か。JavaScriptっぽい!
エラー回避はtypeofとかisNaNとか使えばいけるかな。
今回だけならfor文だけ抜いてもいけますが、max()よく使うなら検討の価値アリですね。

とっても参考になりました。ありがとう! 2008–10–10 16:23
chihiro wrote:
お久しぶりです。ちょっと追記させて下さい。
IEで Math.max.apply は実装されているようです(IE7のみ検証)。

for(var i=0;i<chd.length;i++){
if(isNaN(chd[i])){ chd.splice(i,1); }
}
をMath.max.applyよりも先に実行するとIEでも動きますね。
最後の,から来る空要素が問題のようです。 2008–10–10 16:54
Sig. wrote:
大 感 謝 !!!

ありがとー!
IE5.5まで動作を確認しました。

どおりで情報が少ない筈だ^^;
しかし未実装って記述も多いんですよね。これはどこから来てるんだろう。
なんか実装方法が違うとか(prototype周りで)いう話をどっかで拾った気がする... この辺はもうちょい調べてみよう。

いやーブログ書いて良かった。これで今日の仕事も安泰です(ぇ 2008–10–10 17:45
chihiro wrote:
お役に立ててなによりです。
ちょっと別の問題に気がついたのでしつこく追記します…。
現在のコードではIsNan要素の削除時にインデックスがずれてしまい、
連続したIsNan要素([1,2,,5]のようなデータ)に対処できないようです。

for(var i=0;i<chd.length;i++){
if(isNaN(chd[i])){
chd.splice(i,1);
i--;
}
}

上記コードはこの問題に対してインデックスを調整した場合。
下記コードは数値のみの新配列を生成し、置き換える方法。
こちらは続くコードを変更することで元配列も残すことができます。

var numChd = new Array();
for(var i=0;i<chd.length;i++){
if(isNaN(chd[i])==false){ numChd.push(chd[i]); }
}
chd = numChd; 2008–10–11 18:22
Sig. wrote:
反応遅くなりましたー
ふむふむ、チェック済みの値のみの新配列を作るわけですね。元コードでは出力する段階で既にチェックしているので、そういうケースは考えにくいですが、使いまわしを考慮するなら必要な措置かもです。

度々ありがとうございまっす^^b 2008–10–14 06:15