Hello,
I am trying to create a map in ggplot with the following 3 layers:
county fill and outline set to a categorical variable
roads from open street maps
rivers from open street maps
Currently I am able to create this map, but can't properly add an additional legend which includes the river and road features. Apologies for the large open street maps data download. I wanted to create a repex with an existing sf dataset, and the one I was able to find was for the entire state of North Carolina. Any help with this would be much appreciated!
# Repex for ggplot legend with multiple geom_sf layers
library(sf)
library(osmextract)
library(dplyr)
library(ggplot2)
library(stringr)
library(RColorBrewer)
# Using nc data because it is included with sf package
nc <- st_read(system.file("shape/nc.shp", package="sf"))
# Create random categorical variable for example county fill
nc <- nc %>%
mutate(bir74_cat = case_when(BIR74 < 1077 ~ "Low",
BIR74 >= 1077 & BIR74 < 2180.5 ~ "Low-Medium",
BIR74 >= 2180.5 & BIR74 < 3936 ~ "Medium-High",
BIR74 >= 3936 ~ "High"),
bir74_cat = factor(bir74_cat,
levels = c("Low",
"Low-Medium",
"Medium-High",
"High")))
# Get osmdata for the state
nc_lines <- oe_get("North Carolina", stringsAsFactors = FALSE, quiet = TRUE)
# Filter to highway df
nc_highway <- nc_lines %>%
filter(str_detect(name, "Highway|Freeway"))
# Filter to river df
nc_river <- nc_lines %>%
filter(waterway == "river")
# Restrict highway to state boundary
nc_highway_crs <- st_transform(nc_highway, 4267)
nc_highway_map <- st_intersection(nc, nc_highway_crs)
# Restrict rivers to state boundary
nc_river_crs <- st_transform(nc_river, 4267)
nc_river_map <- st_intersection(nc, nc_river_crs)
# Map fill and line color
map_colors <- brewer.pal(n = 4, "YlOrRd")
names(map_colors) <- c("Low", "Low-Medium", "Medium-High",
"High")
ggplot() +
geom_sf(nc,
mapping = aes(fill = bir74_cat,
color = bir74_cat)) +
geom_sf(nc_river_map,
mapping = aes(),
color = "blue",
fill = NA) +
geom_sf(nc_highway_map,
mapping = aes(),
color = "black",
fill = NA) +
scale_fill_manual(values = map_colors) +
scale_color_manual(values = map_colors) +
theme_void() +
labs(title = "Reproducible Example") +
theme(legend.position = "bottom")
Well I spent way too much time on troubleshooting this but I was able to solve it myself. There were several key steps.
combining the river and road lines into one dataframe, and adding a categorical variable to indicate the line type (River or Road).
Using new_scale_color() from the package ggnewscale allowed me to add a new manual color scale to the rive / road categorical variable. This package is great but frustrating when trying to modify legend elements. The issue is color within guides references two different legends. eg. guides(color = ...) doesn't function properly because we have two legends that both specify color. It sounds like the relayer package (GitHub - clauswilke/relayer: Rethinking layers in ggplot2) may get around this but I didn't try it. This was mainly an issue when trying to reorder which legend is displayed first. It also meant that I had to name my legends by making the color variable name a string eg. Number Births 1974.
I used key_glyph = "smooth" to changed the legend glyph from a fill box to a horizontal line. I also used theme(legend.position = "bottom", legend.box="vertical", legend.margin=margin()) to stack the legends on top of one another.