Category Archives: Technical

Image CGI with R

I received an email recently with a question about using R in the common gateway interface (CGI) framework to create and pass image data to the web browser. I posted to stackoverflow about this some time ago, but had forgotten the details. The trouble is that R's graphics devices only write image data to file, rather than to a buffer or R connection. Hence, when passing image data via CGI, it is necessary to write an intermediary image file, read the file, and then output the data to STDOUT. Unfortunately, R will not allow you to write binary data to STDOUT (for example, try writeBin(as.raw("0xFF"), stdout()). So, this becomes even more complicated! One solution is to open a (binary) pipe connection to the Linux cat command, which sends it's output to STDOUT, and write the image data to the pipe connection. Of course, this is a platform dependent solution. Below is a CGI script that implements this:

#!/bin/sh
REXEC="/usr/local/bin/R --vanilla --slave"
$REXEC <<EOF

# create a random file name
pngfile <- paste0(format(Sys.time(), "%Y%m%d%H%M%S"),
  paste(sample(letters,10), collapse=""), ".png")

# create temporary graphic file
png(pngfile, type = "cairo")
x <- seq(-10, 10, length= 30)
y <- x
f <- function(x, y) { r <- sqrt(x^2+y^2); 10 * sin(r)/r }
z <- outer(x, y, f)
z[is.na(z)] <- 1
op <- par(bg = rgb(1,1,1,0))
persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue")
persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue",
  ltheta = 120, shade = 0.75, ticktype = "detailed",
  xlab = "X", ylab = "Y", zlab = "Sinc( r )")
invisible(dev.off())

# write headers
pngsize <- file.info(pngfile)[["size"]]
cat("Content-type: image/png\n")
cat(paste("Content-length: ", pngsize, "\n\n", sep=""))

# open pipe to stdout and pass image data
con <- pipe("cat", "wb")
writeBin(readBin(pngfile, 'raw', n=pngsize), con)
flush(con)
close(con)

# remove intermediate graphic
invisible(file.remove(pngfile))

EOF
###