Vega で予定表を書いてみた

FUJITSU アドベントカレンダー２日目の記事です。

## はじめに

Vega はjsonでデータや描画の設定を記述することで、視覚化できるツールです。[いろんなグラフ](https://vega.github.io/vega/examples/)を表現できますが、今回はグラフでなく予定表をVegaで表現してみたいと思います。


ちなみに、以前[Yamlファイルから線表を作成ツール](https://qiita.com/ihgs/items/082396f59e7a62c342c1)を作りましたが、これは工程表を表現するのにはいいですが、単発のイベントを表現するにはあまりうまく書けませんでした。
ですので、今回は案件ごとに単発イベントがあるような予定表を作成してみます。
(ex. 案件A:2019/11/14 チーム打ち合わせ、2019/11/21 お客さま打ち合わせ ... ）

### 出来上がったもの

案件ごとの予定がマークで表示され、マウスオーバーするとツールチップが表示されます（下はスクリーンショットです）。

![スクリーンショット 2019-12-02 23.07.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51316/1ccd779c-f37e-b63b-7b37-0a763bae9e2e.png)




### ソース

3つのファイルを作成します。
一度つくれば、data.js だけ変更すれば予定の更新ができます。

* index.html
* spec.js
* data.js



#### index.html

Vega, Vega-Embed を使い、`<div id="vis"></div>` に描画をします。

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"> 
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@4"></script>
  <script src="data.js"></script>
  <script src="spec.js"></script>
</head>
<body>

<div id="vis"></div>
<script type="text/javascript">
  vegaEmbed('#vis', spec).then(function(result) {
  }).catch(console.error);
</script>
</body>
</html>
```


#### data.js

案件ごとにevents があり、各eventとして、 date, typeを指定します。
未決定の場合は、`tbd: true` を設定し、グラフでの見え方を区別できるようにします。
`tbd`が設定されている場合、透過するようにしてます。

```js
var raw_data = [
        {
          "name": "案件A",
          "events": [
            {"date": "2019/11/14", "type": "チーム打ち合わせ"},
            {"date": "2019/11/21", "type": "お客様打ち合わせ"},
            {"date": "2019/11/23", "type": "チーム打ち合わせ"}
          ]
        },
        {
          "name": "案件B",
          "events": [
            {"date": "2019/11/21", "type": "チーム打ち合わせ"},
            {"date": "2019/12/02", "type": "お客様打ち合わせ"},
            {
              "date": "2019/12/10",
              "type": "資料提出",
              "shape": "square",
              "tbd": true
            }
          ]
        },
        {
          "name": "案件C",
          "events": [
            {"date": "2019/11/04", "type": "チーム打ち合わせ"},
            {"date": "2019/11/18", "type": "お客様打ち合わせ"},
            {"date": "2019/11/28", "type": "資料提出", "shape": "square"}
          ]
        },
        {
          "name": "案件D",
          "events": [{"date": "2019/12/14", "type": "チーム打ち合わせ", "tbd": true}]
        }
      ]
```

#### spec.js

VegaのSpecを記述します。
`data/values` にdata.js のraw_data を設定してます。

```js

var spec = {
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "width": 600,
  "height": 340,
  "padding": 5,
  "data": [
    {
      "name": "event_data",
      "values": raw_data,
      "transform": [
        {"type": "flatten", "fields":["events"]},
        {"type": "formula", "as":"date", "expr": "datetime(datum.events.date)"}
      ]
    }

  ],
  "scales": [
    {
      "name": "yscale",
      "type": "band",
      "domain": { "data": "event_data", "field": "name" },
      "range": "height",
      "padding": 0.2
    },
    {
      "name": "xscale",
      "type": "time",
      "domain": { "data": "event_data", "field": "date" },
      "range": "width",
      "round": true,
      "nice": "month"
    },
    {
      "name":"color",
      "type": "ordinal",
      "range": {"scheme": "category10"},
      "domain": {
        "fields":[
          {"data": "event_data", "field": "events.type"}
        ]
      }
    }
  ],

  "axes": [
    { "orient": "left", "scale": "yscale", "labelPadding": 4, "grid":true},
    { "orient": "bottom", "scale": "xscale", "tickCount": "week", "format":"%m/%d" }
  ],

  "marks": [
    {
      "name": "layer_0",
      "type": "symbol",
      "from": { "data": "event_data" },
      "encode": {
        "update": {
          "shape": [
            {"test": "datum.events.shape", "signal": "datum.events.shape"},
            {"value":"triangle-up"}
          ],
          "size": { "value": 150 },
          "x": { "scale": "xscale", "field": "date" },
          "y": { "scale": "yscale", "field": "name", "offset": { "scale": "yscale", "band": 0.5 } },
          "tooltip": { "field": "events.type" },
          "stroke":  [{"scale": "color", "field":"events.type"}],
          "fill": [{"scale": "color", "field":"events.type"}],
          "fillOpacity":[
            {"test": "datum.events.tbd", "value":0.2}
          ]
        }
      }
    }
  ],
  "legends": [
    {
      "fill":"color"
    }
  ],
}
```


## おわりに

多少無理矢理感はありますが、Vegaで予定表を表現できました。
マークだけでなく、始まりと終わりがあるものを線で表現するようにもかけます（今日中にまとめれなかった。。。）

Jsonでスキーマを書くのは、ぱっと見とっつきにくいですが、なれると面白くなってきます。
[解説記事](https://qiita.com/ihgs/items/87fda0cd39693bae44f3)もコツコツ書いていますので、皆さんもぜひ Let's try Vega!