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 で動作を確認しています。