メモ:sfにdplyrの関数を使いたいときはlibrary()とかでsf用のメソッドを読み込んでおく必要がある

国土数値情報のGISデータをsfで処理するときのメモ。

単純に読み込むだけならこう:

tmp_zip <- tempfile(fileext = ".zip")
tmp_zipdir <- tempfile()

curl::curl_download('http://nlftp.mlit.go.jp/ksj/gml/data/P12/P12-14/P12-14_05_GML.zip', destfile = tmp_zip)
unzip(tmp_zip, exdir = tmp_zipdir)

layers <- sf::st_layers(tmp_zipdir)

d <- sf::read_sf(tmp_zipdir, layer = layers$name[1])

なんですが、Windowsだとこれは以下のように文字化けして見えます。実際には文字化けではなくて、Shift_JISの文字列にUTF-8という文字コードの指定が誤ってついているだけで、中に入っているデータ(バイト列)は正しいです。

d
#> Simple feature collection with 621 features and 7 fields
#> geometry type:  MULTIPOINT
#> dimension:      XY
#> bbox:           xmin: 139.7046 ymin: 38.95984 xmax: 140.8945 ymax: 40.46308
#> epsg (SRID):    NA
#> proj4string:    +proj=longlat +ellps=GRS80 +no_defs
#> # A tibble: 621 x 8
#>    P12_001                                                                                        P12_002 P12_003
#>      <int>                                                                                          <chr>   <chr>
#>  1   10001                                   "\u008fH\u0093c\u008a<U+0193>\u0095\u0082<U+0702>\u0082\xe8"      05
#>  2   10002                          "\u008fH\u0093c\u008es\u0091\xe5\u0090X\u008eR\u008c\xf6\u0089\u0080"      05
#>  3   10003                       "\u008fH\u0093c\u008es\u0089\u0516\u060a<U+03CC>\xf5\u0094_\u0089\u0080"      05
#>  4   10004   "\u008fH\u0093c\u008es\u0095l\u0093c\u0090X\u0097ё\u008d\u008d\u0087\u008c\xf6\u0089\u0080"      05
#>  5   10005                                 "\u008e<U+00A9>\u0091R\u0089<U+020A>w\u008aw\u008fK\u008a\xd9"      05
#>  6   10006                        "\u008fH\u0093c\u008es\u0095<U+00B6>\u0089<U+00BB>\u0089\xef\u008a\xd9"      05
#>  7   10007                   "\u008fH\u0093c\u008c<U+00A7>\u008e\u0099\u0093<U+00B6>\u0089\xef\u008a\xd9"      05
#>  8   10008  "\u008c\xfc\u0095l\u0089^\u0093<U+00AE>\u008c\xf6\u0089\u0080\u0096<U+C2C5>\u008dL\u008f\xea"      05
#>  9   10009 "\u008fH\u0093c\u008c<U+00A7>\u0097<U+00A7>\u0091\u008d\u008d\u0087\u0083v\u0081[\u0083\u008b"      05
#> 10   10010                              "\u008e\xed\u0091\xf2\u0083\u008a\u0083\u0093\u0083S\u0089\u0080"      05
#> # ... with 611 more rows, and 5 more variables: P12_004 <chr>, P12_005 <chr>, P12_006 <chr>, P12_007 <int>, geometry <S3:
#> #   sfc_MULTIPOINT>

これを一気に変換するには、dplyr;;mutate_if()とかを使います。しかし…

dplyr::mutate_if(d, is.character, iconv, from = "CP932")
#> # A tibble: 621 x 8
#>    P12_001                P12_002 P12_003 P12_004  P12_005                 P12_006 P12_007             geometry
#>      <int>                  <chr>   <chr>   <chr>    <chr>                   <chr>   <int> <S3: sfc_MULTIPOINT>
#>  1   10001         秋田竿燈まつり      05   05201 年中行事              秋田市旭北       1 <S3: sfc_MULTIPOINT>
#>  2   10002       秋田市大森山公園      05   05201       ‐              秋田市浜田       4 <S3: sfc_MULTIPOINT>
#>  3   10003     秋田市花木観光農園      05   05201       ‐ 秋田市雄和向野字桧谷地1       6 <S3: sfc_MULTIPOINT>
#>  4   10004 秋田市浜田森林総合公園      05   05201       ‐              秋田市浜田       1 <S3: sfc_MULTIPOINT>
#>  5   10005         自然科学学習館      05   05201       ‐       秋田市東通仲町4-1       6 <S3: sfc_MULTIPOINT>
#>  6   10006         秋田市文化会館      05   05201       ‐         秋田市山王7-3-1       6 <S3: sfc_MULTIPOINT>
#>  7   10007         秋田県児童会館      05   05201       ‐     秋田市山王中島町1-2       6 <S3: sfc_MULTIPOINT>
#>  8   10008   向浜運動公園野球広場      05   05201       ‐ 秋田市新屋町字砂奴寄4-6       4 <S3: sfc_MULTIPOINT>
#>  9   10009     秋田県立総合プール      05   05201       ‐ 秋田市新屋町字砂奴寄4-6       4 <S3: sfc_MULTIPOINT>
#> 10   10010           種沢リンゴ園      05   05201       ‐ 秋田市雄和種沢熊野堂272       1 <S3: sfc_MULTIPOINT>
#> # ... with 611 more rows

なんと、sfオブジェクトではなくなってしまいます。

これはなぜかというと、sf用のmutate()のメソッドが読み込まれていないからです。以下のようにいちどsfパッケージ自体を読み込むとmutate.sf()が使われるようになり、sfオブジェクトが返ってきます。

library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3

dplyr::mutate_if(d, is.character, iconv, from = "CP932")
#> Simple feature collection with 621 features and 7 fields
#> geometry type:  MULTIPOINT
#> dimension:      XY
#> bbox:           xmin: 139.7046 ymin: 38.95984 xmax: 140.8945 ymax: 40.46308
#> epsg (SRID):    NA
#> proj4string:    +proj=longlat +ellps=GRS80 +no_defs
#> # A tibble: 621 x 8
#>    P12_001                P12_002 P12_003 P12_004  P12_005                 P12_006 P12_007          geometry
#>      <int>                  <chr>   <chr>   <chr>    <chr>                   <chr>   <int>  <simple_feature>
#>  1   10001         秋田竿燈まつり      05   05201 年中行事              秋田市旭北       1 <MULTIPOINT (...>
#>  2   10002       秋田市大森山公園      05   05201       ‐              秋田市浜田       4 <MULTIPOINT (...>
#>  3   10003     秋田市花木観光農園      05   05201       ‐ 秋田市雄和向野字桧谷地1       6 <MULTIPOINT (...>
#>  4   10004 秋田市浜田森林総合公園      05   05201       ‐              秋田市浜田       1 <MULTIPOINT (...>
#>  5   10005         自然科学学習館      05   05201       ‐       秋田市東通仲町4-1       6 <MULTIPOINT (...>
#>  6   10006         秋田市文化会館      05   05201       ‐         秋田市山王7-3-1       6 <MULTIPOINT (...>
#>  7   10007         秋田県児童会館      05   05201       ‐     秋田市山王中島町1-2       6 <MULTIPOINT (...>
#>  8   10008   向浜運動公園野球広場      05   05201       ‐ 秋田市新屋町字砂奴寄4-6       4 <MULTIPOINT (...>
#>  9   10009     秋田県立総合プール      05   05201       ‐ 秋田市新屋町字砂奴寄4-6       4 <MULTIPOINT (...>
#> 10   10010           種沢リンゴ園      05   05201       ‐ 秋田市雄和種沢熊野堂272       1 <MULTIPOINT (...>
#> # ... with 611 more rows

パッケージでは@importFromしておく必要があります。


追記(2017/08/29):

パッケージの名前空間@importFromするだけでは不十分で、ユーザから見える場所、つまりグローバル環境に@exportしておかないとだめでした。これはdplyr::mutate()のメソッドディスパッチが起こるのはkokudosuuchiの中ではなくdplyrの中だからです。具体的にはこんな感じ:

#' @importFrom sf mutate.sf
#' @export
sf::mutate.sf

さらに追記(2017/08/29):

これは、データがShift_JISだとわかっているなら、読み込むときに文字コードを指定すれば済む話ではあります。

sf::read_sf(tmp_zipdir, layer = layers$name[1], options = "ENCODING=CP932")