Removing borders around ggplot2 graphs

R
ggplot2
Author

Philippe Massicotte

Published

December 20, 2019

Recently I was participating in the 30DayMapChallenge where people were invited to make a map based on a different daily theme for one whole month.

When making a geographical map, or any other visualization where I wanted to use a forced coordinate system), choosing the right aspect ratio to save my graphics has always been challenging. Lately, when participating in the map challenge, CΓ©dric Scherer made me realize that I was really struggling with white borders around my plots when it came the time to export them. He was right! :smile:

The problem with choosing the right aspect ratio

Let’s make a simple map of the USA to illustrate the problem. In a markdown document, the generated graph looks good (i.e. no extra border) because knitr is taking care of this for me (more on that later).

# Load the US shapefile
states <- st_as_sf(maps::map("state", plot = FALSE, fill = TRUE))

p <- states %>%
  ggplot() +
  geom_sf(size = 0.25) + #<<
  coord_sf(crs = 2163) +
  theme(
    panel.border = element_blank(),
    axis.text = element_blank(),
    panel.grid = element_blank(),
    axis.ticks = element_blank(),
    plot.background = element_rect(fill = "#3c3c3c"),
    panel.background = element_rect(fill = "#3c3c3c")
  )

p

When it comes time to save my plots, I usually use the PDF format file unless they contain too much point. In that case, I will use the PNG format. When saving a graph in a file, however, it is difficult to find the right aspect ratio when using coordinate system that forces a specified ratio between the physical representation of data units on the axes (ex.: coord_fixed(), geom_sf() or coord_equal()). To visualize the problem, I will save the previous plot using two different aspect ratios and then import them in my document to show you the difficulty of finding the right aspect ratio.

Aspect ratio of 7/4

Choosing an aspect ratio of 7/4 creates white borders on the sides of the plot.

file <- "img/fig_border_7_4.png"

ggsave(
  file,
  plot = p,
  type = "cairo",
  device = "png",
  dpi = 600,
  width = 7,
  height = 4
)

Aspect ratio of 6/7

Choosing an aspect ratio of 6/7 creates white borders at the bottom and the top of the plot.

file <- "img/fig_border_6_7.png"

ggsave(
  file,
  plot = p,
  type = "cairo",
  device = "png",
  dpi = 600,
  width = 6,
  height = 7
)

As it can be seen in the two figures above, there are two large white borders located either on the sides or above/below of the graph. It is because I have used geom_sf() which set automatically the aspect ratio of the plot to respect the chosen geographical coordinate system. Of course, I could play around with trials and errors to find the best width and hight to use to save my plot.

But wait! There is a much better way to do it! :smirk: Actually, I found out there was a hidden gem in knitr that allows cropping (using either pdfcrop or convert functions) to remove borders around an image. In fact, this is the function that is used to automatically remove borders around images when knitting an R Markdown document in R (see the initial plot of this post without borders). The function knitr::plot_crop(x) (where x is the filename of the plot to be cropped) will trim any existing images on your hard drive.

First, let’s create our plot in PDF format and use knitr::plot_crop() to remove the borders.

pdf_file <- "img/fig_border.pdf"

ggsave(
  pdf_file,
  device = cairo_pdf,
  width = 5.51,
  height = 4.68
)

knitr::plot_crop(pdf_file)
#> [1] "img/fig_border.pdf"

At this point, the borders have been removed from the original PDF. But what if you want to export this PDF into a bitmap image? This can be achieved using a combination of pdftools::pdf_render_page() and png::writePNG(). pdftools::pdf_render_page() will take the filename of a PDF file and render into a raw bitmap array whereas png::writePNG() will actually save the bitmap into a file.

png_file <- "img/fig_border_cropped.png"

bitmap <- pdftools::pdf_render_page(pdf_file, dpi = 600)
png::writePNG(bitmap, png_file)

Bang! No more borders around our plot πŸ‘

Session info
#> ─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.1 (2022-06-23)
#>  os       Linux Mint 21
#>  system   x86_64, linux-gnu
#>  ui       X11
#>  language en_CA:en
#>  collate  en_CA.UTF-8
#>  ctype    en_CA.UTF-8
#>  tz       America/Montreal
#>  date     2022-08-10
#>  pandoc   2.18 @ /usr/lib/rstudio/bin/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────
#>  ! package       * version date (UTC) lib source
#>  P askpass         1.1     2019-01-13 [?] RSPM
#>  P assertthat      0.2.1   2019-03-21 [?] RSPM
#>  P backports       1.4.1   2021-12-13 [?] RSPM
#>  P broom           1.0.0   2022-07-01 [?] RSPM (R 4.2.1)
#>  P cachem          1.0.6   2021-08-19 [?] RSPM (R 4.2.0)
#>  P callr           3.7.1   2022-07-13 [?] RSPM (R 4.2.1)
#>  P cellranger      1.1.0   2016-07-27 [?] RSPM
#>  P class           7.3-20  2022-01-13 [?] RSPM (R 4.2.0)
#>  P classInt        0.4-7   2022-06-10 [?] RSPM (R 4.2.0)
#>  P cli             3.3.0   2022-04-25 [?] RSPM (R 4.2.0)
#>  P colorspace      2.0-3   2022-02-21 [?] RSPM (R 4.2.0)
#>  P crayon          1.5.1   2022-03-26 [?] RSPM (R 4.2.0)
#>  P DBI             1.1.3   2022-06-18 [?] RSPM (R 4.2.0)
#>  P dbplyr          2.2.1   2022-06-27 [?] RSPM (R 4.2.0)
#>  P devtools        2.4.4   2022-07-20 [?] RSPM (R 4.2.0)
#>  P digest          0.6.29  2021-12-01 [?] RSPM
#>  P dplyr         * 1.0.9   2022-04-28 [?] RSPM (R 4.2.0)
#>  P e1071           1.7-11  2022-06-07 [?] RSPM (R 4.2.0)
#>  P ellipsis        0.3.2   2021-04-29 [?] RSPM
#>  P evaluate        0.15    2022-02-18 [?] RSPM (R 4.2.0)
#>  P extrafont       0.18    2022-04-12 [?] RSPM (R 4.2.0)
#>  P extrafontdb     1.0     2012-06-11 [?] RSPM (R 4.2.0)
#>  P fansi           1.0.3   2022-03-24 [?] RSPM (R 4.2.0)
#>  P farver          2.1.1   2022-07-06 [?] RSPM (R 4.2.1)
#>  P fastmap         1.1.0   2021-01-25 [?] RSPM
#>  P forcats       * 0.5.1   2021-01-27 [?] RSPM
#>  P fs              1.5.2   2021-12-08 [?] RSPM
#>  P gargle          1.2.0   2021-07-02 [?] RSPM
#>  P generics        0.1.3   2022-07-05 [?] RSPM (R 4.2.1)
#>  P ggplot2       * 3.3.6   2022-05-03 [?] RSPM (R 4.2.0)
#>  P ggpmthemes    * 0.0.2   2022-08-08 [?] Github (pmassicotte/ggpmthemes@993d61e)
#>  P glue            1.6.2   2022-02-24 [?] RSPM (R 4.2.0)
#>  P googledrive     2.0.0   2021-07-08 [?] RSPM
#>  P googlesheets4   1.0.0   2021-07-21 [?] RSPM
#>  P gtable          0.3.0   2019-03-25 [?] RSPM
#>  P haven           2.5.0   2022-04-15 [?] RSPM (R 4.2.0)
#>  P hms             1.1.1   2021-09-26 [?] RSPM
#>  P htmltools       0.5.3   2022-07-18 [?] RSPM (R 4.2.1)
#>  P htmlwidgets     1.5.4   2021-09-08 [?] RSPM (R 4.2.0)
#>  P httpuv          1.6.5   2022-01-05 [?] RSPM (R 4.2.0)
#>  P httr            1.4.3   2022-05-04 [?] RSPM (R 4.2.0)
#>  P jsonlite        1.8.0   2022-02-22 [?] RSPM (R 4.2.0)
#>  P KernSmooth      2.23-20 2021-05-03 [?] RSPM (R 4.2.0)
#>  P knitr           1.39    2022-04-26 [?] RSPM (R 4.2.0)
#>  P later           1.3.0   2021-08-18 [?] RSPM (R 4.2.0)
#>  P lifecycle       1.0.1   2021-09-24 [?] RSPM
#>  P lubridate       1.8.0   2021-10-07 [?] RSPM
#>  P magrittr        2.0.3   2022-03-30 [?] RSPM (R 4.2.0)
#>  P maps            3.4.0   2021-09-25 [?] CRAN (R 4.2.0)
#>  P memoise         2.0.1   2021-11-26 [?] CRAN (R 4.2.0)
#>  P mime            0.12    2021-09-28 [?] RSPM
#>  P miniUI          0.1.1.1 2018-05-18 [?] RSPM (R 4.2.0)
#>  P modelr          0.1.8   2020-05-19 [?] RSPM
#>  P munsell         0.5.0   2018-06-12 [?] RSPM
#>  P pdftools        3.3.0   2022-07-07 [?] RSPM (R 4.2.0)
#>  P pillar          1.8.0   2022-07-18 [?] RSPM (R 4.2.1)
#>  P pkgbuild        1.3.1   2021-12-20 [?] CRAN (R 4.2.0)
#>  P pkgconfig       2.0.3   2019-09-22 [?] RSPM
#>  P pkgload         1.3.0   2022-06-27 [?] RSPM (R 4.2.0)
#>  P png             0.1-7   2013-12-03 [?] RSPM (R 4.2.0)
#>  P prettyunits     1.1.1   2020-01-24 [?] RSPM
#>  P processx        3.7.0   2022-07-07 [?] RSPM (R 4.2.1)
#>  P profvis         0.3.7   2020-11-02 [?] RSPM (R 4.2.0)
#>  P promises        1.2.0.1 2021-02-11 [?] RSPM (R 4.2.0)
#>  P proxy           0.4-27  2022-06-09 [?] RSPM (R 4.2.0)
#>  P ps              1.7.1   2022-06-18 [?] RSPM (R 4.2.0)
#>  P purrr         * 0.3.4   2020-04-17 [?] RSPM
#>  P qpdf            1.2.0   2022-05-29 [?] RSPM (R 4.2.0)
#>  P R6              2.5.1   2021-08-19 [?] RSPM
#>  P ragg            1.2.2   2022-02-21 [?] CRAN (R 4.2.0)
#>  P Rcpp            1.0.9   2022-07-08 [?] RSPM (R 4.2.1)
#>  P readr         * 2.1.2   2022-01-30 [?] RSPM (R 4.2.0)
#>  P readxl          1.4.0   2022-03-28 [?] RSPM (R 4.2.0)
#>  P remotes         2.4.2   2021-11-30 [?] CRAN (R 4.2.0)
#>    renv            0.15.5  2022-05-26 [1] RSPM (R 4.2.0)
#>  P reprex          2.0.1   2021-08-05 [?] RSPM
#>  P rlang           1.0.4   2022-07-12 [?] RSPM (R 4.2.1)
#>  P rmarkdown       2.14    2022-04-25 [?] RSPM (R 4.2.0)
#>  P rstudioapi      0.13    2020-11-12 [?] RSPM
#>  P Rttf2pt1        1.3.10  2022-02-07 [?] RSPM (R 4.2.0)
#>  P rvest           1.0.2   2021-10-16 [?] RSPM
#>  P s2              1.1.0   2022-07-18 [?] RSPM (R 4.2.0)
#>  P scales          1.2.0   2022-04-13 [?] RSPM (R 4.2.0)
#>  P sessioninfo     1.2.2   2021-12-06 [?] CRAN (R 4.2.0)
#>  P sf            * 1.0-8   2022-07-14 [?] RSPM (R 4.2.0)
#>  P shiny           1.7.2   2022-07-19 [?] RSPM (R 4.2.1)
#>  P stringi         1.7.8   2022-07-11 [?] RSPM (R 4.2.1)
#>  P stringr       * 1.4.0   2019-02-10 [?] RSPM
#>  P systemfonts     1.0.4   2022-02-11 [?] CRAN (R 4.2.0)
#>  P textshaping     0.3.6   2021-10-13 [?] CRAN (R 4.2.0)
#>  P tibble        * 3.1.8   2022-07-22 [?] RSPM (R 4.2.1)
#>  P tidyr         * 1.2.0   2022-02-01 [?] RSPM (R 4.2.0)
#>  P tidyselect      1.1.2   2022-02-21 [?] RSPM (R 4.2.0)
#>  P tidyverse     * 1.3.2   2022-07-18 [?] RSPM (R 4.2.0)
#>  P tzdb            0.3.0   2022-03-28 [?] RSPM (R 4.2.0)
#>  P units           0.8-0   2022-02-05 [?] RSPM (R 4.2.0)
#>  P urlchecker      1.0.1   2021-11-30 [?] RSPM (R 4.2.0)
#>  P usethis         2.1.6   2022-05-25 [?] RSPM (R 4.2.0)
#>  P utf8            1.2.2   2021-07-24 [?] RSPM
#>  P vctrs           0.4.1   2022-04-13 [?] RSPM (R 4.2.0)
#>  P withr           2.5.0   2022-03-03 [?] RSPM (R 4.2.0)
#>  P wk              0.6.0   2022-01-03 [?] RSPM (R 4.2.0)
#>  P xfun            0.31    2022-05-10 [?] RSPM (R 4.2.0)
#>  P xml2            1.3.3   2021-11-30 [?] RSPM
#>  P xtable          1.8-4   2019-04-21 [?] RSPM (R 4.2.0)
#>  P yaml            2.3.5   2022-02-21 [?] RSPM (R 4.2.0)
#> 
#>  [1] /media/LaCie16TB/work/r-blog/renv/library/R-4.2/x86_64-pc-linux-gnu
#>  [2] /usr/local/lib/R/library
#> 
#>  P ── Loaded and on-disk path mismatch.
#> 
#> ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────