问题
I want to plot a label over a line in a monochrome graphic. So I need small white border on each letter of the label.
The border or background of rectangle of the text label is not useful because hide a lot of data plotted.
Is there any way to put border, shadow or buffer around text labels in R plots?
EDIT:
shadowtext <- function(x, y=NULL, labels, col='white', bg='black',
theta= seq(pi/4, 2*pi, length.out=8), r=0.1, ... ) {
xy <- xy.coords(x,y)
xo <- r*strwidth('x')
yo <- r*strheight('x')
for (i in theta) {
text( xy$x + cos(i)*xo, xy$y + sin(i)*yo, labels, col=bg, ... )
}
text(xy$x, xy$y, labels, col=col, ... )
}
pdf(file="test.pdf", width=2, height=2); par(mar=c(0,0,0,0)+.1)
plot(c(0,1), c(0,1), type="l", lwd=20, axes=FALSE, xlab="", ylab="")
text(1/6, 1/6, "Test 1")
text(2/6, 2/6, "Test 2", col="white")
shadowtext(3/6, 3/6, "Test 3")
shadowtext(4/6, 4/6, "Test 4", col="black", bg="white")
shadowtext(5/6, 5/6, "Test 5", col="black", bg="white", theta = seq(pi/4, 2*pi, length.out=24))
dev.off()
The code above use the solution from koekenbakker. This is fine for PNG graphic but I need a different approach for high resolution PDF.
回答1:
You can try this 'shadowtext' function that draws a halo or border around the text by printing it several times with a slight offset in a different colour. All credits to Greg Snow here.
shadowtext <- function(x, y=NULL, labels, col='white', bg='black',
theta= seq(0, 2*pi, length.out=50), r=0.1, ... ) {
xy <- xy.coords(x,y)
xo <- r*strwidth('A')
yo <- r*strheight('A')
# draw background text with small shift in x and y in background colour
for (i in theta) {
text( xy$x + cos(i)*xo, xy$y + sin(i)*yo, labels, col=bg, ... )
}
# draw actual text in exact xy position in foreground colour
text(xy$x, xy$y, labels, col=col, ... )
}
# And here is an example of use:
# pdf(file="test2.pdf", width=2, height=2); par(mar=c(0,0,0,0)+.1)
plot(c(0,1), c(0,1), type="n", lwd=20, axes=FALSE, xlab="", ylab="")
rect(xleft = 0.5, xright = 1, ybottom = 0, ytop = 1, col=1)
text(1/6, 1/6, "Test 1")
shadowtext(2/6, 2/6, "Test 2", col='red', bg="blue")
shadowtext(3/6, 3/6, "Test 3", cex=2)
# `r` controls the width of the border
shadowtext(5/6, 5/6, "Test 4", col="black", bg="white", cex=4, r=0.2)
# dev.off()
回答2:
I needed to do this for a map in R and ended up using the "raster" package to draw halos around text labels.
http://rpackages.ianhowson.com/cran/raster/man/text.html
e.g.,
library(raster)
text(Points, labels = Points$Labels, halo = TRUE, hw = 0.08, hc = "white", cex = 0.8)
# hw = halo width
# hc = halo color
回答3:
The shadowtext package can be used to draw outline or shadow around text for ggplot2 plots.
library(ggplot2)
library(shadowtext)
jet.colors <- colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F",
"yellow", "#FF7F00", "red", "#7F0000"))
### Note: jet (rainbow) is not color-blind friendly, not perceptually uniform, and can be misleading
# so please don't use it for your plots
# https://blogs.egu.eu/divisions/gd/2017/08/23/the-rainbow-colour-map/
# https://www.nature.com/articles/519291d
# Choose viridis instead https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html
# I only use jet here for the demonstration of the `shadowtext` package.
ggplot(faithfuld, aes(waiting, eruptions)) +
geom_raster(aes(fill = density)) +
scale_fill_gradientn(colors = jet.colors(7)) +
geom_shadowtext(aes(x = 75, y = 4.5),
label = "White text with black outline\nwill be visible on any background",
check_overlap = TRUE,
size = 8) +
theme_minimal()
Created on 2018-10-14 by the reprex package (v0.2.1.9000)
回答4:
Here is my attempt to add a text buffer when labelling contours (or circles in this example). It's definitely not as effective or pretty as koekenbakker's solution but it served its purpose for me to isolate the text label from the line.
require(shape)
plot(250,250, xlim=c(0,500), ylim=c(0,500), axes=F, ylab="", xlab="", col="black") ## set up the plotting area
circle_radius<-c(200, 150, 50) ## we wan't to draw a few circles which we will label after using our buffer
for (i in 1:length(circle_radius)){
plotcircle(mid=c(250,250), r=circle_radius[i], lwd=0.5,lcol="grey40") ## iteratively plot the circles
text_buffer<-seq(0.1, 0.7, by=0.01) ## this is the key to the text buffer. Create a vector of values to set the character size (cex) during the following loop
for(j in text_buffer){
text(x=250+circle_radius[i], 250, print(paste(circle_radius[i],"m")), cex=j, srt=270, col="white") ## write the text buffer in white starting from the smallest value of cex to the largest
} ## end of loop for writing buffer
text(x=250+circle_radius[i], 250, print(paste(circle_radius[i],"m")), cex=0.5, srt=270) ## write over the buffer with black text
} ## end of loop for drawing circles
回答5:
I wrote a similar function for text fields, which also works on logarithmic scales.
install.packages("berryFunctions")
library("berryFunctions")
?textField
This may be considered nicer for vector graphics. Here are some examples:
PS: If you want to contribute: https://github.com/brry/berryFunctions
来源:https://stackoverflow.com/questions/25631216/r-plots-is-there-any-way-to-draw-border-shadow-or-buffer-around-text-labels