Etiquetas
Instituto Nacional de Estadística (Spain), javascript, R, SVG, visualization
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)

Andre dijo:
Thank you for this great application of gridSVG!
I get everything to work fine with my data until this chunk:
## 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]
The output I get for IDs is character[0], which I suspect is not what I should have. I fail to see in general where the various id in this chunk and the following loop come from. Are we filtering the grid.ls() output? Any clarification would be much appreciated.
Thank you.
Oscar Perpiñán Lamigueiro dijo:
Hi Andre,
Thanks for your positive feedback.
You are right. With this code we are filtering the output of grid.ls(), which gives a listing of the names of the grobs and viewports of the graphical scene you are displaying. If you get character(0) for IDs, I suspect the content of the graphical scene is empty. Perhaps you closed the window before calling grid.ls()? What is the value of grobs?
Best,
Oscar.
Andre dijo:
With the original code, my grobs output is flatGridListing[6] (I did not close the graphical device). I changed the filter to ID instead of ID: and now have integer[50]., which seems right. However, when running
i <- sapply(i, function(x)as.numeric(x[2]))
in the loop, all I get is NAs and warnings, and my beautiful map disappears.
Thanks.
Andre
Oscar Perpiñán Lamigueiro dijo:
Hi Andre,
I have found the problem. The
panel.polygonsplothas been changed to manage holes in polygons. Now this function usesgrid.pathinstead ofgrid.polygonby default. Thus, my hack is not working. But there is an easy solution:panel.polygonsplotfirst checks if the optionPolypathisTRUE(default) withget_Polypath(). You can change it withset_Polypath(FALSE)and then the hole-handling is disabled, andgrid.polygonis used instead ofgrid.path.I will edit the post with this information. Thanks for your message!
Oscar.
afro dijo:
I appreciate the output. Your brains please. Am new 2 R. I am trying to map a shapefile in R2.15 after loading packages (Maptools, RColorBrwer and ClassInt). I try reading the .shp with readShapePoly or readShapeSpatial only getting this message,
Error: could not find function “readShapePoly”; Error: could not find function “readShapeSpatial”.
Tnx in advance 4 ur advise.
Afro
Oscar Perpiñán Lamigueiro dijo:
Both functions are included in the package
maptools, so perhaps you got an error when callinglibrary(maptools). Could you post the result ofsessionInfo()?Oscar
Erin Hodgess dijo:
This is very beautiful.
I have a question please: I’m having problems with the last part:
> gridToSVG(‘map_with_annotations.svg’)
Warning message:
In grabDL(warn, wrap, …) :
grob(s) overwritten (grab WILL not be faithful; try ‘wrap=TRUE’)
and no plot appears.
Do you have any suggestions, please?
Thanks,
Erin
Oscar Perpiñán Lamigueiro dijo:
Hi Erin,
Thanks for the comment.
When you say “no plot appears”, do you mean that you get nothing if you ask for “p”? Remember that you should not close the graphical window after drawing the result of spplot (p).
After the call to gridToSVG you should obtain two files in your working directory: SVG and HTML files. Is that true for you? What happens when you open the html file?
Oscar.
Andrés Bustamante dijo:
Estimado Oscar
Que fantástica habilidad en el manejo de éste excelente software libre! Yo lo he estado usando para investigación en agricultura y es una potente herramienta para mi toma de decisiones.
Quiero preguntarte si es posible compartir un script que tengo. Este script en base a coordenadas y valores de clorofila, me hace un mapa del cultivo haciendo una predicción en colores de los valores de clorofila, de modo que se pueden identificar aquellas zonas con menor nutrición.
Me puedes compartir tu email para enviarlo?, así lo puedes checar y mejorar para la comunidad R que lo use en investigación agrícola.
Mucho éxito y muchas gracias.