D3.js ver.3向けのコードをver.4でも動かすよ

# 目的
　私は**D3.js(Data Driven Document：JavaScriptの情報可視化ライブラリ)**を導入する上で、バージョンの違いに苦しめられました。主に、D3のver.3(以下v3)からver.4(以下v4、2016/7月リリース)で大規模な変更があったためです。Webに掲載されているサンプルコードはv3で作られたものが多く、**v4上ではその大半が動きません。**

　本記事の目的は、自分の備忘録および、同じくD3v4を新規導入する方々のために、**v3からの変更点や、v3用に書かれたコードをv4用に再コーディングする場合のポイントを述べること**です。

# 内容
　重要な点は以下3点です。それぞれセクションを区切って説明します。
1. [Scale](#scale)
2. [selection.attr / selection.style](#selectionattr--selectionstyle)
3. [selection.merge](#selectionmerge)

## Scale
　Scaleは値の正規化（線形変換、対数変換）など、主にグラフ描画の前段階で使用するメソッドが含まれている名前空間です。
　Scale名前空間はv4において以下のように分離し、**d3直下となりました。**

> * d3.scale.linear ↦ d3.scaleLinear
> * d3.scale.sqrt ↦ d3.scaleSqrt
> * d3.scale.pow ↦ d3.scalePow
> * d3.scale.log ↦ d3.scaleLog
> * d3.scale.quantize ↦ d3.scaleQuantize
> * d3.scale.threshold ↦ d3.scaleThreshold
> * d3.scale.quantile ↦ d3.scaleQuantile
> * d3.scale.identity ↦ d3.scaleIdentity
> * d3.scale.ordinal ↦ d3.scaleOrdinal
> * d3.time.scale ↦ d3.scaleTime
> * d3.time.scale.utc ↦ d3.scaleUtc
>
> ["Changes in D3 4.0 #Scales (d3-scale)": GitHub](https://github.com/d3/d3/blob/master/CHANGES.md#scales-d3-scale)より抜粋

　よって、v3のコードをv4向けに再コーディングする場合、**Scale関連のメソッドは以下のように修正しなければなりません**。

```js
// 「文字の出現回数によって線形にサイズを変える」ような関数(v3版)
var countMax = d3.max(data, function(d){ return d.count} );
var sizeScale = d3.scale.linear().domain([0, countMax]).range([10, 100]);
// ↓ If it update to v4...
var sizeScale = d3.scaleLinear().domain([0, countMax]).range([10, 100]); //changed
```

　カラースケールについても同様です。

```js
// カラースケール関数
var colorScale = d3.scale.category20();
// ↓ If it update to v4...
var colorScale = d3.scaleOrdinal(d3.schemeCategory10); //changed
```

## selection.attr / selection.style
　SelectionはDOM要素の追加・編集・削除などを行うメソッドが含まれる名前空間です。
　v4になって**attrメソッドとstyleメソッドは、オブジェクトを引数とすることができなくなりました**。

```js
// svgタグに対してスタイルと属性追加する
d3.select('svg')
  .append(text)
  .style({
    "font-family": "Impact",
    "font-size":function(d) { return d.size + "px"; },
    "fill": function(d, i) { return colorScale(i); }
  })
  .attr({
    "text-anchor":"middle",
    "transform": function(d) {
    return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
    }
  })

// ↓ If it update to v4...

d3.select('svg')
  .append(text)
  .style("font-family", "Impact") //changed
  .style("font-size", function(d) { return d.size + "px"; }) //changed
  .style("fill", function(d, i) { return colorScale(i); }) //changed
  .attr("text-anchor", "middle") //changed
  .attr("transform", function(d) { //changed
    return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; //changed
  })
```

## selection.merge()
　**v4ではmerge()というメソッドがselectionに新たに追加されました。**
　v3ではselection.enter()とselection.exit()の両メソッドで、新規に追加するデータ点群と消すデータ点群の属性や挙動を指定していました。そのどちらでもないデータ点群（そのまま存在しつづける点）は、別途属性・挙動を指定する必要がありました。
　v4ではmerge()メソッドが追加され、**新規に追加するデータ点群の指定後に".merge(親要素)"とすることで、続けて、存在し続けているデータ点群属性や挙動も指定可能となりました**。

　mergeを使わずv3のコードのまま、v4で動作させた場合、**点群が異様に小さくなるなど、描画に不具合が生じます。**

```js
//Draw the word cloud
function draw(words) {
  var cloud = svg.selectAll("g text")
                 .data(words, function(d) { return d.text; })
               
  //Entering words
  cloud.enter()
    .append("text")
    .style("font-family", "Impact")
    .style("fill", function(d, i) { return fill(i); })
    .attr("text-anchor", "middle")
    .attr('font-size', 1)
    .text(function(d) { return d.text; });
  
  //Entering and existing words
  cloud
    .transition()
      .duration(600)
      .style("font-size", function(d) { return d.size + "px"; })
      .attr("transform", function(d) {
        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
      })
      .style("fill-opacity", 1);
    
  //Exiting words
  cloud.exit()
    .transition()
      .duration(200)
      .style('fill-opacity', 1e-6)
      .attr('font-size', 1)
      .remove();
}
```

　If it update to v4.

```js
//Draw the word cloud
function draw(words) {
  var cloud = svg.selectAll("g text")
                 .data(words, function(d) { return d.text; })
               
  //Entering words
  cloud.enter()
    .append("text")
    .style("font-family", "Impact")
    .style("fill", function(d, i) { return fill(i); })
    .attr("text-anchor", "middle")
    .attr('font-size', 1)
    .text(function(d) { return d.text; });
  //Entering and existing words
  .merge(cloud) // changed!
    .transition()
      .duration(600)
      .style("font-size", function(d) { return d.size + "px"; })
      .attr("transform", function(d) {
        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
      })
      .style("fill-opacity", 1);
    
  //Exiting words
  cloud.exit()
    .transition()
      .duration(200)
      .style('fill-opacity', 1e-6)
      .attr('font-size', 1)
      .remove();
}
```

　以上です。間違っている点がありましたら、ご指摘をお願いしたいです。

# 参考
*1. ["Changes in D3 4.0": GitHub](https://github.com/d3/d3/blob/master/CHANGES.md)
2. ["【D3.js】「全ツイート履歴」からWord cloudを作ってみた。": GUNMA GIS GEEK](https://shimz.me/blog/d3-js/2711) 
3. ["D3.js v4のデータバインド": Qiita](http://qiita.com/mojaie/items/be0a3eaf84273e2d42e7) @mojaie 氏*