amChartsのTreemapを使って、予算配分を可視化してみよう!

著者名:Mamoru Fujinoki( Fuji Business International (External link)

目次

はじめに

amChartsというグラフ描画ライブラリを使って、予算管理のTreemapをカスタムビューで作成します。
Treemap形式で可視化することにより、レコード一覧よりも視覚的にわかりやすく管理できます。
今回は、部署と用途毎に割り当てられた予算をTreemap形式で可視化していきます。

kintoneで予算管理アプリの開発・設定

kintoneで予算管理アプリを作成します。次の画像およびテーブルの設定項目を参照してください。

フィールドの種類 フィールド名 フィールドコード
ドロップダウン 部署 department
ドロップダウン 用途 purpose
数値 予算 budget

また、ドロップダウンの項目は以下の画像を参考に設定してください。

これで、予算管理アプリフォームは完成ですので「フォームを保存」します。

カスタマイズビューの設定

次に「一覧」タブをクリックし、「+」ボタンで、一覧を追加します。
一覧名を入力し、レコードの表示形式として「カスタマイズ」を選択します。
また、APIよりデータを取得するので、「ページネーションを表示する」のチェックを外します。
HTML欄には、amChartsの表示されるコンテナーを設定します。今回は、<div>タグを使用し、IDを「chartdiv」と設定しています。
そして、このとき生成された「一覧ID」を記録しておきます。

設定を「保存」します。
これでkintoneアプリのカスタムビューの設定が完了しましたので、「アプリを更新」をクリックしアプリの変更を反映します。

JavaScriptによるカスタムコードの開発

以下のサンプルコードを参考にプログラムを作成します。11行目の{一覧ID}には、カスタマイズビューを作成した時に記録した「一覧ID」を設定します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 *  Tree view sample program by amCharts
 * Copyright (c) 2022 Cybozu
 *
 * Licensed under the MIT License
*/
(() => {
  'use strict';
  // レコード一覧イベント
  kintone.events.on('app.record.index.show', (event) => {
    if (event.viewId !== {一覧ID}) { // 作成したカスタマイズビューのIDを指定
      return event;
    }
    try {
      // ルートエレメントを作成
      const root = am5.Root.new('chartdiv');
      // テーマを設定
      root.setThemes([
        am5themes_Animated.new(root)
      ]);
      // ラッパーコンテナーを作成
      const container = root.container.children.push(
        am5.Container.new(root, {
          width: am5.percent(100),
          height: am5.percent(100),
          layout: root.verticalLayout
        })
      );
        // シリーズを作成
      const series = container.children.push(
        am5hierarchy.Treemap.new(root, {
          topDepth: -1,
          initialDepth: 2,
          valueField: 'value',
          categoryField: 'name',
          childDataField: 'children',
          nodePaddingOuter: 5,
          nodePaddingInner: 0
        })
      );
      series.rectangles.template.setAll({
        strokeWidth: 2,
        cornerRadiusTL: 10,
        cornerRadiusTR: 10,
        cornerRadiusBL: 10,
        cornerRadiusBR: 10
      });
      const appId = kintone.app.getId();
      const query = kintone.app.getQuery();
      return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {app: appId, query: query}).then((resp) => {
        const records = resp.records;
        const children = [];
        records.forEach((record) => {
          if (children.length > 0) {
            const same_department = children.find((el) => {
              return el.name === record.department.value;
            });
            if (!same_department) {
              const child = {
                name: record.department.value,
                children: [
                  {
                    name: record.purpose.value,
                    value: parseInt(record.budget.value)
                  }
                ]
              };
              children.push(child);
            } else {
              const child2 = {
                name: record.purpose.value,
                value: parseInt(record.budget.value)
              };
              same_department.children.push(child2);
            }
          } else {
            const child3 = {
              name: record.department.value,
              children: [
                {
                  name: record.purpose.value,
                  value: parseInt(record.budget.value)
                }
              ]
            };
            children.push(child3);
          }
        });

        const data = {
          name: '予算',
          children: children
        };
        series.data.setAll([data]);
        series.set('selectedDataItem', series.dataItems[0]);

        // パンくずリストを追加
        container.children.unshift(
          am5hierarchy.BreadcrumbBar.new(root, {
            series: series
          })
        );

        //  ロード時にアニメ化
        series.appear(1000, 100);
        return event;
      }, (resp) => {
        return event;
      });
    } catch (error) {
      console.log(error);
    }
    return event;
  });
})();

また、Treemapを適性に表示するため、以下のCSSスタイルシートを設定します。

1
2
3
4
#chartdiv {
  width: 100%;
  height: 500px;
  }

動作の確認

予算管理アプリの一覧画面で「(すべて)」を選択し、以下の画像を参考にして、予算管理アプリにレコードを追加します。

次に以下の画像を参考にamCharts 5のTreemap用のライブラリおよび先ほど作成したJavaScriptプログラムおよびCSSスタイルシートを設定します。
以下が、amCharts5 Treemap用のライブラリのCDNのURLです。

  • https://cdn.amcharts.com/lib/5/index.js
  • https://cdn.amcharts.com/lib/5/hierarchy.js
  • https://cdn.amcharts.com/lib/5/themes/Animated.js

また、ライブラリのJavaScriptをサーバーにインストールする場合、次の手順でファイルを入手します。

  1. ダウンロードページ (External link) にアクセスします。
  2. 「amCharts 5(JavaScript Charts & Maps)」をクリックします。
  3. ダウンロードされたamcharts_5.x.x.zipを展開します。

レコードの追加が完了したら、今度は「Tree Map」を選択します。
次の画像のようにTree Mapが表示されたら成功です。

また、「予算」をクリックすると予算配分の詳細が表示され、「部署名」にカーソルを合わせると予算の値が表示されます。

最初の画面に戻るには、再び「予算」をクリックします。

プログラムの解説

kintoneの一覧イベント関数で、作成したカスタマイズビュー以外は処理しないように定義します。

 9
10
11
12
13
// レコード一覧イベント
kintone.events.on('app.record.index.show', (event) => {
  if (event.viewId !== {一覧ID}) { // 作成したカスタマイズビューのIDを指定
    return event;
  }

amChartsのTreemapの初期設定を定義します。設定の詳細については、amCharts公式ドキュメントの「 Treemap (External link) 」を参照してください。

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    try {
      // ルートエレメントを作成
      const root = am5.Root.new('chartdiv');
      // テーマを設定
      root.setThemes([
        am5themes_Animated.new(root)
      ]);
      // ラッパーコンテナーを作成
      const container = root.container.children.push(
        am5.Container.new(root, {
          width: am5.percent(100),
          height: am5.percent(100),
          layout: root.verticalLayout
        })
      );
        // シリーズを作成
      const series = container.children.push(
        am5hierarchy.Treemap.new(root, {
          topDepth: -1,
          initialDepth: 2,
          valueField: 'value',
          categoryField: 'name',
          childDataField: 'children',
          nodePaddingOuter: 5,
          nodePaddingInner: 0
        })
      );
      series.rectangles.template.setAll({
        strokeWidth: 2,
        cornerRadiusTL: 10,
        cornerRadiusTR: 10,
        cornerRadiusBL: 10,
        cornerRadiusBR: 10
      });

kintone APIにより、予算管理アプリのレコードを取得します。Promiseを使って同期処理で結果を待ってから次の処理に進んでいます。

48
49
50
      const appId = kintone.app.getId();
      const query = kintone.app.getQuery();
      return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {app: appId, query: query}).then((resp) => {

各レコード毎に部署の値をチェックし、表示するTreemapノードにすでに同じ部署が存在する場合は、予算の値を同じ部署のchildノードに追加します。
存在しない場合には、新たに部署のノードを追加し、childノードに予算の値を追加しています。
詳細は、amCharts公式ドキュメントの Simple Treemapのサンプルコード (External link) を参照してください。

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
        records.forEach((record) => {
          if (children.length > 0) {
            const same_department = children.find((el) => {
              return el.name === record.department.value;
            });
            if (!same_department) {
              const child = {
                name: record.department.value,
                children: [
                  {
                    name: record.purpose.value,
                    value: parseInt(record.budget.value)
                  }
                ]
              };
              children.push(child);
            } else {
              const child2 = {
                name: record.purpose.value,
                value: parseInt(record.budget.value)
              };
              same_department.children.push(child2);
            }
          } else {
            const child3 = {
              name: record.department.value,
              children: [
                {
                  name: record.purpose.value,
                  value: parseInt(record.budget.value)
                }
              ]
            };
            children.push(child3);
          }
        });

作成したTreemapのノードデータをチャートに反映します。

88
89
90
91
92
93
        const data = {
          name: '予算',
          children: children
        };
        series.data.setAll([data]);
        series.set('selectedDataItem', series.dataItems[0]);

パンくずリストで詳細表示から初期表示へ戻れるようにし、Treemapのアニメ化表示の設定をしています。

 95
 96
 97
 98
 99
100
101
102
103
        // パンくずリストを追加
        container.children.unshift(
          am5hierarchy.BreadcrumbBar.new(root, {
            series: series
          })
        );

        //  ロード時にアニメ化
        series.appear(1000, 100);

まとめ

今回はamChartsのTreemap機能を使って、予算管理アプリの可視化で予算配分をわかりやすくする把握できるサンプルアプリを作成してみました。
amChartsのJavaScriptライブラリを使うと、Treemap以外にもさまざまなチャートやマップを表示できます。kintoneアプリのカスタムビューを使って用途に応じたチャートやマップを利用し、より効率的に業務を管理できます。

参考サイト

information

このTipsは、2022年7月版kintoneで動作を確認しています。