Enhance your map with a scale bar and a north arrow using the package ggsn
!
Have you ever created a map in ggplot and wondered how to add a scale bar and a north arrow to it? Well, you can do it easily using the package ggsn
, and this is the topic for this short post.
We’ll create a topographic map of Taiwan as our example here. First, we’ll use the function get_elev_raster()
from the package elevatr
to retrieve the elevation tiles of Taiwan from the online map databases. Basically, what we need to do is to read in a shapefile of the area of interest and pass it to the function. It’ll return a “RasterLayer” object, which is then converted to a dataframe for plotting in ggplot.
library(tidyverse)
### Download and read in the shapefile of Taiwan's administrative boundary
### The zipped shapefile is available in the GitHub folder of this post
library(sf)
Taiwan_boundary_admin0_url <- "https://github.com/GenChangHSU/ggGallery/blob/master/_posts/2023-12-24-post-32-ggplot-map-series-no5-map-annotation-with-ggsn/geoBoundaries-TWN-ADM0-all.zip?raw=TRUE"
download.file(Taiwan_boundary_admin0_url, "geoBoundaries-TWN-ADM0-all.zip", mode = "wb")
unzip("geoBoundaries-TWN-ADM0-all.zip")
Taiwan_boundary_admin0 <- st_read("geoBoundaries-TWN-ADM0-all/geoBoundaries-TWN-ADM0.shp", quiet = T)
### Get the elevation tiles of Taiwan
library(elevatr)
Taiwan_elevation <- get_elev_raster(locations = Taiwan_boundary_admin0, # the shapefile
z = 9, # z controls the resolution of the elevation tiles
clip = "locations" # clip the tiles to the polygon area
) %>%
terra::as.data.frame(xy = T, na.rm = T) %>% # convert the RasterLayer to a dataframe
`colnames<-`(c("lon", "lat", "elevation")) %>% # rename the columns
mutate(elevation = if_else(elevation < 0 | is.na(elevation), 0, elevation)) # set the negative elevation values and NAs to 0
### Make the topographic map
library(tidyterra) # for the function hypso.colors()
topo_pal <- hypso.colors(n = 10, palette = "dem_poster") # a topographic color palette
Taiwan_topographic_map <- ggplot() +
geom_raster(data = Taiwan_elevation, aes(x = lon, y = lat, fill = elevation), show.legend = F) +
scale_fill_gradientn(colors = topo_pal,
values = c(seq(0, 0.35, length.out = 5), seq(0.5, 1, length.out = 5))) +
coord_sf(xlim = c(119, 123), ylim = c(21.5, 25.5)) +
labs(x = NULL, y = NULL) +
theme_bw() +
theme(panel.grid = element_blank())
Taiwan_topographic_map
It’s time to add a scale bar! ggsn
has the function scalebar()
for it. There are several things we can customize: the position of the scale bar, the distance and unit of the bar segment, the appearance of the bar and bar labels, etc. Let’s see how it works:
library(ggsn)
Taiwan_topographic_map_annotated <- Taiwan_topographic_map +
scalebar(x.min = 121.9,
x.max = 122.9,
y.min = 21.6,
y.max = 22.1,
dist = 50, # the distance of the bar segment
dist_unit = "km", # the unit of the bar segment
transform = TRUE, # "TRUE" if the coordinates are in the long-lat format
model = "WGS84", # ellipsoid model for the transformation if "transform = TRUE"
height = 0.2, # the height of the scale bar
st.dist = 0.2, # the distance between the scale bar and the bar labels
st.size = 2.5, # the size of the bar labels
border.size = 0.25 # the width of the bar border
)
Taiwan_topographic_map_annotated
Next, the north arrow. This is also easy-peasy: Just use the function north()
and specify the position as well as the type of the arrow you like (there are various types of north arrows available; use northSymbols()
to see them), and you’re all set! We’ll also add an “N” above the north arrow. It does take some experimentation to get the desired arrow position and size, so be patient!
Taiwan_topographic_map_annotated <- Taiwan_topographic_map_annotated +
north(x.min = 122.58,
x.max = 122.68,
y.min = 22.4,
y.max = 22.6,
symbol = 10, # the type of the north arrow
scale = 5 # the size of the north arrow
) +
annotate(geom = "text", x = 122.45, y = 22.5, label = "N", size = 5)
Taiwan_topographic_map_annotated
Done!
Let’s play around with the map (a bit extra and just for fun)!
### A vector of individual characters in the title
title_vec <- str_split("A Topographic Map of Taiwan", pattern = "")[[1]]
### Italicize the letters
title_vec_italic <- if_else(title_vec == " ", title_vec, str_glue("_{title_vec}_"))
### A dataframe for mapping the characters to the plot
x_seq <- seq(119.2, 121.8, by = 0.1)
x_nudge <- c(-0.04, 0.00, 0.02, 0.02, -0.04, 0.00, -0.03, 0.00, 0.00, -0.03,
0.02, 0.01, 0.01, 0.00, 0.02, 0.07, 0.04, 0.00, 0.04, -0.01,
0.05, 0.05, 0.05, 0.05, 0.07, 0.09, 0.11)
x_position <- x_seq + x_nudge
title_df <- data.frame(x = x_position,
y = 25.6,
label = title_vec_italic)
### The font
library(showtext)
font_add_google("IM FELL English")
showtext_auto()
### The map
library(ggtext)
ggplot() +
geom_raster(data = Taiwan_elevation, aes(x = lon, y = lat, fill = elevation), show.legend = F) +
geom_richtext(data = title_df, aes(x = x, y = y, label = label, color = x),
fill = NA,
label.color = NA,
family = "IM FELL English",
size = 7,
show.legend = F) +
scale_fill_gradientn(colors = topo_pal,
values = c(seq(0, 0.35, length.out = 5), seq(0.5, 1, length.out = 5))) +
scale_color_gradientn(colors = topo_pal, limits = c(NA, 122.5)) +
coord_sf(xlim = c(119, 123), ylim = c(21.5, 25.73)) +
theme_void() +
theme(panel.background = element_rect(fill = "#FEFEDF", color = NA)) +
scalebar(x.min = 122.1,
x.max = 122.9,
y.min = 21.6,
y.max = 22.2,
dist = 50,
dist_unit = "km",
transform = TRUE,
model = "WGS84",
height = 0.1,
st.dist = 0.15,
st.size = 4.5,
border.size = 0.2,
family = "IM FELL English") +
north(x.min = 122.51,
x.max = 122.61,
y.min = 22.2,
y.max = 22.4,
symbol = 10,
scale = 4) +
annotate(geom = "text", x = 122.41, y = 22.35, label = "N", size = 7, family = "IM FELL English", fontface = "italic")
To summarize what we did, we used the package elevatr
to get the elevation tiles and made a topographic map of Taiwan, and then we added a scale bar and a north arrow to the map using the scalebar()
and north()
function from the package ggsn
. Now, it’s your turn to make use of what we learned here!
Hope you learn something useful from this post and don’t forget to leave your comments and suggestions below if you have any!
If you see mistakes or want to suggest changes, please create an issue on the source repository.