sf-compatible spatial operations on DuckDB-backed data — out-of-core geometries, stored as GeoParquet.
DuckDBSpatial extends DuckDBDataFrame
with sf-compatible spatial methods that run on
DuckDB-backed columns and tables, powered by DuckDB's native spatial extension. It
exposes much of the sf spatial vocabulary --- predicates (st_intersects,
st_within, st_contains), measurements (st_area, st_length), and geometry
transforms (st_centroid, st_buffer, st_union) --- but the geometries stay
on disk in columnar Parquet and operations are recorded as lazy SQL, so you
can filter and transform spatial layers without loading them into memory.
It reads and writes GeoParquet 1.0, so the same files interoperate with
GeoPandas, GDAL, QGIS, and DuckDB. Within the BiocDuckDB suite it is the
spatial layer: it serves GEOMETRY columns for DuckDBDataFrame and underpins the
MultiAssaySpatialExperiment on-disk format in BiocDuckDB.
# once available from Bioconductor:
if (!require("BiocManager")) install.packages("BiocManager")
BiocManager::install("DuckDBSpatial")
The DuckDB spatial extension is fetched and cached automatically the first time a spatial operation runs.
library(DuckDBSpatial)
library(sf)
# open a GeoParquet layer as a lazy DuckDBDataFrame
spatial_path <- system.file("extdata", "spatial", package = "DuckDBSpatial")
df <- DuckDBDataFrame(spatial_path)
geom <- df[["geometry"]]
head(st_area(geom)) # lazy; ST_Area pushed into DuckDB
query_pt <- st_sfc(st_point(c(30, 10)))
st_filter(df, query_pt) # keep rows intersecting the query
For plain x/y coordinate columns (e.g. transcript centroids), the layer*
helpers run predicates without a geometry column:
layerSubsetByGeometry(pts, poly, coords = c("x", "y"))
ddb <- readGeoParquet("layer.parquet") # lazy DuckDBDataFrame w/ GEOMETRY column
writeGeoParquet(sf_object, "out.parquet") # GeoParquet 1.0 (WKB + metadata)
writeGeoParquet() conforms to GeoParquet 1.0.0 (WKB
encoding, geometry-type and bounding-box metadata) and needs the suggested
nanoparquet and jsonlite packages.
A good fit when the spatial layer is larger than memory, when the workload is
filter-heavy (selecting features by region or predicate before the expensive
step), or when the data lives on disk as GeoParquet shared with other spatial
tooling. An in-memory sf object remains preferable for small layers and
interactive geometry editing.
vignettes/DuckDBSpatial.Rmd).sf-generic to DuckDB spatial
SQL translation, the two dispatch paths, GeoParquet 1.0 I/O, and the BiocDuckDB
integration, for developers (vignettes/DuckDBSpatial-design.Rmd).MIT License. Copyright Genentech, Inc., 2026.