Power BI カスタムビジュアル開発 : 積み上げ棒グラフの開発 : X、Y 軸および凡例の描画

[前回](https://qiita.com/kenakamu/items/040d80f1bf8724b3c68d)まででメインのグラフは描画できました。今回は X 軸などグラフに必要な他の要素を描写します。

# X 軸

d3 の機能を使って X 軸が簡単に描写できます。

1\. まず X 軸の描写領域用の入れ物を定義。Visual クラスのプロパティに以下コードを追加。

```typescript
private xAxis: d3.Selection<d3.BaseType, any, any, any>;
```

2\. コンストラクタ内で初期化。ルートの SVC に対して g 要素を追加。

```typescript
constructor(options: VisualConstructorOptions) {
    this.svg = d3.select<SVGElement, any>(<any> options.element)
        .append('svg');
    this.stackBarContainer = this.svg
        .append('g');
    this.xAxis = this.svg
        .append('g');
}
```

3\. X 軸を表示できるようグラフ領域のマージンを変更。

```typescript
let margin = { top: 0, bottom: 30, left: 0, right: 0 }
```

4\. update 関数の一番下に以下コードを追加。

- transform 要素を使って表示場所を下に移動
- d3.axisBottom に、定義した x 関数を渡して X 軸を作成

```typescript
let xAxis = g => g
    .attr("transform", `translate(0,${height - margin.bottom})`)
    .call(d3.axisBottom(x).tickSizeOuter(0))
    .call(g => g.selectAll(".domain").remove());
this.xAxis.call(xAxis);
```

5\. カスタムビジュアルを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/f400523d-569b-18de-d749-cfbd61a83b2f.png)

# Y 軸

ほぼ X 軸と同じように実装できます。

1\. Y 軸の描写領域用の入れ物を定義。Visual クラスのプロパティに以下コードを追加。

```typescript
private yAxis: d3.Selection<d3.BaseType, any, any, any>;
```

2\. コンストラクタ内で初期化。ルートの SVC に対して g 要素を追加。

```typescript
this.yAxis = this.svg
    .append('g');
```

3\. Y 軸を表示できるようグラフ領域のマージンを変更。

```typescript
let margin = { top: 0, bottom: 30, left: 40, right: 0 }
```

4\. update 関数の一番下に以下コードを追加。

- transform 要素を使って表示場所を左に移動
- d3.axisLeft に、定義した y 関数を渡して Y 軸を作成

```typescript
let yAxis = g => g
    .attr("transform", `translate(${margin.left},0)`)
    .call(d3.axisLeft(y).ticks(null, "s"))
    .call(g => g.selectAll(".domain").remove());
this.yAxis.call(yAxis);
```

5\. カスタムビジュアルを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/1190e5cc-3542-5397-f4b3-c046ddb3f151.png)

# 凡例

基本的な考え方は同じですが、凡例は通常、四角や丸などと文字列を一緒に表示するため、要素が増えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/cc94e14e-a8cb-ef61-b0e8-3d7ab1a8c1f1.png)

1\. 凡例描写領域用の入れ物を定義。Visual クラスのプロパティに以下コードを追加。

```typescript
private legend: d3.Selection<d3.BaseType, any, any, any>;
```

2\. コンストラクタ内で初期化。ルートの SVC に対して g 要素を追加。

```typescript
this.legend = this.svg
    .append("g");
```

3\. 凡例を表示できるようグラフ領域のマージンを変更。

```typescript
let margin = { top: 0, bottom: 30, left: 40, right: 130 }
```

4\. update 関数の一番下に以下コードを追加。

- circle と text 要素で構成するため、要素を一段階追加
- transform で画面右側に表示
- 凡例の順番とグラフの順番が逆のため、reverse() で入れ替え
- 各凡例が重ならないよう data 関数後に 20 ピクセルずつずれるよう transform を実行

```typescript
let legend = svg => {
    let g = svg
        .attr("transform", `translate(${width - margin.right}, 0)`)
        .selectAll("g")
        .data(legends.reverse())
        .join("g")
        .attr("transform", (d, i) => `translate(0,${i * 20})`)

    g.call(g => g.selectAll("circle").remove());
    g.call(g => g.selectAll("text").remove());

    g.append("circle")
        .attr("cx", 20)
        .attr("cy", 10)
        .attr("r", 5)
        .attr("fill", d => color(d));

    g.append("text")
        .attr("x", 30)
        .attr("y", 9.5)
        .attr("dy", "0.35em")
        .text(d => d);
}
this.legend.call(legend);
```

5\. カスタムビジュアルを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/852e28a4-dfb8-366a-d755-0b0872befa3b.png)

# まとめ

今回はメインのグラフ以外の要素を描写しました。うまく表示されているように見えますが、凡例の文字列が長くなったり、短くなったりすると動的に対応できないため、次回は提供されている Utility を使って動的に計算してみます。また書式も設定してみます。

[次の記事へ](https://qiita.com/kenakamu/items/071662bc2a49afb9bd36)
[目次へ戻る](https://qiita.com/kenakamu/items/f57ab7f73d6ea10bc9d9)