Search code examples
rggplot2dplyrr-sfggrepel

ggplot2 and sf: geom_sf_text within limits set by coord_sf


I am using sf and ggplot2 to read shapefiles as simple features and plot various maps. I have been working through the maps chapter in the ggplot2 book but could not really find an answer to the following issue:

Plotting a map using geom_sf and labelling its features with geom_sf_text is a pretty straightforward task.

library(ggplot2)
library(sf)
library(ozmaps)

oz_states <- ozmaps::ozmap_states

ggplot() +
  geom_sf(data = oz_states) +
  geom_sf_text(data = oz_states, aes(label = NAME))

enter image description here

Once we zoom in on a section of the previous map, not all labels of the features present in the plot are visible.

xlim <- c(120.0, 140.0)
ylim <- c(-40, -24)

ggplot() +
  geom_sf(data = oz_states) +
  geom_sf_text(data = oz_states, aes(label = NAME)) +
  coord_sf(xlim = xlim, ylim = ylim)

enter image description here

I have found a workaround to zoom in on sections of the map and still be able to label the features present in the plot by calculating the centroids of the features, extracting the coordinates as separate columns, selecting the elements I would like to be displayed in the final map, and using ggrepel to label them.

library(dplyr)
library(ggrepel)

oz_states_labels <- oz_states %>% st_centroid()

oz_states_labels <- do.call(rbind, st_geometry(oz_states_labels)) %>%
  as_tibble() %>%
  rename(x = V1) %>% 
  rename(y = V2) %>% 
  cbind(oz_states_labels) %>%
  slice(4,5,7,3)

ggplot() +
  geom_sf(data = oz_states) +
  geom_text_repel(data = oz_states_labels, aes(label = NAME, x = x, y = y)) +
  coord_sf(xlim = xlim, ylim = ylim)

enter image description here

Naturally, if possible, I would like to avoid the workaround of first having to calculate the centroids, extract the coordinates from the resulting sf and select the labels to be shown in the final map.

Hence my question: Is there a faster way of labelling all elements visible in the plot for example by specifying this either in geom_sf_text or coord_sf?

Thanks in advance for your tips and answers!


Solution

  • I believe the issue you are facing is caused by your applying the crop at presentation level / the actual data underlying your ggplot object is not cropped.

    I suggest applying the crop at data level, for example via sf::st_crop(). In this example I am using the values of your xlim and ylim objects to create a bounding box (called crop_factor for no good reason) to limit the extent of the oz_states at the data level, by creating a new object called oz_cropped & continuing in your original workflow.

    All the centroids and labels and what not will be much better behaved now.

    library(ggplot2)
    library(sf)
    library(ozmaps)
    
    oz_states <- ozmaps::ozmap_states
    
    
    crop_factor <- st_bbox(c(xmin = 120, 
                             xmax = 140, 
                             ymax = -24, 
                             ymin = -40),
                           crs = st_crs(oz_states))
    
    oz_cropped <- st_crop(oz_states, crop_factor)
    
    ggplot() +
      geom_sf(data =oz_cropped) +
      geom_sf_text(data = oz_cropped, aes(label = NAME))
    

    enter image description here