# Customising brain atlases

    library(ggseg.formats)
    # The atlas-manipulation helpers shown here operate on sf-backed atlas
    # geometry. Since the sf-optional milestone, sf is a Suggests dependency,
    # so load it explicitly here.
    library(sf)
    #> Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE

Say you are preparing a figure that focuses on the subcortical structures visible in a single sagittal slice. You want to drop the views you don’t need, remove small polygon fragments that clutter the image, turn some regions into non-legend context, and add a metadata column so you can facet by structure type. Every one of those steps is a single function call, and they all pipe together.

## The manipulation toolkit

ggseg.formats provides three groups of manipulation functions. Region functions (`atlas_region_*`) control which regions are active in the atlas. View functions (`atlas_view_*`) control the 2D sf geometry—which views are present and how they are arranged. Core functions (`atlas_core_*`) enrich the region metadata. All of them accept a `ggseg_atlas` as the first argument, return a new `ggseg_atlas`, and play nicely with the pipe.

## Keeping and removing regions

`atlas_region_remove()` strips a region entirely—from core, palette, sf geometry, and 3D data. The `pattern` argument is passed to `grepl()` with `ignore.case = TRUE`, so partial matches work:

    no_cc <- atlas_region_remove(dk(), "corpus callosum")
    "corpus callosum" %in% atlas_regions(no_cc)
    #> [1] FALSE

`atlas_region_keep()` is the inverse: it keeps only the regions that match and turns everything else into context geometry (sf is preserved for surface continuity, but non-matching regions leave core and palette):

    frontal <- atlas_region_keep(dk(), "frontal")
    atlas_regions(frontal)
    #> [1] "caudal middle frontal"  "frontal pole"           "lateral orbitofrontal"
    #> [4] "medial orbitofrontal"   "rostral middle frontal" "superior frontal"

Both functions accept a `match_on` argument to choose whether the pattern matches against `"region"` (the default, human-readable name) or `"label"` (the unique identifier):

    lh_only <- atlas_region_keep(dk(), "^lh_", match_on = "label")
    head(atlas_labels(lh_only))
    #> [1] "lh_bankssts"                "lh_caudalanteriorcingulate"
    #> [3] "lh_caudalmiddlefrontal"     "lh_corpuscallosum"
    #> [5] "lh_cuneus"                  "lh_entorhinal"

## Context regions

Sometimes you want a region’s polygon to stay visible (rendered in grey) but not appear in the legend or carry data. This is what `atlas_region_contextual()` does. It removes the region from core, palette, and 3D data but leaves the sf geometry in place:

    ctx <- atlas_region_contextual(aseg(), "ventricle")
    "lateral ventricle" %in% atlas_regions(ctx)
    #> [1] FALSE

Compare that to `atlas_region_remove()`, which would delete the ventricle polygons entirely, leaving gaps in the 2D slices.

## Renaming regions

`atlas_region_rename()` changes the `region` column without touching `label` (so geometry links stay intact). Pass a fixed string:

    renamed <- atlas_region_rename(
      dk(),
      "banks of superior temporal sulcus",
      "STS banks"
    )
    "STS banks" %in% atlas_regions(renamed)
    #> [1] TRUE

Or pass a function for programmatic renaming:

    upper <- atlas_region_rename(dk(), ".*", toupper)
    head(atlas_regions(upper))
    #> [1] "BANKS OF SUPERIOR TEMPORAL SULCUS" "CAUDAL ANTERIOR CINGULATE"
    #> [3] "CAUDAL MIDDLE FRONTAL"             "CORPUS CALLOSUM"
    #> [5] "CUNEUS"                            "ENTORHINAL"

## Managing views

`atlas_views()` tells you what 2D views an atlas has:

    atlas_views(aseg())
    #> [1] "axial_3"   "axial_4"   "axial_5"   "axial_6"   "coronal_1" "coronal_2"
    #> [7] "sagittal"

`atlas_view_keep()` and `atlas_view_remove()` filter views by pattern. If you only want the sagittal slice:

    sag <- atlas_view_keep(aseg(), "sagittal")
    atlas_views(sag)
    #> [1] "sagittal"

Or remove several views at once by passing a vector:

    fewer <- atlas_view_remove(aseg(), c("axial_3", "coronal_2"))
    atlas_views(fewer)
    #> [1] "axial_4"   "axial_5"   "axial_6"   "coronal_1" "sagittal"

## Cleaning up geometry

After filtering regions you may have small polygon fragments left over—tiny slivers where a region just barely crossed a slice plane. `atlas_view_remove_small()` removes region polygons below a minimum area threshold. Context polygons (those not in core) are never removed:

    cleaned <- atlas_view_remove_small(aseg(), min_area = 50)
    #> ℹ Removed 20 geometries below area 50

You can scope the removal to specific views:

    cleaned_sag <- atlas_view_remove_small(
      aseg(),
      min_area = 50,
      views = "sagittal"
    )
    #> ℹ Removed 2 geometries below area 50

`atlas_view_remove_region()` removes a specific region’s sf geometry without touching core, palette, or 3D data. This is useful when a region’s 2D projection is misleading but you still want it in 3D:

    no_stem_sf <- atlas_view_remove_region(
      aseg(),
      "brain stem",
      match_on = "region"
    )

## Reordering and gathering

When you remove views, the remaining geometry keeps its original coordinates, which can leave awkward gaps. `atlas_view_gather()` repositions views side-by-side with a configurable gap:

    trimmed <- aseg() |>
      atlas_view_keep(c("sagittal", "coronal_3", "axial_3")) |>
      atlas_view_gather()
    atlas_views(trimmed)
    #> [1] "axial_3"  "sagittal"

`atlas_view_reorder()` lets you choose the left-to-right order and repositions at the same time. Views not mentioned in `order` are appended at the end:

    reordered <- aseg() |>
      atlas_view_keep(c("sagittal", "coronal_3", "axial_3")) |>
      atlas_view_reorder(c("axial_3", "sagittal", "coronal_3"))
    atlas_views(reordered)
    #> [1] "axial_3"  "sagittal"

## Adding metadata

`atlas_core_add()` left-joins a data frame onto the core table. This is useful for attaching grouping variables, statistical results, or any other per-region information. Here we add a custom network column to a handful of regions:

    network_info <- data.frame(
      region = c(
        "superior frontal",
        "precuneus",
        "inferior parietal",
        "posterior cingulate"
      ),
      network = "default mode"
    )
    enriched <- atlas_core_add(dk(), network_info)
    enriched$core[!is.na(enriched$core$network), c("region", "network")]
    #>                 region      network
    #> 8    inferior parietal default mode
    #> 23 posterior cingulate default mode
    #> 25           precuneus default mode
    #> 28    superior frontal default mode
    #> 43   inferior parietal default mode
    #> 58 posterior cingulate default mode
    #> 60           precuneus default mode
    #> 63    superior frontal default mode

The `by` argument defaults to `"region"` but you can join on any shared column.

## A full pipeline

Here is a realistic pipeline that prepares the `aseg` atlas for a compact two-view figure of deep grey matter structures:

    publication_aseg <- aseg() |>
      atlas_view_keep(c("sagittal", "coronal_3")) |>
      atlas_region_contextual("ventricle|choroid|white|cc") |>
      atlas_view_remove_small(min_area = 30) |>
      atlas_view_gather(gap = 0.1)
    #> ℹ Removed 2 geometries below area 30

    publication_aseg
    #>
    #> ── aseg ggseg atlas ────────────────────────────────────────────────────────────
    #> Type: subcortical
    #> Regions: 12
    #> Hemispheres: left, NA, right
    #> Views: sagittal
    #> Palette: ✔
    #> Rendering: ✔ ggseg
    #> ✔ ggseg3d (meshes)
    #> ────────────────────────────────────────────────────────────────────────────────
    #>    hemi          region                  label     structure
    #> 1  left      Cerebellum Left-Cerebellum-Cortex    cerebellum
    #> 2  left      Cerebellum Left-Cerebellum-Cortex    cerebellum
    #> 3  left        Thalamus          Left-Thalamus basal ganglia
    #> 4  left        Thalamus          Left-Thalamus basal ganglia
    #> 5  left Thalamus Proper          Left-Thalamus basal ganglia
    #> 6  left Thalamus Proper          Left-Thalamus basal ganglia
    #> 7  left         Caudate           Left-Caudate basal ganglia
    #> 8  left         Caudate           Left-Caudate basal ganglia
    #> 9  left         Putamen           Left-Putamen basal ganglia
    #> 10 left         Putamen           Left-Putamen basal ganglia
    #> ... with 28 more rows

Each function returns a valid `ggseg_atlas`, so you can inspect intermediate results, branch the pipeline, or hand the final object straight to ggseg for plotting.