6

I'm trying to split a map of the united states into multiple windows (some of which contain the same state twice). I'd like the scales to be constant (so that the maps aren't distorted) but also minimize space between maps. I can't use facet_wrap (due to the overlapping nature of the regions--and anyway, facet_wrap can't have scales both fixed and have different xlims for each window). Any suggestions on how to improve the spacing on the results?

require(data.table)
require(ggplot2)
require(maps)
require(gridExtra)
all_states <- as.data.table(map_data("state"))

setnames(all_states,"region","state")

##define regions with overlapping states
weco.states <- c("oregon","washington","california")
west.states <- c("washington","montana", "idaho","utah","nevada","arizona","new mexico",
    "wyoming","colorado","south dakota","texas")
east.states <- c(setdiff(unique(all_states$state), union(weco.states,west.states)),
    "texas","south dakota")
all_states[,c("weco","west","east"):=FALSE]
all_states[state%in% weco.states, weco:=TRUE]
all_states[state%in% west.states, west:=TRUE]
all_states[state%in% east.states, east:=TRUE]


p.regbase <- ggplot() + coord_equal() +ylim(c(25,50))
p.weco <- p.regbase + geom_polygon(data=all_states[(weco),], aes(x=long, y=lat, group = group),colour="white", fill="grey" ) 
p.west <- p.regbase + geom_polygon(data=all_states[(west),], aes(x=long, y=lat, group = group),colour="white", fill="grey" )  
p.east <- p.regbase + geom_polygon(data=all_states[(east),], aes(x=long, y=lat, group = group),colour="white", fill="grey" )  

print(arrangeGrob(p.weco,p.west,p.east,ncol=3,nrow=1))

depending on how I resize the graphics window in the Windows GUI, the results are either bad (scales are different) enter image description here

or decent (same heights) but there's too much space: How can I get rid of the extra space?

enter image description here

Michael
  • 5,808
  • 4
  • 30
  • 39
  • you can pass `widths = unit(c(1, 2, 3), "null")` to `arrangeGrob`, and tweak the relative factors manually. – baptiste Jul 31 '13 at 22:06
  • This doesn't address the fact that arrangeGrob is padding blank space around the first graph. – Michael Jul 31 '13 at 22:09
  • I'm not seeing how this fixed this issue. If it can, could you demonstrate how? I provided fully usable sample code... – Michael Jul 31 '13 at 22:15
  • It decreases the white space at the expense of distorting the maps. I'd like a solution that maintains the scale of the maps within each plot (x:y=1:1) and between plots (one unit long on each map is the same space on the screen). – Michael Jul 31 '13 at 22:21

2 Answers2

5

Let's clarify a few things.

  • grid.arrange doesn't "add padding", it merely puts grobs side-by-side in a grid layout. You can change the widths of each cell in the row,

grid.newpage()
pushViewport(viewport(width=5, height=2.5, default.units="in")); 
gt = grobTree(rectGrob(), circleGrob()) 
grid.arrange(gt, gt, gt, nrow=1, newpage=FALSE,
             widths = unit(c(1, 2, 3), "null"))
upViewport()

enter image description here

  • aligning plots is easy when they don't have a fixed aspect ratio, e.g. using gtable:::cbind_gtable

  • the fixed aspect ratio is encoded in the gtable through relative null grid units, as you can readily check with,


g = ggplotGrob(p.weco)
g[["heights"]][3] # 2.37008405379269 is the aspect ratio for that plot panel

  • the reason setting the widths is not working well with fixed-aspect ratio ggplot2 is two-fold: i) it is hard to guess what relative widths we should assign, because that depends on the aspect ratio that was calculated according to the data ranges; ii) the device width and height should also be set according to the three aspect ratios to avoid extra blank space (necessary to maintain the correct aspect ratios)

These issues have been discussed here; I do not know an elegant and general solution.

Community
  • 1
  • 1
baptiste
  • 75,767
  • 19
  • 198
  • 294
5

Here is a solution using facet_grid() and the somewhat obscure setting theme(aspect.ratio=1). The plot is not perfect, but I hope that it gives you most of what you need. Note that the states look a bit wider than they should. Apparently 1 degree of latitude is noticeably not the same distance as 1 degree of longitude in the USA.

# Create a new factor column for faceting.
newfactor = ifelse(all_states$weco, "weco", 
                           ifelse(all_states$west, "west", "east"))

# Manually specify sort order of factor levels.
newfactor = factor(as.character(newfactor), levels=c("weco", "west", "east"))

all_states$region = newfactor

plot_1 = ggplot(all_states, aes(x=long, y=lat, group=group)) + 
         geom_polygon(colour="white", fill="grey") + 
         facet_grid(. ~ region, space="free", scales="free") + 
         theme(aspect.ratio=1)

ggsave("plot_1.png", plot=plot_1, height=4, width=8, dpi=150)

enter image description here

bdemarest
  • 14,397
  • 3
  • 53
  • 56
  • This is almost perfect--I'd like some states to be in multiple regions but this is easily done by copying them and rbinding them (so there are two sets of washington rows, for example--one in weco and one in west). Thanks a ton! I was really starting to believe this wasn't possible. The stretched-out states issue is just due to the fact the map isn't using projected coordinates, which isn't a big deal. – Michael Aug 01 '13 at 17:43
  • 1
    `+ coord_map("lambert", lat0=25, lat1=50)` projects the maps (into lambert, in this example) – Michael Aug 01 '13 at 20:02