Category Archives: Graphics

Finally, an easy way to fix the horizontal lines in ggplot2 maps

ggplot2 tries to make it super easy to add country or state borders to your map, and for the most part it works great as long as you include the entire map region in your plot (all the states or the entire world map for example). A long standing issue with the ggplot2 borders() function is when you try and zoom in a way that removes data outside of the plotting region, countries and states get cut off and not reconnected correctly, resulting in spurious horizontal lines in the plot. A forum post and ggplot2 issue both allude to this problem.

Here is a simple example showing this issue. First, a simple unprojected map that looks great because we are not trying to zoom in at all.

ggplot() +
    borders("world") + borders("state") +
    theme_bw()

Alt text

An obvious extension would be to zoom in on a region and project the map:

ggplot() +
    borders("world") + borders("state") +
    theme_bw() +
    coord_map(xlim=xlim,ylim=ylim) +
    labs(y="",x="")

Alt text

Which pretty clearly illustrates the issue. Things look even worse if we try and add color:

ggplot() +
    borders("world",fill="darkseagreen") +
    borders("state",fill="darkseagreen") +
    theme_bw() +
    coord_map(xlim=xlim,ylim=ylim) +
    labs(y="",x="")

Alt text

The underlying issue is that when we zoom with coord_map() the polygons get cut up, with some parts appearing on either side of the map, hence the horizontal lines when ggplot tries to connect a country or state that appears on two sides of the map. The solution to this issue is to get the raw map data then use one of the various polygon clipping libraries in R. I used PBSmapping. Here is a complete example

library("PBSmapping")
library("ggplot2")
library("maps")
library("data.table")

# plot limits
xlim = c(-165,-90)
ylim = c(10,60)

worldmap = map_data("world")
setnames(worldmap, c("X","Y","PID","POS","region","subregion"))
worldmap = clipPolys(worldmap, xlim=xlim,ylim=ylim, keepExtra=TRUE)

statemap = map_data("state")
setnames(statemap, c("X","Y","PID","POS","region","subregion"))
statemap = clipPolys(statemap, xlim=xlim,ylim=ylim, keepExtra=TRUE)

p = ggplot() +
    coord_map(xlim=xlim,ylim=ylim) +
    geom_polygon(data=worldmap,aes(X,Y,group=PID),
        fill = "darkseagreen",color="grey50") +
    geom_polygon(data=statemap,aes(X,Y,group=PID),
        fill = "darkseagreen",color="grey50") +
    labs(y="",x="") +
    theme_bw()
 print(p)

Alt text

This could also be used to fix the fill issues when doing ggplot filled contour plots.

Who wants to maintain pgfSweave?

So the time has come for me to face the fact that I have no time to maintain pgfSweave. It was recently archived because I didn’t make necessary changes to comply with some CRAN policies. SO, I need someone to step up to the plate to make some tweakes, put it back up on CRAN and maintain it in the future. Is it you?

To do the job you need at a basic understanding (or a desire to learn) the following:

  • Git/Github
  • Sweave
  • LaTeX
  • Make
  • R (duh :p)

Compensation includes:

  • Undying glory
  • The respect of your peers
  • Self satisfaction
  • A high-five from me if we ever meet in person!

If you are interested in the job please contact me at cameron[dot]bracken[at]gmail[dot]com

Using tikzDevice with Sweave in R 2.13

R 2.13 introduces an option to specify a custom graphics device in an Sweave code chunk. This is really cool and allows you to use tikzDevice output like pgfSweave does. In an pinch, say when you dont have access to any non-core packages, you can use tikzDevice output with the regular Sweave driver (RweaveLatex) more simply than before. Here is a minimal example showing how.

\documentclass{article}

\usepackage[noae,nogin]{Sweave}
\usepackage{tikz}



\begin{document}
\SweaveOpts{prefix.string=fig}

Example using \texttt{tikzDevice} with the default \textbf{Sweave}
driver (\texttt{RweaveLatex}).  First define a custom device:

<<results=hide>>=
tikz.Swd <- function(name, width, height, …){
    require(tikzDevice)
    tikzDevice::tikz(file = paste(name, "tikz", sep = "."),
                 width = width, height = height)

}
@

Then use the device for plotting, but you have to include the figure manually.

<<myplot, fig=T, grdevice=tikz.Swd, include=F, width=4,height=4>>=
plot(1)
@
\input{fig-myplot.tikz}


\end{document
}

WARNING: This could be very slow if your graphic is complex