Etiquetas

, , , ,

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. EDITED: The javascript file (tooltip.js) must be saved in the same folder as the SVG and HTML files.

## 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)

https://procomun.files.wordpress.com/2012/02/map.jpg