WHAT'S NEW?
Loading...

PHPとjQueryライブラリ「jqPlot」で綺麗なグラフを描画する【4/10】

さて、次にインタフェースの挙動をカスタマイズしてみよう。
まずは、グラフのデータのポインタにマウスオーバーすると、ツールチップを表示するようにして見る。
これにはhighlighterというプラグインが必要になる。
まずは読みこんでみる。headerタグ内に以下を追記する。
<script type="text/javascript" src="plugins/jqplot.highlighter.min.js"></script>
これでツールチップを表示するハイライト機能が読みこまれた。
次に、ハイライト機能を実際に記述してみよう。
highlighterプロパティに、ツールチップの位置、フォーマットなどが指定可能だ。
plot = $.jqplot('graph', [data1, data2], {
    title:'会員サイト アクセス数',
    legend:{
      show:true,
      location: 'nw',
      yoffset: 6
    },
    series:[
      {label:'メンバー'},
      {label:'ビジター'}
    ],
    axes: {
      xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        min:'2010/01/01',
        max:'2010-01-04',
        numberTicks: 4,
        label: '過去7日分'
      },
      yaxis:{
        min:0,
        max:12,
        label:'アクセス数'
      }
    },
    highlighter: {
      tooltipLocation: 'n',
      tooltipFormatstring: '<strong>%d</strong>'
    }
  });
この状態でブラウザでグラフを表示させ、データのポインタにマウスを当ててみよう。
ツールチップがフェードインして表示されるようになった。

現状だとツールチップにはxaxis(横軸)とyaxis(縦軸)のデータが表示されるようになっているが、たとえばアクセス数だけを表示したい場合の方が多いと思う。
その場合、highlighterプロパティに、tooltipAxesを設定し、表示したい軸を指定する。具体的には、yaxisのアクセス数を表示したければyを、xaxisの日付を表示したければxを代入する。両方表示したい場合はbothを指定だ。
highlighter: {
      tooltipLocation: 'n',
      tooltipAxes: 'y',
      tooltipFormatstring: '<strong>%d</strong>'
    }
これで、ツールチップに日付は表示されず、アクセス数のみが表示されるようになった。
ただし、これだけでは以下の図のように、「6.0」と表示したいところ「6.0,」と、余計なカンマが表示されてしまう。
これを避けるためには、アクセス数と日付の間に設定されている、セパレータを設定しなおせば良い。
highlighter: {
      tooltipLocation: 'n',
      tooltipAxes: 'y',
      tooltipSeparator: '',
      tooltipFormatstring: '%d',
    }
セパレータを空文字にしてみたところ、問題なく「6.0」と表示されるようになった。
空文字ではなくためしにnullを指定してみたところ、なんと「6.0null」と表示されてしまった。素直に空文字を指定しよう。

ちなみにtooltipStringに指定してある「%d」だが、これはsprintf関数で使われるフォーマットがそのまま使えるようになっているそうだ。sprintfはC言語にもPHPにも存在するので、詳しくは各言後のマニュアルを読んでいただきたい。参考までに、%sが文字列、%dが整数、%fが浮動少数だ。
SmartにPerl版の詳しい解説があるので参考にされたし。

次に、いくつかデータを増やしてみる。
まずデータ自体を4日から7日へ、やや右肩上がり風に、最高値も増やしてみた。
軸も4つから7つに増やしてみた。
$(document).ready(function(){
  var data1 = [['2010/01/01',2],['2010/01/02',4],['2010/01/03',6],['2010/01/04',8],['2010/01/05',12],['2010/01/06',10],['2010/01/07',14]];
  var data2 = [['2010/01/01',5],['2010/01/02',6],['2010/01/03',4],['2010/01/04',12],['2010/01/05',11],['2010/01/06',9],['2010/01/07',15]];
  plot = $.jqplot('graph', [data1, data2], {
    title:'会員サイト アクセス数',
    legend:{
      show:true,
      location: 'nw',
      yoffset: 6
    },
    series:[
      {label:'メンバー'},
      {label:'ビジター'}
    ],
    axes: {
      xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        min:'2010/01/01',
        max:'2010-01-07',
        numberTicks: 7,
        label: '過去7日分'
      },
      yaxis:{
        min:0,
        max:15,
        label:'アクセス数'
      }
    },
    highlighter: {
      tooltipLocation: 'n',
      tooltipAxes: 'y',
      tooltipSeparator: '',
      tooltipFormatstring: '<strong>%d</strong>',
    }
  });
});
すると、残念ながら日付同士が重なってしまい、非常によろしくない描画になってしまったようだ。
この問題に対して、「divタグのstyleでwidthを広げればいいじゃん!」と思った人は、もっとほかに解決法を考えた方が良いかもしれない。もしstyleでwidthを広げる事になった場合、それは個人的にはやっつけ対応だと思う。

なので、こういう場合、いくつか別の解決策をとる。
  • 年を別のとこに表示する
  • 1桁の際頭に0を付けない
  • 文字を小さくする
などなど。しかし、ここまで来てレイアウトデザインを変更するのはかなり面倒な場合が多いのではないか、と思う。

もしレイアウトを変更するのであれば、それは最小限にとどめるべきだと思うが、そもそもレイアウトを変更するのはやっぱりやっつけ感が満載で寝覚め悪いだろう。

というわけで、上記選択肢にはないが、
  • tickを回転させる
という方法で対処してみよう。
使うライブラリはcanvasTextRendererとcanvasAxisTickRendererの二つだ。まずは例によってheadタグ内に記述する。
読みこむjsファイルの数が多くなってきているので、以下のソースコードではscriptタグを全部を載せてみた。
<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.min.js"></script><![endif]-->
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="jquery.jqplot.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasTextRenderer.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
これでtickの文字を回転させる準備は整った。
実際に回転させる場合、角度を設定しないといけないのだが、基本的にコンピュータは度ではなくラジアンで指定する場合が多い。しかしjqPlotでは度の指定が可能になっている。しかも時計回りだ。すばらしい。
アナログ時計などでおなじみの感覚で、好きな角度を指定する事が出来るようになっている。
今回は30度を設定してみよう。

まず、角度を付けて回転させたいtickはx軸にあるため、xaxisプロパティ内でプラグインを読みこむようにする。
xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        tickRenderer: $.jqplot.CanvasAxisTickRenderer,
        min:'2010/01/01',
        max:'2010-01-07',
        numberTicks: 7,
        label: '過去7日分',
      },
この状態ですぐにブラウザで確認してみると、ただフォントがきれいになっただけで終わる。フォントがきれいになったのは、canvasTextRendererのおかげなのだが、相変わらず日付が重なってしまっているので可読性が著しく損なわれているのは変わらずだ。
というわけで、さっそくxaxisに角度を設定してみよう。
xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        tickRenderer: $.jqplot.CanvasAxisTickRenderer,
        min:'2010/01/01',
        max:'2010-01-07',
        numberTicks: 7,
        label: '過去7日分',
        tickOptions: {
         angle: 30
        }
      },
さて、この状態でブラウザでグラフを描画してみよう。かなりよろしげな描画がされたと思う。
驚くことに、角度を付ける前と付けた後では、グラフ描画エリアのサイズは変わっていない。まずグラフ部分の高さが若干縮小され、tickは文字列の右下を基準に回転されたので、全体的な「下」「右」の位置が変わらないで済む、という、少々天才っぽい実装になっている。俺は正直おどろいた。ここまでやってくれてOSSか、と。

調子に乗って次へ進んでみる。次は、ドラッグして選択された領域を拡大して描画するという、驚きの機能だ。
これはつまりどういうことかと言うと、まずグラフ内の任意の場所からドラッグを開始し、拡大したい領域まで引っ張り、マウスを放すと、選択された領域のみが、現在の描画エリアいっぱいに表示される、と言うものだ。これはすごい。
ドラッグ開始
拡大完了
ちゃんと日付も細かく変更される。拡大した内容をさらに拡大することも可能だ。さりげなくすばらしい機能ではあるが、あまりグラフがドラッグして拡大できるという事例の経験が乏しいせいか、気づかれなければ非常に残念な実装になってしまう。

とはいえ、すばらしい機能であることは確実なので、早速実装してみよう。
拡大するにはzoomプラグインが必要か、と思われたが、実はカーソル系のプラグインになっている。headタグ内にjqplot.cursor.jsを読み込むように記述する。
※以下ソースコードは読み込むscriptタグを全て表示
<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.min.js"></script><![endif]-->
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="jquery.jqplot.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasTextRenderer.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.cursor.js"></script>
これでカーソルプラグインを使う準備が整った。次は実装だ。
カーソルプラグインを使用するので、プロパティはcursorとなる。このcursorを使うと、グラフ上にあるマウスポインタがクロスヘアになる。ただこれだけなのだが、zoomプロパティをtrueにすることで、この拡大表示機能が直ぐに動くようになるという、非常に簡単な実装が可能になっている。
plot = $.jqplot('graph', [data1, data2], {
    title:'会員サイト アクセス数',
    legend:{
      show:true,
      location: 'nw',
      yoffset: 6
    },
    series:[
      {label:'メンバー'},
      {label:'ビジター'}
    ],
    axes: {
      xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        tickRenderer: $.jqplot.CanvasAxisTickRenderer,
        min:'2010/01/01',
        max:'2010-01-07',
        numberTicks: 7,
        label: '過去7日分',
        tickOptions: {
         angle: 30
        }
      },
      yaxis:{
        min:0,
        max:15,
        label:'アクセス数'
      }
    },
    highlighter: {
      tooltipLocation: 'n',
      tooltipAxes: 'y',
      tooltipSeparator: '',
      tooltipFormatstring: '<strong>%d</strong>',
    },
    cursor:{
      zoom:true
    } 
  });
たったこれだけだ。これだけで、ドラッグした範囲を拡大表示する機能が実装される。
おそろしや。

ちなみにだが、このままでは拡大するだけで、元に戻せない。人によってはページをリロードしてしまう可能性もあるため、原寸サイズに戻すメソッドが必要になる。
幸いにもjqPlotには、ズームをリセットするresetZoom()メソッドが実装されているので、このメソッドを走らせればよい。
問題は、どのタイミングで実行するかだ。画面に「サイズをリセット」ボタンをつけても良いが、1つの画像エリアとして確保できているHTMLコンテナ内に、さらに別のエレメントを追加するのは、レイアウト的にもよろしくないし、特定のエレメントが別のエレメントを操作する、というのも、10年前のショボいWebアプリみたいで個人的に好きじゃない。
というわけで、拡大後、グラフをダブルクリックすると元に戻るようにしてみようとおもっていろいろ試したら、なんとデフォルトでダブルクリックで元に戻るではないか。
plot.dbclick(function(){
    plot.resetZoom();
  });
こんなメソッドを実装しておこうと思ったが、全く必要なかったようだ。

と言うわけで、現在までの全てのソースコードは、以下のようになっている。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>jqPlot テスト</title>
<link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.min.js"></script><![endif]-->
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="jquery.jqplot.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasTextRenderer.js"></script>
<script type="text/javascript" src="plugins/jqplot.canvasAxisTickRenderer.min.js"></script>
<script type="text/javascript" src="plugins/jqplot.cursor.js"></script>
<script type="text/javascript">
$(document).ready(function(){
  var data1 = [['2010/01/01',2],['2010/01/02',4],['2010/01/03',6],['2010/01/04',8],['2010/01/05',12],['2010/01/06',10],['2010/01/07',14]];
  var data2 = [['2010/01/01',5],['2010/01/02',6],['2010/01/03',4],['2010/01/04',12],['2010/01/05',11],['2010/01/06',9],['2010/01/07',15]];
  plot = $.jqplot('graph', [data1, data2], {
    title:'会員サイト アクセス数',
    legend:{
      show:true,
      location: 'nw',
      yoffset: 6
    },
    series:[
      {label:'メンバー'},
      {label:'ビジター'}
    ],
    axes: {
      xaxis:{
        renderer: $.jqplot.DateAxisRenderer,
        tickRenderer: $.jqplot.CanvasAxisTickRenderer,
        min:'2010/01/01',
        max:'2010-01-07',
        numberTicks: 7,
        label: '過去7日分',
        tickOptions: {
         angle: 30
        }
      },
      yaxis:{
        min:0,
        max:15,
        label:'アクセス数'
      }
    },
    highlighter: {
      tooltipLocation: 'n',
      tooltipAxes: 'y',
      tooltipSeparator: '',
      tooltipFormatstring: '<strong>%d</strong>',
    },
    cursor:{
      zoom:true
    } 
  });

});
</script>
</head>
<body>

<h1>jqPlot Test</h1>
<div id="graph" style="width:400px;height:300px;margin:30px;"></div>

</body>
</html>
次回は、棒グラフ、円グラフなどにチャレンジしてみたいと思う。

※【追記】横軸を7日に増やしたのにラベルが「過去4日分」 になっていたのでソースコードのみ修正

まだつづく