-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
09-mapping-ja.Rmd
1226 lines (996 loc) · 90.7 KB
/
09-mapping-ja.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# (PART) 拡張機能 {- #extensions}
# R で地図を作成 {#adv-map}
```{r, include=FALSE}
source("code/before_script.R")
```
## 必須パッケージ {- #prerequisites-09}
- この章では、すでに使用している以下のパッケージが必要である。
```{r 08-mapping-1, message=FALSE }
library(sf)
library(terra)
library(dplyr)
library(spData)
library(spDataLarge)
```
- 本章での主要なパッケージは **tmap** である。
CRAN よりも頻繁に更新されている [r-universe](https://r-universe.dev/) 版をお勧めする。訳註: macOS では、**tmap** は文字化けすることがある。[macOS で ragg を使用することで文字化けが解消する](https://uribo.hatenablog.com/entry/2021/03/29/202756)。
```{r}
#| eval: false
install.packages("tmap", repos = c("https://r-tmap.r-universe.dev",
"https://cloud.r-project.org"))
```
- 以下の可視化に関するパッケージを使用する (動的な地図アプリを開発したい場合は、**shiny** もインストールしよう)。
```{r 08-mapping-2, message=FALSE}
library(tmap) # 静的地図と動的地図
library(leaflet) # 動的地図
library(ggplot2) # tidyverse データ可視化パッケージ
```
- Section \@ref(spatial-ras) については、以下の二つのデータセットを読み込む必要がある。
```{r 04-spatial-operations-1-1}
nz_elev = rast(system.file("raster/nz_elev.tif", package = "spDataLarge"))
```
## イントロダクション {#introduction-09}
地理学的研究の満足度と重要性は、その結果を伝えることにある。
地図作成は、コミュニケーションと細部への注意、そして創造力を必要とする古来の技術である。\index{ちずせいさく@地図作成}
R における静的地図は、Section \@ref(basic-map) で見たように、`plot()` 関数を使えば簡単にできる。
R の基本メソッドを使って高度な地図を作成することも可能ではある [@murrell_r_2016]。
しかし、この章の焦点は、専用の地図作成パッケージを使った地図作成にある。
新しいスキルを身につけるには、1 つの分野の知識を深めてから手を広げていくことが大切である。
地図の作成も例外ではない。そのため、この章では多くのパッケージを表面的にではなく、1 つのパッケージ (**tmap**) を深く掘り下げて説明する。
地図作成は、楽しくてクリエイティブなだけでなく、実用的にも重要な役割を担っている。
丁寧に作られた地図は、仕事の成果を伝えるのに最適な方法であるが、デザインの悪い地図は印象を悪くすることがある。
よくあるデザイン上の問題点としては、*Journal of Maps* の[スタイルガイド](https://files.taylorandfrancis.com/TJOM-suppmaterial-quick-guide.pdf)で説明されているように、文字の配置やサイズ、読みにくさ、色の選び方の不注意などが挙げられる。
さらに、地図作りが不十分だと、結果の伝達にも支障が生じることがある [@brewer_designing_2015]。
> 素人が作ったような地図では、情報の重要性が伝わらず、専門家によるデータ調査をうまく表現できないことがある。
地図は数千年前からさまざまな用途に使われてきた。
歴史的な例としては、3000年以上前の古バビロニア王朝の建物や土地所有の地図、約2000年前のプトレマイオスの代表作<u>地理学</u>の世界地図などがある [@talbert_ancient_2014]。\index{ちりがく@地理学}
地図は、歴史的にエリートが自分のために作るか、あるいはエリートのために誰かが作る行為であった。
しかし、R パッケージの **tmap** や QGIS\index{QGIS} の印刷レイアウトのようなオープンソースの地図作成ソフトが登場し、誰でも高品質の地図を作ることができるようになり、「市民科学」が可能になったことで、状況は一変した。
また、ジオコンピュテーションの研究成果をわかりやすく紹介するためには、地図が最適な方法であることが多い。
したがって、地図作成はジオコンピュテーション\index{じおこんぴゅてーしょん@ジオコンピュテーション}の重要な一部であり、世界を記述するだけでなく、世界を<u>変えること</u>にも重点を置いているのである。
この章では、さまざまな地図の作り方を紹介する。
次のセクションでは、美観への配慮、ファセット、差し込み地図 (inset map) など、さまざまな静的地図について説明する。
また、Section \@ref(animated-maps) から Section \@ref(mapping-applications) では、アニメーションやインタラクティブな地図 (Web 地図や地図アプリケーションを含む) を紹介している。
最後に、Section \@ref(other-mapping-packages) は、**ggplot2** や **cartogram** など他の地図作成用パッケージを紹介する。
## 静的地図 {#static-maps}
\index{ちずさくせい@地図作成!せいてきちず@静的地図}
ジオコンピュテーションの視覚的な出力として最も一般的なのが静的地図であろう。
標準的なフォーマットとしては、ラスタ出力用に `.png`、ベクタ出力用に `.pdf` がよく用いられる。
当初、R が作成できる地図の種類は静的地図だけだった。
**sp** [@pebesma_classes_2005 参照] のリリースで状況が進展し、その後、地図作成のための多くの技術、関数、パッケージが開発された。
しかし、インタラクティブ地図がどんどん発明されてきたにもかかわらず、10 年経っても R では依然として静的なプロットが地理データの可視化の重点となっていた [@cheshire_spatial_2015]。
ジェネリック関数の `plot()` 関数は、ベクタやラスタの空間オブジェクトから静的地図を作成する最速の方法であることが多い (Section \@ref(basic-map) と Section \@ref(basic-map-raster) の項を参照)。
特にプロジェクトの開発段階では、シンプルさとスピードが優先され、`plot()` はこの点で優れている。
Base R のアプローチは拡張可能で、`plot()` は何十もの引数を提供している。
また、@murrell_r_2016 の Chapter [14](https://www.stat.auckland.ac.nz/~paul/RG2e/chapter14.html) に示されているように、静的地図の低レベル制御を可能にする **grid** パッケージもアプローチの 1 つである。
この章では、**tmap** に焦点を当て、重要な美観とレイアウトのオプションに重点を置いている。
\index{tmap (package)}
**tmap** は強力で柔軟な地図作成パッケージで、賢明なデフォルトが設定されている。
簡潔な構文で、**ggplot2** のユーザには馴染みのある最小限のコードで魅力的な地図を作成することができる。
また、`tmap_mode()` を介して、同じコードで静的な地図とインタラクティブな地図を生成するユニークな機能を備えている。
最後に、(**sf** オブジェクトと **terra** オブジェクトを含む) 空間クラスを受け入れることができる点では、**ggplot2** などよりも優れている。
### tmap の基礎知識 {#tmap-basics}
\index{tmap (package)!きそ@基礎}
**ggplot2**と同様に、**tmap** は「グラフィックの文法」という考えに基づいている [@wilkinson_grammar_2005]。
各入力データセットは、地図上の位置 (データの `geometry` で定義)、色、その他の視覚的変数など、さまざまな方法で「地図作成」することができる。
基本的な構成要素は `tm_shape()` (入力データ、ベクタまたはラスタのオブジェクトを定義する) で、その後に `tm_fill()` や `tm_dots()` などの 1 つまたは複数のレイヤ要素が続く。
以下のチャンクは、このようなレイヤ構成を示し、Figure \@ref(fig:tmshape) の地図を生成する。
```{r 08-mapping-3, eval=FALSE}
# nz shape に塗りつぶしレイヤを追加
tm_shape(nz) +
tm_fill()
# nz shape に境界レイヤを追加
tm_shape(nz) +
tm_borders()
# nz shape に塗りつぶしと境界レイヤを追加
tm_shape(nz) +
tm_fill() +
tm_borders()
```
```{r tmshape, echo=FALSE, message=FALSE, fig.cap="New Zealand の形状を **tmap** 関数で塗りつぶし (左)、境界 (中)、塗りつぶしと境界 (右) のレイヤを追加してプロット。", fig.scap="New Zealand's shape plotted using tmap functions."}
source("https://github.com/geocompx/geocompr/raw/main/code/09-tmshape.R", print.eval = TRUE)
```
この場合、`tm_shape()` に渡されるオブジェクトは `nz` で、New Zealand の地域を表す `sf` オブジェクトである (`sf` オブジェクトについては Section \@ref(intro-sf) を参照)。
`nz` を視覚的に表現するためにレイヤを追加し、`tm_fill()` と `tm_borders()` でそれぞれ Figure \@ref(fig:tmshape) の陰影部分 (左図) と枠線 (中図) を作成している。
\index{ちずさくせい@地図作成!れいや@レイヤ}
これは、直感的な地図作りの手法である。
新しいレイヤを<u>追加する</u>一般的なタスクは、追加演算子 `+` とそれに続く `tm_*()` によって引き受けられる。
アスタリスク(\*)は、以下のように名前から明らかなレイヤを指す。
- `tm_fill()`: (複合) ポリゴンの塗りつぶし
- `tm_borders()`: (複合) ポリゴンの境界線
- `tm_polygons()`: (複合) ポリゴンの塗りつぶしと境界線
- `tm_lines()`: (複合) 線の線
- `tm_symbols()`: (複合) 点、(複合) 線、(複合) ポリゴンのシンボル
- `tm_raster()`: ラスタデータの色付きのセル (3レイヤのあるラスタには `tm_rgb()` もある)
- `tm_text()`: (複合) 点、(複合) 線、(複合) ポリゴンのテキスト
Figure \@ref(fig:tmshape) の右側のパネルでは、塗りつぶし (fill) レイヤ<u>の上に</u>境界 (borders) を重ねた結果を示す。
```{block2 qtm, type = 'rmdnote'}
`qtm()` (**q**uick **t**hematic **m**aps) は、主題図を簡単に作成する関数である。
簡潔で、多くの場合、良いデフォルトの可視化を提供する。
例えば、`qtm(nz)` は `tm_shape(nz) + tm_fill() + tm_borders()` と全く同じである。
さらに、レイヤ追加も `qtm()` では `qtm(nz) + qtm(nz_height)` と簡単である。
欠点としては、美観をコントロールすることが難しい点がある。このため、この Chapter では解説しない。
```
### 地図オブジェクト {#map-obj}
**tmap** の便利な点は、地図を表す<u>オブジェクト</u>を格納できることである。
以下のコードは、Figure \@ref(fig:tmshape) の最後のプロットをクラス `tmap` のオブジェクトとして保存することでこれを示している (`tm_fill() + tm_borders()` を単一の関数に凝縮した `tm_polygons()` の使用に注意してみよう)。
```{r 08-mapping-4}
map_nz = tm_shape(nz) + tm_polygons()
class(map_nz)
```
`map_nz` は後でプロットすることができる。例えば、レイヤを追加したり (下図参照)、コンソールで `map_nz` を実行するだけで、`print(map_nz)` と同じ意味になる。
新しい *shape* は、`+ tm_shape(new_obj)` で追加することができる。
この場合、`new_obj` は、先行するレイヤの上にプロットされる新しい空間オブジェクトを表す。
このようにして新しい形状が追加されると、次の新しい形状が追加されるまで、それ以降のすべての美観機能はその形状を参照する。
この構文により、複数の形状やレイヤを持つ地図を作成することができる。次のコードでは、関数 `tm_raster()` を使ってラスタレイヤ (レイヤを半透明にするために `col_alpha` を設定している) を描画している様子を示している。
```{r 08-mapping-5, results='hide'}
map_nz1 = map_nz +
tm_shape(nz_elev) + tm_raster(col_alpha = 0.7)
```
先に作成した `map_nz` オブジェクトをベースに、新しい地図オブジェクト `map_nz1` を作成する。このオブジェクトには、New Zealand 全土の平均標高を表す別の図形 (`nz_elev`) が含まれている (Figure \@ref(fig:tmlayers) 左図)。
さらに図形やレイヤを追加することもできる。以下のコードでは、New Zealand の[領海](https://en.wikipedia.org/wiki/Territorial_waters)を表す `nz_water` を作成し、作成した線を既存の地図オブジェクトに追加している。
```{r 08-mapping-6}
nz_water = st_union(nz) |>
st_buffer(22200) |>
st_cast(to = "LINESTRING")
map_nz2 = map_nz1 +
tm_shape(nz_water) + tm_lines()
```
`tmap` オブジェクトに追加できるレイヤやシェイプの数に制限はない。同じシェイプを複数回使用することも可能である。
Figure \@ref(fig:tmlayers) に示される最終的な地図は、先に作成された `map_nz2` オブジェクトに `tm_dots()` で高ポイントを表すレイヤ (オブジェクト `nz_height` に格納) を追加して作成される (**tmap** のポイントプロット機能の詳細については `?tm_dots` と `?tm_bubbles` を参照)。
その結果、4つのレイヤを持つ地図ができあがり、Figure \@ref(fig:tmlayers) の右側のパネルに示されている。
```{r 08-mapping-7}
map_nz3 = map_nz2 +
tm_shape(nz_height) + tm_symbols()
```
\index{ちずさくせい@地図作成!めたぷろっと@メタプロット}
便利だがあまり知られていない **tmap** の機能として、`tmap_arrange()` がある。これは、複数の地図オブジェクトを一つの「メタプロット」に配置することができる。
Figure \@ref(fig:tmlayers)は、`map_nz1` から `map_nz3` までをメタプロットしている例である。
```{r tmlayers, message=FALSE, fig.cap="Figure 9.1 の最終地図にレイヤを追加した地図。", fig.scap="Added layers to the output of Figure 9.1."}
tmap_arrange(map_nz1, map_nz2, map_nz3)
```
また、`+` 演算子でさらに要素を追加することができる。
ただし、美観の設定は、レイヤ関数の引数で制御する。
### 可視化の変数 {#visual-variables}
\index{ちずさくせい@地図作成!びかん@美観}
\index{ちずさくせい@地図作成!かしかのへんすう@可視化の変数}
前節のプロットは、**tmap** のデフォルトの美観セッティングを示してきた。
`tm_fill()` と `tm_symbols()` のレイヤには灰色の影を使用し、`tm_lines()` で作成した線を表現するために、連続した黒い線を使用する。
もちろん、これらのデフォルト値やその他の美観はオーバーライドすることができる。
このセクションの目的は、その方法を示すことである。
地図の美観には、大きく分けて「データによって変化するもの」と「一定であるもの」がある。
ヘルパー関数 `aes()` を使って変数の美観を表現する **ggplot2** とは異なり、 **tmap** はレイヤの種別に応じた美観の引数を直接受け付ける。
- `fill`: ポリゴンの塗りつぶし色
- `col`: ポリゴン境界線、線、点、ラスタの色
- `lwd`: 線幅
- `lty`: 線種
- `size`: シンボルの大きさ
- `shape`: シンボルの形
さらに、塗りつぶしと線の色の透過率を `fill_alpha` and `col_alpha` で指定することができる。
美観に変数に応じて変化させるには、対応する引数に列名を渡す。美観を固定するには、希望の値を渡す。^[
固定値と列名の間に衝突があった場合、列名が優先される。これは、`nz$red = 1:nrow(nz)` を実行した後に次のコードチャンクを実行することで確認できる。
]
固定値を設定した例を Figure \@ref(fig:tmstatic) に示す。
```{r tmstatic, message=FALSE, fig.cap="よく使われる塗りつぶしや枠線の美観を固定値に変更した場合の影響。", fig.scap="Impact of changing commonly used aesthetics."}
ma1 = tm_shape(nz) + tm_polygons(fill = "red")
ma2 = tm_shape(nz) + tm_polygons(fill = "red", alpha = 0.3)
ma3 = tm_shape(nz) + tm_polygons(col = "blue")
ma4 = tm_shape(nz) + tm_polygons(lwd = 3)
ma5 = tm_shape(nz) + tm_polygons(lty = 2)
ma6 = tm_shape(nz) + tm_polygons(fill = "red", fill_alpha = 0.3,
col = "blue", lwd = 3, lty = 2)
tmap_arrange(ma1, ma2, ma3, ma4, ma5, ma6)
```
Base R のプロットと同様に、美観を定義する引数もまた、様々な値を受け取ることができる。
以下の Base R コード (Figure \@ref(fig:tmcol) の左のパネルを生成) とは異なり、**tmap** 美観引数は数値ベクタを受け付けない。
```{r 08-mapping-9, eval=FALSE}
plot(st_geometry(nz), col = nz$Land_area) # 成功
tm_shape(nz) + tm_fill(fill = nz$Land_area) # 失敗
#> Error: palette should be a character value
```
代わりに、`fill` (および、ラインレイヤのための `lwd`、ポイントレイヤのための `size` など、異なることがある他の美観) は、プロットされるジオメトリに関連する属性を指定する文字列を必要とする。
したがって、次のように望ましい結果を得ることができる (Figure \@ref(fig:tmcol) 右図)。
```{r 08-mapping-10, fig.show='hide', message=FALSE}
tm_shape(nz) + tm_fill(fill = "Land_area")
```
```{r tmcol, message=FALSE, fig.cap="数値色フィールドの Base (左) と **tmap** (右) の処理方法の比較。", fig.scap="Comparison of base graphics and tmap", echo=FALSE, fig.show='hold', warning=FALSE, message=FALSE, fig.height=6, out.width="45%"}
plot(nz["Land_area"])
tm_shape(nz) + tm_fill(fill = "Land_area")
```
視覚化の変数には、`.scale`、`.legend`、`.free` という文字列を後ろにつけた 3 つの追加引数がある。
例えば、`tm_fill()` には `fill`、`fill.scale`、`fill.legend`、`fill.free` といった引数がある。
`.scale` 引数は、地図と凡例での表示方法を指定し (Section \@ref(scales))、`.legend` はタイトル、方向、位置を指定する (Section \@ref(legends))。
`.free` 引数は、多くのファセットをもつ地図で、ファセットによって縮尺や凡例が変わる場合などに使用する。
### スケール (scale) {#scales}
\index{tmap (package)!すけーる@スケール}
ここでいうスケールとは、縮尺のことではなく、地図と凡例で値がどのように表示されるかを制御することである。
例えば、`col` という変数の場合、`col.scale` が空間オブジェクトの色が値にどう対応するかを制御する。`size` という変数の場合、`size.scale` は大きさが値にどう対応するかを制御する。
デフォルトでは、`tm_scale()` が使われ、入力データ種別 (因子型、数値型、整数型) によって自動的に設定を選択する。
\index{tmap (package)!いろわけ@色分け}
ポリゴンの塗りつぶしの設定によってスケールがどのように機能するか見てみよう。
カラー設定は、地図デザインの重要な要素である。Figure \@ref(fig:tmpal) に示すように、空間変動の描き方に大きな影響を与える可能性がある。
これは、New Zealand の地域を中央値によって色分けする 4 つの方法を、左から右に示している (下のコードチャンクでも示している)。
- デフォルトの設定では、次の段落で説明する 'pretty' 区切りが使用される
- `breaks` では、手動で区切りを設定することができる
- `n` は、数値変数を分類するビンの数を設定する
- `palette` は配色を定義するもので、例えば `BuGn`
```{r 08-mapping-12, eval=FALSE}
tm_shape(nz) + tm_polygons(fill = "Median_income")
tm_shape(nz) + tm_polygons(fill = "Median_income",
fill.scale = tm_scale(breaks = c(0, 30000, 40000, 50000)))
tm_shape(nz) + tm_polygons(fill = "Median_income",
fill.scale = tm_scale(n = 10))
tm_shape(nz) + tm_polygons(fill = "Median_income",
fill.scale = tm_scale(values = "BuGn"))
```
```{r tmpal, message=FALSE, fig.cap="色設定。結果は (左から) デフォルト設定、手動区切り、n 区切り、パレットを変更した場合の結果を示している。", fig.scap="Color settings.", echo=FALSE, fig.asp=0.56, warning=FALSE}
source("https://github.com/geocompx/geocompr/raw/main/code/09-tmpal.R", print.eval = TRUE)
```
```{block2 break-style-note0, type='rmdnote'}
上の引数 (`breaks`、`n`、`values`) は、他の視覚化変数でも機能する。
例えば、`values` は、`fill.scale` or `col.scale` に対しては色のベクトル、パレット名、`size.scale` に対しては大きさのベクトル、`shape.scale` に対してはシンボルのベクトルを期待する。
```
\index{tmap (package)!break}
`tm_scale_` から始まる関数ファミリーを使うことで縮尺をカスタマイズすることもできる。
最も重要なものは、`tm_scale_intervals()`、`tm_scale_continuous()`、`tm_scale_categorical()` である。
```{r}
#| eval: false
#| echo: false
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_intervals())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_continuous())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_categorical())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_continuous_log())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_continuous_log1p())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_discrete())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_ordinal())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_rgb())
tm_shape(nz) + tm_fill(fill = "Land_area", fill.scale = tm_scale_continuous())
tm_shape(nz) + tm_symbols(size = "Land_area", size.scale = tm_scale_intervals())
```
\index{tmap (package)!interval}
`tm_scale_intervals()` 関数は、入力データ値を間隔セットに分割する。
`breaks` を手動で設定する代わりに、`style` 引数で **tmap** に自動的にブレイクを作成するアルゴリズムを選択することもできる。
デフォルトは `tm_scale_intervals(style = "pretty")` で、可能な限り整数に丸めて均等の間隔にする。
その他のオプションは、Figure \@ref(fig:break-styles) に示している。
- `style = "equal"`: 入力値を同じ範囲のビンに分割し、一様な分布を持つ変数に適している (結果の地図が色の多様性に乏しくなる可能性があるため、歪んだ分布を持つ変数には推奨されていない)。
- `style = "quantile"`: 同じ数の観測が各カテゴリに入ることを保証する (ビンの範囲が広く変化する可能性があるというマイナス面を含む)。
- `style = "jenks"`: データ中の類似した値のグループを識別し、カテゴリ間の差異を最大化する。
- `style = "log10_pretty"`: 右に裾が広がっている分布の変数に使われる、pretty スタイルの対数版 (底は 10)
```{block2 break-style-note, type='rmdnote'}
`style` は **tmap** 関数の引数ではあるが、これはもともと `classInt::classIntervals()` の引数である。よって、詳細はこの関数のヘルプを参照。
```
```{r break-styles, message=FALSE, fig.cap="**tmap** の style 引数で設定するビン方法の違い。", , fig.scap="Different binning methods using tmap.", echo=FALSE, warning=FALSE, fig.width=8}
source("code/09-break-styles.R", print.eval = TRUE)
```
\index{tmap (package)!continuous}
`tm_scale_continuous()` 関数は、連続色フィールドの色を提示する。連続ラスタによく用いられる (Figure \@ref(fig:concat) 左図)。
分布が偏っている場合に対して、`tm_scale_continuous_log()` や `tm_scale_continuous_log1p()` といった派生もある。
\index{tmap (package)!categorical}
最後に、`tm_scale_categorical()` は、カテゴリ値を代表し、各カテゴリに固有の色が割り当てられる (Figure \@ref(fig:concat) 右図)。
```{r concat, message=FALSE, fig.cap="**tmap** における連続スケールとカテゴリスケール", echo=FALSE, fig.width=8}
library(tmap)
library(spData)
library(spDataLarge)
m_cont1 = tm_shape(nz) +
tm_polygons(fill = "Median_income", fill.scale = tm_scale_continuous(n = 5)) +
tm_title('tm_scale_continuous()', fontfamily = "monospace") +
tm_layout(legend.position = tm_pos_auto_in(), scale = 0.9)
m_cat1 = tm_shape(nz) +
tm_polygons(fill = "Island", fill.scale = tm_scale_categorical()) +
tm_title('tm_scale_categorical()', fontfamily = "monospace") +
tm_layout(legend.position = tm_pos_auto_in(), scale = 0.9)
tmap_arrange(m_cont1, m_cat1)
```
\index{tmap (package)!いろぱれっと@色パレット}
パレットは、ビンに関連付けられ、前述の `breaks`、`n`、`style` 引数で決定される色域を定義する。
この引数には色ベクトルまたは新しい色パレット名を与えるが、`tmaptools::palette_explorer()` で対話的に選択することができる。
プレフィックスとして `-` を付けると、パレットの順序を逆にすることができる。
```{block2 visual-vars-values, type='rmdnote'}
入力データに応じた色パレットなどの美観に関わるデフォルト`値`は、`tmap_options() で確認することができる。
例として、`tmap_options()$values.var` を実行してみよう。
```
\index{いろぱれっと@色パレット}
色パレット\index{ちずさくせい@地図作成!いろぱれっと@色パレット}は大きく分けて、カテゴリ、連続、発散の三種類ある (Figure \@ref(fig:colpal))。目的に応じてこの三種類を使い分ける。^[
第四の色パレットとして二変量 (bivariate) がある。
これは、地図上の二つの変数の関係を代表する。
]
カテゴリパレットは、区別しやすい色で構成されており、州名や土地被覆クラスなど、特定の順序を持たないカテゴリデータに最適である。
色は直感的にわかるように、例えば川は青、牧草地は緑にする。
カテゴリを増やしすぎると、大きな凡例や多くの色を使った地図は理解できないことがある。^[国別に色分けするように、個別のポリゴンが多い場合、`fill = "MAP_COLORS"` としてみよう。隣接するポリゴンにユニークな色を設定する。]
次に、連続パレットをみてみよう。
連続パレットは、例えば明るい色から暗い色への勾配に沿っており (明るい色は低い値を表す傾向がある)、連続的な (数値) 変数に適している。
連続パレットは、以下のコードで示すように、単色 (例えば、`greens` は明るいから暗い緑へ) または多色/色相 (例えば、`yl_gn_bu` は明るい黄色から緑色を経て青色へのグラデーション) である (出力は示していない)。結果を見るために自分でコードを実行してみよう。
```{r 08-mapping-13, eval=FALSE}
tm_shape(nz) +
tm_polygons("Median_income", fill.scale = tm_scale(values = "greens"))
tm_shape(nz) +
tm_polygons("Median_income", fill.scale = tm_scale(values = "yl_gn_bu"))
```
3 番目のパレットである発散パレットは、通常 3 色の間 (紫-白-緑: Figure \@ref(fig:colpal)) で、通常 2 つの単色パレットの両端を濃い色で結合して作成される。
その主な目的は、ある気温、世帯収入の中央値、干ばつイベントの平均確率など、重要な基準点からの差異を可視化することである。
参照点の値は、`midpoint` の引数を用いて **tmap** で調整することができる。
```{r 08-mapping-13b, eval=FALSE}
tm_shape(nz) +
tm_polygons("Median_income",
fill.scale = tm_scale_continuous(values = "pu_gn_div",
midpoint = 28000))
```
```{r colpal, echo=FALSE, message=FALSE, fig.cap="カテゴリ、連続色、発散のパレットの例。", out.width="75%", dev="ragg_png"}
library(cols4all)
many_palette_plotter = function(color_names, n, titles){
n_colors = length(color_names)
ylim = c(0, n_colors)
par(mar = c(0, 5, 0, 0), family= "HiraKakuProN-W3")
plot(1, 1, xlim = c(0, max(n)), ylim = ylim,
type = "n", axes = FALSE, bty = "n", xlab = "", ylab = "")
for(i in seq_len(n_colors)){
one_color = cols4all::c4a(n = n, palette = color_names[i])
rect(xleft = 0:(n - 1), ybottom = i - 1, xright = 1:n, ytop = i - 0.2,
col = one_color, border = "light gray")
}
text(rep(-0.1, n_colors), (1: n_colors) - 0.6, labels = titles, xpd = TRUE, adj = 1)
}
all_default_pals = tmap_options()$values.var$fill
many_palette_plotter(c(all_default_pals$div, all_default_pals$seq, all_default_pals$unord), 7,
titles = c("発散", "連続", "カテゴリ"))
```
色を扱う際に考慮すべき重要な原則は、「知覚可能性」と「アクセシビリティ」の 2 つである。
まず、地図の色は感覚と合っていなければならない。
特定の色は、経験や文化的なレンズを通して見ることができる。
例えば、緑は植物や低地を表し、青は水や涼しさを連想させる色である。
また、情報を効果的に伝えるために、色パレットは分かりやすいものが望ましい。
どの数値が低く、どの数値が高いかが明確で、色も徐々に変化することが望ましい。
第二に、色の変化は、多くの人がアクセスできるものでなければならない。
そのため、色弱者用のパレットをできるだけ多く使うことが大切である。^[`cols4all::c4a_gui()` の "Color Blind Friendliness" パネルの "Color vision" オプションを参照。]
### 凡例 {#legends}
\index{tmap (package)!はんれい@凡例}
美観変数と設定を決定したのち、地図の凡例スタイルに注意を向けよう。
`tm_legend()` 関数を使うと、タイトル、位置、方向を変えたり、あるいは非表示することができる。
最も重要なのは`タイトル`で、凡例のタイトルとなる。
一般に、タイトルには二つの情報を記述する。一つ目は内容で、二つ目は値の単位である。
以下のコードは、変数名 `Land_area` よりも魅力的な名前を提供することで、この機能を示している (`expression()` は上付き文字を設定するために使用)。
```{r 08-mapping-11}
#| eval: false
legend_title = expression("Area (km"^2*")")
tm_shape(nz) +
tm_polygons(fill = "Land_area", fill.legend = tm_legend(title = legend_title))
```
**tmap** の凡例方向はデフォルトでは縦長 `"portrait"` であるが、`"landscape"` で横長とすることもできる。
凡例の位置は、`position` 引数で設定する。
```{r}
#| eval: false
tm_shape(nz) +
tm_polygons(fill = "Land_area",
fill.legend = tm_legend(title = legend_title,
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
```
```{r}
#| eval: false
#| echo: false
legend_title = expression("Area (km"^2*")")
map_nza = tm_shape(nz) +
tm_polygons(fill = "Land_area", fill.legend = tm_legend(title = legend_title), position = tm_pos_out("right", "top"))
map_nza2 = tm_shape(nz) +
tm_polygons(fill = "Land_area",
fill.legend = tm_legend(title = legend_title,
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
tmap_arrange(map_nza, map_nza2)
```
凡例の位置 (およびその他の **tmap** の地図要素の位置) は、関数でカスタマイズできる。
最も重要なものを二つ紹介する。
- `tm_pos_out()`: これがデフォルトで、凡例を図郭の外に配置する。
位置については、横方向 (`"left"`、`"center"`、`"right"`) と縦方向 (`"bottom"`、`"center"`、`"top"`) で指定する。
- `tm_pos_in()`: は、図郭内に配置する。
最初の引数は `"left"`、`"center"`、`"right"` のいずれかで、2 番目の引数は `"bottom"`、`"center"`、`"top"` のいずれかである。
または、2 つの値のベクトル (または 0 から 1 の 2 つの値) を与えても良い。この場合、凡例は図郭内に配置される。
### レイアウト {#layouts}
\index{tmap (package)!れいあうと@レイアウト}
地図レイアウトとは、すべての地図要素を組み合わせて、まとまりのある地図にすることである。
地図要素には、マップされるオブジェクト、地図グリッド (メッシュ)、縮尺バー、タイトル、マージンなどがあり、前のセクションで説明したカラー設定は、地図の見え方に影響を与えるパレットとブレークポイントに関連している。
どちらも微妙な変化をもたらすだろうが、地図が残す印象には同じように大きな影響を与える。
経緯線網\index{tmap (package)!けいいせんもう@経緯線網}、方位記号\index{tmap (package)!ほういきごう@方位記号}、スケールバー\index{tmap (package)!すけーるばー@スケールバー}、タイトルなどの整飾には、それぞれ `tm_graticules()`、`tm_compass()`、`tm_scalebar()`、`tm_title()` といった関数がある (Figure \@ref(fig:na-sb))。^[この他、`tm_grid()`、`tm_logo()`、`tm_credits()` がある。]
```{r na-sb, message=FALSE, fig.cap="方位記号とスケールバーを追加した地図。", out.width="65%", fig.asp=1, fig.scap="Map with a north arrow and scale bar.", dev="ragg_png"}
map_nz +
tm_graticules() +
tm_compass(type = "8star", position = c("left", "top")) +
tm_scalebar(breaks = c(0, 100, 200), text.size = 1, position = c("left", "top")) +
tm_title("New Zealand ", fontfamily = "HiraginoSans-W3")
```
また、**tmap** では、様々なレイアウト設定を変更することができる。その一部を、以下のコードで作成し、Figure \@ref(fig:layout1) に図示している (全リストは、`args(tm_layout)` または `?tm_layout` を参照)。
```{r 08-mapping-14, eval=FALSE}
map_nz + tm_layout(scale = 4, fontfamily = "HiraginoSans-W3")
map_nz + tm_layout(bg.color = "lightblue")
map_nz + tm_layout(frame = FALSE)
```
```{r layout1, message=FALSE, fig.cap="レイアウトオプションは、(左から) scale、bg.color、frame の各引数で指定。", fig.scap="Layout options specified by the tmap arguments.", echo=FALSE, fig.asp=0.56}
source("code/09-layout1.R", print.eval = TRUE)
```
`tm_layout()` の他の引数は、地図が配置されるキャンバスとの関係で、地図の多くの側面を制御する。
ここでは、便利なレイアウト設定をご紹介する (一部、Figure \@ref(fig:layout2))。
- `inner.margin` と `outer.margin` はマージンを設定
- `fontface` で制御されるフォント設定と `fontfamily`
- 凡例設定には、`legend.show` (凡例を表示すかどうか)、`legend.only` (地図を省略するか)、`legend.outside` (凡例を地図の外に出すか) などの二値オプションや、`legend.position` ですべて設定
- 図郭の幅 (`frame.lwd`) と二重線 (`frame.double.line`) を許可するオプション
- `sepia.intensity` (地図のセピア度合) と `saturation` (色・グレースケール) を制御する色設定
```{r layout2, message=FALSE, fig.cap="選択したレイアウトオプション。", echo=FALSE, fig.asp=0.56, warning=FALSE, fig.width=8}
source("code/09-layout2.R", print.eval = TRUE)
```
### ファセット地図 {#faceted-maps}
\index{ちずさくせい@地図作成!ふぁせっとちず@ファセット地図}
\index{tmap (package)!ふぁせっとちず@ファセット地図}
ファセット地図は「スモール・マルチプル」とも呼ばれ、多数の地図を横に並べ、時には縦に重ねて構成する [@meulemans_small_2017]。
ファセットは、空間的な関係が時間などの別の変数に対してどのように変化するかを視覚化することができる。
例えば、集落の人口の変化を、各パネルが特定の時点の人口を表すファセット地図で表現することができる。
時間の次元は、色などの別の<u>視覚化に関する変数</u>で表現できる。
しかし、これは複数のポイントが重なるため、地図が乱雑になる危険性がある (都市は移動しない!)。
ファセット地図では、一つのジオメトリ・データの属性データのそれぞれの列に対して一つのファセットとなる (`sf` オブジェクトのデフォルトのプロット方法、Chapter \@ref(spatial-class) を参照)。
また、ファセットを用いて、点パターンの経時変化など、ジオメトリの変化を表現することもできる。
このファセット化されたプロットの使用例を Figure \@ref(fig:urban-facet) に示す。
```{r urban-facet, message=FALSE, fig.cap="国連による人口予測に基づき、1970年から2030年までの都市集積の上位30位までを示したファセット地図。", fig.scap="Faceted map showing urban agglomerations.", fig.asp=0.5}
urb_1970_2030 = urban_agglomerations |>
filter(year %in% c(1970, 1990, 2010, 2030))
tm_shape(world) +
tm_polygons() +
tm_shape(urb_1970_2030) +
tm_symbols(fill = "black", col = "white", size = "population_millions") +
tm_facets_wrap(by = "year", nrow = 2)
```
このコードでは、**tmap** で作成されたファセット地図の主要な特徴を示している。
- ファセット変数を持たないシェイプは繰り返される (この場合、`world` の国々)
- 変数によって変化する `by` の引数 (この場合は `"year"`)
- `nrow` / `ncol` ファセットが配置される行と列の数を指定
あるいは、`tm_facets_grid()` 関数を使って 3 変数 `rows`、`columns`、`pages` からファセットを作る。
ファセット地図は、変化する空間的関係を示すのに有用なだけでなく、地図アニメーションの基礎としても有用である (Section \@ref(animated-maps) 参照)。
### 差し込み地図
\index{ちずさくせい@地図作成!さしこみちず@差し込み地図}
\index{tmap (package)!さしこみちず@差し込み地図}
差し込み地図とは、メイン地図の中や横に描画される小さな地図のことである。
コンテキストを提供したり (Figure \@ref(fig:insetmap1))、非連続な領域を接近させて比較を容易にしたり (Figure \@ref(fig:insetmap2))、様々な目的を果たすことが可能である。
また、より小さなエリアに焦点を当てたり、地図と同じエリアを別のトピックでカバーしたりすることもできる。
下の例では、New Zealand の南アルプスの中央部の地図を作成している。
差し込み地図は、メイン地図が New Zealand 全体に対してどのような位置にあるかを示すものである。
最初のステップは、関心のある領域を定義することである。これは、新しい空間オブジェクト `nz_region` を作成することで可能である。
```{r 08-mapping-16}
nz_region = st_bbox(c(xmin = 1340000, xmax = 1450000,
ymin = 5130000, ymax = 5210000),
crs = st_crs(nz_height)) |>
st_as_sfc()
```
ステップ 2 では、New Zealand の南アルプス周辺を示すベース地図を作成する。
ここは、最も重要なメッセージが述べられている場所である。
```{r 08-mapping-17}
nz_height_map = tm_shape(nz_elev, bbox = nz_region) +
tm_raster(col.scale = tm_scale_continuous(values = "YlGn"),
col.legend = tm_legend(position = c("left", "top"))) +
tm_shape(nz_height) + tm_symbols(shape = 2, col = "red", size = 1) +
tm_scalebar(position = c("left", "bottom"))
```
ステップ 3 で、差し込み地図を作成する。
差し込み地図はコンテキストを示し、関心のある領域を特定するのに役立つ。
差し込み地図には境界線を記載するなどして、メイン地図の位置を明確に示す必要がある。
```{r 08-mapping-18}
nz_map = tm_shape(nz) + tm_polygons() +
tm_shape(nz_height) + tm_symbols(shape = 2, col = "red", size = 0.1) +
tm_shape(nz_region) + tm_borders(lwd = 3) +
tm_layout(bg.color = "lightblue")
```
散布図など通常のグラフと地図の違いの一つとして、入力データは地図のアスペクト比を決定する。
よって、この場合、`nz_region` と `nz` という二つのデータセットのアスペクト比を計算する必要がある。
`norm_dim()` 関数は、オブジェクトの幅 (`"w"`) と高さ (`"h"`) を正規化する (画像デバイスは `"snpc"` の単位を理解する)。
```{r, message=FALSE}
library(grid)
norm_dim = function(obj){
bbox = st_bbox(obj)
width = bbox[["xmax"]] - bbox[["xmin"]]
height = bbox[["ymax"]] - bbox[["ymin"]]
w = width / max(width, height)
h = height / max(width, height)
return(unit(c(w, h), "snpc"))
}
main_dim = norm_dim(nz_region)
ins_dim = norm_dim(nz)
```
アスペクト比を得て、`viewport()` 関数を使い、二つの地図 (主地図と差し込み地図) の大きさと位置を指定する。
viewport とは、ある瞬間のグラフィック要素を描画するために使用するグラフィックデバイスの一部である。
私たちのメイン地図の viewport は、ちょうどそのアスペクト比の表現である。
```{r}
main_vp = viewport(width = main_dim[1], height = main_dim[2])
```
差し込み地図の表示領域は、大きさと位置を指定する必要がある。
ここでは、メイン地図の半分の大きさにするために幅と高さに 0.5 をかけ、メイン地図フレームの右下から 0.5 cm の位置に配置する。
```{r}
ins_vp = viewport(width = ins_dim[1] * 0.5, height = ins_dim[2] * 0.5,
x = unit(1, "npc") - unit(0.5, "cm"), y = unit(0.5, "cm"),
just = c("right", "bottom"))
```
最後に、新規にキャンバスを作り、メイン地図を表示し、メイン地図の領域内に差し込み地図を配置する。
```{r insetmap1, message=FALSE, fig.cap="差し込み地図で背景を説明 - New Zealand の南アルプスの中央部の位置。", fig.scap="Inset map providing a context.", fig.width=9}
grid.newpage()
print(nz_height_map, vp = main_vp)
pushViewport(main_vp)
print(nz_map, vp = ins_vp)
```
差し込み地図を保存するには、グラフィックデバイス (Section \@ref(visual-outputs) 参照) を使うか、`tmap_save()` 関数に引数 `insets_tm` および `insets_vp` を設定する方法がある。
また、差し込み地図は、非連続なエリアを 1 つの地図にするために使用する。
よく使われる例はアメリカ合衆国の地図で、アメリカ本土とハワイ、アラスカで構成されている。
このようなケースでは、個々の差し込みに最適なプロジェクションを見つけることが非常に重要である (詳しくは Chapter \@ref(reproj-geo-data) を参照)。
`tm_shape()` の引数 `crs` に US National Atlas Equal Area の EPSG コードを指定すれば、米国本土の地図に US National Atlas Equal Area を使用することができる。
```{r 08-mapping-19}
us_states_map = tm_shape(us_states, crs = "EPSG:9311") +
tm_polygons() +
tm_layout(frame = FALSE)
```
残りのオブジェクト `hawaii` と `alaska` は、すでに適切な投影を持っている。したがって、2 つの地図を別々に作成するだけでよい。
```{r 08-mapping-20}
hawaii_map = tm_shape(hawaii) +
tm_polygons() +
tm_title("Hawaii") +
tm_layout(frame = FALSE, bg.color = NA,
title.position = c("LEFT", "BOTTOM"))
alaska_map = tm_shape(alaska) +
tm_polygons() +
tm_title("Alaska") +
tm_layout(frame = FALSE, bg.color = NA)
```
これら 3 つの地図を組み合わせ、サイズを調整し配置することで、最終的な地図ができあがる。
```{r insetmap2, message=FALSE, fig.cap="アメリカ合衆国の地図。", warning=FALSE}
us_states_map
print(hawaii_map, vp = grid::viewport(0.35, 0.1, width = 0.2, height = 0.1))
print(alaska_map, vp = grid::viewport(0.15, 0.15, width = 0.3, height = 0.3))
```
上記で紹介したコードはコンパクトで、他の差し込み地図のベースとして使用することができる。ただし、Figure \@ref(fig:insetmap2) ではハワイとアラスカの位置とサイズがうまく表現されていないことがわかる。
より詳細なアプローチについては、**geocompkg** の [`us-map`](https://geocompx.github.io/geocompkg/articles/us-map.html) vignette を参照。
## 地図アニメーション {#animated-maps}
\index{ちずさくせい@地図作成!ちずあにめーしょん@地図アニメーション}
\index{tmap (package)!ちずあにめーしょん@地図アニメーション}
Section \@ref(faceted-maps) で紹介されているファセット地図は、変数の空間分布が (例えば時間経過とともに) どのように変化するかを示すことができるが、このアプローチには欠点がある。
ファセットは数が多いと小さくなる。
さらに、画面やページ上で各ファセットが物理的に分離しているため、ファセット間の微妙な差異を検出することが難しい。
地図アニメーションは、これらの問題を解決する。
デジタル版でしか表示できないが、より多くのコンテンツがオンラインに移行するにつれて、これは問題ではなくなってきている。
印刷された地図から地図アニメーション (またはインタラクティブ) バージョンを含むウェブページに読者をリンクすることで、地図を生き生きとさせることができる。
R でアニメーションを生成する方法はいくつかあり、**ggplot2** をベースにした **ganimate** のようなアニメーションパッケージもある (Section \@ref(other-mapping-packages) を参照)。
このセクションでは、**tmap** を使った地図アニメーションの作成に焦点を当てる。構文はこれまでのセクションでも使っており、アプローチの柔軟性がある。
Figure \@ref(fig:urban-animated) は、地図アニメーションの簡単な例である。
ファセットプロットとは異なり、複数の地図を一画面に押し込むことはなく、世界で最も人口の多い集積地の空間分布が時間とともにどのように進化していくかを見ることができる (アニメーション版は同書のウェブサイトを参照)。
```{r urban-animated, message=FALSE, fig.cap="1950年から2030年までの、国連による人口予測に基づく都市集積の上位30位を示した地図アニメーション。アニメーション版は、geocompr.robinlovelace.net で見ることができる。", fig.scap="Animated map showing the top 30 largest 'urban agglomerations'.", echo=FALSE, fig.height=3.3}
if (knitr::is_latex_output()){
knitr::include_graphics("images/urban-animated.png")
} else if (knitr::is_html_output()){
knitr::include_graphics("images/urban-animated.gif")
}
```
```{r 08-mapping-21, echo=FALSE, eval=FALSE}
source("https://github.com/geocompx/geocompr/raw/main/code/09-urban-animation.R")
```
Figure \@ref(fig:urban-animated) に示した地図アニメーションは、Section \@ref(faceted-maps) で示したファセット・地図を生成するのと同じ **tmap** 技術を使用して作成することができる。
ただし、`tm_facets_wrap()` の引数に関連して、2 つの違いがある。
- `nrow = 1, ncol = 1` として、一つの時間を一つのレイヤとしている
- `free.coords = FALSE` で、アニメーションのために地図の範囲を維持する
追加した引数を、次のコードチャンクで示そう。^[`tm_facets_pagewise()` を使うと、さらに簡潔になる。]
```{r 08-mapping-22}
urb_anim = tm_shape(world) + tm_polygons() +
tm_shape(urban_agglomerations) + tm_symbols(size = "population_millions") +
tm_facets_wrap(by = "year", nrow = 1, ncol = 1, free.coords = FALSE)
```
結果である `urb_anim` は、各年度の個別の地図のセットを表している。
最終的には、`tmap_animation()` でこれらを合成して、`.gif` ファイルとして保存する。
次のコマンドは、Figure \@ref(fig:urban-animated) に示されたアニメーションを作成する。ただし、いくつかの要素が欠けているので、演習で追加する。
```{r 08-mapping-23, eval=FALSE}
tmap_animation(urb_anim, filename = "urb_anim.gif", delay = 25)
```
地図アニメーションの威力を示すもう一つの例が、Figure \@ref(fig:animus) にある。
これは、アメリカにおける州の発達を示すもので、最初は東部で形成され、その後徐々に西部へ、最後は内陸部へと発展していった。
この地図を再現するためのコードは、本書の GitHub リポジトリのスクリプト `code/09-usboundaries.R` に記載されている。
```{r 08-mapping-24, echo=FALSE, eval=FALSE}
source("https://github.com/geocompx/geocompr/raw/main/code/09-usboundaries.R")
```
```{r animus, echo=FALSE, message=FALSE, fig.cap="米国における人口増加、州形成、境界線の変化を示す地図アニメーション (1790-2010年)。アニメーション版は r.geocompx.org でオンライン公開。", fig.scap="Animated map showing boundary changes in the United States."}
u_animus_html = "https://user-images.githubusercontent.com/1825120/38543030-5794b6f0-3c9b-11e8-9da9-10ec1f3ea726.gif"
u_animus_pdf = "images/animus.png"
if (knitr::is_latex_output()){
knitr::include_graphics(u_animus_pdf)
} else if (knitr::is_html_output()){
knitr::include_graphics(u_animus_html)
}
```
## インタラクティブ地図 {#interactive-maps}
\index{ちずさくせい@地図作成!いんたらくてぃぶちず@インタラクティブ地図}
\index{tmap (package)!いんたらくてぃぶちず@インタラクティブ地図}
静止画や地図アニメーションは、地理データセットを盛り上げることができるが、インタラクティブな地図は、それらを新しいレベルに引き上げることができる。
インタラクティブ性には様々な形態があるが、最も一般的で有用なのは、地理データセットのどの部分でもパンしたりズームしたりして、「ウェブ地図」の上に重ねてコンテキストを表示す機能である。
より高度なインタラクティブ性のレベルとしては、さまざまなフィーチャをクリックすると表示されるポップアップ、つまりインタラクティブラベルのようなものがある。
より高度なインタラクティブ機能としては、下記の **mapdeck** の例で示したように、地図を傾けたり回転させたりする機能や、ユーザーがパンやズームをすると自動的に更新される「動的にリンクした」サブプロット [@pezanowski_senseplace3_2018] を提供する機能などが挙げられる。
しかし、インタラクティブ性の最も重要なタイプは、インタラクティブまたは「スリッピー」なウェブ地図上での地理データの表示である。
2015年にリリースされた **leaflet** (leaflet JavaScript ライブラリを使用) パッケージは、R 内からインタラクティブな Web 地図の作成に革命をもたらし、多くのパッケージがこれらの基盤の上に新機能を追加し (例: **leaflet.extras2**)、Web 地図の作成を静的地図作成と同じくらいシンプルにしている (例: **mapview** や **tmap** など)。
ここでは、各アプローチを紹介した順と逆に説明する。
**tmap** (すでに学習済みの構文)、**mapview**\index{mapview (package)}、**mapdeck**\index{mapdeck (package)}そして最後に **leaflet** \index{leaflet (package)} (対話型地図の低レベル制御を提供) を使って、動く地図を作成する方法を探究する。
Section \@ref(static-maps) で述べた **tmap** は、同じコードを使って静的な地図とインタラクティブな地図を作ることができる。
`tmap_mode("view")` というコマンドでビューモードに切り替えることで、任意の時点でインタラクティブ表示に切り替えることができる。
以下のコードは、`tmap` オブジェクト `map_nz` に基づいて New Zealand のインタラクティブ地図を作成し、Section \@ref(map-obj) で作成し、Figure \@ref(fig:tmview) で図示している。
```{r 08-mapping-25, eval=FALSE}
tmap_mode("view")
map_nz
```
```{r tmview, message=FALSE, fig.cap="**tmap** のビューモードで作成されたNew Zealand のインタラクティブ地図。インタラクティブ版は r.geocompx.org からオンラインで入手可能。", fig.scap="Interactive map of New Zealand.", echo=FALSE}
if (knitr::is_latex_output()){
knitr::include_graphics("images/tmview-1.png")
} else if (knitr::is_html_output()){
# tmap_mode("view")
# m_tmview = map_nz
# tmap_save(m_tmview, "tmview-1.html")
# file.copy("tmview-1.html", "../geocompx.org/static/img/tmview-1.html")
knitr::include_url("https://geocompx.org/static/img/tmview-1.html")
}
```
インタラクティブモードが「オン」になったので、**tmap** で作成したすべての地図が起動する (インタラクティブな地図を作成する別の方法として、`tmap_leaflet` 機能がある)。
このインタラクティブモードの特筆すべき点は、以下のデモのように `tm_basemap()` (または `tmap_options()`) でベース地図を指定できることである (結果は表示していない)。
```{r 08-mapping-26, eval=FALSE}
map_nz + tm_basemap(server = "OpenTopoMap")
```
あまり知られていないが、**tmap** の表示モードは、ファセット・プロットにも対応している。
この場合、`tm_facets_wrap()` の引数 `sync` を使用すると、以下のコードで作成した Figure \@ref(fig:sync) のように、複数の地図を作成し、ズームとパンの設定を同期させることができる。
```{r 08-mapping-27, eval=FALSE}
world_coffee = left_join(world, coffee_data, by = "name_long")
facets = c("coffee_production_2016", "coffee_production_2017")
tm_shape(world_coffee) + tm_polygons(facets) +
tm_facets_wrap(nrow = 1, sync = TRUE)
```
```{r sync, message=FALSE, fig.cap="2016年と2017年の世界のコーヒー生産量を同期させたファセット化されたインタラクティブ地図で、tmapのビューモードの動作を実演。", fig.scap="Faceted interactive maps of global coffee production.", echo=FALSE}
knitr::include_graphics("images/interactive-facets.png")
```
同じ機能で **tmap** をプロットモードに戻す。
```{r 08-mapping-28}
tmap_mode("plot")
```
**tmap** を使いこなせない場合は、**mapview**\index{mapview (package)} を使ってインタラクティブ地図を作成するのが一番手っ取り早いだろう。
以下の 1 行コードは、さまざまな地理データ形式をインタラクティブに探索するための信頼できる方法である。
```{r 08-mapping-29, eval=FALSE}
mapview::mapview(nz)
```
```{r mapview, message=FALSE, fig.cap="**mapview** の動作イメージ図。", echo=FALSE}
knitr::include_graphics("images/mapview.png")
# knitr::include_graphics("https://user-images.githubusercontent.com/1825120/39979522-e8277398-573e-11e8-8c55-d72c6bcc58a4.png")
# mv = mapview::mapview(nz)
# mv@map
```
**mapview** は簡潔な構文でありながら、強力な機能を備えている。
デフォルトでは、マウスの位置情報、(ポップアップによる) 属性問い合わせ、スケールバー、レイヤへのズームボタンなどの標準的な GIS 機能が提供されている。
データセットを複数のレイヤに「バースト」する機能や、`+` の後に地理的オブジェクトの名前を付けて複数のレイヤを追加する機能など、高度な制御を提供する。
さらに、属性の自動的な色付けも可能である (引数 `zcol`)。
要するに、データドリブンの **leaflet** API\index{API} と考えることができる (**leaflet** については後述する)。
**mapview** は常に空間オブジェクト (`sf` と `SpatRaster`) を最初の引数として期待することから、パイプ式の末尾でうまく機能する。
次の例では、**sf** を使って直線とポリゴンを交差させ、**mapview** (Figure \@ref(fig:mapview2)) で可視化する場合を考えてみよう。
```{r 08-mapping-30, eval=FALSE}
library(mapview)
oberfranken = subset(franconia, district == "Oberfranken")
trails |>
st_transform(st_crs(oberfranken)) |>
st_intersection(oberfranken) |>
st_collection_extract("LINESTRING") |>
mapview(color = "red", lwd = 3, layer.name = "trails") +
mapview(franconia, zcol = "district") +
breweries
```
```{r mapview2, message=FALSE, fig.cap="sf ベースのパイプ式の末尾で **mapview** を使用。", echo=FALSE, warning=FALSE}
knitr::include_graphics("images/mapview-example.png")
# knitr::include_graphics("https://user-images.githubusercontent.com/1825120/39979271-5f515256-573d-11e8-9ede-e472ca007d73.png")
```
注意点としては、**mapview** のレイヤは `+` 演算子で追加する (**ggplot2** や **tmap** に似ている)。
デフォルトでは、**mapview** はユーザーフレンドリーで機能の多い leaflet JavaScript ライブラリを使い、地図を出力する。
しかし、他のレンダリングライブラリの方がパフォーマンスが良い (巨大なデータでもスムーズ)。
**mapview** は、レンダリングライブラリ (`"leafgl"` と `"mapdeck"`) を、`mapviewOptions()` で設定することができる。^[巨大なラスタデータの可視化には、`mapviewOptions(georaster = TRUE)` も試してみると良いだろう。]
**mapview** の詳細については、パッケージのウェブサイトを参照。 [r-spatial.github.io/mapview/](https://r-spatial.github.io/mapview/articles/) を参照。
R でインタラクティブな地図を作成する方法は他にもある。
例えば、**googleway**\index{googleway (package)} パッケージは、柔軟で拡張性の高いインタラクティブなマッピングインターフェースを提供する
(詳細は [`googleway-vignette`](https://cran.r-project.org/package=googleway/vignettes/googleway-vignette.html) 参照)。
同じ著者による別のアプローチとして、**[mapdeck](https://github.com/SymbolixAU/mapdeck)** があり、Uber の `Deck.gl` フレームワーク \index{mapdeck (package)} にアクセスできるようになっている。
WebGL を使用することで、大規模なデータセット (最大数百万点) をインタラクティブに可視化することができる。
本パッケージは、Mapbox [access tokens](https://docs.mapbox.com/help/getting-started/access-tokens/) を使用している。本パッケージを使用する前に、登録する必要がある。
```{block2 08-mapping-31, type='rmdnote'}
以下のブロックは、`MAPBOX=your_unique_key` という形式で R 環境にアクセストークンがあることを想定している。
これは、**usethis** パッケージの `edit_r_environ()` で追加することができる。
```
**mapdeck** のユニークな点は、Figure \@ref(fig:mapdeck) で図示するようにインタラクティブな 2.5 次元パースペクティブを提供できる点にある。
これによって、地図をパン、ズーム、回転することができる上に、地図から「押し出した」データを見ることができるのである。
Figure \@ref(fig:mapdeck) は、英国における交通事故を可視化したもので、棒の高さは地域ごとの死傷者数を表している。
```{r 08-mapping-32, engine='zsh', echo=FALSE, eval=FALSE}
https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv
curl -i https://git.io -F "url=https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv" \
-F "code=geocompr-mapdeck"
```
```{r 08-mapping-33, eval=FALSE}
library(mapdeck)
set_token(Sys.getenv("MAPBOX"))
crash_data = read.csv("https://git.io/geocompr-mapdeck")
crash_data = na.omit(crash_data)
ms = mapdeck_style("dark")
mapdeck(style = ms, pitch = 45, location = c(0, 52), zoom = 4) |>
add_grid(data = crash_data, lat = "lat", lon = "lng", cell_size = 1000,
elevation_scale = 50, colour_range = hcl.colors(6, "plasma"))
```
```{r mapdeck, echo=FALSE, fig.cap="**mapdeck** によって生成された、イギリス全土の道路交通事故死傷者数を表す地図。1 km のセルの高さは事故件数を表す。", fig.scap="Map generated by mapdeck."}
knitr::include_graphics("images/mapdeck-mini.png")
```
ブラウザでは、ズームやドラッグのほか、`Cmd` / `Ctrl` を押すと、地図を回転させたり傾けたりすることができる。
[`mapdeck` vignette](https://cran.r-project.org/package=mapdeck/vignettes/mapdeck.html) で示されているように、パイプ演算子で複数のレイヤを追加することができる。
**mapdeck** は `sf` オブジェクトもサポートしている。先のコードチャンクの `add_grid()` 関数呼び出しを `add_polygon(data = lnd, layer_id = "polygon_layer")` に置き換えて、インタラクティブな傾いた地図にロンドンを表すポリゴンを追加してみるとわかる。
```{r 08-mapping-35, eval=FALSE, echo=FALSE}
library(mapdeck)
set_token(Sys.getenv("MAPBOX"))
df = read.csv("https://git.io/geocompr-mapdeck")
ms = mapdeck_style('dark')
mapdeck(style = ms, pitch = 45, location = c(0, 52), zoom = 4) |>
add_polygon(data = lnd, layer_id = "polygon_layer")
```
最後に、**leaflet**\index{leaflet (package)} は R で最も成熟し、広く使われている対話型の地図作成パッケージである。
**leaflet** は、Leaflet JavaScript ライブラリへの比較的低レベルのインタフェースを提供し、その引数の多くは、オリジナルの JavaScript ライブラリのドキュメントを読めば理解できる ( [leafletjs.com](https://leafletjs.com/) を参照)。
Leaflet 地図は `leaflet()` で作成され、その結果は `leaflet` 地図オブジェクトとなり、他の **leaflet** 関数にパイプで渡すことができる。
これにより、Figure \@ref(fig:leaflet) を生成する以下のコードで示すように、複数の地図レイヤや制御設定をインタラクティブに追加することができる (詳しくは [rstudio.github.io/leaflet/](https://rstudio.github.io/leaflet/) を参照)。
```{r leaflet-code, echo=TRUE, eval=FALSE}
pal = colorNumeric("RdYlBu", domain = cycle_hire$nbikes)
leaflet(data = cycle_hire) |>
addProviderTiles(providers$CartoDB.Positron) |>
addCircles(col = ~pal(nbikes), opacity = 0.9) |>
addPolygons(data = lnd, fill = FALSE) |>
addLegend(pal = pal, values = ~nbikes) |>
setView(lng = -0.1, 51.5, zoom = 12) |>
addMiniMap()
```
```{r leaflet, message=FALSE, fig.cap="ロンドン市内の自転車レンタルポイントを紹介した **leaflet** パッケージの実例。インタラクティブ版は[オンライン](https://geocompr.github.io/img/leaflet.html)を参照。", fig.scap="The leaflet package in action.", echo=FALSE}
if (knitr::is_latex_output() || knitr::is_html_output()){
knitr::include_graphics("images/leaflet-1.png")
} else {
# pre-generated for https://github.com/ropensci/stplanr/issues/385
# pal = colorNumeric("RdYlBu", domain = cycle_hire$nbikes)
# m = leaflet(data = cycle_hire) |>
# addProviderTiles(providers$CartoDB.Positron) |>
# addCircles(col = ~pal(nbikes), opacity = 0.9) |>
# addPolygons(data = lnd, fill = FALSE) |>
# addLegend(pal = pal, values = ~nbikes) |>
# setView(lng = -0.1, 51.5, zoom = 12) |>
# addMiniMap()
# htmlwidgets::saveWidget(m, "leaflet.html")
# browseURL("leaflet.html")
# file.rename("leaflet.html", "~/geocompr/geocompr.github.io/static/img/leaflet.html")
# abort old way of including - mixed content issues
knitr::include_url("https://geocompr.github.io/img/leaflet.html")
}
```
## 地図アプリ {#mapping-applications}
\index{ちずさくせい@地図作成!ちずあぷり@地図アプリ}
Section \@ref(interactive-maps) で示したインタラクティブなウェブ地図は、遠くまで行くことができる。
表示するレイヤを慎重に選択し、ベース地図とポップアップを使用することで、ジオコンピュテーションを含む多くのプロジェクトの主な結果を伝えることができる。
しかし、ウェブ地図というアプローチでインタラクティブ性を追求することには限界がある。
- 地図はパン、ズーム、クリックといったインタラクティブな動きをするが、コードは静的で、ユーザーインターフェースは固定されている。
- ウェブ地図では、すべての地図コンテンツが一般的に静的であるため、ウェブ地図は大規模なデータセットを容易に扱うことができない。
- 変数間の関係を示すグラフや「ダッシュボード」のようなインタラクティブなレイヤを追加することは、ウェブ地図のアプローチでは困難である
これらの制約を克服するためには、静的なウェブ地図にとどまらず、地理空間系のフレームワークや地図サーバーを利用することが必要である。
この分野の製品には、[GeoDjango](https://docs.djangoproject.com/en/2.0/ref/contrib/gis/)\index{GeoDjango} (Django Web フレームワークを拡張したもので、[Python](https://github.com/django/django)\index{Python})、[MapServer](https://github.com/mapserver/mapserver)\index{MapServer} (Web アプリケーション開発用のフレームワークで、大部分が C と C++\index{C++} で書かれている) や [GeoServer](https://github.com/geoserver/geoserver) (Java\index{Java} で書かれた成熟した強力な地図サーバ) が含まれる。
これらはそれぞれ拡張性があり、毎日何千人もの人々に地図を提供することが可能である (あなたの地図に対する人々の関心が十分に高ければの話であるが)。
欠点としては、このようなサーバーサイドのソリューションは、セットアップと保守に多くの熟練した開発者の時間を必要とし、地理空間データベース管理者 ([DBA](https://wiki.gis.com/wiki/index.php/Database_administrator)) などの役割を持つ人々を巻き込んでしまうこともよくある。
R の場合は幸運なことに、**shiny**\index{shiny (package)} を使って、ウェブ地図アプリケーションを素早く作成できるようになった。
オープンソース本 [Mastering Shiny](https://mastering-shiny.org/) で説明されているように、 **shiny** は、R コードをインタラクティブなウェブアプリに変換する R パッケージでありフレームワークである [@wickham_mastering_2021]。
<!-- `tmap::renderTmap()` と --> [`leaflet::renderLeaflet()`](https://rstudio.github.io/leaflet/shiny.html) を使うことで、shiny アプリにインタラクティブ地図を追加することができる。
このセクションでは、ウェブ地図の観点から **shiny** の基本を学び、100 行未満のコードで全画面の地図アプリケーションを完成させることができる。
**shiny** の仕組みは、[shiny.posit.co](https://shiny.posit.co/) に詳しく書かれているが、「フロントエンド」 (ユーザーが見る部分) と「バックエンド」コードという 2 つの構成要素がある。
**shiny** アプリでは、これらの要素は通常、`app フォルダ`内にある `app.R` という R スクリプト内の `ui` と `server` というオブジェクトで作成される。
これにより、ウェブの地図アプリケーションを 1 つのファイルで表現することも可能で、例えば、本書の GitHub リポジトリにある [`CycleHireApp/app.R`](https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) は単一のファイルで表現している。
```{block2 shiny, type = 'rmdnote'}
**shiny** アプリでは、これらは `ui.R` (ユーザーインターフェースの略) と `server.R` ファイルに分けられることが多い。この命名規則は、一般向けの Web サイトで shiny アプリを提供するサーバーサイド Linux アプリケーション、`shiny-server` で使用されている。
`shiny-server` は、'app フォルダ' 内にある `app.R` という単一ファイルで定義されるアプリを提供することもある。
詳細は https://github.com/rstudio/shiny-server 。
```
大規模なアプリを検討する前に、「lifeApp」と名付けた最小限の例を実際に見てみよう。^[
ここでいう「アプリ」とは「Web アプリケーション」のことであり、一般的な意味であるスマートフォンのアプリと混同しないようにしよう。
]
以下のコードでは、`shinyApp()` というコマンドで、lifeApp を定義して起動する。これは、平均寿命のレベルが低い国を表示させることができるインタラクティブなスライダーである (Figure \@ref(fig:lifeApp) を参照)。
```{r 08-mapping-37, eval=FALSE}
library(shiny) # shiny
library(leaflet) # renderLeaflet 関数
library(spData) # world データを読み込む
ui = fluidPage(
sliderInput(inputId = "life", "Life expectancy", 49, 84, value = 80),
leafletOutput(outputId = "map")
)
server = function(input, output) {
output$map = renderLeaflet({
leaflet() |>
# addProviderTiles("OpenStreetMap.BlackAndWhite") |>
addPolygons(data = world[world$lifeExp < input$life, ])})
}
shinyApp(ui, server)
```
```{r lifeApp, echo=FALSE, message=FALSE, fig.cap="shiny で作成したWeb地図アプリケーションの最小限の例を示す画面。", fig.scap="Minimal example of a web mapping application."}
# knitr::include_graphics("https://user-images.githubusercontent.com/1825120/39690606-8f9400c8-51d2-11e8-84d7-f4a66a477d2a.png")
knitr::include_graphics("images/shiny-app.png")
```