d3.jsで地図をぬるっとアニメーションさせる。

前回、d3で気象庁降灰予報を描画しましたが、
https://qiita.com/miyawa-tarou/items/f101f0248cdc5744302d
地図の中心緯度経度やズームなどは一度描画した後でも変えることができます。
また単に変えるのではなく、ぬるっとスムーズにアニメーションしながら動かすことができます。

完成版はこちら：https://miyawa-tarou.github.io/d3_nuru_map/

基本は前回のコードをもとに説明します。今回の説明に邪魔なので桜島に置いた丸は消しています。
https://github.com/miyawa-tarou/d3_nuru_map/tree/01.setting

### 1.クリックしたらズームするようにする

やることは、初めに設定した

```javascript
        // 中心座標とスケール
        var projection = d3.geoMercator()
            .center([137, 38])
            .scale(1600)
            .translate([width / 2, height / 2]);
```

この部分を
pathのtransform属性で再定義するような形になります。

今回鹿児島をクリックしたらズームするという形にします。

```javascript
            svg.selectAll("path")  // おまじない。というか理解できてない
                .data(topojson.feature(data, data.objects.pref).features)
                .enter() // dataで入れた配列をそれぞれ以下のpathにしていく
                .append("path")
                .attr("d", path) // <path d="">に　path()の返り値を入れていく
                .attr("fill", function(d,i){ return d.properties.code === 46 ? "darkgreen" : "green";}) // 鹿児島だけダークグリーン
                .on("click", function(d,i){ // 各pathをクリックすると実行されることを書く
                    if (d.properties.code === 46) { // 鹿児島をクリックしたときに
                        var zoomCenter = projection([130.6, 31.6]); // 中心座標
                        var zoom = 20000/1600; // ズーム
                        svg.selectAll("path").attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(" + zoom + ")translate(" + - zoomCenter[0] + "," + - zoomCenter[1] + ")");
                    }
                });

```

すべてのpathに対して移動・ズームするため、
svg.selectAll("path")
に対して行っています。classやidなどで指定を限定すれば一部だけ動かすみたいなことも可能です。

この時ズームに20000/1600を入れていますが、ここは「はじめの設定からのズーム比」になります。
なので前回桜島にズームしたときscale(20000)にしたため、初期のズーム1600との比の20000/1600にしています。
もし2度以上移動する場合も、scale()ははじめの1600基準の数値になります（多分）

そしてtransform属性に
translate()：おまじないのwidth/2, height/2　を入れる
scale()：上で説明した通り
translate()：移動先の座標
を入れます（参考：https://developer.mozilla.org/ja/docs/Web/SVG/Attribute/transform ）

scale()を指定しなければ移動するだけです。１以下にすればズームアウトするはずです。

![ashfall1.png](https://qiita-image-store.s3.amazonaws.com/0/140947/3928962f-742e-862d-213f-bfbf27557618.png)

https://github.com/miyawa-tarou/d3_nuru_map/compare/01.setting...02.zoom_sakurajima

### 2.降灰予報もズームする

1では地図はズームしましたが、肝心の降灰予報がごみのようにズームされずにそのまま残っています。
これは前回書いたのがpolygonタグだったためで、pathで書けば同様に処理されます。

初めにMをつけて区切り文字をLにしZで終了すると同じものが描画できます。
https://qiita.com/a-ide/items/107c9044d0f4e0354112#%E3%83%91%E3%82%B9

```javascript
            svg.append('g')
                .attr('class', 'ashFall')
                .append("path")
                .attr("d", 'M' + svgCoordinate.join('L') + 'Z') // "Mx,yLx,yLx,yZ"
```

![ashfall2.png](https://qiita-image-store.s3.amazonaws.com/0/140947/910ab8d9-67e2-66ce-0315-313f32a1de80.png)


https://github.com/miyawa-tarou/d3_nuru_map/compare/02.zoom_sakurajima...03.ashfall_zoom


### 3.ぬるっと動かす

d3ではズームする際に簡単にアニメーションにすることができます

```javascript
svg.selectAll("path").transition().duration(2000).attr("transform", ...);
```

.transition().duration(2000)
を追加しただけです。
duration()の引数が移動の時間でミリ秒になります。

ここが一番楽しいけどQiitaには貼れないのが残念
ちなみに回転とかもできますが、今回はそれはなしで。

また動き方も直線的なのから加速していくのなども指定できます（ease()を挟む）
詳しくは

- https://github.com/d3/d3-transition#transition_ease
- https://github.com/d3/d3-ease


https://github.com/miyawa-tarou/d3_nuru_map/compare/03.ashfall_zoom...04.animation

### 4.火山灰の枠線をどうにかする

ズームすると線がズームの分太くなります。
なんかきもいのでこれも変更します。
地図の方は影響を避けるために別にアニメーションを指定します。

```javascript
            svg.select(".ashFall").select('path').transition().duration(2000).attr("stroke-width", 1600/20000).attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(" + zoom + ")translate(" + - zoomCenter[0] + "," + - zoomCenter[1] + ")");
```

.attr("stroke-width", 1600/20000)を追加しています。
ズーム前はstroke-widthが1だったので、ズームの分細くしている感じです。
二つのアニメーションが同時に動かせなくて、上で設定したtransformよりこちらの設定が勝ってしまうので、transformをこちらにも記載します。
なんかきれいに細くなっていってない気がするが。。。

![ashfall3.png](https://qiita-image-store.s3.amazonaws.com/0/140947/e3d14f6f-f724-dd68-0cc5-ca5176ae81cd.png)

https://github.com/miyawa-tarou/d3_nuru_map/compare/04.animation...05.modAshFallBorder

### ex.クリッカブルに見せるためにカーソルにする

鹿児島をクリックできる感を持たせるためにカーソルにします。
せっかくなので、都道府県ごとにclassを指定し、鹿児島のclassに対してカーソルがつくスタイルシートを当てます

```javascript
        svg.selectAll("path")  // おまじない。というか理解できてない
            .data(topojson.feature(data, data.objects.pref).features)
            .enter() // dataで入れた配列をそれぞれ以下のpathにしていく
            .append("path")
            .attr("d", path) // <path d="">に　path()の返り値を入れていく
            .attr("class", function(d,i){ return "Prefs Pref" + d.properties.code;}) // すべての都道府県にPrefsクラスを、各都道府県にPref{Code}クラスを指定
```

```css

<style>
    .Prefs {
        fill: green; /* 緑で塗る */
    }
    .Pref46 { /* 鹿児島限定 */
        fill: darkgreen;
        cursor: pointer;
    }
</style>
```

https://github.com/miyawa-tarou/d3_nuru_map/compare/05.modAshFallBorder...ex.cursor