前回の記事で、data引数にSpatialPolygonsDataFrame(以下、SPDF)を渡したときにどうなるかわからなかったのでメモ。
SpatialPolygonsDataFrameとは
そもそもこいつは何者かというと、spパッケージの地図データ用のdata.frameです。
?"SpatialPolygonsDataFrame-class"を見ると、以下のようなスロットを持っています。
data: Object of class "data.frame"; attribute table polygons: Object of class "list"; see SpatialPolygons-class plotOrder: Object of class "integer"; see SpatialPolygons-class bbox: Object of class "matrix"; see Spatial-class proj4string: Object of class "CRS"; see CRS-class
dataは各ポリゴンに関するデータ、polygonsはポリゴンのリストです。あとは細かそうなので省略。
SPDFはdata.frame同様に扱えます。[やsplitでサブセットを作ると、ポリゴンとそれに紐づくデータが適切にサブセットされます。
l[1:10, ] %>% is #> [1] "SpatialPolygonsDataFrame" "SpatialPolygons" "Spatial"
SPDF全体のサブセットではなく、データ(dataスロット)だけがほしいときは$を使います。
l$nam %>% is #> [1] "factor" "integer" "oldClass" "numeric" "vector" "data.frameRowLabels"
dataとかpolygonsとか、S4オブジェクトの要素(スロット)に直接アクセスしたいときは@を使います。
l@polygons %>% is #> [1] "list" "vector" l@polygons[[1]] %>% is #> [1] "Polygons"
leafletによる扱い
SPDFをdata引数に指定した場合は以下のような処理になるみたいです。
ポリゴン部分の扱い
derivePolygons()polygonData()polygonData.SpatialPolygonsDataFrame()でsp::polygons()を適用してSpatialPolygonsクラスに変換polygonData.SpatialPolygons()polygons2coords()plural2coords()
あとは追えないのでパス。とりあえずむりやりやってみると、以下のようになります。
leaflet:::derivePolygons(l, NULL, NULL, TRUE, TRUE) %>% .[[1]] %>% str(max.level = 2) #> List of 1 #> $ :List of 2 #> ..$ lng: num [1:255] 141 141 141 141 141 ... #> ..$ lat: num [1:255] 43.2 43.2 43.1 43.1 43.1 ...
ということで、lng, latというベクトルを持つリストのリストができています。addPolygons()のドキュメントを見ると、lngのヘルプに以下のような記載があります。
(if not explicitly provided), it will be automatically inferred from data by looking for a column named lng, long, or longitude (case-insensitively)
つまり、lngとlatを明示的に指定したりしなくても、ここで変換したポリゴンのdata.frameの緯度経度を勝手に使ってくれます。
データ部分の扱い
データ部分は~XXXという形式で列名を参照することができます。これはどういうことかというと、
color_generator <- colorNumeric("Oranges", domain = c(0, 1000)) leaflet() %>% addTiles() %>% addPolygons(data = poly, color = color_generator(poly$x))
と、poly$xという形で直接値を渡す部分を、
leaflet() %>% addTiles() %>% addPolygons(data = poly, color = ~color_generator(x))
のように書けるということです。(color_generator(~x)だとエラーになるので注意)
こっちのほうがかっこよさそうなので、前回のを書き直してみたいと思います。
SpatialPolygonsをSpatialPolygonsDataFrameに
rgeos::gUnaryUnion()した結果は@dataがないSpatialPolygonsというクラスのオブジェクトです。これにデータを紐づけてSPDFをつくります。
SPDFは、data.frameをrownameでポリゴンのIDと紐づけます。なので、rownameのあるdata.frameを作る必要があります。
# d: data.frame # l_union: SpatialPolygons orig_ids <- sapply(l_union@polygons, function(x) x@ID) target_ids <- sapply(str_split(str_to_lower(orig_ids), " "), `[`, 1) id_crsp <- data.frame(orig_id = orig_ids, target_id = target_ids, stringsAsFactors = FALSE) id_crsp[13,"target_id"] <- "hokkaido" d_ordered <- right_join(id_crsp, d, by = c( "target_id" = "pref_alpha" )) rownames(d_ordered) <- id_crsp$orig_id
これをSpatialPolygonsDataFrame()に渡せばおわりです。
s <- SpatialPolygonsDataFrame(l_union, as.data.frame(d_ordered))
描く
これを~を使って書くと、以下のような書き方になります。
color_generator <- colorNumeric("Oranges", domain = range(values)) leaflet(s) %>% addTiles() %>% addPolygons(color = ~color_generator(チョコレート), fillOpacity = 1, stroke = FALSE)
(結果は前回と同じなので省略)
感想
一度SPDFを作ってからやるとデータとポリゴンが紐づけられるので間違いがなくて安心ですが、ちょっと手間です。rownameでマッチさせるというのがけっこうトリッキーでつらかったです。