Example simple feature to data frame…

library(ggseg)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.0     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.1     ✔ tibble    3.1.8
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
dk_geometries_frame <- ggseg::dk$data
aseg_geometries_frame <- ggseg::aseg$data


aseg_df <- ggseg::aseg$data
aseg_df$geometry <- NULL
aseg_df <- aseg_df %>% data.frame() %>% remove_missing()
## Warning: Removed 3 rows containing missing values.
aseg_df %>% count(label)
##                            label n
## 1                     Brain-Stem 1
## 2                    CC_Anterior 1
## 3                     CC_Central 1
## 4                CC_Mid_Anterior 1
## 5               CC_Mid_Posterior 1
## 6                   CC_Posterior 1
## 7                  Left-Amygdala 1
## 8                   Left-Caudate 1
## 9               Left-Hippocampus 1
## 10        Left-Lateral-Ventricle 1
## 11                 Left-Pallidum 1
## 12                  Left-Putamen 1
## 13          Left-Thalamus-Proper 1
## 14                Left-VentralDC 1
## 15                Right-Amygdala 1
## 16                 Right-Caudate 1
## 17       Right-Cerebellum-Cortex 1
## 18 Right-Cerebellum-White-Matter 1
## 19             Right-Hippocampus 1
## 20       Right-Lateral-Ventricle 1
## 21                Right-Pallidum 1
## 22                 Right-Putamen 1
## 23         Right-Thalamus-Proper 1
## 24               Right-VentralDC 1
## 25                x3rd-ventricle 1
## 26                x4th-ventricle 1
#

# imagine you only have nc_df
#' Title
#'
#' @param geometries_frame 
#'
#' @return
#' @export
#'
#' @examples
#' geometries_frame_to_id_group()
geometries_frame_to_id_group <- function(geometries_frame){

    nsubgroup <- geometries_frame$geometry %>% purrr::map_dbl(.f = length)

    tibble(nsubgroup) %>% 
      mutate(major_group = 1:n()) %>% # row number 
      uncount(weights = nsubgroup) %>% 
      group_by(major_group) %>% 
      mutate(subgroup = 1:n()) %>% 
      mutate(major_and_minor = paste0(major_group,'.', subgroup)) %>% 
      ungroup() %>% 
      mutate(polygon = 1:n()) %>% 
      ungroup()

}



#' Title
#'
#' @param geometries_frame 
#'
#' @return
#' @export
#'
#' @examples
#' geometries_frame_to_df(nc) 
geometries_frame_to_df <- function(geometries_frame){

      polygons_flat <- data.frame()
    
      polygons <- geometries_frame$geometry %>% purrr::flatten() %>% purrr::flatten()
      
    for (i in 1:length(polygons)){
      
      temp <- polygons[[i]] %>% 
        data.frame() %>% 
        mutate(polygon = i)
      
      polygons_flat = bind_rows(polygons_flat, temp)
  

  }

  names(polygons_flat)[1] <- "x"
  names(polygons_flat)[2] <- "y"

  polygons_flat
  
}
  


#' Title
#'
#' @param geometries_frame 
#'
#' @return
#' @export
#'
#' @examples
#' geometries_frame_to_id_group(nc) %>% 
#' count(mgroup)
geometries_frame_to_ggplot_reference <- function(geometries_frame = aseg_geometries_frame){
  
  keep_frame <- geometries_frame
  
  keep_frame$geometry = NULL
  
  keep_frame %>% 
    data.frame() %>% 
    janitor::clean_names() %>% 
    mutate(major_group = row_number()) %>% 
    left_join(geometries_frame_to_id_group(geometries_frame)) %>% 
    left_join(geometries_frame_to_df(geometries_frame))
  
}
  


  
aseg_ggplot2_reference <- aseg_geometries_frame %>% 
  filter(!is.na(label)) %>% 
  geometries_frame_to_ggplot_reference()  
## Joining with `by = join_by(major_group)`
## Joining with `by = join_by(polygon)`
## Warning in left_join(., geometries_frame_to_df(geometries_frame)): Each row in `x` is expected to match at most 1 row in `y`.
## ℹ Row 1 of `x` matches multiple rows.
## ℹ If multiple matches are expected, set `multiple = "all"` to silence this
##   warning.
compute_seg <- function(data, scales){
 
  data %>% 
    inner_join(aseg_ggplot2_reference, multiple = "all") 

}

Step 2: pass to ggproto

StatSeg <- ggplot2::ggproto(`_class` = "StatSeg",
                                  `_inherit` = ggplot2::Stat,
                                  # required_aes = c("fips"),
                                  # setup_data = my_setup_data,
                                  compute_panel = compute_seg,
                                  default_aes = aes(group = after_stat(polygon))
                                  )

Step 3: write geom_* function

geom_polygon_seg <- function(
  mapping = NULL,
  data = NULL,
  position = "identity",
  na.rm = FALSE,
  show.legend = NA,
  inherit.aes = TRUE, ...) {
  ggplot2::layer(
    stat = StatSeg,  # proto object from step 2
    geom = ggplot2::GeomPolygon,  # inherit other behavior
    data = data,
    mapping = mapping,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}

step 4: test geom_* function, celebrate!

aseg_df %>% #count(region)
  # mutate(label = label %>% as.factor()) %>% 
  ggplot(data = .) + 
  aes(label = label) +  # name indicates position instead of x and y 
  geom_sf(data = ggseg::aseg$data) +
  geom_polygon_seg() + 
  aes(fill = label) + 
  geom_polygon_seg(data = . %>% filter(str_detect(label, "Amygdala")), 
                                       color = "red") + 
  theme(legend.position = "none")
## Joining with `by = join_by(label)`
## Joining with `by = join_by(label)`

ggplot() + 
  geom_sf(data = ggseg::aseg$data)

ggplot() + 
  geom_sf(data = ggseg::dk$data)