enebular INFOTYPEでChartJSを導入しデフォルトD3.jsと比較してやるべきことを探るメモ

この記事は enebular Advent Calendar 2018 2日目です。

さて、私が書くのは、enebular INFOTYPEでChartJSを導入しデフォルトD3.jsと比較してやるべきことを探るメモです。

今回やりたいこと

elebularの Sample InfoTypes の挙動を見ていると、グラフの挙動は、やはり懐深くデータを受け止められるD3.jsでできています。(ちょっと設定が大変ではありますが)

ですが、挙動を見る限り、addDataやrefreshのAPI、初期化挙動をわきまえれば、フロントエンドの他のライブラリも使えそうに思っています。

image

そこで、今回はD3.jsのSVGベースからちょっと離れて、この画像のように、CavasベースのChartJSをどこまで入れ込めるかやってみます。

enebularでINFOMOTIONでの可視化のもととなるINFOTYPE

INFOMOTIONでの可視化のもととなるINFOTYPEがあります。普段、Discover InfoTypeでのほうで、すでに出来上がっているものを使いますが、実は独自に作ることができます。

image

INFOTYPEは独自に作ることができる

INFOTYPEは、ドキュメントもあって

あたりの手順を踏むと自分でも作ることができます。

実はDiscover InfoTypeにネタが少なかった半年くらい前は、サンプルを参考に毎度独自で作っていました。

infomotion-toolは 1.0.0-alpha.24 でとりあえず

2018/12/01現在、応急的に infomotion-toolは 1.0.0-alpha.24 でとりあえずやってます。

素直にインストールすると、 1.0.0-alpha.25 が入るんですが、パッケージ化が Error: Cannot find module ‘babelify’ from なエラーが出てしまっていて、しかも、6日前の更新なので、ちょっと危ないかなーと思って控えました。

テンプレートを使う

さて、気を取り直して。

ChartJSを使っていきましょう。

Creating an InfoType に、テンプレートを生成できる機能があります。

image

便利ですね。今回は、シンプルに、barchartを使うことにします。

eit create seigo_chartjs_graph -t barchart

同じ階層にフォルダが作られ生成されます。

cd seigo_chartjs_graph

出来上がったら、フォルダに移動します。

一度試す

この段階でD3ベースのテンプレートですが、一度試せるようにしておきます。

npm i

で、フォルダ内にライブラリをダウンロードしておきます。

image

これで、準備完了です。

パッケージ

つづいて、パッケージ化です。

フォルダを制作フォルダから一つあがっておきます。つまり、ルートですね。

eit package seigo_chartjs_graph

うまくいくと、 Built into ~~~~\seigo_chartjs_graph\build\target という成功ログが出ます。

ブラウザでプレビュー

では一回見てみましょう。

eit run seigo_chartjs_graph

image

無事動きました。いいですね。

ChartJSの準備

自分の seigo_chartjs_graph へ準備をします。

cd seigo_chartjs_graph

また、フォルダに移動します。「自分のグラフプログラムをいじくるぞ」という意識。

paskage.json にChartJSを追加します。

{
  "name": "bar-chart",
  "version": "1.0.0",
  "description": "template bar chart",
  "main": "plugin.js",
  "dependencies": {
    "d3": "^3.0.0",
    "chart.js": "*"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

できあがりです。インストールを忘れないようにしましょう。

npm i

これがうまくいくと、以下のように準備されるはず。

image

chartjsの名前のついたものが色々入りますね。

ChartJSに書き換える

というわけで、あとはChartJSに書き換えるだけ。

Chart.jsのBarサンプル をベースに準備していきます。

出来上がった plugin.js コードがこちらです。


var d3 = require("d3")
var chartjs = require("chart.js")

BarChart.defaultSettings = {
        "label" : "country",
        "value": "value"
}

BarChart.settings = EnebularIntelligence.SchemaProcessor(
    [
        {
          "type" : "key", "name" : "label", "help" : "Please specify the key of the data to be the label."
        },{
          "type" : "key", "name" : "value", "help" : "Please specify the key of the data representing the value."
        }
    ]
    , BarChart.defaultSettings);

function BarChart(settings, options) { 
    var that = this; 
    this.el = window.document.createElement('div'); 
    this.settings = settings; 
    this.options = options; 
    this.data = [];

    this.colors = ["#70C1B3","#247BA0","#FFE066","#F25F5C","#50514F","#F45B69","#211103","#5C8001","#23395B","#470063"];
    // this.colors = ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"];

    var margin = {top: 50, right: 50, bottom: 50, left: 120}, 
        width = (options.width || 700) - margin.left - margin.right, 
        height = (options.height || 500) - margin.top - margin.bottom; 

    this.width = width; 
    this.height = height; 
    this.margin = margin;

    var canvas = window.document.createElement("canvas");
    this.el.appendChild(canvas);
    var ctx = canvas.getContext('2d');
    ctx.width = this.width;
    ctx.height = this.height;

    this.chart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ["CN", "EN", "JP"],
            datasets: [{
                label: "value",
                data: [],
                backgroundColor: [  // 本来はここで the.color を反映させるはず。ただし、ChartJSの表示設定依存。
                    'rgba(255, 99, 132, 0.2)',
                    'rgba(54, 162, 235, 0.2)',
                    'rgba(255, 206, 86, 0.2)',
                    'rgba(75, 192, 192, 0.2)',
                    'rgba(153, 102, 255, 0.2)',
                    'rgba(255, 159, 64, 0.2)'
                ],
                borderColor: [
                    'rgba(255,99,132,1)',
                    'rgba(54, 162, 235, 1)',
                    'rgba(255, 206, 86, 1)',
                    'rgba(75, 192, 192, 1)',
                    'rgba(153, 102, 255, 1)',
                    'rgba(255, 159, 64, 1)'
                ],
                borderWidth: 1
            }]
        },
        options: {  // 本来 enebular の設定を反映すべきだけど一旦粗く実装
            scales: {
                yAxes: [{
                    ticks: {
                        beginAtZero:true
                    }
                }]
            }
        }
    });
}

BarChart.prototype.addData = function(data) {
    var that = this;

    if(data instanceof Array) {
        data.forEach(function(d) {
            that.data.push(d);
        });
        this.refresh();
    }else{
        this.data = [];
        this.data.push(data);
        this.refresh();
    }
}

BarChart.prototype.clearData = function() {
    this.data = [];
    this.refresh( );
}

BarChart.prototype.calculate = function() {
    var that = this; 
    var newdata = {}; 

    this.data.forEach(function(d) { 
        var k = d[that.settings.label]; 
        if(!newdata[k]) newdata[k] = 0; 
        newdata[k] += Number(d[that.settings.value]); 
    }); 

    return Object.keys(newdata).map(function(k,i) { 
        return { 
            key : k, 
            value : newdata[k].toFixed(2)
        } 
    }).sort(function(a, b){
            if(a.key < b.key) return -1;
            if(a.key > b.key) return 1;
            return 0;
    })
} 

BarChart.prototype.resize = function(options) {
    var that = this;

    this.height = options.height - that.margin.top - that.margin.bottom;
    this.width = options.width - that.margin.left - that.margin.right;

    this.refresh();
}

BarChart.prototype.refresh = function() {
    var that = this;
    var data = this.calculate();

    console.log(data);

    // 本来はlabelsに合わせた順番でデータを入れる必要があるはず
    // いまは目視で合わせててよろしくない
    var refreshData = [];

    data.forEach(function(d) {
        refreshData.push(Number(d.value));
    });

    console.log("refreshData");
    console.log(refreshData);

    this.chart.data.datasets[0].data = refreshData;
}

BarChart.prototype.getEl = function() {
  return this.el;
}

window.EnebularIntelligence.register('barchart',BarChart);
module.exports = BarChart;

実際に動かしてみるとこうなりました!

image

うん、ランダムの値をカテゴリごとに表示するってところまではできてる!ChartJS、かわいい。

ChartJSを導入しデフォルトで使われてるD3.jsと比較してやるべきことを探る

やはり、enebularでデフォルトで使われてるD3.jsまわりが良くできていますね。相性も良い。予想はしていたんですが、ChartJSはまずChartJSの作法をenebularに合わせる必要がありそうでう。

また、時間を見つけて頑張ってみますが、こんな学びがありました。

  • できたこと
    • ChartJSを組み込むためにライブラリをnpmで準備できた
    • plugin.jsにChartJSを呼び出せた
    • ChartJSが表示できるようcanvasを作法に従って準備できた
    • とりあえず表示できた
  • やるべきこと(デフォルトで使われてるD3.jsの仕様に追従するところ)

という進捗。

さすがに、1日ほど触れてみての、フルカスタマイズ可能&フロントエンド知見即反映というのは厳しかったー。しかし、たっくさんのハマりポイントをたくさん踏めたのは経験値になってよかったです。

少なくとも、初期化からの表示の基礎が分かり、基本的なライブラリの導入がわかったので、うまく慣らしていけそうです!今度、ある程度踏み抜いてから、こちらを開発されている方にも質問してみようと思います。

それでは、よき enebular InfoType Life を!

明日は

zono_0 さんの「enebular未経験者がenebularとは何なのかかんがえてみる。」です!

enebularのファーストステップ楽しみですー!