Week02 - Pan and Zoom Almost Any R Graph
January 15, 2015
htmlwidgets News This Week
@yutannihiliation has been prolific this week with a couple new widget seeds and contributions to this widget metricsgraphics
announced last week.
chartist
- Chartist.js wrappergneisschart
- Gneisschart/Chartbuilder (Quartz) wrapper
@dcenergy
@armish
coffeewheel
-coffeewheel
by Jason Davies wrappermetabologram
- a specialized form ofcoffeewheel
mentioned above
@lmullen
Not launched last week, but a big miss by me is this htmlwidget
for mapping from @lmullen. It was one of the first htmlwidgets
built after the Dec. 17 release announcement, and offers some nice functionality and interesting examples.
cartographer
-d3.carto.map
wrapper by @lmullen
@rich-iannone
Also, be sure to check out the new graphviz
functionality added to last week’s widget of the week DiagrammeR
.
This Week’s Widget - svgPanZoom
I wanted to head in a slightly different direction with this week’s widget. Instead of wrapping Javascript charting libraries as most htmlwidgets
to date have done, I wanted to let R
still do the plotting, since so many of us know and love base graphics
, ggplot2
, and lattice
. I hope nearly every R
user can easily incorporate this little widget into their R
graphical analysis and publishing workflow.
What Does it Do?
With some help from SVGAnnotation
or gridSVG
, svgPanZoom
will add pan and zoom functionality to nearly all R
graphics using svg-pan-zoom.js
, a tiny and dependency-free Javascript library. Maybe a little graphic drawn from last week’s DiagrammeR
can help explain the process.
library(DiagrammeR)
DiagrammeR("
graph LR;
gr[<code>graphics</code>]-->sp[<code>svgPlot</code>]
la[<code>lattice/trellis</code>]-->sp
la[<code>lattice/trellis</code>]-->gs[<code>gridSVG</code>]
gg[<code>ggplot2</code>]-->sp
gg[<code>ggplot2</code>]-->gs
sp-->|svg+js|ht[Interactive Graphic]
gs-->|svg+js|ht
ht-->Shiny
ht-->rs[RStudio Viewer]
ht-->rm[R Markdown]
ht-->html[standalone HTML]
ht-->iframe
classDef default fill:none;
", height = 300, width = 550) %>>%
htmlwidgets::as.iframe(
file="diagram.html"
, libdir = "lib"
, selfcontained=F
, width = 650
, height =400
, overflow = "hidden"
)
Examples in R
Let’s start with the base
-ics adding svgPanZoom
to some plots created by the base graphics
in R
. We’ll need svgPanZoom
and SVGAnnotation
for this first set of examples.
#devtools::install_github("timelyportfolio/svgPanZoom")
library(svgPanZoom)
library(SVGAnnotation)
Base Graphics + svgPanZoom
Plots don’t get much more basic than this, but starting simple is the best way to isolate what is happening. When you run the example below, the Pan/zoom-able graph should appear in RStudio Viewer (if using RStudio) or show up in your browser of choice.
# as simple as it gets
svgPanZoom(
svgPlot(
plot(1:10)
)
)
It is hard to tell that our simple little graph now has special powers. Some icons and hints might help. I’m struggling with whether to make this behavior the default. Let me know if you have an opinion.
# as simple as it gets
svgPanZoom(
svgPlot(
plot(1:10)
)
,controlIconsEnabled=T
)
Just to prove that it also works with more complicated plots, let’s do one more example in base graphics. We'll pull this "persian rug art" from
?contour`.
svgPanZoom(
svgPlot(
{
x <- y <- seq(-4*pi, 4*pi, len = 27)
r <- sqrt(outer(x^2, y^2, "+"))
opar <- par(mfrow = c(2, 2), mar = rep(0, 4))
dev.new()
for(f in pi^(0:3))
contour(
cos(r^2)*exp(-r/f)
, drawlabels = FALSE
, axes = FALSE
, frame = TRUE, col = 'cadetblue')
}
)
)
For one more really nice example, see this beautiful little Kohonen masterpiece from volunteer tester and lover of interactive graphics Alex Bresler.
Grid (lattice | ggplot2) + svgPanZoom
svgPanZoom
would be very incomplete if it did not also support the graphics libraries built on top of grid
–ggplot2
and lattice
–that spoil us R
users. Before we start with the examples, I think I should explain the two choices we have to convert grid
graphics to SVG
. svgPlot
from SVGAnnotation
wraps the svg grDevice
(probably Cairo
) to convert R
plots to SVG
. Unfortunately, it often results in ugly XML
that is difficult to customize or enhance. However, it is much faster than the other choice gridSVG
function grid.export()
. gridSVG
though concentrates on clean, well-structured XML
with text
elements intact, unique identifiers, helpful meta
information, and some helper Javascript. svgPanZoom
prefers gridSVG
, but if you’re looking for speed choose svgPlot
. I’ll demonstrate both with the next example. Let’s go straight into a choropleth map in ggplot2
using Ari Lamstein’s package choroplethr
.
library(choroplethr)
library(ggplot2)
# start with svgPlot
# and use example from ?state_choropleth
data(df_pop_state)
sc <- state_choropleth(
df_pop_state
, title="US 2012 State Population Estimates"
, legend="Population"
)
svgPanZoom(
svgPlot(
show(sc)
# will need to manually specify height/width
,height = 9, width = 17
), controlIconsEnabled = T
)
Now with gridSVG. You’ll probably notice it takes much longer.
# now with gridSVG
svgPanZoom( sc, controlIconsEnabled = T)
Just in case you are thinking “maps are great, but what about my regular old ggplot2
”, let’s do one from ?facet_wrap
.
# example from ggplot2 facet_wrap documentation
d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
xlim(0, 2) + stat_binhex(na.rm = TRUE) + theme(aspect.ratio = 1)
# to do with svgPlot
# svgPlot( { show( d + facet_wrap(~color) ) } )
svgPanZoom(
(d + facet_wrap(~ color) )
, controlIconsEnabled = T
)
I still love lattice
graphics. Just in case you forgot about some of its functionality, I want to revive this example from ?wireframe
.
library(lattice)
m <- wireframe(volcano, shade = TRUE,
aspect = c(61/87, 0.4),
light.source = c(10,0,10))
svgPanZoom(
svgPlot( {show(m)} )
)
# gridSVG will take a little too long, but feel free to try
# svgPanZoom(m)
Then as with all htmlwidgets
, we can pair them with tags
from htmltools
. How about storytelling with our pan and zoom ?
library(htmltools)
spz = svgPanZoom(
svgPlot( {show(m)} )
,controlIconsEnabled = T
, height = 500
, width = 500
, elementId = "story-volcano"
)
html_print(tagList(
"my favorite part of the volcano"
,tags$button("click here", onclick="zoomFavorite()")
,spz
,tags$script("
function zoomFavorite (){
var z = document.getElementById('story-volcano').zoomWidget;
// programatically zoom to specified point
z.zoomAtPoint( 3, {x:200, y:200});
}
")
))
Not Enough Examples
Before the post gets too long and unwieldy, I’ll stop here with the examples. If you still want to see more, see a whole new batch of examples in the Readme.md. So far I have not found a single plot that didn’t work with svgPanZoom
. Anybody that reports a failure wins the grand prize of getting mentioned in next week’s blog post.
Some Limitations
I feel like I should mention some of the limitations of svgPanZoom
. As mentioned, it is aimed wholly at ease and simplicity with minimal dependencies, so things like an axis that stays put while the graph zooms are not currently possible. Also, not really a fault of svgPanZoom
more a weakness of the svg grDevice
, the SVG
s are not namespaced, so if you have more than one on a page, you’ll notice some funky labels. With gridSVG
however, this is not a problem. I’ll try to demonstrate some solutions to this in the documentation or implement into the functionality of svgPanZoom
.
I have two killer features that I would like to implement, so stay tuned.