Map layout and themes with ggplot2 utility functions
ggsave_ext.Rmd
Getting started
Set parameters for the map and location
overedge is designed to easily support a workflow where the page or image size, page margin, and map area (defined by the location along with dist and diag_ratio parameters) are used repeatly across a range of related functions map making and layout functions.
# Set initial parameters
params <-
list(
paper = "Instagram post",
margin = "narrow",
name = "Harwood",
type = "neighborhoods",
package = "mapbaltimore",
source = "Baltimore City",
dist = NULL,
diag_ratio = 0.75
)
In this example, the location name, location type, and package with the type data are used to get an sf object for a Baltimore neighborhood.
# Create wrapper function to get neighborhood
get_neighborhood <- function(name = NULL, location = NULL) {
get_location(
type = params$type,
package = params$package,
name = name,
location = location
)
}
# Use the get_neighborhood wrapper function to get the neighborhood named in param
params$neighborhood <-
get_neighborhood(name = params$name)
Additional parameters can be derived based on the preliminary parameters and geometry. For example, page orientation can be based on the aspect ratio of the buffered bounding box for the named location. The aspect ratio for a location map is based on the combination of the paper, margin, and orientation. The overall bounding box for the map area requires the distance or diagonal ratio and the aspect ratio.
# Get appropriate orientation based on buffer parameters
params$orientation <-
sf_bbox_asp(
bbox = st_bbox_ext(
x = params$neighborhood,
dist = params$dist,
diag_ratio = params$diag_ratio
),
orientation = TRUE
)
# Get aspect ratio for content block based on paper size, margin, and orientation
params$block_asp <-
get_asp(
paper = params$paper,
unit = params$unit,
margin = params$margin,
orientation = params$orientation,
block_asp = TRUE
)
# Get additional parameters based on the paper (used in later sections of this vignette)
params$paper_df <-
get_paper(
paper = params$paper,
orientation = params$orientation
)
params$asp <- params$paper_df$asp
params$width <- params$paper_df$width
params$height <- params$paper_df$height
# Get map bounding box for neighborhood based on adjustment parameters
block_bbox <-
st_bbox_ext(
x = params$neighborhood,
dist = params$dist,
diag_ratio = params$diag_ratio,
asp = params$asp_block
)
# Get nearby neighborhoods that overlap with the buffer and aspect ratio adjusted bounding box
nearby_neighborhoods <-
get_neighborhood(
location = block_bbox
)
A quick map is helpful for illustrating the relative size and proportion of the different simple feature objects and bounding boxes created through this process:
ggplot() +
layer_location_data(
data = "baltimore_city_detailed",
package = "mapbaltimore"
) +
geom_sf(data = nearby_neighborhoods, alpha = 0.25, fill = "yellow") +
geom_sf(data = sf_bbox_to_sf(block_bbox), alpha = 0.25, fill = "blue") +
geom_sf(data = params$neighborhood, alpha = 0.25, fill = "green") +
layer_neatline(
data = nearby_neighborhoods
)
In this example, the objects block_bbox
and
nearby_neighborhoods
are as separate objects but could
easily be added to the list of parameters. The approach of using
parameters is intended to make this code easy to reuse for a
parameterized RMarkdown document.
In some cases, it may be convenient to wrap an overedge function with
preset parameters or use purrr::partial()
for a similar
purpose:
# Use partial to create a version of layer_location_data that applies a half-mile buffer to the specified neighborhood
layer_half_mi_buffer <-
purrr::partial(
layer_location_data,
location = params$neighborhood,
dist = 0.5,
unit = "mi",
diag_ratio = NULL,
asp = get_asp(paper = "letter", orientation = "landscape")
)
This can be helpful in reducing repetition when setting up maps but it also can get confusing so use this approach with care.
ggplot() +
layer_half_mi_buffer(
data = params$type,
package = params$package,
aes(fill = name),
alpha = 0.75
) +
guides(fill = "none") +
theme_void()
Make a map
Create a basemap and label layer
In order to maintain some flexibility in the map layout process, it may be helpful to set up a basemap that covers a larger area. This example illustrates how to combine street center line data, neighborhood boundaries, and labels for residential neighborhoods into a larger basemap centered on the selected neighborhood.
basemap <-
ggplot() +
layer_location_data(
location = block_bbox,
data = "streets",
package = "mapbaltimore",
color = "gray40"
) +
layer_location_data(
data = nearby_neighborhoods,
mapping = aes(fill = name),
color = NA,
alpha = 0.6
) +
scale_fill_brewer() +
guides(
fill = "none"
)
basemap
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
#> Returning the palette you asked for with that many colors
layer_location_data()
is a flexible wrapper function
around several different types of geoms for sf objects including geoms
from ggrepel and geomtextpath.
This makes it easy to create separate layers for a basemap, labels, or
other features.
label_layer <-
layer_location_data(
location = block_bbox,
data = "neighborhoods",
package = "mapbaltimore",
geom = "label",
mapping = aes(label = name),
fn = ~ dplyr::filter(
.x,
name %in% nearby_neighborhoods$name,
type == "Residential"
),
fill = "white",
color = "black"
)
basemap +
label_layer
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
#> Returning the palette you asked for with that many colors
Add a neatline, location mask, or location boundary
After creating any needed layers for a map, you may want to focus on
a specific area and clean up the appearance of the map by removing grid
lines and axis labels. The layer_neatline()
function makes
it easy to effectively “zoom” in to a specific area while hiding the
grid lines and axis labels.
neighborhood_map <-
basemap +
# Draw a neatline around the selected neighborhood
layer_neatline(
data = params$neighborhood,
diag_ratio = params$diag_ratio,
asp = params$paper_df$asp,
size = 1.5,
color = "gray60",
expand = TRUE
)
neighborhood_map
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
#> Returning the palette you asked for with that many colors
The layer_mask()
function also allow you to add
layer_neatline()
to a map along with a mask using the same
parameters.
basemap +
layer_mask(
data = params$neighborhood,
dist = 200,
asp = params$asp,
neatline = TRUE
)
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
#> Returning the palette you asked for with that many colors
letter_map <-
basemap +
birdseyeview::layer_show_location(
data = params$neighborhood,
dist = 200,
size = 1.5,
asp = get_asp(paper = "letter", margin = c(2, 1, 1.5, 1), block_asp = TRUE),
neatline = TRUE
)
Add margins
The last step is to add margins. Margins can also include a header and footer to ensure there is sufficient space for a title, subtitle, and caption.
letter_map +
theme_margin(
paper = "letter",
margin = "standard",
header = 1,
footer = 0.5,
fill = "gray90"
) # +
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
#> Returning the palette you asked for with that many colors
# labs_ext(
# title = "{params$name} and nearby neighborhoods",
# caption = "Data courtesy {params$source}."
# )
The resulting map appears oddly proportioned at the display size for this vignette but the units ensure the margin is appropriately sized for the paper size provided.
Export a map
Finally, the ggsave_exif()
function uses the
make_filename()
function also used by
write_sf_ext()
to create consistent file names that can
optionally include a date or date-time prefix.
make_filename(
name = "Neighborhood map",
label = params$name,
prefix = "date"
)
#> [1] "2022-04-13_harwood_neighborhood_map"
Save map with ggsave_ext
The dimensions of the exported file may be set automatically based on
paper and orientation. The title and author parameter are used to update
the EXIF metadata for the file after it is exported. The title and
author are both evaluated with glue()
which allow you to
use the same parameters to label and title the map.
ggsave_ext(
plot = neighborhood_map,
name = "Neighborhood map",
label = params$name,
title = "Map of {params$name} and surrounding area",
author = "Eli Pousson",
paper = params$paper,
orientation = params$orientation,
device = "png",
prefix = "date",
exif = TRUE,
bg = "white"
)