Rのtmapパッケージでインタラクティブな主題図の作成&ウェブ上での公開

![all2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/83f29aab-905c-2551-bb57-57aeb6c84f78.png)


Rでインタラクティブマップを作成する方法はいくつかありますが今回はtmapを使用します。
tmapはレイヤの重ね合わせをggplot2のように`+`で表現し、描画をオブジェクトに格納できるので非常に使いやすいです。
また地図をHTML形式で保存できるのでウェブにもあげられます。

以前投稿した[Rのcartographyパッケージで主題図の作成](https://qiita.com/ocean_f/items/416a216bc038ca4644ce)が静的な地図の作成方法を解説したもので、今回が動的な地図の作成方法を解説したものになります。

# 準備

必要パッケージとデータを準備します。

なおGISの処理や描画処理が重いので注意してください。
スペックに余裕があるのであればRstudio上で作業すると非常に楽です。

## パッケージの準備
tmapとtmaptools（とshinyjs）をインストールします。

```R
install.packages(c("tmap", "tmaptools", "shinyjs"))
```

その他tidyverseとsfを使用するのでインストールされていない場合はインストールしてください。

インストールできたらR上で呼び出しておきます。

```R
library(tidyverse)
library(sf)
library(tmap)
library(tmaptools)
```


## 使用データの準備
今回は

- [国土数値情報　人口集中地区（DID）データ](http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-A16-v2_3.html)の平成 27年　東京（ポリゴン）

- [国土数値情報　鉄道データ](http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_3.html)の平成30年　全国（ライン）

- [国土数値情報　都市公園データ](http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-P13.html)の平成23年　東京（ポイント）

を使用します。それぞれ手順通りにダウンロードして作業ディレクトリに展開してください。


## データの読み込みと編集
描画前にデータを読み込んで編集しておきます。

### 読み込み
`sf::st_read`でsfクラスのオブジェクトとして読み込みます。

```R
# DIDデータの読み込み
tokyo <- st_read("A16-15_13_DID.geojson")
# 鉄道データの読み込み
rail <- st_read("N02-18_RailroadSection.geojson")
# 都市公園データの読み込み
park <- st_read("P13-11_13.shp", options = "ENCODING=CP932", crs = 4612) # エンコーディングとCRS（ここではJGD2000）を指定
```
参考：[sfでShapefileの読み書きをするときの文字コードの指定方法](https://notchained.hatenablog.com/entry/2017/09/13/102358) 、[GISのための測地成果、測地系、楕円体、投影座標系、EPSGコードのまとめ](http://tmizu23.hatenablog.com/entry/20091215/1260868350)

### 編集
処理前にJGD2011の平面直角座標9系に投影変換しておきます。
その上で鉄道データと都市公園データをDIDの範囲で切り出します。
また描画の際に使う属性を追加で作成しておきます。

```R
# DIDデータ
tokyo.proj <- st_transform(tokyo, crs = 6677)  %>% st_make_valid() # 投影変換と不正な地物の整形

# 鉄道データ
rail.proj <- st_transform(rail,  crs = 6677) # 投影変換
rail.proj.clip <- st_intersection(x = rail.proj, y = tokyo.proj) %>% mutate(`鉄道路線（運営会社）` = fct_drop(N02_004)) # DIDで切り出し、属性を追加

# 都市公園データ
park.proj <- st_transform(park, crs = 6677)
park.proj.clip <- st_intersection(x = park.proj, y = tokyo.proj) %>% mutate(`公園面積（m2）` = P13_008) # DIDで切り出し、属性を追加
```
なお

- DIDデータは投影変換した後にポリゴンの境界線が交差してしまっている？ようなので、`lwgeom::st_make_valid`で整形しています。
- 切り出したあとでもfactorのlevelはそのままで、存在しない属性も凡例に追加されてしまいます。ここではそれを防ぐために`forcats::fct_drop`で使われていないlevelを削除しています。

参考：[Package 'sf' - CRAN](https://cran.r-project.org/web/packages/sf/sf.pdf)、　[sf cheatsheets](https://github.com/rstudio/cheatsheets/blob/master/sf.pdf)、　[lwgeomパッケージでRからliblwgeomの関数を使う](https://notchained.hatenablog.com/entry/2017/12/10/112430)、　[{forcats}パッケージでカテゴリカル変数(factor型データ)をいじってみる](https://kazutan.github.io/kazutanR/forcats_test.html)

# 描画
## モードの設定
描画前に描画モードの設定をしておきます。
今回はインタラクティブなviewモードに設定します。

```R
tmap_mode("view")
```

## ポリゴンの描画とラベルの表示
まずDIDデータを描画してみます。

```R
tm_shape(shp = tokyo.proj) +
  tm_polygons(col = "gray") + 
  tm_text(text = "市町村名称")
```
- `tm_shape`で描画するデータを指定します。
- `tm_polygons`でポリゴンを描画します。ここでは色分けをせず、塗りつぶし色だけを指定しています。
- `tm_text`で描画するラベルを指定します。ここでは参照する属性として「市町村名称」を指定しています。

描画するとこんな感じです。背景地図はEsri.WorldGrayCanvas、OpenStreetMap、Esri.WorldTopoMapから選べます。
![DID.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/c47be81e-7321-e886-e97a-8aae4c2cd7aa.png)


## ラインの描画と類型による色分け
次に鉄道データを描画してみます。

```R
tm_shape(shp = rail.proj.clip) + 
  tm_lines(col = "鉄道路線（運営会社）", lwd = 2, palette = get_brewer_pal("Paired", n = 18)) 
```
- `tm_shape`で描画するデータを指定します。
- `tm_lines`でラインを描画します。ここでは色分け用の属性として「鉄道路線（運営会社）」を指定しています。また`lwd`で線分の大きさを、`palette`で色分け用のパレットを指定しています。

描画するとこんな感じです。
![rail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/1a72521a-b92b-ad38-649f-fb885dad9886.png)


### パレットについて
`tmaptools::palette_explorer()`で使用できるパレットを確認できます。実行すると以下のようにインタラクティブにいくつかのパレットをプレビューできます。

![palette.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/f2aa4f0a-6387-316a-eb04-df294386abcf.png)

…これふつうに便利なのでは


## ポイントの描画と階級による色分け
最後に都市公園データを描画します。

```R
tm_shape(shp = park.proj.clip) + 
  tm_dots(col = "公園面積（m2）", style = "quantile", size = 0.01,palette = get_brewer_pal("Greens"))
```
- `tm_shape`で描画するデータを指定します。
- `tm_dots`でポイントを描画します。ここでは色分け用の属性として「公園面積（m2）」を指定しています。また`style`で区分方法を（ここでは四分位点）。`size`で点の大きさを（デフォルトは0.01）、`palette`で色分け用のパレットを指定しています。

描画するとこんな感じです。
![park.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/451bd6df-94cf-b16c-3423-78fdc6acf8f2.png)



## レイヤの重ね合わせ
以上のレイヤは`+`で重ね合わせられます。また`tm_layout`でタイトルを指定できます。

```R
m <- tm_shape(shp = tokyo.proj) +
      tm_polygons(col = "gray") + 
      tm_text(text = "市町村名称") + 
     tm_shape(shp = rail.proj.clip) + 
      tm_lines(col = "鉄道路線（運営会社）", lwd = 2, palette = get_brewer_pal("Paired", n = 18)) + 
     tm_shape(shp = park.proj.clip) + 
      tm_dots(col = "公園面積（m2）", style = "quantile", size = 0.01,palette = get_brewer_pal("Greens")) + 
     tm_layout(title = "<p>東京のDID内の鉄道と都市公園</p><p><small>国土交通省国土政策局「国土数値情報（人口集中地区データ、鉄道データ、都市公園データ ）」（http://nlftp.mlit.go.jp/ksj/index.html）をもとにocean_fが編集、加工。</small></p>") 

m # 描画
```

タイトルの部分はHTMLとして解釈されるので、ここでは`<p>`と`<small>`で改行とサイズ変更を指定しています。（マニュアルでは`\n`で改行できることになっていますがうまく行きませんでした）

描画するとこんな感じ。
![all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535720/10d08d25-ce81-c6c6-98c1-977562c96f6e.png)


# 保存とウェブ上での公開
## 保存
上の図を保存するには`tmap_save`を使用します。

```R
tmap_save(m, "tmap.html")
```

これでインタラクティブな地図がHTMLで保存できます。

## ウェブ上での公開
実際はShinyと連携させるようなのですが、ファイルをサーバーにあげるだけでもウェブ上で公開できます。
今回はGitHub pagesにアップしました。Gitの知識があれば登録からアップロードまで数分でできると思います。（参考：[GitHubを使って3分でHPを公開する。](https://qiita.com/budougumi0617/items/221bb946d1c90d6769e9)）

ページは[こんな感じです](https://ocean-v.github.io/tmap_review/tmap.html)。　←　スマホだと重くてうまく動かないので注意
HTMLやCSSやJSの知識が全然ないのでそのまま上げただけです。
それでも地図としてかなり見やすく、クリックで属性も見れてとてもGOOD！

地図を広く公開したいときには非常に便利だと思います！
（ただおそらく描画処理はクライアント側で行っているので、表示のスムーズさはマシンスペックに依存します。データが増えると難しいかもしれません）

# 参考ページ
- [Package 'tmap' - CRAN](https://cran.r-project.org/web/packages/tmap/tmap.pdf)
- [tmap: get started! - CRAN](https://cran.r-project.org/web/packages/tmap/vignettes/tmap-getstarted.html)