geom_sf()
と戯れていて、ちょっと点線でも引いてみるか、と思ってやってみると、なにこれ??という感じの汚い線の図になりました。
library(ggplot2) library(sf) #> Linking to GEOS 3.6.1, GDAL 2.2.3, proj.4 4.9.3 nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE) ggplot(nc) + geom_sf( colour = "black", fill = "lightgreen", linetype = "dotted", size = 1.5 )
さすがにもうちょっとなんとかなるのでは?と思って光の速さでRStudio Communityに相談しつついろいろ調べたときのメモ。
ポリゴンの面と線は別々に描く
nc
はポリゴンなので、面と線は1つのレイヤーで描くことができます。
なんですけど、描画は「面を全地物に対して描いてから線」という順番ではなく、「面を描いて線」というのを地物ごとにやっていく感じになるみたいです。
なので、面と線でレイヤーを分けるとある程度きれいに描画されます。
library(ggplot2) library(patchwork) nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE) p1 <- ggplot(nc) + geom_sf(linetype = "dotted", size = 1.5) + theme_minimal() p2 <- ggplot(nc) + # 面だけを描くので線は透明に geom_sf(colour = "transparent") + # 線だけを描くので面は透明に geom_sf(fill = "transparent", linetype = "dotted", size = 1.5) + theme_minimal() p1 / p2
ちゃんと境界線をつくる
ポリゴン間にある境界線は、当然ポリゴンの数だけ重ね描きされるので、点線だと点がずれて描かれることになります。 なので、ポリゴンの輪郭で満足せず、ちゃんと境界線をつくってからプロットする必要があるっぽい。
library(sf) # わかりやすいように、地物を2つだけ取り出し、色分けのためにIDを割り振る nc_12 <- tibble::rownames_to_column(nc[1:2,], "id") nc_12_merged <- nc_12 %>% # 境界線を取り出す st_boundary() %>% # 1つの地物にまとめる(このときにsfからsfcになる) st_union() %>% # 重なっている線をマージする st_line_merge() %>% # ggplot2でプロットするためにsfcをsfにする st_sf() p1 <- ggplot(nc_12) + geom_sf(aes(colour = id), fill = "transparent", linetype = "dotted", size = 4) + theme_minimal() p2 <- ggplot(nc_12_merged) + geom_sf(colour = "black", fill = "transparent", linetype = "dotted", size = 4) + theme_minimal() p1 / p2
そもそも線の幅と図形の細かさに比べてドットの間隔が広すぎる
dotted
というのは線と空白の長さの比が1:3になるような線なんですが(けっこう深い部分に定義されている)、もうちょっと狭い間隔じゃないときれいにならないみたいです。というのをRStudio Communityの質問についた回答で知りました。
線種の指定の仕方の詳細は省きますが(詳しくは以下を参照)、1:1の間隔にしたい場合は"11"
を指定します。
まとめ
これをふまえたプロットと初めのプロットを比較すると、こんな感じになるはずです。
p1 <- ggplot(nc) + geom_sf( colour = "black", fill = "lightgreen", linetype = "dotted", size = 1.5 ) + theme_minimal() nc_merged <- nc %>% st_boundary() %>% st_union() %>% st_line_merge() %>% st_sf() p2 <- ggplot() + geom_sf(data = nc, colour = "transparent", fill = "lightgreen") + geom_sf(data = nc_merged, colour = "black", linetype = "11", size = 1.5) + theme_minimal() p1 / p2
うーん、やっぱそもそも線が太すぎるのかも...。まあ初めよりはだいぶましになったということで。
追記(2018/06/06)
st_boundary()
の部分はst_cast("MULTILINESTRING")
でもいいらしい。なるほど!
@yutannihilation yutani式でいいじゃんってなりましたが、st_cast("MULTILINESTRING")する方法も思いつきました。どうでしょうか。線と点の間隔は広くしちゃうとダメそうだね。勉強になります。 pic.twitter.com/kvIWU5ybA1
— Uryu Shinya (@u_ribo) 2018年6月5日