% !TeX root = RJwrapper.tex \title{The gridSVG Package} \author{by Paul Murrell and Simon Potter} \newcommand{\js}{JavaScript} \maketitle \abstract{ The \pkg{gridSVG} package can be used to generate a \pkg{grid}-based R plot in an SVG format, with the ability to add special effects to the plot. The special effects include animation, interactivity, and advanced graphical features, such as masks and filters. This article provides a basic introduction to important functions in the \pkg{gridSVG} package and discusses the advantages and disadvantages of \pkg{gridSVG} compared to similar R packages. } <>= options(continue=" ") @ \section{Introduction} The SVG graphics format \citep{Dengler:11:SVG} is a good format for including plots in web pages because it is a vector format (so it scales well) and because it offers features for animation and interactivity. It is possible to produce a static R plot in an SVG format with the built-in \code{svg()} function (from the \pkg{grDevices} package), but the \CRANpkg{gridSVG} package \citep{gridsvg} provides an alternative way to generate an SVG plot that allows for creating animated and interactive graphics. This article demonstrates basic usage of the \pkg{gridSVG} package and outlines some of the ways that \pkg{gridSVG} can be used to produce graphical results that are not possible in standard R graphics. There is also a discussion of other packages that provide ways to generate dynamic and interactive graphics for the web and the strengths and weaknesses of \pkg{gridSVG} compared to those packages. \section{Basic usage} As the package name suggests, the \pkg{gridSVG} package starts with a plot that was drawn using the \pkg{grid} graphics package, or a package built on top of \pkg{grid}, like \CRANpkg{lattice} \citep{pkg:lattice} or \CRANpkg{ggplot2} \citep{pkg:ggplot}. Figure \ref{fig:lattice} shows an example plot, on a standard R graphics device, that was produced using the \CRANpkg{osmar} and \CRANpkg{sp} packages \citep{pkg:osmar, pkg:sp}. The \pkg{sp} function \code{spplot()} uses \pkg{lattice} (which uses \pkg{grid}) for drawing. \begin{figure} \begin{center} \includegraphics[width=.5\textwidth]{campus-3} \end{center} \caption{\label{fig:lattice}A map showing buildings, streets, and paths in the region around The University of Auckland campus. The map was produced using the \pkg{osmar} and \pkg{sp} packages (and \pkg{lattice} and \pkg{grid} for the drawing) on the standard \code{pdf()} graphics device in R.} \end{figure} The \code{grid.export()} function in \pkg{gridSVG} converts the current (\pkg{grid}) scene on the active graphics device to an SVG format in an external file. The SVG file can be viewed directly in a browser (see Figure \ref{fig:lattice-web}) or embedded within HTML as part of a larger web page. \begin{figure} \begin{center} \includegraphics[width=.5\textwidth]{campus-svg.png} \end{center} \caption{\label{fig:lattice-web}The map from Figure \ref{fig:lattice} exported to an SVG file by \pkg{gridSVG} and viewed in Firefox. This demonstrates that a static R plot can be converted to an SVG format with \pkg{gridSVG} for use on the web.} \end{figure} This usage of \pkg{gridSVG}, to produce a static SVG version of an R plot for use on the web, offers no obvious benefit compared to the built-in \code{svg()} graphics device. However, the \pkg{gridSVG} package provides several other functions that can be used to enhance the SVG version of an R plot. \section{A simple example} In order to demonstrate, with code, some of the distinctive features of \pkg{gridSVG}, we introduce a simple \pkg{grid} scene that is inspired by the Monty Hall problem.\footnote{\url{http://en.wikipedia.org/wiki/Monty\_Hall\_problem}} <<>>= library(grid) @ The scene consists of three words, ``goat'', ``goat'', and ``car'', drawn in random order across the page, with an opaque rectangle drawn on top of each word. In relation to the Monty Hall problem, the three rectangles represent three ``doors'', behind which are hidden two goats and a car. A ``contestant'' must choose a door and then he or she gets the ``prize'' behind that door. However, after the contestant has chosen a door, a ``game show host'' opens one of the other doors to reveal a ``goat'' and the contestant gets the opportunity to change to the remaining unopened door or stick with the original choice. Should the contestant stick or switch?\footnote{An exercise for the reader is to determine which door conceals the car based on the R code and figures presented in this article.} The following code produces the scene and the result is shown in Figure \ref{fig:montyhall}. The main drawing code is wrapped up in a function so that we can reuse it later on. <>= text <- sample(c("goat", "goat", "car")) cols <- hcl(c(0, 120, 240), 80, 80) @ <>= # Fix the order of the text so I can rely on it in screenshots, etc text <- c("car", "goat", "goat") @ <>= MontyHall <- function() { grid.newpage() grid.text(text, 1:3/4, gp=gpar(cex=2), name="prizes") for (i in 1:3) { grid.rect(i/4 - .1, width=.2, height=.8, just="left", gp=gpar(fill=cols[i]), name=paste0("door", i)) } } @ <>= MontyHall() @ \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{murrell-potter-montyhall} \end{center} \caption{\label{fig:montyhall}A diagram of the Monty Hall problem, drawn using \pkg{grid}. Hidden behind each rectangle is either the word ``goat'' or the word ``car'').} \end{figure} The three rectangles in this scene have been given names---\code{"door1"}, \code{"door2"}, and \code{"door3"}---as shown in the output from \pkg{grid}'s \code{grid.ls()} function below. <>= grid.ls() @ <>= MontyHall() grid.ls(full=TRUE) @ These names will be used later to identify the rectangles so that we can modify them to generate special effects. \section{Hyperlinks} The \code{grid.hyperlink()} function from the \pkg{gridSVG} package can be used to add hyperlinks to parts of a \pkg{grid} scene. For example, the following code adds a link to each door so that clicking on a door (while viewing the SVG version of the scene in a browser) leads to a Google Image Search on either ``car'' or ``goat'' depending on what is behind the door. The first argument to \code{grid.hyperlink()} is the name of the \pkg{grid} object with which to associate the hyperlink. The \code{href} argument provides the actual link. <<>>= library(gridSVG) @ <>= links <- c("http://www.google.com/search?q=car&tbm=isch", "http://www.google.com/search?q=goat&tbm=isch") for (i in 1:3) { grid.hyperlink(paste0("door", i), href=links[match(text[i], c("car", "goat"))]) } @ After running this code, the scene is completely unchanged on a normal graphics device, but if we use \code{grid.export()} to convert the scene to SVG, we end up with an image that contains hyperlinks. Figure \ref{fig:hyper} shows the result, with the mouse hovering over the middle door; at the bottom-left of the browser window, we can see from the hyperlink that there is a goat behind this door. <>= grid.export("montyhall-hyper.svg") @ <>= gridsvg("montyhall-hyper.svg", width=6, height=3, prefix="hyper-") <> <> dev.off() @ % profile paul-shutter includes "capture mouse" and "delay 3 secs" % sleep 3; shutter -p paul-shutter -a -e -o montyhall-hyper-svg.png \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{montyhall-hyper-svg.png} \end{center} \caption{\label{fig:hyper}The Monty Hall image, with a hyperlink on each door. The mouse is hovering over the middle door and the browser is showing the hyperlink target in the bottom-left of its window. If we click the mouse, we will navigate to a Google Image Search for the word ``goat''.} \end{figure} \section{Animation} The function \code{grid.animate()} allows us to animate the features of shapes in a \pkg{grid} scene. For example, the following code draws the Monty Hall scene again and then animates the width of the middle door so that it slides open (to reveal the word ``goat''). The first argument to \code{grid.animate()} is the name of the object to animate. Subsequent arguments specify which feature of the object to animate, in this case \code{width}, plus the values for the animation. The \code{duration} argument controls how long the animation will last. <>= MontyHall() goatDoor <- grep("goat", text)[1] grid.animate(paste0("door", goatDoor), width=c(.2, 0), duration=2) @ <>= grid.export("montyhall-anim.svg") @ <>= gridsvg("montyhall-anim.svg", width=6, height=3, prefix="anim-") <> dev.off() @ <>= # Version for screen cap (end with door half open) gridsvg("montyhall-anim-cap.svg", width=6, height=3) MontyHall() grid.animate(paste0("door", goatDoor), width=c(.2, .1), duration=2) dev.off() @ Again, no change is visible on a normal R graphics device, but if we export to SVG and view the result in a browser, we see the animation (see Figure \ref{fig:anim}). % sleep 3; shutter -p paul-shutter -a -e -o montyhall-anim-svg.png \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{montyhall-anim-svg.png} \end{center} \caption{\label{fig:anim}The Monty Hall image, with the middle ``door'' animated so that it slides open (to reveal the word ``goat'').} \end{figure} \section{Advanced graphics features} The \pkg{gridSVG} package offers several graphics features that are not available in standard R graphics devices. These include non-rectangular clipping paths, masks, fill patterns and fill gradients, and filters \citep{svgadvancedtech}. This section demonstrates the use of a mask on the Monty Hall scene. A mask is a greyscale image that is used to affect the transparency (or alpha-channel) of another image: anywhere the mask is white, the masked image is fully visible; anywhere the mask is black, the masked image is invisible; and anywhere the mask is grey, the masked image is semitransparent. The following code defines a simple scene consisting of a white cross on top of a grey circle on a white background, which we will use as a mask (see Figure \ref{fig:mask}). Any \pkg{grid} scene can be used to create a mask. <>= circleMask <- gTree(children=gList(rectGrob(gp=gpar(col=NA, fill="white")), circleGrob(x=goatDoor/4, r=.15, gp=gpar(col=NA, fill="grey")), polylineGrob(c(0, 1, .5, .5), c(.5, .5, 0, 1), id=rep(1:2, each=2), gp=gpar(lwd=10, col="white")))) @ <>= grid.draw(circleMask) grid.rect(gp=gpar(col="grey")) @ The next code shows how this crossed circle on a white background can be used as a mask to affect the transparency of one of the rectangles in the Monty Hall scene. The first argument to \code{grid.mask()} is the name of the object to mask and the second argument is the mask object (as created by the \code{mask()} function). <>= MontyHall() grid.mask(paste0("door", goatDoor), mask(circleMask)) @ <>= grid.export("montyhall-masked.svg") @ <>= gridsvg("montyhall-masked.svg", width=6, height=3, prefix="masked-") <> dev.off() @ The effect (in the exported SVG image) is to create a little window in the middle door (to reveal what looks suspiciously like the word ``goat''; see Figure \ref{fig:mask}). % shutter -p paul-shutter -a -e -o montyhall-masked-svg.png \begin{figure} \begin{center} \hfill \includegraphics[width=.45\textwidth]{murrell-potter-mask-fig} \hfill \includegraphics[width=.45\textwidth]{montyhall-masked-svg.png} \hspace*{\fill} \end{center} \caption{\label{fig:mask}Using a mask on an image. The picture on the left shows a white cross on top of a grey circle on a white background. This is used as a mask on the rectangles in the Monty Hall image on the right. The effect is to create a semitransparent window in the middle door (through which we can glimpse the word ``goat'').} \end{figure} \section{Interactivity} The \code{grid.garnish()} function in the \pkg{gridSVG} package opens up a broad range of possibilities for enhancing a \pkg{grid} scene, particularly for adding interactivity to the scene. A simple example is shown in the code below. Here we are adding tooltips to each of the doors in the Monty Hall scene so that hovering the mouse over a door produces a label that shows what is behind the door (see Figure \ref{fig:tooltip}). The first argument to \code{grid.garnish()} is the name of the object to modify. Subsequent arguments specify SVG attributes to add to the object; in this case, we add a \code{title} attribute, which results in a tooltip (in most browsers). <>= MontyHall() for (i in 1:3) { grid.garnish(paste0("door", i), title=text[i]) } @ <>= grid.export("montyhall-tooltip.svg") @ <>= gridsvg("montyhall-tooltip.svg", width=6, height=3, prefix="tooltip-") <> dev.off() @ % sleep 3; shutter -p paul-shutter -a -e -o montyhall-tooltip-svg.png \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{montyhall-tooltip-svg.png} \end{center} \caption{\label{fig:tooltip}The Monty Hall image with tooltips added to each door. The mouse is hovering over the middle door, which results in a tooltip being displayed to show that there is a ``goat'' behind this door.} \end{figure} The \code{grid.garnish()} function can also be used to associate \js{} code with an object in the scene. The following code shows a simple example where clicking on one of the rectangles pops up an alert box showing what is behind that door (see Figure \ref{fig:alert}). The attribute in this example is \code{onclick}, which is used to define an action that occurs when the object is clicked with the mouse (in a browser). <>= MontyHall() for (i in 1:3) { grid.garnish(paste0("door", i), onclick=paste("alert('", text[i], "')")) } @ <>= grid.export("montyhall-alert.svg") @ <>= gridsvg("montyhall-alert.svg", width=6, height=3, prefix="alert-") <> dev.off() @ % sleep 3; shutter -p paul-shutter -a -e -o montyhall-alert-svg.png \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{montyhall-alert-svg.png} \end{center} \caption{\label{fig:alert}The Monty Hall image with interactivity. The mouse has just been clicked on the middle door, which has resulted in an alert box popping up to show that this door has a ``goat'' behind it.} \end{figure} For more complex interactions, it is possible to include \js{} code within the scene, using the \code{grid.script()} function, so that an event on an object within the scene can be associated with a \js{} function call to perform a more sophisticated action. The code below shows a simple example where clicking on one of the rectangles in the Monty Hall scene will call the \js{} function \code{open()} to ``open'' the door (by making the rectangle invisible; see Figure \ref{fig:js}). The \code{open()} function is defined in a separate file called \code{"MontyHall.js"} (shown in Figure \ref{fig:jscode}). <>= MontyHall() for (i in 1:3) { grid.garnish(paste0("door", i), onclick="open(evt)") } grid.script(file="MontyHall.js") @ <>= grid.export("montyhall-js.svg") @ <>= gridsvg("montyhall-js.svg", width=6, height=3, prefix="js-") <> dev.off() @ % sleep 3; shutter -p paul-shutter -a -e -o montyhall-js-svg.png \begin{figure} \begin{center} \includegraphics[width=.45\textwidth]{montyhall-js-svg.png} \end{center} \caption{\label{fig:js}The Monty Hall image with more interactivity. The mouse has just been clicked on the middle door, which has resulted in the middle door becoming invisible, thereby revealing a ``goat'' behind the door.} \end{figure} \begin{figure} \begin{boxedminipage}{\textwidth} \VerbatimInput{MontyHall.js} \end{boxedminipage} \caption{\label{fig:jscode}The \js{} code used in Figure \ref{fig:js} that defines the \code{open()} function to ``open'' a door by making the rectangle invisible.} \end{figure} \section{A more complex demonstration} The previous section kept things very simple in order to be able to show all of the R code involved in the examples. In this section, we present some more complex examples. The R code is not shown, but is available online, along with ``live'' versions of the images (links are given at the end of this article). Figure \ref{fig:campus-anim} shows the campus map from Figure \ref{fig:lattice} with some animations added in the form of blue, green and red lines that describe three inner-city bus routes that pass through The University of Auckland campus. This demonstrates the use of \code{grid.animate()} with a much more complex \pkg{grid} scene. % sleep 3; shutter -p paul-shutter -a -e -o campus-animation-cap-svg.png \begin{figure} \begin{center} \includegraphics[width=.5\textwidth]{campus-animation-cap-svg.png} \end{center} \caption{\label{fig:campus-anim}The map from Figure \ref{fig:lattice} with animated lines added to show several bus routes around The University of Auckland campus.} \end{figure} Figure \ref{fig:campus-mask} shows a mask being applied to the campus map from Figure \ref{fig:lattice}. On the left, the mask consists of a complex shape that defines the extent of The University of Auckland campus as white regions on a dark grey background. On the right is the result of using that mask on the campus map; all regions outside the extent of the campus are faint because the dark grey areas on the mask result in semitransparency for those regions. \begin{figure} \begin{center} \includegraphics[width=.49\textwidth]{mask-5} \includegraphics[width=.49\textwidth]{campus-mask-svg.png} \end{center} \caption{\label{fig:campus-mask}The map from Figure \ref{fig:lattice} (right) with a mask (left) used to de-emphasize areas of the map that are not part of The University of Auckland campus.} \end{figure} \section{Limitations} The \pkg{gridSVG} package provides an opportunity to produce more sophisticated, more dynamic, and more interactive R plots compared to the standard R graphics devices. However, there are some strict limitations on what can be achieved with this package. First of all, the package only works for plots that are based on the \pkg{grid} graphics system. This includes some major graphics packages, such as \pkg{lattice} and \pkg{ggplot2}, but excludes a large amount of graphics functionality that is only available in the default \pkg{graphics} package or packages that build on \pkg{graphics}. Another major limitation is that \pkg{gridSVG} does not generate any \js{} code itself. This means that anything beyond the most basic interactivity will require the user to write \js{} code, which imposes a burden on the user in terms of both time and knowledge. Another point that has only briefly been acknowledged in the example R code so far is that the \pkg{gridSVG} functions that add special features to a \pkg{grid} scene (such as hyperlinks and animation) rely heavily on the ability to \emph{identify} specific components of a \pkg{grid} scene. The Monty Hall examples all rely on the fact that the rectangles that are drawn to represent doors each have a name---\code{"door1"}, \code{"door2"}, and \code{"door3"}---and the code that adds hyperlinks or animation identifies the rectangles by using these names. This means that \pkg{gridSVG} is dependent upon an appropriate naming scheme being used for any \pkg{grid} drawing \citep{RJournal_2012-2_Murrell}. This requirement is met by the \pkg{lattice} package and, to a lesser extent by the \pkg{ggplot2} package, but cannot be relied on in general. % Some of these are pretty detailed so really belong in the JSS version(?) % Although doesn't write any javascript for you, DOES provide nice hooks for % javacsript to address. % Cairo SVG not all bad! (e.g., faithful font rendering (as paths)) % plotmath becomes MathML ? % Anything with drawDetails() method or grid.draw() method will NOT % export. % Anything with makeContent() (post R 3.0) will need to be explicitly % forced before doing any hyperlinking or animating (otherwise the % hyperlinking and animating is destroyed by forcing). % Some things do NOT work (e.g., making clipping region BIGGER) \section{Alternative approaches} The \pkg{gridSVG} package provides one way to produce dynamic and interactive versions of R plots for use on the web, but there are several other packages that provide alternative routes to the same destination. This section discusses the differences between \pkg{gridSVG} and several other packages that have similar goals. % NOT 'canvas' cos static graphics only % NOT 'RIGHT' cos not a fan % NOT 'clickme' or 'ggvis' cos like 'rCharts' % NOT 'cranvas' or 'Acinonyx' cos not web % NOT 'polycharts' or 'vega' or 'D3' or 'kineticJS' cos do not start from R % NOT 'svgmaps' cos it builds on top of gridSVG!!! % NOT 'animint' cos it's a bit like 'rCharts' (?) The \CRANpkg{animation} package \citep{pkg:animation} provides a convenient front-end for producing animations in various formats (some of which are appropriate for use on the web), but the approach is frame-based (draw lots of separate images and then stitch them together to make an animation). The advantage of an SVG-based approach to animation is that the animation is declarative, which means that the animation can be described more succinctly and efficiently and the resulting animation will often appear smoother. The \pkg{SVGAnnotation} package \citep{pkg:svgannotation} performs a very similar role to \pkg{gridSVG}, by providing functions to export R plots to an SVG format with the possiblity of adding dynamic and interactive features. One major advantage of \pkg{SVGAnnotation} is that it will export R plots that are based the standard \pkg{graphics} package (as well as plots that are based on \pkg{grid}). \pkg{SVGAnnotation} also provides some higher-level functions that automatically generate \js{} code to implement specific sorts of more complex interactivity. For example, the \code{linkPlots()} function can be used to generate linked plots, where moving the mouse over a data symbol in one plot automatically highlights a corresponding point in another plot. The main disadvantage of \pkg{SVGAnnotation} is that it works with the SVG that is produced by the built-in \code{svg()} device, which is much less structured than the SVG that is generated by \pkg{gridSVG}. That is not a problem if the functions that \pkg{SVGAnnotation} provides do everything that we need, but it makes for much more work if we need to, for example, write our own \js{} code to work with the SVG that \pkg{SVGAnnotation} has generated. A number of packages, including \pkg{rCharts} and \CRANpkg{googleVis} \citep{pkg:rcharts,article:googlevis}, provide a quite different approach to producing dynamic and interactive plots for the web. These packages outsource the plot drawing to \js{} libraries such as NVD3, highcharts, and the Google Visualisation API \citep{nvd3js,highcharts,googlevis}. The difference here is that the plots produced are not R plots. The advantage is that very little R code is required to produce a nice result, provided the \js{} library can produce the style of plot and the sort of interactivity that we want. Another approach to interactivity that is implemented in several packages, notably \CRANpkg{shiny} \citep{pkg:shiny}, is running R as a web server and producing new R graphics in response to user events in the browser. The difference here is that the user typically interacts with GUI widgets (buttons and menus) outside the graphic and each user event generates a completely new R graphic. With \pkg{gridSVG}, the user can interact directly with elements of the graphic itself and all of the changes to the graphic occur in the browser with no further need of R. In summary, using the \pkg{gridSVG} package is appropriate if we want to add advanced graphics features to a \pkg{grid}-based R plot, or if we want to add dynamic or interactive elements to a \pkg{grid}-based R plot, particularly if we want to produce a result that is not already provided by a high-level function in the \pkg{SVGAnnotation} package. An approach that holds some promise is to generate SVG content using \pkg{gridSVG} and then manipulate that content by adding \js{} code based on a sophisticated \js{} library such as d3 \citep{d3}. \section{Availability} The \pkg{gridSVG} package is available from CRAN. The code examples are known to work for \pkg{gridSVG} versions 1.3 and 1.4. Online versions of the figures in this article, along with complete code for the campus map examples, are available from \url{http://www.stat.auckland.ac.nz/~paul/Reports/gridSVGrj/}. Further documentation and examples for \pkg{gridSVG} are available from \url{https://www.stat.auckland.ac.nz/~paul/R/gridSVG/}. \bibliography{murrell-potter} \address{Paul Murrell\\ The University of Auckland\\ Auckland\\ New Zealand} \email{paul@stat.auckland.ac.nz} \address{Simon Potter\\ The University of Auckland\\ Auckland\\ New Zealand} \email{simon@sjp.co.nz}