#' Take lists corresponding to several annotations, combine annotations
#' that share parameters and geom, and return a list-of-lists.
#' @name combine_layers
#' @param lists List of lists to combine. Each sub-list should have
#' at least these two elements:
#' \itemize{
#' \item{`geom`}{length-one character vector such as `"text"`, or `"point"`}
#' \item{`aes`}{named list containing variable-value mappings,
#' such as `list(x = 3, y = 40)`)}
#' }
#'
#' It can also have two optional additional elements:
#' \itemize{}
#' \item{`param`}{named list containing parameter names and values, such as
#' `list(colour = "black")`}
#' \item{`facets`}{named list containing facet variable-value pairs, such as
#' `list(cyl = 4)`}
#' }
#'
#'
#' @return A list of lists. Each sub-list will have `geom`, `aes`, `params`,
#' and `facet` elements.
#'
#' Any supplied lists that share a geom, parameters, and facet variable + level
#' will be combined, so the length of the returned list will not necessarily
#' equal the number of lists supplied to `lists`.
#'
#' The order of the
#' sub-lists in the returned list may also differ from the order
#' in the supplied list.
#'
#' @examples
#' library(ggplot2)
#'
#' layer_1 <- list(
#' geom = "text",
#' aes = list(x = 3, y = 30, label = "Some text"),
#' facets = list(cyl = 4),
#' params = list(colour = "red")
#' )
#'
#' layer_2 <- list(
#' geom = "text",
#' aes = list(x = 4, y = 35, label = "Some other text"),
#' facets = list(cyl = 4),
#' params = list(colour = "red")
#' )
#'
#' layer_3 <- list(
#' geom = "point",
#' aes = list(x = 3, y = 40)
#' )
#'
#' layer_4 <- list(
#' geom = "text",
#' aes = list(x = 4, y = 45, label = "Some more text"),
#' params = list(colour = "black")
#' )
#'
#' lists <- list(layer_1, layer_2, layer_3, layer_4)
#'
#' # combine_layers() combined layers 1 and 2 as they share a geom and params.
#'
#' layers <- combine_layers(lists)
#'
#' # The resulting list can be used to create ggplot2 annotations
#'
#' annots <- purrr::map(
#' layers,
#' ~ make_layer(
#' geom = .x$geom,
#' aes = .x$aes,
#' params = .x$params
#' ) %>%
#' eval()
#' )
#'
#' ggplot2::ggplot() +
#' annots
#' @noRd
#' @importFrom rlang .data
combine_layers <- function(lists) {
if (missing(lists)) {
stop("Must supply list of lists")
}
if (length(lists) == 0) {
stop("Must supply list of lists")
}
if (inherits(lists, "reactivevalues")) {
lists <- shiny::reactiveValuesToList(lists)
}
check_element_is_layer <- function(element) {
element_is_list <- is.list(element)
has_expected_sub_elements <- all(c("geom", "aes")
%in% names(element))
has_no_unexpected_sub_elements <- all(names(element) %in%
c(
"geom", "aes",
"facets", "params"
))
aes_is_list <- is.list(element[["aes"]])
all(
element_is_list,
has_expected_sub_elements,
aes_is_list,
has_no_unexpected_sub_elements
)
}
each_element_is_layer <- all(purrr::map_lgl(
lists,
check_element_is_layer
))
stopifnot(each_element_is_layer)
x <- dplyr::tibble(layer = lists)
x <- tidyr::unnest_wider(x, .data$layer)
x <- dplyr::group_by(x, dplyr::across(!dplyr::one_of("aes")))
x <- x %>%
dplyr::summarise(aes = list(.data$aes), .groups = "drop") %>%
dplyr::mutate(annot = dplyr::row_number())
x <- split(x, x$annot)
create_aes_out <- function(split_tib) {
aes_col <- split_tib %>%
dplyr::select(.data$aes) %>%
tidyr::unnest_longer(.data$aes)
if (all(is.na(aes_col))) {
list_out <- list(aes = NULL)
} else {
list_out <- aes_col %>%
tidyr::unnest_wider(.data$aes) %>%
as.list()
}
list_out
}
aes <- purrr::map(x, create_aes_out)
geoms <- purrr::map(x, ~ .x[["geom"]])
add_element_or_flatten <- function(list, element) {
if (is.null(list[[element]])) {
out <- list()
} else {
out <- purrr::flatten(list[[element]])
}
out
}
params <- purrr::map(x, add_element_or_flatten,
element = "params"
)
facets <- purrr::map(x, add_element_or_flatten,
element = "facets"
)
out <- list(
aes = aes,
params = params,
facets = facets,
geom = geoms
)
purrr::transpose(out)
}
safely_combine_layers <- purrr::safely(combine_layers)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.