How to have a clickable picture in a PDF created using pictureGrob? - r

I create a simple plot and have a picture actually an SVG icon as follows:
library(ggplot2); library(grid); library(gridExtra)
facebookGrob <- gTree(children=gList(pictureGrob(readPicture("inst/svg/facebook2.svg"))))
p1 <- ggplot() +
ggplot2::annotation_custom(facebookGrob, xmin=1.8, xmax=3.2, ymin=-0.6, ymax=1)
final <- arrangeGrob(p1,...,)
ggsave(filename='output.pdf',plot=final,...)
Is there any way to generate a clickable link on top of this SVG icon in the final PDF?

the tikzDevice package lets you insert hyperref links as nodes,
library(tikzDevice)
tikz("annotation.tex",width=4,height=4, standAlone = TRUE,
packages = c(getOption('tikzLatexPackages'),
"\\usepackage{hyperref}",
"\\usetikzlibrary{positioning}")
)
tg <- tikzNodeGrob(x = 0.5, y = 0.5, name = 'google',
content='\\href{http://www.google.com}{\\includegraphics[width=1in]{google.png}}',
units = "native")
qplot(1:10, 1:10) +
annotation_custom(grob = tg, xmin=3,xmax=3,ymin=5,ymax=5)
dev.off()

Related

Distorted image when adding text outside ggplot facets with gridExtra

I am trying to add the file name outside a plot area obtained with ggplot, using facet_wrap. I was pretty sure I found the solution in this post:
Add filename or other annotation to ggplot figures. However, applying the solution to my problem gives a distorted image.
The code to generate this is here:
require("gridExtra")
library(tidyverse)
df <- data.frame(x =runif(100, 1, 10),
y = runif(100, 1, 10),
myfacet = c("one", "two"))
p <- ggplot(data = df,
aes(x = x,
y = y)) +
geom_point() +
facet_wrap(~myfacet)
print(p)
script.name <- "myscript.R"
sub.label = textGrob(script.name,
gp=gpar(fontsize=6),
x = unit(1, "npc"),
hjust = 1,
vjust = 1)
ggsave(filename="../plots/myplot.png",
plot = arrangeGrob(p,
sub = sub.label,
clip = FALSE))
If I just use
ggsave(filename="../plots/myplot2.png",
plot = p)
I get the following image:
Please note that I need a solution that works outside the facets.
Could anyone provide a hint as to what is going on? Thank you!
grid.arrange(p, bottom = sub.label)

plot xtable or save it as image

I want to use xtable layouts in my powerpoint presentations and I need a way to directly convert a data.frame into a picture or plot of an xtable such as the ones displayed here.
The ideal solution would give me a ggplot object as I have the most flexibility from there, but I can work with another output as long as I can see a xtable in a pic (raster or vector) or plot window.
Using ggplot2 and grid.extra we can achieve decently looking tables:
library(ggplot2)
library(gridExtra)
ggplot() + annotation_custom(tableGrob(head(iris))) + theme_void()
tableGrob has a theme parameter that allows enough flexibility to reproduce something close to xtable. see this vignette.
Here's a convenient function to do a bit more, you'll need packages grid and gtable :
table_plot <- function(x,
theme_fun= gridExtra::ttheme_minimal,
base_size = 12,
base_colour = "black",
base_family = "",
parse = FALSE,
padding = unit(c(4, 4), "mm"),
col = base_colour,
lwd=1,
lty=1
){
g <- gridExtra::tableGrob(x,
theme = theme_fun(base_size, base_colour, base_family, parse, padding))
separators <- replicate(ncol(g) - 2,
grid::segmentsGrob(x1 = unit(0, "npc"), gp=gpar(col=col,lwd=lwd,lty=lty)),
simplify=FALSE)
g <- gtable::gtable_add_grob(g, grobs = separators,
t = 2, b = nrow(g), l = seq_len(ncol(g)-2)+2)
ggplot2::ggplot() + ggplot2::annotation_custom(g) + ggplot2::theme_void()
}
simple example:
table_plot(head(iris))
complicated example :
iris2 <- setNames(head(iris)[1:3],c("alpha*integral(xdx,a,infinity)", "this text\nis high", 'alpha/beta'))
table_plot(iris2,
base_size=10,
base_colour="darkred",
base_family = "serif",
parse=TRUE,
theme=ttheme_default,
lwd=3,
lty=2,
col="darkblue")

Rasterise ggplot images in R for tikzdevice

I use R to analyse data, ggplot to create plots, tikzDevice to print them and finally latex to create a report. THe problem is that large plots with many points fail due to the memory limit of latex. I found here https://github.com/yihui/tikzDevice/issues/103 a solution that rasterises the plot before printing the tikz file, which allows printing the points and the text individually.
require(png)
require(ggplot2)
require(tikzDevice)
## generate data
n=1000000; x=rnorm(n); y=rnorm(n)
## first try primitive
tikz("test.tex",standAlone=TRUE)
plot(x,y)
dev.off()
## fails due to memory
system("pdflatex test.tex")
## rasterise points first
png("inner.png",width=8,height=6,units="in",res=300,bg="transparent")
par(mar=c(0,0,0,0))
plot.new(); plot.window(range(x), range(y))
usr <- par("usr")
points(x,y)
dev.off()
# create tikz file with rasterised points
im <- readPNG("inner.png",native=TRUE)
tikz("test.tex",7,6,standAlone=TRUE)
plot.new()
plot.window(usr[1:2],usr[3:4],xaxs="i",yaxs="i")
rasterImage(im, usr[1],usr[3],usr[2],usr[4])
axis(1); axis(2); box(); title(xlab="x",ylab="y")
dev.off()
## this works
system("pdflatex test.tex")
## now with ggplot
p <- ggplot(data.frame(x=x, y=y), aes(x=x, y=y)) + geom_point()
## what here?
In this example the first pdflatex fails. The second succeeds due to the rasterisation.
How can I apply this using ggplot?
here's a proof-of-principle to illustrate the steps that would be involved. As pointed out in the comments it's not recommendable or practical, but could be the basis of a lower-level implementation.
require(png)
require(ggplot2)
require(tikzDevice)
n=100;
d <- data.frame(x=rnorm(n), y=rnorm(n), z=rnorm(n))
p <- ggplot(d, aes(x=x, y=y, colour=z, size=z, alpha=x)) + geom_point()
## draw the layer by itself on a png file
library(grid)
g <- ggplotGrob(p)
# grid.newpage()
gg <- g$grobs[[6]]$children[[3]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=10, height=4, bg = "transparent", res = 30, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
rl <- readPNG(tmp, native = TRUE)
unlink(tmp)
## add it to a plot - note that the positions match,
## but the size can be off unless one ensures that the panel has the same size and aspect ratio
ggplot(d, aes(x=x, y=y)) + geom_point(shape="+", colour="red") +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"))) +
geom_point(aes(size=z), shape=1, colour="red", show.legend = FALSE)
## to illustrate the practical use, we use a blank layer to train the scales
## and set the panel size to match the png file
pf <- ggplot(d, aes(x=x, y=y)) + geom_blank() +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"), interpolate = FALSE))
tikz("test.tex", standAlone=TRUE)
grid.draw(egg::set_panel_size(pf, width=unit(10, "cm"), height=unit(4, "cm")))
dev.off()
system("lualatex test.tex")
system("open test.pdf")
we can zoom in and check that the text is vector-based while the layer is (here low-res for demonstration) raster.
ok, I will write it here because it was too big for the comment box. Instead of adding the rasterised points to a nw plot with new scales you can actually replace the original grob with the rasterised grob by
g$grobs[[6]]$children[[3]] <- rasterGrob(rl). The problem is that it doesn't scale, so you have to know the size of the final image before. Then you can sue sth like this:
rasterise <- function(ggp,
width = 6,
height = 3,
res.raster = 300,
raster.id= c(4,3),
file = ""){
## RASTERISE
require(grid)
require(png)
## draw the layer by itself on a png file
gb <- ggplot_build(ggp)
gt <- ggplot_gtable(gb)
## calculate widths
h <- as.numeric(convertUnit(sum(gt$heights), unitTo="in"))
w <- as.numeric(convertUnit(sum(gt$widths) , unitTo="in"))
w.raster <- width-w
h.raster <- height-h
## print points as png
grid.newpage()
gg <- gt$grobs[[raster.id[1]]]$children[[raster.id[2]]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=w.raster, height=h.raster, bg = "transparent", res = res.raster, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
points <- readPNG(tmp, native = TRUE)
points <- rasterGrob(points, width = w.raster, height = h.raster, default.units = "in")
unlink(tmp)
## ADD TO PLOT
gt$grobs[[raster.id[1]]]$children[[raster.id[2]]] <- points
## PLOT TMP
### HERE YOU CAN ONLY PRINT IT IN THIS DIMENSIONS!
pdf(file, width = width, height = height)
grid.draw(gt)
dev.off()
}
And then use it with
data <- data.frame(x = rnorm(1000), y = rnorm(1000))
plot <- ggplot(data, aes(x = x, y = y)) +
geom_point() +
annotate("text", x = 2, y = 2, label = "annotation")
rasterise(ggp = plot,
width = 6,
height = 3,
res.raster = 10,
raster.id = c(4,2),
file = "~/test.pdf")
The problem remains the ID of the grob you want to rasterise. I didn't figure out a good way to find the correct one automatically. It depends on which layers you add to the plot.

Add image (png file) to header of pdf file created with R

I am trying to add an .png image (logo) to the header of my pdf report of graphs created with ggplot and printed to pdf.
I found the following example how to add a image to a ggplot plot. But, I am looking to add the .png image to he header of the pdf which is outside the ggplot area.
#-------------------------------------------------------------------------------
# Example png file
#-------------------------------------------------------------------------------
library(reshape2)
library(png)
mypngfile = download.file('http://api.altmetric.com/donut/502878_64x64.png',
destfile = 'mypng.png', mode = 'wb')
mypng = readPNG('mypng.png')
#-------------------------------------------------------------------------------
# create example plot using mtcars data frame from ggplot
#-------------------------------------------------------------------------------
library(ggplot2)
p.example = qplot(mpg, wt, data = mtcars) +
annotation_raster(mypng, ymin = 4.5, ymax= 5, xmin = 30, xmax = 35)
#-------------------------------------------------------------------------------
# print to pdf file with footnote
#-------------------------------------------------------------------------------
fname = "C:/temp/my report.pdf"
pdf(fname, 10.75, 6.5, onefile=TRUE, paper="a4r")
print(p.example)
dev.off()
...which produces a pdf that looks like this:
But, I would like the image to show up outside the ggplot area. Or more specifically, I want the image to show up in the report header (in upper left) like the following example:
I found the following function that can be used to create a text footnote but wasn't sure how to modify it to insert a .png image.
makeFootnote <- function(footnoteText= format(Sys.time(), "%d %b %Y"),
size= .4, color= grey(.5))
{
require(grid)
pushViewport(viewport())
grid.text(label= footnoteText ,
x = unit(1,"npc") - unit(12, "mm"),
y = unit(0.1, "mm"),
just=c("right", "bottom"),
gp=gpar(cex= size, col=color))
popViewport()
}
Any assistance would be greatly appreciated.
here's a suggestion,
library(ggplot2)
p.example = qplot(mpg, wt, data = mtcars)
library(grid)
library(gtable)
ann <- rasterGrob(mypng, width=unit(1,"cm"), x = unit(0.5,"cm"))
g <- ggplotGrob(p.example)
g <- gtable_add_rows(g, grobHeight(ann), 0)
g <- gtable_add_grob(g, ann, t=1, l=4)
grid.newpage()
grid.draw(g)

Adding text to a grid.table plot

I have recently started using the grid.table function from the gridExtra package to turn tabular data into png image files for use on the web. I've been delighted with it so far as it produces very good-looking output by default, sort of like a ggplot2 for tables. Like the person who asked this question I would love to see the ability to specify the justification for individual columns but that would be icing on what is an already more-ish cake.
My question is whether it is possible to add text around a grid.table so that I can give plotted tables a title and a footnote. It seems to me this should be feasible, but I don't know enough about grid graphics to be able to work out how to add grobs to the table grob. For example, this code:
require(gridExtra)
mydf <- data.frame(Item = c('Item 1','Item 2','Item 3'),
                    Value = c(10,15,20), check.names = FALSE)
grid.table(mydf,
gpar.coretext=gpar(fontsize = 16),
gpar.coltext = gpar(fontsize = 16),
gpar.rowtext = gpar(fontsize = 16),
gpar.corefill = gpar(fill = "blue", alpha = 0.5, col = NA),
h.even.alpha = 0.5,
equal.width = FALSE,
show.rownames = FALSE,
show.vlines = TRUE,
padding.h = unit(15, "mm"),
padding.v = unit(8, "mm")
)
generates this plot:
when I would really like to be able to do something like the following in code rather than by editing the image with another application:
To place text close to the table you'll want to evaluate the table size first,
library(gridExtra)
d <- head(iris)
table <- tableGrob(d)
grid.newpage()
h <- grobHeight(table)
w <- grobWidth(table)
title <- textGrob("Title", y=unit(0.5,"npc") + 0.5*h,
vjust=0, gp=gpar(fontsize=20))
footnote <- textGrob("footnote",
x=unit(0.5,"npc") - 0.5*w,
y=unit(0.5,"npc") - 0.5*h,
vjust=1, hjust=0,gp=gpar( fontface="italic"))
gt <- gTree(children=gList(table, title, footnote))
grid.draw(gt)
Edit (17/07/2015) With gridExtra >=2.0.0, this approach is no longer suitable. tableGrob now returns a gtable, which can be more easily customised.
library(gridExtra)
d <- head(iris)
table <- tableGrob(d)
library(grid)
library(gtable)
title <- textGrob("Title",gp=gpar(fontsize=50))
footnote <- textGrob("footnote", x=0, hjust=0,
gp=gpar( fontface="italic"))
padding <- unit(0.5,"line")
table <- gtable_add_rows(table,
heights = grobHeight(title) + padding,
pos = 0)
table <- gtable_add_rows(table,
heights = grobHeight(footnote)+ padding)
table <- gtable_add_grob(table, list(title, footnote),
t=c(1, nrow(table)), l=c(1,2),
r=ncol(table))
grid.newpage()
grid.draw(table)
If you want just the title (no footnote), here is a simplified version of #baptiste's example:
title <- textGrob("Title", gp = gpar(fontsize = 50))
padding <- unit(0.5,"line")
table <- gtable_add_rows(
table, heights = grobHeight(title) + padding, pos = 0
)
table <- gtable_add_grob(
table, list(title),
t = 1, l = 1, r = ncol(table)
)
grid.newpage()
grid.draw(table)

Resources