Skip to content

Commit fb2c4e9

Browse files
committed
Implement geom_label. Closes #1039
1 parent a1827a1 commit fb2c4e9

11 files changed

+211
-11
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Collate:
100100
'geom-freqpoly.r'
101101
'geom-hex.r'
102102
'geom-hline.r'
103+
'geom-label.R'
103104
'geom-linerange.r'
104105
'geom-path-contour.r'
105106
'geom-path-density2d.r'

NAMESPACE

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ S3method(limits,POSIXlt)
119119
S3method(limits,character)
120120
S3method(limits,factor)
121121
S3method(limits,numeric)
122+
S3method(makeContent,labelgrob)
122123
S3method(plot,ggplot)
123124
S3method(predictdf,default)
124125
S3method(predictdf,glm)
@@ -197,6 +198,7 @@ export(GeomErrorbar)
197198
export(GeomErrorbarh)
198199
export(GeomHex)
199200
export(GeomHline)
201+
export(GeomLabel)
200202
export(GeomLine)
201203
export(GeomLinerange)
202204
export(GeomLogticks)
@@ -282,10 +284,12 @@ export(draw_key_blank)
282284
export(draw_key_boxplot)
283285
export(draw_key_crossbar)
284286
export(draw_key_dotplot)
287+
export(draw_key_label)
285288
export(draw_key_path)
286289
export(draw_key_point)
287290
export(draw_key_pointrange)
288291
export(draw_key_polygon)
292+
export(draw_key_rect)
289293
export(draw_key_smooth)
290294
export(draw_key_text)
291295
export(draw_key_vline)
@@ -319,6 +323,7 @@ export(geom_hex)
319323
export(geom_histogram)
320324
export(geom_hline)
321325
export(geom_jitter)
326+
export(geom_label)
322327
export(geom_line)
323328
export(geom_linerange)
324329
export(geom_map)

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
ggplot2 1.0.1.9xxx
22
----------------------------------------------------------------
33

4+
* `geom_label()` works like `geom_text()` but draws a solid background
5+
behind each label (#1039).
6+
47
* `stat_bindot()` has been removed because it's so tightly coupled to
58
`geom_dotplot()`. If you happened to use `stat_bindot()`, just change to
69
`geom_dotplot()` (#1194).

R/geom-label.R

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#' @export
2+
#' @rdname geom_text
3+
#' @param label.padding Amount of padding around label. Defaults to 0.25 lines.
4+
#' @param label.r Radius of rounded corners. Defaults to 0.15 lines.
5+
geom_label <- function(mapping = NULL, data = NULL, stat = "identity",
6+
position = "identity", parse = FALSE, show_guide = NA,
7+
inherit.aes = TRUE, ..., nudge_x = 0, nudge_y = 0,
8+
label.padding = grid::unit(0.25, "lines"),
9+
label.r = grid::unit(0.15, "lines")) {
10+
if (!missing(nudge_x) || !missing(nudge_y)) {
11+
if (!missing(position)) {
12+
stop("Specify either `position` or `nudge_x`/`nudge_y`", call. = FALSE)
13+
}
14+
15+
position <- position_nudge(nudge_x, nudge_y)
16+
}
17+
18+
layer(
19+
data = data,
20+
mapping = mapping,
21+
stat = stat,
22+
geom = GeomLabel,
23+
position = position,
24+
show_guide = show_guide,
25+
inherit.aes = inherit.aes,
26+
geom_params = list(
27+
parse = parse,
28+
label.padding = label.padding,
29+
label.r = label.r
30+
),
31+
params = list(...)
32+
)
33+
}
34+
35+
36+
#' @rdname ggplot2-ggproto
37+
#' @format NULL
38+
#' @usage NULL
39+
#' @export
40+
GeomLabel <- ggproto("GeomLabel", Geom,
41+
draw_groups = function(self, data, ...) {
42+
if (empty(data)) return(zeroGrob())
43+
44+
grobs <- lapply(1:nrow(data), function(i) {
45+
self$draw_one(data[i, , drop = FALSE], ...)
46+
})
47+
class(grobs) <- "gList"
48+
49+
ggname("geom_label", grobTree(children = grobs))
50+
},
51+
52+
draw_one = function(data, scales, coordinates, ..., parse = FALSE, na.rm = FALSE,
53+
label.padding = grid::unit(0.25, "lines"),
54+
label.r = grid::unit(0.15, "lines")) {
55+
data <- remove_missing(data, na.rm, c("x", "y", "label"), name = "geom_label")
56+
lab <- data$label
57+
if (parse) {
58+
lab <- parse(text = lab)
59+
}
60+
61+
coords <- coord_transform(coordinates, data, scales)
62+
if (is.character(coords$vjust)) {
63+
coords$vjust <- compute_just(coords$vjust, coords$y)
64+
}
65+
if (is.character(coords$hjust)) {
66+
coords$hjust <- compute_just(coords$hjust, coords$x)
67+
}
68+
69+
labelGrob(lab,
70+
x = unit(coords$x, "native"),
71+
y = unit(coords$y, "native"),
72+
just = c(coords$hjust, coords$vjust),
73+
padding = label.padding,
74+
r = label.r,
75+
text.gp = gpar(
76+
col = coords$colour,
77+
fontsize = coords$size * .pt,
78+
fontfamily = coords$family,
79+
fontface = coords$fontface,
80+
lineheight = coords$lineheight
81+
),
82+
rect.gp = gpar(
83+
col = coords$colour,
84+
fill = alpha(coords$fill, coords$alpha)
85+
)
86+
)
87+
},
88+
89+
required_aes = c("x", "y", "label"),
90+
91+
default_aes = aes(colour = "black", fill = "white", size = 5, angle = 0,
92+
hjust = 0.5, vjust = 0.5, alpha = NA, family = "", fontface = 1, lineheight = 1.2),
93+
94+
draw_key = draw_key_label
95+
)
96+
97+
labelGrob <- function(label, x = unit(0.5, "npc"), y = unit(0.5, "npc"),
98+
just = "center", padding = unit(0.25, "lines"), r = unit(0.1, "snpc"),
99+
default.units = "npc", name = NULL,
100+
text.gp = gpar(), rect.gp = gpar(fill = "white"), vp = NULL) {
101+
102+
stopifnot(length(label) == 1)
103+
104+
if (!is.unit(x))
105+
x <- unit(x, default.units)
106+
if (!is.unit(y))
107+
y <- unit(y, default.units)
108+
109+
gTree(label = label, x = x, y = y, just = just, padding = padding, r = r,
110+
name = name, text.gp = text.gp, rect.gp = rect.gp, vp = vp, cl = "labelgrob")
111+
}
112+
113+
#' @export
114+
makeContent.labelgrob <- function(x) {
115+
hj <- resolveHJust(x$just, NULL)
116+
vj <- resolveVJust(x$just, NULL)
117+
118+
t <- textGrob(
119+
x$label,
120+
x$x + 2 * (0.5 - hj) * x$padding,
121+
x$y + 2 * (0.5 - vj) * x$padding,
122+
just = c(hj, vj),
123+
gp = x$text.gp,
124+
name = "text"
125+
)
126+
127+
r <- roundrectGrob(x$x, x$y, default.units = "native",
128+
width = grobWidth(t) + 2 * x$padding,
129+
height = grobHeight(t) + 2 * x$padding,
130+
just = c(hj, vj),
131+
r = x$r,
132+
gp = x$rect.gp,
133+
name = "box"
134+
)
135+
136+
setChildren(x, gList(r, t))
137+
}

R/geom-raster.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,5 @@ GeomRaster <- ggproto("GeomRaster", Geom,
130130

131131
required_aes = c("x", "y"),
132132

133-
draw_key = draw_key_polygon
133+
draw_key = draw_key_rect
134134
)

R/geom-text.r

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
#' Textual annotations.
22
#'
3+
#' \code{geom_text} adds text directly to the plot. \code{geom_label} draws
4+
#' a rectangle underneath the text, making it easier to read.
5+
#'
36
#' @section Aesthetics:
47
#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "text")}
58
#'
9+
#' @section \code{geom_label}:
10+
#' Currently \code{geom_label} does not support the \code{rot} parameter and
11+
#' is considerably slower than \code{geom_text}. The \code{fill} aesthetic
12+
#' controls the background colour of the label.
13+
#'
614
#' @section Alignment:
715
#' You can modify text alignment with the \code{vjust} and \code{hjust}
816
#' aesthetics. These can either be a number between 0 (right/bottom) and
@@ -25,6 +33,8 @@
2533
#' p + geom_text()
2634
#' # Avoid overlaps
2735
#' p + geom_text(check_overlap = TRUE)
36+
#' # Labels with background
37+
#' p + geom_label()
2838
#' # Change size of the label
2939
#' p + geom_text(size = 10)
3040
#'
@@ -40,6 +50,7 @@
4050
#' p + geom_text(aes(colour = factor(cyl)))
4151
#' p + geom_text(aes(colour = factor(cyl))) +
4252
#' scale_colour_discrete(l = 40)
53+
#' p + geom_label(aes(fill = factor(cyl)), colour = "white", fontface = "bold")
4354
#'
4455
#' p + geom_text(aes(size = wt))
4556
#' # Scale height of text, rather than sqrt(height)

R/geom-tile.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,5 @@ GeomTile <- ggproto("GeomTile", Geom,
105105

106106
required_aes = c("x", "y"),
107107

108-
draw_key = draw_key_polygon
108+
draw_key = draw_key_rect
109109
)

R/legend-draw.r

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,20 @@ draw_key_abline <- function(data, params) {
3838
)
3939
}
4040

41+
#' @export
42+
#' @rdname draw_key
43+
draw_key_rect <- function(data, params) {
44+
rectGrob(gp = gpar(
45+
col = NA,
46+
fill = alpha(data$fill, data$alpha),
47+
lty = data$linetype
48+
))
49+
}
4150
#' @export
4251
#' @rdname draw_key
4352
draw_key_polygon <- function(data, params) {
4453
grobTree(
45-
rectGrob(gp = gpar(
46-
col = data$colour,
47-
fill = alpha(data$fill, data$alpha),
48-
lty = data$linetype
49-
)),
54+
draw_key_rect(data, list()),
5055
linesGrob(gp = gpar(
5156
col = data$colour,
5257
lwd = data$size * .pt,
@@ -155,6 +160,15 @@ draw_key_text <- function(data, params) {
155160
)
156161
}
157162

163+
#' @export
164+
#' @rdname draw_key
165+
draw_key_label <- function(data, params) {
166+
grobTree(
167+
draw_key_rect(data, list()),
168+
draw_key_text(data, list())
169+
)
170+
}
171+
158172
#' @export
159173
#' @rdname draw_key
160174
draw_key_vline <- function(data, params) {

man/draw_key.Rd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
\alias{draw_key_boxplot}
88
\alias{draw_key_crossbar}
99
\alias{draw_key_dotplot}
10+
\alias{draw_key_label}
1011
\alias{draw_key_path}
1112
\alias{draw_key_point}
1213
\alias{draw_key_pointrange}
1314
\alias{draw_key_polygon}
15+
\alias{draw_key_rect}
1416
\alias{draw_key_smooth}
1517
\alias{draw_key_text}
1618
\alias{draw_key_vline}
@@ -20,6 +22,8 @@ draw_key_point(data, params)
2022

2123
draw_key_abline(data, params)
2224

25+
draw_key_rect(data, params)
26+
2327
draw_key_polygon(data, params)
2428

2529
draw_key_blank(data, params)
@@ -38,6 +42,8 @@ draw_key_smooth(data, params)
3842

3943
draw_key_text(data, params)
4044

45+
draw_key_label(data, params)
46+
4147
draw_key_vline(data, params)
4248
}
4349
\arguments{

man/geom_text.Rd

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
% Generated by roxygen2 (4.1.1): do not edit by hand
2-
% Please edit documentation in R/geom-text.r
3-
\name{geom_text}
2+
% Please edit documentation in R/geom-label.R, R/geom-text.r
3+
\name{geom_label}
4+
\alias{geom_label}
45
\alias{geom_text}
56
\title{Textual annotations.}
67
\usage{
8+
geom_label(mapping = NULL, data = NULL, stat = "identity",
9+
position = "identity", parse = FALSE, show_guide = NA,
10+
inherit.aes = TRUE, ..., nudge_x = 0, nudge_y = 0,
11+
label.padding = grid::unit(0.25, "lines"), label.r = grid::unit(0.15,
12+
"lines"))
13+
714
geom_text(mapping = NULL, data = NULL, stat = "identity",
815
position = "identity", parse = FALSE, show_guide = NA,
916
inherit.aes = TRUE, ..., nudge_x = 0, nudge_y = 0,
@@ -49,17 +56,29 @@ the default plot specification, e.g. \code{\link{borders}}.}
4956
\item{nudge_x,nudge_y}{Horizontal and vertical adjustment to nudge labels by.
5057
Useful for offseting text from points, particularly on discrete scales.}
5158
59+
\item{label.padding}{Amount of padding around label. Defaults to 0.25 lines.}
60+
61+
\item{label.r}{Radius of rounded corners. Defaults to 0.15 lines.}
62+
5263
\item{check_overlap}{If \code{TRUE}, text that overlaps previous text in the
5364
same layer will not be plotted. A quick and dirty way}
5465
}
5566
\description{
56-
Textual annotations.
67+
\code{geom_text} adds text directly to the plot. \code{geom_label} draws
68+
a rectangle underneath the text, making it easier to read.
5769
}
5870
\section{Aesthetics}{
5971
6072
\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "text")}
6173
}
6274
75+
\section{\code{geom_label}}{
76+
77+
Currently \code{geom_label} does not support the \code{rot} parameter and
78+
is considerably slower than \code{geom_text}. The \code{fill} aesthetic
79+
controls the background colour of the label.
80+
}
81+
6382
\section{Alignment}{
6483
6584
You can modify text alignment with the \code{vjust} and \code{hjust}
@@ -75,6 +94,8 @@ p <- ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars)))
7594
p + geom_text()
7695
# Avoid overlaps
7796
p + geom_text(check_overlap = TRUE)
97+
# Labels with background
98+
p + geom_label()
7899
# Change size of the label
79100
p + geom_text(size = 10)
80101
@@ -90,6 +111,7 @@ p + geom_text(family = "Times New Roman")
90111
p + geom_text(aes(colour = factor(cyl)))
91112
p + geom_text(aes(colour = factor(cyl))) +
92113
scale_colour_discrete(l = 40)
114+
p + geom_label(aes(fill = factor(cyl)), colour = "white", fontface = "bold")
93115
94116
p + geom_text(aes(size = wt))
95117
# Scale height of text, rather than sqrt(height)

man/ggplot2-ggproto.Rd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
% Generated by roxygen2 (4.1.1): do not edit by hand
2-
% Please edit documentation in R/aaa-.r, R/annotation-custom.r, R/annotation-logticks.r, R/annotation-map.r, R/annotation-raster.r, R/geom-.r, R/geom-abline.r, R/geom-bar-.r, R/geom-blank.r, R/geom-boxplot.r, R/geom-crossbar.r, R/geom-curve.r, R/geom-dotplot.r, R/geom-errorbar.r, R/geom-errorbarh.r, R/geom-hex.r, R/geom-hline.r, R/geom-linerange.r, R/geom-map.r, R/geom-path-.r, R/geom-path-contour.r, R/geom-path-density2d.r, R/geom-path-line.r, R/geom-path-step.r, R/geom-point-.r, R/geom-pointrange.r, R/geom-polygon.r, R/geom-quantile.r, R/geom-raster.r, R/geom-rect.r, R/geom-ribbon-.r, R/geom-ribbon-density.r, R/geom-rug.r, R/geom-segment.r, R/geom-smooth.r, R/geom-text.r, R/geom-tile.r, R/geom-violin.r, R/geom-vline.r, R/position-.r, R/position-dodge.r, R/position-fill.r, R/position-identity.r, R/position-jitter.r, R/position-jitterdodge.R, R/position-nudge.R, R/position-stack.r, R/stat-.r, R/stat-bin.r, R/stat-bin2d.r, R/stat-bindot.r, R/stat-binhex.r, R/stat-boxplot.r, R/stat-contour.r, R/stat-density-2d.r, R/stat-density.r, R/stat-ecdf.r, R/stat-ellipse.R, R/stat-function.r, R/stat-identity.r, R/stat-qq.r, R/stat-quantile.r, R/stat-smooth.r, R/stat-spoke.r, R/stat-sum.r, R/stat-summary-2d.r, R/stat-summary-hex.r, R/stat-summary.r, R/stat-unique.r, R/stat-ydensity.r
2+
% Please edit documentation in R/aaa-.r, R/annotation-custom.r, R/annotation-logticks.r, R/annotation-map.r, R/annotation-raster.r, R/geom-.r, R/geom-abline.r, R/geom-bar-.r, R/geom-blank.r, R/geom-boxplot.r, R/geom-crossbar.r, R/geom-curve.r, R/geom-dotplot.r, R/geom-errorbar.r, R/geom-errorbarh.r, R/geom-hex.r, R/geom-hline.r, R/geom-label.R, R/geom-linerange.r, R/geom-map.r, R/geom-path-.r, R/geom-path-contour.r, R/geom-path-density2d.r, R/geom-path-line.r, R/geom-path-step.r, R/geom-point-.r, R/geom-pointrange.r, R/geom-polygon.r, R/geom-quantile.r, R/geom-raster.r, R/geom-rect.r, R/geom-ribbon-.r, R/geom-ribbon-density.r, R/geom-rug.r, R/geom-segment.r, R/geom-smooth.r, R/geom-text.r, R/geom-tile.r, R/geom-violin.r, R/geom-vline.r, R/position-.r, R/position-dodge.r, R/position-fill.r, R/position-identity.r, R/position-jitter.r, R/position-jitterdodge.R, R/position-nudge.R, R/position-stack.r, R/stat-.r, R/stat-bin.r, R/stat-bin2d.r, R/stat-bindot.r, R/stat-binhex.r, R/stat-boxplot.r, R/stat-contour.r, R/stat-density-2d.r, R/stat-density.r, R/stat-ecdf.r, R/stat-ellipse.R, R/stat-function.r, R/stat-identity.r, R/stat-qq.r, R/stat-quantile.r, R/stat-smooth.r, R/stat-spoke.r, R/stat-sum.r, R/stat-summary-2d.r, R/stat-summary-hex.r, R/stat-summary.r, R/stat-unique.r, R/stat-ydensity.r
33
\docType{data}
44
\name{ggplot2-ggproto}
55
\alias{Geom}
@@ -20,6 +20,7 @@
2020
\alias{GeomErrorbarh}
2121
\alias{GeomHex}
2222
\alias{GeomHline}
23+
\alias{GeomLabel}
2324
\alias{GeomLine}
2425
\alias{GeomLinerange}
2526
\alias{GeomLogticks}

0 commit comments

Comments
 (0)