## load packages
library(duckspatial)
library(sf)
## load Argentina boundaries as sf
argentina_sf <- read_sf(system.file("spatial/argentina.geojson", package = "duckspatial"))
## get centroid
argentina_centroid_sf <- ddbs_centroid(argentina_sf)
argentina_centroid_sfduckspatial v0.9.0
1 Introduction
I’m exited to announce the release of {duckspatial} v0.9.0 (Cidre González, Pereira, and Kotov 2026) , now available ON CRAN! This new version brings a major evolution of the package, with substantial changes that make the way towards version 1.0.0.
2 What is duckspatial?
For those new to the package, {duckspatial} brings the power of DuckDB’s spatial extension to R. It provides fast and memory-efficient functions to analyze and manipulate large spatial vector datasets while maintaining full compatibility with R’s spatial ecosystem, especially the {sf} package (Pebesma and Bivand 2023).
The package is designed for situations where:
You’re working with large spatial datasets
You need to speed up spatial analysis at scale
Your data doesn’t fit in memory
3 Major changes in 0.9.0
If you’ve used {duckspatial} before, you might ask why did we jump from version 0.2.0 to version 0.9.0, instead of 0.3.0 or 1.0.0. Well, this new release brings substantial changes, some of them might have incompatibilities with the previous version. However, we are reserving the v1.0.0 for the next release that we are working in, which will bring fantastic news, such as lazy duckspatial tables, and native support of Coordinates Reference Systems (CRS) within DuckDB (see this issue for a reference). In this sense, to we decided that moving to 0.3.0 wasn’t enough for the big changes this versions brings, and we think version 1.0.0 should be released with the previously mentioned roadmap. So now that we know why this is v0.9.0, let’s dive into the major changes.
3.1 Simplified connection handling
The biggest change in this release is how the connections are handled. The conn argument now defaults to NULL in every spatial operation (e.g. ddbs_centroid, ddbs_filter, ddbs_area, …), and it’s no longer a mandatory argument. This means that the package will handle internally the connection if the user prefer to work with {sf} objects instead of DuckDB tables, making the package more user friendly. The next examples show how to calculate the centroid using a connection, or without using a connection.
NOTE: The first time you use one function from {duckspatial}, it will run internally ddbs_install() and ddbs_load() to set up the spatial extension, and it will take slightly longer. After the first time, everything will go faster.
## load packages
library(duckspatial)
library(sf)
## create a connection (in memory)
## NOTE: ddbs_create_conn() is a wrapper of dbConnect(), ddbs_install(), and ddbs_load()
conn <- ddbs_create_conn()
## load Argentina boundaries as sf
argentina_sf <- read_sf(system.file("spatial/argentina.geojson", package = "duckspatial"))
## get centroid
argentina_centroid_sf <- ddbs_centroid(argentina_sf, conn)
argentina_centroid_sfThis change makes {duckspatial} feel more natural for {sf} users while still offering the flexibility to work with existing DuckDB connections when needed.
3.2 Flexible Input/Output
All spatial functions now support four workflows:
Input sf → Output sf
Input sf → Output DuckDB table
Input DuckDB table → Output sf
Input DuckDB table → Output DuckDB table
This flexibility lets you choose the right approach for your data size and workflow. Note that working inside DuckDB without pulling the data into R’s memory will be much more efficient, but it will be different than an {sf} workflow.
argentina_centroid_sf <- ddbs_centroid(argentina_sf)## write data into the connection
ddbs_write_vector(conn, argentina_centroid_sf, name = "argentina")
## input duckdb table, output sf
argentina_centroid_sf <- ddbs_centroid("argentina", conn)ddbs_centroid(argentina_sf, conn, name = "argentina_centroid")ddbs_centroid("argentina", conn, name = "argentina_centroid", overwrite = TRUE)3.3 Performance Improvements
Two key optimizations make this release significantly faster:
Faster table creation:
ddbs_write_vector()now supportstemp = TRUEto create temporary views, which is much faster than creating permanent tables.Faster data retrieval:
ddbs_read_vector()uses internal optimizations with {wk} (Dunnington and Pebesma 2025) and {geoarrow} (Dunnington 2025) for substantially improved performance.
3.4 CRS Deprecation
The crs and crs_column arguments are now deprecated and will be removed in version 1.0.0. This change aligns with native CRS support coming to DuckDB v1.5.0 (expected February 2026). The package will handle coordinate reference systems more seamlessly once DuckDB’s native support is available.
4 New Features
This release adds a substantial number of new spatial functions, greatly expanding what you can do with {duckspatial}. Particularly, it brings more than 30 new functions!! 🚀
4.1 Geometric Operations
-
Hulls:
ddbs_convex_hull()andddbs_concave_hull()for creating geometric envelopes -
Boundaries:
ddbs_bbox(),ddbs_boundary()andddbs_envelope()for extracting geometry boundaries -
Transformations: Complete suite of affine transformations including
ddbs_rotate(),ddbs_shift(),ddbs_scale(),ddbs_flip(), andddbs_shear() -
Geometry creation:
ddbs_make_polygon()to create polygons from linestrings,ddbs_generate_points()for random point generation -
Geometry manipulation:
ddbs_exterior_ring(),ddbs_union(),ddbs_combine(),ddbs_make_valid(), andddbs_simplify()
4.2 Spatial Analysis
Measurements:
ddbs_area(),ddbs_length(), andddbs_distance(),Spatial joins:
ddbs_join()for database-backed spatial joinsSpatial predicates: Comprehensive support through
ddbs_predicate()or convenient shortcuts likeddbs_intersects(),ddbs_crosses(),ddbs_touches(),ddbs_within(),ddbs_contains(), and more
4.3 Coordinate Transformations and Format Conversions
ddbs_transform(): Transform between coordinate reference systemsddbs_as_text()andddbs_as_wkb(): Convert geometries to WKT and WKB formatsddbs_quadkey(): Calculate quadkey tiles from point geometries
4.4 Utility Functions
ddbs_create_conn(): Convenient function to create a DuckDB connection with the spatial extension already installed and loadedddbs_drivers(): List available GDAL drivers and supported file formatsddbs_is_valid()andddbs_is_simple(): Validate geometry quality
5 Minor improvements
- All functions now have a
quietparameter to suppress messages - Fixed issue where columns with dots in their names would cause failures
- Improved
ddbs_filter()to avoid duplicates when geometries match multiple predicates - Added comprehensive vignettes for getting started
6 Looking Ahead to 1.0.0
Version 0.9.0 sets the stage for the 1.0.0 release, which will bring additional substantial changes. The upcoming release will fully integrate DuckDB’s native CRS support and lazy duckspatial tables.
7 Acknowledgments
This release wouldn’t be possible without the incredible work of many people.
First and foremost, my deepest thanks to the DuckDB team for building such a powerful and elegant analytical engine. Their commitment to performance, correctness, and developer experience has created a foundation that makes tools like {duckspatial} possible.
Special recognition goes to the developers of the DuckDB Spatial Extension, the engine that powers everything in {duckspatial}. Their unvaluable work on spatial operations, format support, and performance optimization is what makes this package capable of handling large-scale spatial analysis efficiently.
Most importantly, this release represents a true collaborative effort. Rafael Pereira and Egor Kotov have been fundamental in shaping duckspatial’s new design and implementation. Their ideas, code contributions, and thoughtful feedback have shaped this package far beyond what I could have achieved alone. This is a shared accomplishment.
Finally, thank you to the broader R spatial community for building the ecosystem that {duckspatial} integrates with, and to everyone who has tested early versions, reported issues, or provided feedback.