Jqueryでグラフ表示

仕事でグラフを表示したいという要件が出てきた。
そのため、Jqueryでグラフ表示できるプラグインがないか調査することに。
んで、下記が候補として見つかったプラグイン

グラフの見栄えがよく、様々なグラフを表示できる。
表現方法が多い。(色やプロットの形など)
特に、グラフをドラッグで変更できる点などは、素晴らしい。
APIも整備されていて、実装時に、探しやすい。
見栄えはjqplotより落ちるが、様々なグラフを表示できる。
機能的な部分が多い。ズーム、スクロール、リアルタイム描画、描画対象の変更等。
軽量のスクリプトになっているので、ソースリーディングが容易。

上記2つで作成した所、スマフォでのグラフ表示ということもあり、グラフ内部のスクロールが出来なければならないため、今回は「flot」を採用することにした。

それぞれで作成した方法を記述しておく。

jqplot

まず、WEBアプリ側で下記のようなJSONを返却するものを作成する。(今回は割愛。)

{"tempList":[["0","25"],["1","30"],["2","0"],["3","10"],["4","80"],["5","50"],["6","70"]],"rainList":[["0","18"],["1","13"],["2","12"],["3","13"],["4","9"],["5","4"],["6","3"]]}

そして、javascriptで上記JSONを返却するURLを非同期で呼び出し、結果からグラフを作成する。
JavaScriptは下記。

$.ajax({
    type: 'GET',
    url: '/json/',
    dataType: 'json',
    async: true,
    success: function(data, textStatus) {
        var line1 = data.tempList;
        var line2 = data.rainList;
        var xticks = [[0, '12時'], [1, '13時'], [2, '14時'], [3, '15時'], [4, '16時'], [5, '17時'], [6, '18時']];
        var yticks = [0, 5, 10, 15, 20];
        var yticks2 = [0, 25, 50, 75, 100];
        var jqplot = $.jqplot('plotGraf', [line1, line2], {
            legend: {show:true}, 
            title: '2010/01/01',
            grid: {background:'#f3f3f3', gridLineColor:'#accf9b'},
            series: [
                {label:'気温', markerOptions:{style:'square'}},
                {label:'降水確率', markerOptions:{style:'circle'}, yaxis:'y2axis'}
            ],
            axes: {
                xaxis:{ticks:xticks}, 
                yaxis:{ticks:yticks, tickOptions:{formatString:'%d度'}},
                y2axis:{ticks:yticks2, tickOptions:{formatString:'%d%'}}
            }
        });
    },
    error: function() {
        console.log('error');
    }
});

その結果がこれ。

flot

同じく、WEBアプリ側で下記のようなJSONを返却するものを作成する。(今回は割愛。)

{"tempList":[["1293850800000","25"],["1293854400000","30"],["1293858000000","0"],["1293861600000","10"],["1293865200000","80"],["1293868800000","50"],["1293872400000","70"],["1293876000000","50"],["1293879600000","70"]],"rainList":[["1293850800000","18"],["1293854400000","13"],["1293858000000","12"],["1293861600000","13"],["1293865200000","9"],["1293868800000","4"],["1293872400000","3"],["1293876000000","3"],["1293879600000","3"]]}

そして、javascriptで上記JSONを返却するURLを非同期で呼び出し、結果からグラフを作成する。
JavaScriptは下記。

$.ajax({
    type: 'GET',
    url: '/json/',
    dataType: 'json',
    async: true,
    success: function(data, textStatus) {
        var tempData = data['tempList'];
        var rainData = data['rainList'];
        
        var options = {
            series: { lines: { show: true }, shadowSize: 0 },
            grid: { backgroundColor: '#FFFFFF' },
            xaxes: [
                    {
                    	panRange: [tempData[0][0], tempData[tempData.length-1][0]],
                        min: tempData[0][0],
                        max: tempData.length < 4 ? tempData[tempData.length-1][0] : tempData[3][0],    // 4つぶん出力する
                        tickSize: 3600000,    // 1日ぶんの間隔を空ける
                        tickFormatter: function(val, axis) {
                            var date = new Date(val);
                            return date.getDate() + "日" + date.getHours() + "時";
                        }
                    }
            	],
            yaxes: [
                {
                    panRange: false,
                    tickFormatter: function(val, axis) {
                        return val + '度';
                    }
                },
                {
                    panRange: false,
                    alignTicksWithAxis: "right",
                    position: "right",
                    tickFormatter: function(val, axis) {
                        return val + '%';
                    }
                }
            	],
            pan: {
                interactive: true
            }
        };

        var plot = $.plot($("#placeholder"),
            [
        	{data: data.list, label: '気温', points: { show: true } },
                {data: data.list2, label: '降水確率', points: { show: true }, yaxis: 2},
            ], options);
    },
    error: function() {
        console.log('error');
    }
});

また、flotのグラフスクロールは、スマフォには対応しておらず、少し改良が必要になる。
jquery.flot.navigation.jsの改良。

//147行目
function onDragStart(e) {
	// modify by ochi0218
	// スマートフォンで対応出来るように修正。
//  if (e.which != 1)  // only accept left-click
//  	return false;
//  var c = plot.getPlaceholder().css('cursor');
//  if (c)
//  	prevCursor = c;
//  plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
	if(!$.support.touch) {
        if (e.which != 1)  // only accept left-click
            return false;
        var c = plot.getPlaceholder().css('cursor');
        if (c)
            prevCursor = c;
        plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
	}
	e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e;
	// modify by ochi0218
    prevPageX = e.pageX;
    prevPageY = e.pageY;
}

function onDrag(e) {
    var frameRate = plot.getOptions().pan.frameRate;
    if (panTimeout || !frameRate)
        return;
    
    // add by ochi0218
	e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e;
    // add by ochi0218

    panTimeout = setTimeout(function () {
        plot.pan({ left: prevPageX - e.pageX,
                   top: prevPageY - e.pageY });
        prevPageX = e.pageX;
        prevPageY = e.pageY;
        panTimeout = null;
    }, 1 / frameRate * 1000);
}

function onDragEnd(e) {
    if (panTimeout) {
        clearTimeout(panTimeout);
        panTimeout = null;
    }

    // add by ochi0218
	e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e;
    // add by ochi0218

    plot.getPlaceholder().css('cursor', prevCursor);
    // modify by ochi0218
    // iphoneで正常に動作しないため、コメントアウト。
//    plot.pan({ left: prevPageX - e.pageX,
//               top: prevPageY - e.pageY });
    // modify by ochi0218
}

---------------------------------------------------------------------------------

// 191行目
if (o.pan.interactive) {
	// modify by ochi0218
	// スマートフォンで対応出来るように修正。
//      eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
//      eventHolder.bind("drag", onDrag);
//      eventHolder.bind("dragend", onDragEnd);
	if ($.support.touch) {
            eventHolder.bind("touchstart", { distance: 10 }, onDragStart);
            eventHolder.bind("touchmove", onDrag);
            eventHolder.bind("touchend", onDragEnd);
	} else {
	    eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
	    eventHolder.bind("drag", onDrag);
	    eventHolder.bind("dragend", onDragEnd);
	}
	// modify by ochi0218
}

---------------------------------------------------------------------------------

//316行目
function shutdown(plot, eventHolder) {
    eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
    eventHolder.unbind("mousewheel", onMouseWheel);
    // modify by ochi0218
//  eventHolder.unbind("dragstart", onDragStart);
//  eventHolder.unbind("drag", onDrag);
//  eventHolder.unbind("dragend", onDragEnd);
	if ($.support.touch) {
        eventHolder.unbind("touchstart", onDragStart);
        eventHolder.unbind("touchmove", onDrag);
        eventHolder.unbind("touchend", onDragEnd);
	} else {
        eventHolder.unbind("dragstart", onDragStart);
        eventHolder.unbind("drag", onDrag);
        eventHolder.unbind("dragend", onDragEnd);
	}
    // modify by ochi0218
    if (panTimeout)
        clearTimeout(panTimeout);
}

plot.hooks.bindEvents.push(bindEvents);
plot.hooks.shutdown.push(shutdown);
}

その結果がこれ。
スマートフォンブラウザでも、PCブラウザでも、ドラッグorスワイプで次の要素を表示させることが出来る。

やっぱり、見栄えはjqplotの方がよいね。。
flotでも、見栄えが近づけれるようにもうちょい頑張ってみよう!