D3.jsのプラグイン「venn.js」を使ってベン図を描く

# はじめに
ユーザーのサイト利用に関する併用状況をWebで可視化する必要があり、
（３つまでであれば）ベン図が直感的でわかりやすいと思ったのですが
意外と簡単にベン図を作成するJavaScriptライブラリが見つからなかった中
D3.jsのプラグイン「venn.js」が使いやすかったので備忘録的に残します。

venn.jsのGitHubこちら
[venn.js](https://github.com/benfred/venn.js/)

# ベン図
`examples`フォルダにサンプルが豊富にありますので、
そこを見ればだいたいやりたいことは載っています。
いくつか実際に使ってみたサンプルについて書いていきます。

## 基本
`examples/simple.html`を少し手直ししています。
・日本語でも動きます。
・D3.jsのバージョンをv4→v5に上げても動きます。

```html:
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>venn.js sample</title>
<style>
body {
    font-family: "Hiragino Kaku Gothic Pro W3","Hiragino Kaku Gothic ProN",Meiryo,sans-serif;
    font-size: 14px;
}
</style>
</head>

<body>
    <div id="venn"></div>
</body>

<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="../venn.js"></script>
<script>
// データセットの用意
var sets = [ {sets: ['サイトA'], size: 12},
             {sets: ['サイトB'], size: 12},
             {sets: ['サイトA','サイトB'], size: 2}];

// #vennに描画
var chart = venn.VennDiagram();
d3.select("#venn").datum(sets).call(chart);
</script>
</html>
```

![chart1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270785/ed739df3-a220-888a-888b-f5f516d1cc87.png)

## 色の変更
`examples/styled.html`を参考にしています。
以降はscript内のみ書きます。
・色を指定することができます。
・丸の色及びテキストの色両方変更することもできます（もちろん片方だけも可能）

```javascript:
// データセットの用意
var sets = [ {sets: ['サイトA'], size: 12},
             {sets: ['サイトB'], size: 12},
             {sets: ['サイトA','サイトB'], size: 2}];

// #vennに描画
var chart = venn.VennDiagram();
d3.select("#venn").datum(sets).call(chart);

// 色の変更
var colors = ['red', 'blue']; // 1つ目を赤、2つ目を青に
d3.selectAll("#venn .venn-circle path")
    .style("fill", function(d,i) { return colors[i]})
d3.selectAll("#venn .venn-circle text")
    .style("fill", function(d,i) { return colors[i]})
```

![chart2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270785/bbbc9ed6-2674-1578-3e63-08d28fd3974d.png)

## 順番の変更
`examples/orientation_order.html`を参考にしています。
・描画の順番を変えることができます。

```javascript:
// データセットの用意
var sets = [ {sets: ['サイトA'], size: 12},
             {sets: ['サイトB'], size: 12},
             {sets: ['サイトA','サイトB'], size: 2}];

// #vennに描画
var chart = venn.VennDiagram();

// 順番の変更
var customOrdering = {'サイトB': 1, 'サイトA': 2}; // 1つ目サイトBに、2つ目をサイトAに
chart.orientationOrder(function (a, b) { return customOrdering[a.setid] - customOrdering[b.setid]});

d3.select("#venn").datum(sets).call(chart);
```

![chart3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270785/8b37379a-a621-de60-00ff-643cdc88f795.png)

## ツールチップの表示
`examples/intersection_tooltip.html`を参考にしています。
ツールチップのstyleを定義する必要があるため、ここはHTMLすべて書きます。
・ツールチップで数値（ここではユーザー数）を表示することができます。
・マウス操作に合わせて色の透明度などを変化させることができます。

```html:
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>venn.js sample</title>
<style>
body {
    font-family: "Hiragino Kaku Gothic Pro W3","Hiragino Kaku Gothic ProN",Meiryo,sans-serif;
    font-size: 14px;
}
</style>
</head>

<body>
    <div id="venn"></div>
</body>

<style>
.venntooltip {
  position: absolute;
  text-align: center;
  width: 128px;
  height: 22px;
  background: #333;
  color: #ddd;
  padding: 2px;
  border: 0px;
  border-radius: 8px;
  opacity: 0;
}
</style>

<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="../venn.js"></script>
<script>
// データセットの用意
var sets = [ {sets: ['サイトA'], size: 12},
             {sets: ['サイトB'], size: 12},
             {sets: ['サイトA','サイトB'], size: 2}];

// #vennに描画
var chart = venn.VennDiagram();
var div = d3.select("#venn")
div.datum(sets).call(chart);

var tooltip = d3.select("body").append("div")
    .attr("class", "venntooltip");

div.selectAll("path")
    .style("stroke-opacity", 0)
    .style("stroke", "#fff")
    .style("stroke-width", 3)

div.selectAll("g")
    .on("mouseover", function(d, i) {
        // sort all the areas relative to the current item
        venn.sortAreas(div, d);

        // Display a tooltip with the current size
        tooltip.transition().duration(400).style("opacity", .9);
        tooltip.text(d.size + " ユーザー");

        // highlight the current path
        var selection = d3.select(this).transition("tooltip").duration(400);
        selection.select("path")
            .style("fill-opacity", d.sets.length == 1 ? .4 : .1)
            .style("stroke-opacity", 1);
    })

    .on("mousemove", function() {
        tooltip.style("left", (d3.event.pageX) + "px")
               .style("top", (d3.event.pageY - 28) + "px");
    })

    .on("mouseout", function(d, i) {
        tooltip.transition().duration(400).style("opacity", 0);
        var selection = d3.select(this).transition("tooltip").duration(400);
        selection.select("path")
            .style("fill-opacity", d.sets.length == 1 ? .25 : .0)
            .style("stroke-opacity", 0);
    });
</script>
</html>
```

![chart4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270785/658eb553-d50a-0d95-f857-4cb64eb170a0.png)

# おわりに
venn.jsの説明というより、ほとんどD3.jsの説明になってしまいましたが。。。
見方を変えればvenn.jsでSVGに描画してしまえば、あとはD3.jsの世界なので
スタイルの変更などは比較的自由に行えます。
難易度は上がりますが、レイアウトを動的に変えることなどもできるので、
ビジュアライズの世界が広がりますね。