duckspatial: new geometry functions, MVT/MBTiles export, and more
R
spatial
duckspatial
Highlights from duckspatial v1.1.2 and v1.2.0
Author
Adrián Cidre
Published
July 4, 2026
1 Introduction
Two versions of {duckspatial} have shipped since the last update: v1.1.2 and v1.2.0. Both are now on CRAN, so let’s go through the highlights together.
Snaps coordinates to a grid to shrink geometry size. The next example reduces the precision of argentina boundary to 1 degree (e.g. 62.4328 degrees would shrink to 62 degrees).
A full set of extent helpers landed too: ddbs_xmax(), ddbs_xmin(), ddbs_ymax(), ddbs_ymin(), plus the Z/M equivalents. By default they work by_feature = TRUE adding a new column per feature (useful for maintaining workflows within duckdb, and apply filters):
ddbs_point() creates POINT geometries directly from coordinate vectors, with 2D/3D/4D support and CRS assignment:
cities_ddbs<-ddbs_point( x =c(-58.3816, -64.1811), y =c(-34.6037, -31.4201), another_column =c("A", "B"), crs =4326)cities_ddbs
For line work, ddbs_shortest_line() returns the connecting LINESTRING between the closest points of two geometries, and ddbs_line_locate_point() gives the fractional position (0–1) of the closest point on a line to a reference point. ddbs_line_node() is the batch version: it nodes a set of lines, splitting them at every crossing into a fully noded MULTILINESTRING.
ddbs_azimuth() computes the clockwise bearing between two sets of points, in radians by default or degrees via unit = "degrees".
On the aggregation side, ddbs_intersection_agg() joins ddbs_union_agg() as the intersection counterpart — computes the common area of a set of geometries, optionally grouped by column.
polys_inters (the black point): intersects all four polygons together (ignoring grp). Since each square is shifted diagonally by 1 unit and only 3×3 in size, the only area common to all four is that single point at (3,3).
polys_inters_grp: the intersection computed within each group (by = "grp"). The two transparent red squares (grp = "a") overlap in the red square; the two transparent teal squares (grp = "b") overlap in the teal square.
ddbs_as_geojson() itself got more useful: it now carries all non-geometry columns as feature properties instead of dropping them, and returns a single FeatureCollection by default — matching geojsonsf::sf_geojson(). Pass feature_collection = FALSE if you want one Feature per row instead.
5 Vector tiles
Two new functions bring Mapbox Vector Tile support into {duckspatial}: ddbs_as_mvt_geom() transforms geometries into MVT coordinate space, clipping to a tile’s bounding box. ddbs_write_mbtiles() goes further and generates a full tile pyramid from a spatial dataset, writing it straight to an MBTiles file — ready to serve or convert to PMTiles.
6 Extension management
ddbs_extension_info() prints a glimpse() of a DuckDB extension’s row from duckdb_extensions() — installed/loaded status, version, install path — for the spatial extension by default:
ddbs_install() gains a repos argument to pull an extension from a specific DuckDB repository ("core", "core_nightly", "community"). Leaving it NULL keeps the previous fallback behavior (core, then community).
7 Bug fixes
ddbs_install() had a broken “already on the latest version” check — it referenced a column DuckDB doesn’t provide and compared install_mode with the wrong case, so it silently never triggered. Removed.
A mistake in the startup message is fixed.
ddbs_union_agg() gains a mem argument: set mem = TRUE to use ST_MemUnion_Agg() instead of ST_Union_Agg() — slower, but lighter on memory for large unions.
8 Wrapping up
That’s about 20 new functions across the two releases, plus some quality-of-life fixes for extension installation and GeoJSON export. As always, full changelogs are on GitHub, and the package is on CRAN.
Feedback and issues are very welcome — open one here if you run into anything.