Migrating atlas packages to sf-optional

library(ggseg.formats)

Since the sf-optional milestone (ggseg 2.2), a ggseg_atlas stores its 2D geometry in a single geom slot that can hold either an sf table or the sf-optional brain_polygons representation. The polygon form renders identically through the geom_polygon-based path in ggseg, but carries no dependency on the sf class machinery — so atlases keep working on wasm builds and air-gapped installs where sf (and its GDAL/GEOS/PROJ system libraries) cannot be installed.

This vignette is for maintainers of downstream atlas packages (ggsegFreeSurfer, ggsegSchaefer, ggsegGlasser, …). Your package ships brain_atlas/ggseg_atlas objects as .rda files under data/. Migrating means rewriting those files once so the geometry is stored as polygons, then dropping sf from your DESCRIPTION.

The recipe

From the root of your atlas package, run:

# 1. rewrite every atlas in data/ into the polygon format
ggseg.formats::migrate_atlas_files("data")

# 2. drop sf from DESCRIPTION (it is no longer needed at install or run time)
usethis::use_package("sf", type = "Suggests")

# 3. rebuild the package data documentation and reinstall
devtools::document()

That is the whole migration. Commit the rewritten data/*.rda, push, and release.

What migrate_atlas_files() does

It walks the directory, loads each .rda, finds every atlas object inside, converts its geom to brain_polygons, drops any legacy sf/polygons slots, and saves the file back with xz compression. Files with nothing to migrate are left untouched, and it reports what it changed:

ggseg.formats::migrate_atlas_files("data")
#> ✔ Migrated `dk.rda`.
#> ✔ Migrated `aseg.rda`.
#> ℹ Skipped `palette.rda` (nothing to migrate).

The conversion reads sf coordinates, so sf must be installed on the machine you run the migration from. This is a one-time maintainer step; the published package no longer needs sf.

It is idempotent — running it twice is a no-op on already-migrated files, so it is safe to wire into a data-raw/ build script.

Keeping sf instead

If your atlas package genuinely needs sf geometry (for example it exposes geometric operations), pass keep_sf = TRUE to normalise everything into the single geom slot as sf rather than polygons:

ggseg.formats::migrate_atlas_files("data", keep_sf = TRUE)

Verifying the result

After migrating, the geometry is sf-optional and round-trips losslessly. You can rehydrate sf on demand with as_sf_atlas(), and go back with as_polygon_atlas():

poly <- as_polygon_atlas(dk())
is_atlas_polygon(poly)
#> [1] TRUE

atlas_labels(poly) |>
  head()
#> [1] "lh_bankssts"                "lh_caudalanteriorcingulate"
#> [3] "lh_caudalmiddlefrontal"     "lh_corpuscallosum"         
#> [5] "lh_cuneus"                  "lh_entorhinal"

A migrated atlas plots through ggseg with no sf installed. If a lite-only install meets an atlas that is still sf-backed, as_polygon_atlas() converts it on the fly when sf is available, and otherwise aborts with a message naming migrate_atlas_files() — the signal that the atlas package itself needs the one-time migration above.