deschen
deschen

Reputation: 11016

Crop unused margin in a ggplot plot

I have created a ggplot worldmap that I want to save as svg. Both work fine. However, I want to change/specify the area of the plot that gets saved.

My code looks as follows:

raw_plot = ggplot(data = world_for_plot,
                  aes(x = long,
                      y = lat,
                      group = group,
                      fill = "black")) +
  geom_polygon(aes(fill = results)) +
  coord_equal(1.3, expand=F) +
  scale_fill_continuous(na.value = '#F6F6F6', guide=F)

ggsave(file = "FOLDER\\LATAM.svg",
       plot = raw_plot)

This gives me the following plot:

enter image description here

However, I want to have a file/plot that doesn't have all the unnecessary white space on top and bottom:

enter image description here

Any ideas? I already figured out that the coord_equal command creates some problems. Without it, the plot fills out the whole plot area. However, I need the coord_equal command, otherwise the world map would look stretched.

Upvotes: 3

Views: 2337

Answers (2)

Junitar
Junitar

Reputation: 999

I recently faced the same problem so here're my solutions for people with the same issue.

tmaptools has a function get_asp_ratio to compute the aspect ratio of spatial objects. We can use that ratio to remove the outer margin of any map.

library(ggplot2)

world <- rnaturalearth::ne_countries(returnclass = "sf") # to get the world map

ggplot() +
  geom_sf(data = world) +
  coord_sf(expand = FALSE) +
  theme_void()

asp <- tmaptools::get_asp_ratio(world) # returns 2.070007

height <- 5
ggsave("world.svg", width = height * asp, height = height)

Alternatively, if we don't want to depend on tmaptools, we can make our own functions with a wrapper around ggsave. This is essentially based on the tmaptools::get_asp_ratio function.

deg_to_rad <- function(x) {
  (mean(x) * pi) / 180
}

get_aspect_ratio <- function(geometry) {
  if (!inherits(geometry, c("Spatial", "Raster", "sf", "sfc"))) {
    stop('"geometry" must be of class "Spatial", "Raster", "sf" or "sfc".')
  }
  bbox <- sf::st_bbox(geometry)
  xlim <- bbox[c(1, 3)]
  ylim <- bbox[c(2, 4)]
  xdeg <- diff(xlim)
  ydeg <- diff(ylim)
  if (xdeg == 0 || ydeg == 0) {
    asp <- 1
  } else {
    is_lon_lat <- sf::st_is_longlat(geometry)
    asp <- unname((xdeg / ydeg) * ifelse(is_lon_lat, cos(deg_to_rad(ylim)), 1))
  }
  asp
}

save_ggmap <- function(filename, plot = last_plot(), width = NA, height = NA, ...) {
  geometry <- ggplot_build(plot)$data[[1]]$geometry
  asp <- get_aspect_ratio(geometry)
  if (is.na(width) && !is.na(height)) {
    width <- height * asp
  } else if (is.na(height) && !is.na(width)) {
    height <- width / asp
  }
  ggsave(filename, plot, width = width, height = height, ...)
}

We can use the function the same way as ggsave. We only have to specify either the width or height and the function will save the map with the right aspect ratio automatically.

ggplot() +
  geom_sf(data = world) +
  coord_sf(expand = FALSE) +
  theme_void()

save_ggmap("world.svg", width = 8)

To remove the inner margin, we can use theme(plot.margin = margin(0, 0, 0, 0)).

Upvotes: 1

Matthias
Matthias

Reputation: 125

This might work if you adjust the lengths:

raw_plot + theme(plot.margin = margin(-2, 0, -2, 0, "cm"))

The order of the margins here is top, right, bottom, left.

Upvotes: 1

Related Questions