2. Reading, Writing and Converting Simple Features

Edzer Pebesma

sf vignette のより良いバージョンは、以下をご覧ください。 https://r-spatial.github.io/sf/articles/

この vignette は、ファイルやデータベースからシンプルフィーチャをRで読み込む方法と、他の形式(テキスト、sp)に変換する方法について説明しています。

GDAL を通した読み書き

Geospatial Data Abstraction Library (GDAL) は、空間データのための スイスアーミーナイフです。実質的にあらゆるファイルフォーマットやデータベースから、 ベクトルやラスタデータを読み書きすることができます。パッケージ sf では、関数 st_readst_write によって GDAL を使用して読み書きが行われます。

GDAL が使用するデータモデルには以下のようなものがあります。

これは複雑に聞こえるかもしれませんが、200以上のデータフォーマットにマッピングするために必要なことなのです。パッケージ sf は、可能な限りこれを単純化しようと努めていますが (例えば、1つのファイルが1つのレイヤを含むなど)、この vignette では、オプションについて説明しようと思います。

st_read を使う

例として、sf パッケージに同梱されている North Carolina counties SIDS dataset を読み込んでみます。

library(sf)
## Linking to GEOS 3.10.3, GDAL 3.5.1, PROJ 7.2.1; sf_use_s2() is TRUE
fname <- system.file("shape/nc.shp", package="sf")
fname
## [1] "/Users/baba/Library/R/x86_64/4.2/library/sf/shape/nc.shp"
nc <- st_read(fname)
## Reading layer `nc' from data source 
##   `/Users/baba/Library/R/x86_64/4.2/library/sf/shape/nc.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 100 features and 14 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
## Geodetic CRS:  NAD27

一般的には、fname にパス付きのファイル名を使うか、setwd() で R の作業ディレクトリを設定してから、パスなしのファイル名を使うでしょう。

ここでは、1つの引数でデータソースとレイヤの両方を検索していることがわかります。これは、データソースに含まれるレイヤが1つの場合に機能します。レイヤの数がゼロの場合(例えば、テーブルのないデータベース)、エラーメッセージが表示されます。レイヤが複数ある場合は、最初のレイヤが返されますが、メッセージと警告が表示されます。

> st_read("PG:dbname=postgis")
Multiple layers are present in data source PG:dbname=postgis, reading layer `meuse'.
Use `st_layers' to list all layer names and their type in a data source.
Set the `layer' argument in `st_read' to read a particular layer.
Reading layer `meuse' from data source `PG:dbname=postgis' using driver `PostgreSQL'
Simple feature collection with 155 features and 12 fields
geometry type:  POINT
dimension:      XY
bbox:           xmin: 178605 ymin: 329714 xmax: 181390 ymax: 333611
epsg (SRID):    28992
proj4string:    +proj=sterea +lat_0=52.15616055555555 ...
Warning message:
In eval(substitute(expr), envir, enclos) :
  automatically selected the first layer in a data source containing more than one.

このメッセージは st_layers コマンドを指しており、データソースのドライバとレイヤをリストアップしています。

> st_layers("PG:dbname=postgis")
Driver: PostgreSQL 
Available layers:
  layer_name geometry_type features fields
1      meuse         Point      155     12
2   meuse_sf         Point      155     12
3       sids Multi Polygon      100     14
4  meuse_tbl         Point      155     13
5 meuse_tbl2         Point      155     13
> 

特定のレイヤを読み取ることができるようになります。

st_read("PG:dbname=postgis", "sids")

st_layers には、フィーチャの数が足りない場合に備えて、フィーチャの数をカウントするオプションがあります。GDAL では、1 つのフィーチャレイヤに対して複数のジオメトリカラムを指定することができます。

レイヤがジオメトリのみを含み、属性 (フィールド) を含まない場合でも、st_read はジオメトリカラムのみを含む sf オブジェクトを返します。

GDAL はデータソースのドライバ(ファイルフォーマット)を順番に試して、自動的に検出していることがわかります。

st_read は、表形式のデータを data.frame に読み込むのと同じように、ベースとなる R の規則に従っています。つまり、文字データはデフォルトで factor として読み込まれる。 文字データを文字ベクトルとして読み込むことにこだわる人のために、 stringsAsFactors という引数を FALSE に設定することができます。

st_read(fname, stringsAsFactors = FALSE)

あるいは、ユーザーがグローバルオプション stringsAsFactors を設定することで、同じ効果を得ることができます。

options(stringsAsFactors = FALSE)
st_read(fname)
## Reading layer `nc' from data source 
##   `/Users/baba/Library/R/x86_64/4.2/library/sf/shape/nc.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 100 features and 14 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
## Geodetic CRS:  NAD27

st_write の使用方法

シンプルフィーチャオブジェクトをファイルに書き込むには、少なくとも2つの引数、オブジェクトとファイル名が必要です。

st_write(nc, "nc1.shp")

ファイル名はデータソース名として扱われます。レイヤ名のデフォルトは、データソース名のベースネーム(パスを含まないファイル名)です。このため、st_writeはドライバを推測する必要があります。上記のコマンドは、例えば、次のように等価です。

st_write(nc, dsn = "nc1.shp", layer = "nc.shp", driver = "ESRI Shapefile")
## Writing layer `nc' to data source `nc1.shp' using driver `ESRI Shapefile'
## Writing 100 features with 14 fields and geometry type Multi Polygon.

ドライバーの推測の仕組みについては、次章で説明します。

出力のドライバを推測する

出力ドライバは、データソースの拡張子 (.shp: ESRI Shapefile) やプレフィックス (PG:: PostgreSQL) から推測されます。拡張子とそれに対応するドライバ (短いドライバ名) の一覧は以下の通りです。

extension driver short name
bna BNA
csv CSV
e00 AVCE00
gdb FileGDB
geojson GeoJSON
gml GML
gmt GMT
gpkg GPKG
gps GPSBabel
gtm GPSTrackMaker
gxt Geoconcept
jml JML
map WAsP
mdb Geomedia
nc netCDF
ods ODS
osm OSM
pbf OSM
shp ESRI Shapefile
sqlite SQLite
vdv VDV
xls xls
xlsx XLSX

The list with prefixes is:

prefix driver short name
couchdb: CouchDB
DB2ODBC: DB2ODBC
DODS: DODS
GFT: GFT
MSSQL: MSSQLSpatial
MySQL: MySQL
OCI: OCI
ODBC: ODBC
PG: PostgreSQL
SDE: SDE

データセットとレイヤの読み込み、作成のオプション

例えば、データベース内に既にテーブルが存在する場合、ドライバはどのような処理を行うか、テーブルにレコードを追加するか、上書きするかなど、様々なGDALドライバは読み書きの処理に影響するオプションを持っています。

st_write(st_as_sf(meuse), "PG:dbname=postgis", "meuse", 
    layer_options = "OVERWRITE=true")

テーブルが存在し、オプションが指定されていない場合、ドライバはエラーを出します。ドライバ固有のオプションは、gdal のドライバマニュアルに記載されています。 オプションは options に複数の文字列で指定することができます。

st_read では options のみですが、 st_write では dataset_optionslayer_options を区別する必要があり、前者はデータセットのオープンに関連し、後者はデータセット内にレイヤを作成することに関連します。

空間データベースの直接の読み書き

パッケージ sfDBI インターフェイスを使用して、空間データベースからの読み込みと書き込みをサポートします。これまでのところ、主に PostGIS を使ってテストを行っています。他のデータベースでも動作するかもしれませんが、もっと作業が必要かもしれません。読み込みの例としては

library(RPostgreSQL) 
conn = dbConnect(PostgreSQL(), dbname = "postgis") 
meuse = st_read(conn, "meuse")
meuse_1_3 = st_read(conn, query = "select * from meuse limit 3;") 
dbDisconnect(conn) 

ここでは、2番目の例でクエリが与えられていることがわかります。このクエリには空間述語が含まれており、R で巨大な空間データセットを完全にメモリに読み込むことなく作業する方法となることがあります。 同様に、以下のようにテーブルに書き込みことができます。

conn = dbConnect(PostgreSQL(), dbname = "postgis")
st_write(conn, meuse, drop = TRUE)
dbDisconnect(conn)

ここでは、デフォルトのテーブル(レイヤ)名は、オブジェクト名(meuse)から取得されます。論理引数 binary は、ジオメトリを書き込む際に well-known binary と well-known text のどちらを使用するかを決定します (well-known binary の方が高速でロスレスです)。

他のフォーマットへの変換 WKT、WKB、sp

よく知られたテキスト (wkt) との変換

シンプルフィーチャが表示されているのは、通常、Well Known Text (wkt) です。

st_point(c(0,1))
## POINT (0 1)
st_linestring(matrix(0:9,ncol=2,byrow=TRUE))
## LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)

これらの Well Known Text 文字列は、st_as_text を使用して明示的に作成することができます。

x = st_linestring(matrix(0:9,ncol=2,byrow=TRUE))
str = st_as_text(x)
x
## LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)

WKT からは st_as_sfc を使って戻すことができます。

st_as_sfc(str)
## Geometry set for 1 feature 
## Geometry type: LINESTRING
## Dimension:     XY
## Bounding box:  xmin: 0 ymin: 1 xmax: 8 ymax: 9
## CRS:           NA
## LINESTRING (0 1, 2 3, 4 5, 6 7, 8 9)

well-known binary への変換と well-known binary からの変換

Well-known binary は st_as_binary によって、シンプルフィーチャから生成されます。

x = st_linestring(matrix(0:9,ncol=2,byrow=TRUE))
(x = st_as_binary(x))
##  [1] 01 02 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0 3f
## [26] 00 00 00 00 00 00 00 40 00 00 00 00 00 00 08 40 00 00 00 00 00 00 10 40 00
## [51] 00 00 00 00 00 14 40 00 00 00 00 00 00 18 40 00 00 00 00 00 00 1c 40 00 00
## [76] 00 00 00 00 20 40 00 00 00 00 00 00 22 40
class(x)
## [1] "raw"

st_as_binary が返すオブジェクトはクラス WKB で、生のベクトルを含むリストか、単一の生のベクトルのどちらかです。これらは rawToHex を用いて、16進数の文字ベクトルに変換することができます。

rawToHex(x)
## [1] "0102000000050000000000000000000000000000000000f03f000000000000004000000000000008400000000000001040000000000000144000000000000018400000000000001c4000000000000020400000000000002240"

sf に戻すには、 st_as_sfc を使用します。

x = st_as_binary(st_sfc(st_point(0:1), st_point(5:6)))
st_as_sfc(x)
## Geometry set for 2 features 
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 0 ymin: 1 xmax: 5 ymax: 6
## CRS:           NA
## POINT (0 1)
## POINT (5 6)

sp への変換と sp からの変換

パッケージ sp が保持する空間オブジェクトは、 st_as_sfst_as_sfc によってそれぞれシンプルフィーチャオブジェクトとジオメトリに変換することができます。

methods(st_as_sf)
##  [1] st_as_sf.data.frame*   st_as_sf.lpp*          st_as_sf.map*         
##  [4] st_as_sf.owin*         st_as_sf.ppp*          st_as_sf.ppplist*     
##  [7] st_as_sf.psp*          st_as_sf.s2_geography* st_as_sf.sf*          
## [10] st_as_sf.sfc*          st_as_sf.Spatial*      st_as_sf.SpatVector*  
## see '?methods' for accessing help and source code
methods(st_as_sfc)
##  [1] st_as_sfc.bbox*               st_as_sfc.blob*              
##  [3] st_as_sfc.character*          st_as_sfc.dimensions*        
##  [5] st_as_sfc.factor*             st_as_sfc.list*              
##  [7] st_as_sfc.map*                st_as_sfc.owin*              
##  [9] st_as_sfc.pq_geometry*        st_as_sfc.psp*               
## [11] st_as_sfc.raw*                st_as_sfc.s2_geography*      
## [13] st_as_sfc.sf*                 st_as_sfc.SpatialLines*      
## [15] st_as_sfc.SpatialMultiPoints* st_as_sfc.SpatialPixels*     
## [17] st_as_sfc.SpatialPoints*      st_as_sfc.SpatialPolygons*   
## [19] st_as_sfc.tess*               st_as_sfc.WKB*               
## see '?methods' for accessing help and source code

例を挙げると、次のようになります。

library(sp)
data(meuse)
coordinates(meuse) = ~x+y
m.sf = st_as_sf(meuse)
opar = par(mar=rep(0,4))
plot(m.sf)
## Warning: plotting the first 10 out of 12 attributes; use max.plot = 12 to plot
## all

クラス sf または sfc のシンプルフィーチャオブジェクトから、対応する Spatial* オブジェクトへの変換は、 as メソッドを使用して、 Spatial に強制的に変換されます。

x = st_sfc(st_point(c(5,5)), st_point(c(6,9)), crs = 4326)
as(x, "Spatial")
## SpatialPoints:
##      coords.x1 coords.x2
## [1,]         5         5
## [2,]         6         9
## Coordinate Reference System (CRS) arguments: +proj=longlat +datum=WGS84
## +no_defs