Outlined text with ggplot2
Asked Answered
F

4

43

I'd like to know if there is a way to draw "outlined text" with ggplot2, for example black text with a small white border, in order to make it easily readable on backgrounds such as maps.

Ideally I'd like to achieve the same type of labels you can see on Google Maps :

enter image description here

Thanks in advance for any hints !

Fickle answered 21/5, 2012 at 13:19 Comment(2)
related questionEmelia
There is now a pkg for this: cran.r-project.org/web/packages/shadowtext/vignettes/…Regression
E
16

Here is an approach that implements the general idea from the shadowtext function in the TeachingDemos package. The code for the middle part could be wrapped into a function to simplify some things. The example is blatantly stolen from Richie Cotton's answer:

d <- diamonds[sample(nrow(diamonds), 10), ]  


p <- ggplot(d, aes(carat, price) ) 
theta <- seq(pi/8, 2*pi, length.out=16)
xo <- diff(range(d$carat))/200
yo <- diff(range(d$price))/200
for(i in theta) {
    p <- p + geom_text( 
        bquote(aes(x=carat+.(cos(i)*xo),y=price+.(sin(i)*yo),label=cut)), 
                    size=12, colour='black' )
}
p <- p + geom_text( aes(label=cut), size=12, colour='white' )
p <- p + opts( panel.background=theme_rect(fill='green' ) )
print(p)

enter image description here

Ebonee answered 21/5, 2012 at 20:3 Comment(6)
adding many new layers is probably a bit overkill, you could instead define a grob+geom that replaces textGrob+geom_text as one layer.Emelia
That is very cunning. I like it.Fissionable
Another small problem is that all the black labels are written at once, and then all the white labels. That's not ideal where labels overlap such as in the bottom left corner of your example. But that's certainly the best answer so far, thanks !Fickle
@baptiste, that is a good idea, do you have any references or pointers on how to define a grob+geom object?Ebonee
@GregSnow there's no real reference apart from the source code. The only guide that I know of is a bit outdated and unclear. The easiest thing would be to copy the code of geom-text.r, give it a new name, replace the textGrob used in its draw method with a vectorised version of gridExtra::stextGrob.Emelia
Very nice solution! If you add alpha you get a nice shade gradient; e.g. bquote(aes(x=carat+.(cos(i)*xo),y=price+.(sin(i)*yo),label=cut)), size=12, colour='black', alpha=1/8)Blanks
R
30

Much simplier solution is to use shadowtext library and use geom_shadowtext instead of geom_text

Recusancy answered 6/5, 2018 at 15:34 Comment(2)
Is there any way to combine geom_shadowtext with geom_text_repel?Penknife
EDIT: my previous question has been also asked here: #56318512Penknife
E
16

Here is an approach that implements the general idea from the shadowtext function in the TeachingDemos package. The code for the middle part could be wrapped into a function to simplify some things. The example is blatantly stolen from Richie Cotton's answer:

d <- diamonds[sample(nrow(diamonds), 10), ]  


p <- ggplot(d, aes(carat, price) ) 
theta <- seq(pi/8, 2*pi, length.out=16)
xo <- diff(range(d$carat))/200
yo <- diff(range(d$price))/200
for(i in theta) {
    p <- p + geom_text( 
        bquote(aes(x=carat+.(cos(i)*xo),y=price+.(sin(i)*yo),label=cut)), 
                    size=12, colour='black' )
}
p <- p + geom_text( aes(label=cut), size=12, colour='white' )
p <- p + opts( panel.background=theme_rect(fill='green' ) )
print(p)

enter image description here

Ebonee answered 21/5, 2012 at 20:3 Comment(6)
adding many new layers is probably a bit overkill, you could instead define a grob+geom that replaces textGrob+geom_text as one layer.Emelia
That is very cunning. I like it.Fissionable
Another small problem is that all the black labels are written at once, and then all the white labels. That's not ideal where labels overlap such as in the bottom left corner of your example. But that's certainly the best answer so far, thanks !Fickle
@baptiste, that is a good idea, do you have any references or pointers on how to define a grob+geom object?Ebonee
@GregSnow there's no real reference apart from the source code. The only guide that I know of is a bit outdated and unclear. The easiest thing would be to copy the code of geom-text.r, give it a new name, replace the textGrob used in its draw method with a vectorised version of gridExtra::stextGrob.Emelia
Very nice solution! If you add alpha you get a nice shade gradient; e.g. bquote(aes(x=carat+.(cos(i)*xo),y=price+.(sin(i)*yo),label=cut)), size=12, colour='black', alpha=1/8)Blanks
F
6

Not ideal or very flexible but you can get the effect by drawing bold mono text, then standard mono text on top.

I've used a green panel background to simulate the map.

d <- diamonds[sample(nrow(diamonds), 10), ]

(p <- ggplot(d, aes(carat, price)) +
  geom_text(
    aes(label = cut, family = "mono", fontface = "bold"), 
    size = 12, 
    colour = "black"
  ) +
  geom_text(
    aes(label = cut, family = "mono"), 
    size = 12, 
    colour = "white"
  ) +
  opts(panel.background = theme_rect(fill = "green"))
)

text-on-bold-text with the diamonds dataset

Fissionable answered 21/5, 2012 at 16:49 Comment(0)
S
2

The accepted answer by Greg Snow doesn't work anymore with [email protected] because of the call of aes instead of aes_q.

Use

for(i in theta) {
  p <- p + geom_text( 
    aes_q(x = bquote(carat+.(cos(i)*xo)),
          y = bquote(price+.(sin(i)*yo)),
          label = ~cut), 
    size=12, colour='black' )
}

instead.

Spatola answered 13/6, 2017 at 9:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.