---
title: "Converting legacy atlases"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Converting legacy atlases}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r}
#| label: setup
#| include: false
library(ggseg.extra)
```

If you have atlases from older versions of ggseg or ggseg3d, this vignette explains how to convert them to the current `ggseg_atlas` format.

```{r}
#| label: load-libraries
#| eval: false
library(ggseg.formats)
library(ggseg)
library(ggseg3d)
```

## The old system

Previous versions used separate objects for 2D and 3D rendering:

- `ggseg_atlas` objects for 2D plots with `ggseg()`
- `ggseg3d_atlas` objects for 3D plots with `ggseg3d()`

Atlas packages exported pairs like `dk` and `dk_3d`, or `yeo7` and `yeo7_3d`.
You had to remember which object to use with which package.

## The current system

The current system uses a single `ggseg_atlas` object that works with both
packages. One atlas, both renderers.

## Converting old atlases

Use `convert_legacy_brain_atlas()` to convert old atlas objects:

### From a ggseg3d_atlas only

```{r}
#| label: convert-3d
#| eval: false
unified <- convert_legacy_brain_atlas(atlas_3d = old_atlas_3d)

ggseg3d(atlas = unified)
```

### From both 2D and 3D objects

```{r}
#| label: convert-both
#| eval: false
unified <- convert_legacy_brain_atlas(
  atlas_2d = old_atlas,
  atlas_3d = old_atlas_3d
)

ggplot() + geom_brain(atlas = unified)

ggseg3d(atlas = unified)
```

### From a 2D ggseg_atlas only

```{r}
#| label: convert-2d
#| eval: false
unified <- convert_legacy_brain_atlas(atlas_2d = old_atlas)

ggplot() + geom_brain(atlas = unified)
```

This atlas works with ggseg but not ggseg3d (no 3D vertex data).

## Specifying atlas type

The function auto-detects atlas type from the structure.
Override if needed:

```{r}
#| label: override-type
#| eval: false
unified <- convert_legacy_brain_atlas(
  atlas_2d = old_atlas,
  atlas_3d = old_atlas_3d,
  atlas_name = "my_atlas",
  type = "cortical"
)
```

Valid types: "cortical", "subcortical", "tract".

## Batch conversion

When converting many atlas packages, load the `.rda` files into isolated
environments to avoid name collisions:

```{r}
#| label: batch-pattern
#| eval: false
env <- new.env()
load("data/my_atlas.rda", envir = env)
atlas_2d <- env[["my_atlas"]]

result <- convert_legacy_brain_atlas(atlas_2d = atlas_2d)

save(my_atlas = result, file = "data/my_atlas.rda", compress = "xz")
```

Always save with `compress = "xz"` for significant file size reduction.

## Post-conversion checklist

After converting, verify the result:

```{r}
#| label: post-conversion
#| eval: false
stopifnot(is_ggseg_atlas(result))

nrow(result$core)
length(result$palette)
```

Each converted atlas package needs these updates:

- **DESCRIPTION**: Add `ggseg.formats` to `Depends`, remove `ggseg`/`ggseg3d`
  from `Depends`
- **R/data.R**: Update roxygen2 docs to reference `ggseg_atlas` format
- **R/<pkg>-package.R**: Add `@import ggseg.formats`
- **tests**: Use `is_ggseg_atlas()` and `geom_brain()` instead of legacy
  `is_ggseg_atlas()` and `ggseg()`
- **R/sysdata.rda**: Delete. Palettes are embedded in the atlas object
  (`atlas$palette`), not stored separately in `brain_pals`

## Lessons learned from batch conversion

The ggseg ecosystem converted 19 atlas packages using this function. Key
findings:

### 3D data is not always preserved

Subcortical atlases that only had 3D mesh data (no 2D sf geometry) lose their
meshes during conversion. The converter cannot round-trip standalone meshes back
into the new format. These atlases need recreation from source FreeSurfer files
using `create_subcortical_from_volume()`.

Cortical 3D atlases fare better --- vertex indices can be inferred from mesh
coordinates --- but the result is still approximate. Recreation produces more
accurate vertex mappings.

### Palette entries may not match core rows exactly

Some converted atlases have palette entries for labels absent from `core` (e.g.,
`"lh_unknown"`, `"rh_???"`, or labels with special characters). This is normal:
the palette retains all labels from the original annotation, while `core` only
contains labels that have geometry. `is_ggseg_atlas()` emits an informational
message but still validates.

### Labels with special characters lose sf coverage

Atlases like Brainnetome have labels containing slashes (e.g., `"A9/46d_L"`).
These labels may exist in `core` but have no matching polygon geometry, reducing
sf coverage below 100%. This is a data issue in the source atlas, not a
conversion bug.

### Skip ggseg3d tests for atlases without 3D data

If a converted atlas has no vertices or meshes, `ggseg3d()` will error. Either
omit the 3D test entirely or guard it:

```{r}
#| label: conditional-3d-test
#| eval: false
it("renders with ggseg3d", {
  skip_if_not_installed("ggseg3d")
  skip_if(is.null(my_atlas$geometry$vertices), "No 3D data")
  p <- ggseg3d::ggseg3d(atlas = my_atlas)
  expect_s3_class(p, "htmlwidget")
})
```

## Troubleshooting

**No vertices inferred**: The mesh coordinates don't align with the brain
surface. Check that `surface` matches what was used to create the 3D atlas.

**Wrong hemisphere assignment**: The function infers hemisphere from the `hemi`
column. If this is missing or wrong, set it manually after conversion.

**Type detection fails**: Set `type` explicitly.

## When to recreate instead

Converting works for getting legacy atlases running with ggseg 2.0, but
recreating from source data produces better results. Use the creation functions
if you have access to the original neuroimaging files:

- `create_cortical_from_annotation()` --- from FreeSurfer annotation files
- `create_subcortical_from_volume()` --- from volumetric segmentation
- `create_tract_from_tractography()` --- from tractography files
- `create_cortical_from_labels()` --- from individual label files

See the [cortical](tutorial-cortical-atlas.html),
[subcortical](tutorial-subcortical-atlas.html),
[tract](tutorial-tract-atlas.html), and
[label-based](tutorial-label-atlas.html) atlas tutorials.
