Search code examples
rggplot2gisshapesrgdal

Dynamically stretching shapes in ggplot when graphing the map with use of the facet_wrap


I'm interested in changing how the ggplot2 handles shapefiles when plotted with use of the facet_wrap. I'm using the code below to generate a fairly simple maps for groups of various shapes:

# Data sourcing -----------------------------------------------------------

# Download an read US state shapefiles
tmp_shps <- tempfile(); tmp_dir <- tempdir()
download.file("http://www2.census.gov/geo/tiger/GENZ2014/shp/cb_2014_us_state_20m.zip",
              tmp_shps)
unzip(tmp_shps, exdir = tmp_dir)

# Libs
require(rgdal); require(ggplot2)

# Read
us_shps <- readOGR(dsn = tmp_dir, layer = "cb_2014_us_state_20m")
# Prepare data set for ggplot2
us_shps_frt <- fortify(us_shps, region = "NAME")
# Drop some to make smaller graphs for the example
us_shps_frt <- us_shps_frt[us_shps_frt$id == unique(us_shps_frt$id)[10:20],]


# Graph -------------------------------------------------------------------

ggplot(us_shps_frt, aes(long, lat, group = group, fill = id))  + 
    geom_polygon(colour = 'black', size = 0.5) + 
    coord_equal() +
    guides(fill = FALSE) +
    theme_bw() +
    facet_wrap(~id, ncol = 2) +
    ggtitle("Some Title") +
    theme(axis.text = element_blank(),
          axis.title = element_blank(),
          axis.ticks = element_blank(),
          panel.grid.minor = element_blank(),
          panel.grid.major = element_blank(),
          panel.border = element_blank())

As illustrated in the figure below the use of space within each facet is not ideal which makes the shapes difficult to read:

Small shapes

I'm interested in achieving the following:

  1. Minimising the amount of white space allocated to each shape to minimum (leaving only tiny margins)
  2. Centring each shape within a facet

I have tried the combination of coord_equal() and free scales, as illustrated in the snippet below:

# Desperate attempt
ggplot(us_shps_frt, aes(long, lat, group = group, fill = id))  + 
    geom_polygon(colour = 'black', size = 0.5) + 
    coord_equal() +
    guides(fill = FALSE) +
    theme_bw() +
    facet_wrap(~id, ncol = 2, scales = 'free') +
    coord_equal() +
    ggtitle("Some Title") +
    theme(axis.text = element_blank(),
          axis.title = element_blank(),
          axis.ticks = element_blank(),
          panel.grid.minor = element_blank(),
          panel.grid.major = element_blank(),
          panel.border = element_blank())

But the results are pointless:

more messy shapes

Notes

The solution using the multiplot function and plotting single graphs in the loop would be accepted only if proposed in a manner ensuring a convenient usability of the final graph in a R Markdown document.


Solution

  • First: serious kudos for a perfectly reproducible example for a semi-complex problem.

    This isn't much more work:

    # need this to ensure ggplot treats it as factor and so we can tell it
    # not to drop the factor levels for the fill aesthetic
    us_shps_frt$id <- factor(us_shps_frt$id)
    
    # now we build a plot list
    lapply(unique(us_shps_frt$id), function(x) {
    
      ggplot(dplyr::filter(us_shps_frt, id==x), 
             aes(long, lat, group = group, fill = id))  + 
        geom_polygon(colour = 'black', size = 0.5) + 
        coord_quickmap() +                   # better than the other coords_ for this
        guides(fill = FALSE) +
        theme_bw() +
        ggtitle(x) +                         # faux facet labels
        scale_fill_discrete(drop=FALSE) +
        theme(axis.text = element_blank(),
              axis.title = element_blank(),
              axis.ticks = element_blank(),
              panel.grid.minor = element_blank(),
              panel.grid.major = element_blank(),
              panel.border = element_blank())
    
    }) -> states_list
    
    # now mimic facet_wrap layout with grid.arrange
    do.call(gridExtra::grid.arrange, c(states_list, ncol=2))
    

    enter image description here