#' Draw boxes containing text
#'
#' Draw boxes of defined width and height containing word-wrapped text. Multiple
#' boxes can be drawn at once. Most styling parameters can be used as aesthetics
#' and can be applied separately to each text box drawn. The exception is styling
#' parameters that are specified as grid units (e.g., `box.padding` or `box.r`),
#' which can only be specified for all text boxes at once. See examples for details.
#'
#' @section Aesthetics:
#'
#' `geom_textbox()` understands the following aesthetics (required
#' aesthetics are in bold; select aesthetics are annotated):
#'
#' * **`x`**
#' * **`y`**
#' * **`label`**
#' * `alpha`
#' * `box.colour` Color of box outline. Overrides `colour`.
#' * `box.size` Width of box outline.
#' * `colour` Default color of box text and box outline.
#' * `family`
#' * `fontface`
#' * `fill` Default fill color of box background.
#' * `group`
#' * `halign` Horizontal alignment of text inside box.
#' * `hjust` Horizontal alignment of box.
#' * `lineheight`
#' * `orientation` One of `"upright"`, `"left-rotated"`,
#' `"right-rotated"`, `"inverted"`.
#' * `size` Default font size of box text.
#' * `text.colour` Color of box text. Overrides `colour`.
#' * `valign` Vertical alignment of text inside box.
#' * `vjust` Vertical alignment of box.
#'
#' @inheritParams ggplot2::geom_text
#' @param nudge_x,nudge_y Horizontal and vertical adjustment to nudge text boxes by.
#' Useful for offsetting text from points, particularly on discrete scales.
#' Cannot be jointly specified with `position`.
#' @param width,height Unit values specifying the width and height of
#' the text box (including margins!). If `height = NULL` (the default),
#' the height is chosen automatically to accommodate all the text.
#' @param minwidth,maxwidth,minheight,maxheight Unit values specifying
#' the minimum and maximum values for `width` and `height`, respectively.
#' If set to `NULL`, are not enforced.
#' @param box.padding Unit vector of length four specifying the padding
#' inside the text box.
#' @param box.margin Unit vector of length four specifying the margin
#' outside the text box.
#' @param box.r Unit vector of length one specifying the radius of the
#' box.
#' @return A ggplot2 layer that can be added to a plot created with
#' [ggplot2::ggplot()].
#' @seealso [geom_richtext()], [element_textbox()]
#' @examples
#' library(ggplot2)
#'
#' df <- data.frame(
#' label = rep("Lorem ipsum dolor **sit amet,** consectetur adipiscing elit,
#' sed do *eiusmod tempor incididunt* ut labore et dolore magna
#' aliqua.", 2),
#' x = c(0, .6),
#' y = c(1, .6),
#' hjust = c(0, 0),
#' vjust = c(1, 0),
#' orientation = c("upright", "right-rotated"),
#' color = c("black", "blue"),
#' fill = c("cornsilk", "white")
#' )
#'
#' ggplot(df) +
#' aes(
#' x, y, label = label, color = color, fill = fill,
#' hjust = hjust, vjust = vjust,
#' orientation = orientation
#' ) +
#' geom_textbox(width = unit(0.4, "npc")) +
#' geom_point(color = "black", size = 2) +
#' scale_discrete_identity(aesthetics = c("color", "fill", "orientation")) +
#' xlim(0, 1) + ylim(0, 1)
#' @export
geom_textbox <- function(mapping = NULL, data = NULL,
stat = "identity", position = "identity",
...,
nudge_x = 0,
nudge_y = 0,
box.padding = unit(c(5.5, 5.5, 5.5, 5.5), "pt"),
box.margin = unit(c(0, 0, 0, 0), "pt"),
box.r = unit(5.5, "pt"),
width = unit(2, "inch"), minwidth = NULL, maxwidth = NULL,
height = NULL, minheight = NULL, maxheight = NULL,
na.rm = FALSE,
show.legend = NA,
inherit.aes = TRUE)
{
if (!missing(nudge_x) || !missing(nudge_y)) {
if (!missing(position)) {
stop("You must specify either `position` or `nudge_x`/`nudge_y` but not both.", call. = FALSE)
}
position <- position_nudge(nudge_x, nudge_y)
}
layer(
data = data,
mapping = mapping,
stat = stat,
geom = GeomTextBox,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(
box.padding = box.padding,
box.margin = box.margin,
box.r = box.r,
width = width, minwidth = minwidth, maxwidth = maxwidth,
height = height, minheight = minheight, maxheight = maxheight,
na.rm = na.rm,
...
)
)
}
#' @rdname geom_richtext
#' @format NULL
#' @usage NULL
#' @export
GeomTextBox <- ggproto("GeomTextBox", Geom,
required_aes = c("x", "y", "label"),
default_aes = aes(
colour = "black", fill = "white", size = 3.88, hjust = 0.5,
vjust = 0.5, halign = 0, valign = 1, alpha = NA, family = "",
fontface = 1, lineheight = 1.2, text.colour = NULL, box.colour = NULL,
box.size = 0.25, orientation = "upright"
),
draw_panel = function(data, panel_params, coord,
box.padding = unit(c(5.5, 5.5, 5.5, 5.5), "pt"),
box.margin = unit(c(0, 0, 0, 0), "pt"),
box.r = unit(5.5, "pt"),
width = unit(2, "inch"), minwidth = NULL, maxwidth = NULL,
height = NULL, minheight = NULL, maxheight = NULL,
na.rm = FALSE) {
data <- coord$transform(data, panel_params)
# split data frame into list of rows
rows <- split(data, seq(nrow(data)))
names(rows) <- NULL
grobs <- mapply(
make_textbox_grob,
rows,
list(box.padding),
list(box.margin),
list(box.r),
list(width),
list(minwidth),
list(maxwidth),
list(height),
list(minheight),
list(maxheight),
SIMPLIFY = FALSE
)
do.call(grobTree, grobs)
},
draw_key = draw_key_text
)
make_textbox_grob <- function(data,
box.padding = unit(c(5.5, 5.5, 5.5, 5.5), "pt"),
box.margin = unit(c(0, 0, 0, 0), "pt"),
box.r = unit(5.5, "pt"),
width = unit(2, "inch"), minwidth = NULL, maxwidth = NULL,
height = NULL, minheight = NULL, maxheight = NULL) {
textbox_grob(
data$label,
data$x, data$y, default.units = "native",
hjust = data$hjust, vjust = data$vjust,
halign = data$halign, valign = data$valign,
orientation = data$orientation,
padding = box.padding,
margin = box.margin,
width = width, minwidth = minwidth, maxwidth = maxwidth,
height = height, minheight = minheight, maxheight = maxheight,
gp = gpar(
col = scales::alpha(data$text.colour %||% data$colour, data$alpha),
fontsize = data$size * .pt,
fontfamily = data$family,
fontface = data$fontface,
lineheight = data$lineheight
),
box_gp = gpar(
col = scales::alpha(data$box.colour %||% data$colour, data$alpha),
fill = scales::alpha(data$fill, data$alpha),
lwd = data$box.size * .pt
),
r = box.r
)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.