Maps with R (III)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
In my previous posts (1 and 2) I wrote about maps with complex legends but without any kind of interactivity. In this post I show how to produce an SVG file with interactive functionalities with the gridSVG package.
As an example, I use a dataset about population from the Spanish Instituto Nacional de Estadística. This organism publishes information using the PC_Axis format which can be imported into R with the pxR package. This dataset is available at the INE webpage or directly here.
Let’s start loading packages.
library(gridSVG) library(pxR) library(sp) library(lattice) library(latticeExtra) library(maptools) library(classInt) library(colorspace)
Then I read the px
file and make some changes to get the datWide
data.frame
(which is available here if you are not interested in the PC-Axis details).
datPX <- read.px('pcaxis-676270323.px', encoding='latin1') datWide <- as.data.frame(datPX, direction = 'wide', use.codes=FALSE) provID <- strsplit(as.character(datWide$provincias), ' ') provID <- as.data.frame(do.call(rbind, provID)) names(provID) <- c('code', 'prov') datWide <- cbind(datWide, provID)
Now it’s time to read a suitable shapefile
(read the first post of this series for information about it).
mapSHP <- readShapePoly(fn = 'mapas_completo_municipal/spain_provinces_ind_2') Encoding(levels(mapSHP$NOMBRE99)) <- "latin1" ## The encoding must be UTF8 to be correctly displayed in the SVG file levels(mapSHP$NOMBRE99) <- enc2utf8(levels(mapSHP$NOMBRE99))
Both the shapefile
and the data.frame
have to be combined using the matches between the PROV
variable of the shapefile
and code
from the data.frame
. The numeric values of the row names (mapaIDs
) will be useful in the last step.
idx <- match(mapSHP$PROV, datWide$code) Total <- datWide[idx, "Total"] mapSHP@data <- cbind(mapSHP@data, Total) mapaDat <- as.data.frame(mapSHP) mapaIDs <- as.numeric(rownames(mapaDat))
A final step is needed before calling spplot
. I will use the functions of gridSVG
to include information in the SVG file according to the characteristics of each polygon. Since gridSVG
works after the plot has been created, I need a key to identify each polygon and match it with the correspondent element of the original dataset. Unfortunately, the panel.polygonsplot
(which is the function used by spplot
when drawing polygons) does not assign a name to each polygon. Let’s create a new function (panel.polygonNames
) adding a small change in panel.polygonsplot
(you should read the full code of panel.polygonsplot
to understand what is happening here).
panel.str <- deparse(panel.polygonsplot, width=500) panel.str <- sub("grid.polygon\\((.*)\\)", "grid.polygon(\\1, name=paste('ID', slot(pls\\[\\[i\\]\\], 'ID'\\), sep=':'))", panel.str) panel.polygonNames <- eval(parse(text=panel.str), envir=environment(panel.polygonsplot))
EDITED: Thanks to Andre’s comment (see below), I have found that panel.polygonsplot
has been changed to add hole-handling. This hack will only work if this new behaviour is disabled with set_Polypath(FALSE)
before the next code chunk.
Now everything is ready for drawing. I use the jenks style of the classIntervals
function from the classInt package to set the breaks of the color key.
n=7 int <- classIntervals(Total, n, style='jenks') pal <- brewer.pal(n, 'Blues') p <- spplot(mapSHP["Total"], panel=panel.polygonNames, col.regions=pal, at=signif(int$brks, digits=2)) p
EDITED: You can recover the default hole-handling setting with set_Polypath(TRUE)
after the p
object has been printed.
Once the plot has been created (do not close the graphic window!) the grid.garnish
attaches SVG attributes (in this example onmouseover
and onmouseout
) to each polygon, which is identified with its name thanks to the panel.polygonNames function
.
These attributes are related to javascript
functions (showTooltip
and hideTooltip
) included in this javascript file (this file is only a minor modification of the original file available at the webpage of the creator of grid
and gridSVG
). The grid.script
function attaches the javascript
file to the grob
and gridToSVG
produces the SVG file with a simple HTML page.
## grobs in the graphical output grobs <- grid.ls() ## only interested in those with "ID:" in the name nms <- grobs$name[grobs$type == "grobListing"] idxNames <- grep('ID:', nms) IDs <- nms[idxNames] for (id in unique(IDs)){ ## extract information from the data ## according to the ID value i <- strsplit(id, 'ID:') i <- sapply(i, function(x)as.numeric(x[2])) dat <- mapaDat[which(mapaIDs==i),] ## Information to be attached to each polygon info <- paste(dat$NOMBRE99, dat$Total, sep=':') g <- grid.get(id) ## attach SVG attributes grid.garnish(id, onmouseover=paste("showTooltip(evt, '", info, "')"), onmouseout="hideTooltip()") } grid.script(filename="tooltip.js") gridToSVG('map_with_annotations.svg')
(Click on the image to show the SVG graphic. Move the mouse over it to display the information)
Related articles
- Maps with R (I)
- Maps with R (II)
- Animation with SVG
- Spatial data visualization with R
- Tooltips with R
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.