This article provides some recipes for plots that might be of interest. These examples use map data from the nswgeo package.
Many of the examples use the same example dataset modelled after the
structure of a linelist: rows are distinct events, and they can have a
type
of A
or B
. Each event is
associated with a location described at different granularities by the
postcode
, lga
, and lhd
columns.
head(covid_cases_nsw)
#> # A tibble: 6 × 5
#> postcode lga lhd year type
#> <chr> <chr> <chr> <int> <chr>
#> 1 2427 Mid-Coast Hunter New England 2022 B
#> 2 2761 Blacktown Western Sydney 2021 A
#> 3 2426 Mid-Coast Hunter New England 2022 B
#> 4 2148 Blacktown Western Sydney 2022 B
#> 5 2768 Blacktown Western Sydney 2021 A
#> 6 2766 Blacktown Western Sydney 2021 B
You need to specify which column has the feature by setting the
location
aesthetic. This example has three different
columns of locations for different feature types; your dataset only
needs to have one of these.
In general you’ll start with geom_boundaries()
to draw
the base map. This geom needs to be told which feature_type
you’re after (e.g. "nswgeo.lga"
for LGAs). All of the
summary geoms of ggautomap
can then be used to draw your
data.
Scatter
covid_cases_nsw %>%
ggplot(aes(location = lga)) +
geom_boundaries(feature_type = "nswgeo.lga") +
geom_geoscatter(aes(colour = type), sample_type = "random", size = 0.5) +
coord_automap(feature_type = "nswgeo.lga", xlim = c(147, 153), ylim = c(-33.7, -29)) +
guides(colour = guide_legend(override.aes = list(size = 1))) +
theme_void()
Points are drawn at random within the boundaries of their location.
Insets
To show a zoomed in part of the map as an inset, you can configure an inset and provide it to each relevant geom. The geoms in this package are all inset-aware. See ggmapinset for details.
covid_cases_nsw %>%
ggplot(aes(location = lga)) +
geom_boundaries(feature_type = "nswgeo.lga") +
geom_geoscatter(aes(colour = type), size = 0.5) +
geom_inset_frame() +
coord_automap(feature_type = "nswgeo.lga", inset = configure_inset(
centre = "Blacktown", radius = 40, units = "km",
scale = 7, translation = c(400, -100)
)) +
theme_void()
Packed points
This next example uses geom_centroids()
to place the
points in a packed circle in the centre of each feature. It also shows
how you can fine-tune the plot with the usual ggplot2
functions.
covid_cases_nsw %>%
dplyr::filter(year >= 2021) %>%
ggplot(aes(location = lhd)) +
geom_boundaries(feature_type = "nswgeo.lhd") +
geom_centroids(aes(colour = type), position = position_circle_repel_sf(scale = 35), size = 1) +
geom_inset_frame() +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Sydney", radius = 80, units = "km", feature_type = "nswgeo.lhd",
scale = 6, translation = c(650, -100)
)) +
facet_wrap(vars(year)) +
labs(x = NULL, y = NULL) +
theme_void() +
theme(strip.text = element_text(size = 12))
Choropleths
If your data has multiple rows for each location (such as our example
dataset where the rows are disease cases) then you can use
geom_choropleth()
to aggregate these into counts.
covid_cases_nsw %>%
ggplot(aes(location = lhd)) +
geom_choropleth() +
geom_boundaries(
feature_type = "nswgeo.lhd", colour = "black", linewidth = 0.1,
outline.aes = list(colour = NA)
) +
geom_inset_frame() +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Western Sydney", radius = 60, units = "km",
scale = 5, translation = c(400, -100)
)) +
scale_fill_steps(low = "#e6f9ff", high = "#00394d", n.breaks = 5, na.value = "white") +
theme_void()
On the other hand, if your dataset has only one row per location and
there is an existing column that you’d like to map to the
fill
aesthetic, then instead use
geom_sf_inset(..., stat = "automap")
:
summarised_data <- data.frame(
lhd = c("Western Sydney", "Sydney", "Far West", "Mid North Coast", "South Western Sydney"),
cases = c(250, 80, 20, NA, 100)
)
summarised_data %>%
ggplot(aes(location = lhd)) +
geom_sf_inset(aes(fill = cases), stat = "automap", colour = NA) +
geom_boundaries(
feature_type = "nswgeo.lhd", colour = "black", linewidth = 0.1,
outline.aes = list(colour = NA)
) +
geom_inset_frame() +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Western Sydney", radius = 60, units = "km",
scale = 3.5, translation = c(350, 0)
)) +
scale_fill_gradient(low = "#e6f9ff", high = "#00394d", na.value = "grey90") +
theme_void()
Positioning text
These examples give some different ways of placing text, accounting for possible insets.
covid_cases_nsw %>%
ggplot(aes(location = lhd)) +
geom_choropleth() +
geom_boundaries(feature_type = "nswgeo.lhd") +
geom_inset_frame() +
geom_sf_label_inset(aes(label = lhd),
stat = "automap_coords",
data = ~ dplyr::slice_head(.x, by = lhd)
) +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Western Sydney", radius = 60, units = "km",
scale = 3.5, translation = c(350, 0)
)) +
labs(x = NULL, y = NULL) +
theme_void()
The repulsive labels from ggrepel can be used; they
just require a bit of massaging since they don’t natively understand the
spatial data. Note that you may also wish to use
point.padding = NA
to disable the default repulsion caused
by the labelled points, which is good for labelling scatter plots but
often doesn’t make sense in mapping contexts.
library(ggrepel)
# label all features that have data
covid_cases_nsw %>%
ggplot(aes(location = lhd)) +
geom_choropleth() +
geom_boundaries(feature_type = "nswgeo.lhd") +
geom_inset_frame() +
geom_label_repel(
aes(
x = after_stat(x_inset),
y = after_stat(y_inset),
label = lhd
),
stat = "automap_coords",
nudge_x = 3,
nudge_y = 1,
point.padding = NA,
data = ~ dplyr::slice_head(.x, by = lhd)
) +
scale_fill_distiller(direction = 1) +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Western Sydney", radius = 60, units = "km",
scale = 3.5, translation = c(350, 0)
)) +
labs(x = NULL, y = NULL) +
theme_void()
# label all features in the map regardless of data, hiding visually overlapping labels
covid_cases_nsw %>%
ggplot(aes(location = lhd)) +
geom_choropleth() +
geom_boundaries(feature_type = "nswgeo.lhd") +
geom_inset_frame() +
geom_label_repel(
aes(
x = after_stat(x_inset),
y = after_stat(y_inset),
geometry = geometry,
label = lhd_name
),
stat = "sf_coordinates_inset",
data = cartographer::map_sf("nswgeo.lhd"),
point.padding = NA,
inherit.aes = FALSE
) +
scale_fill_distiller(direction = 1, palette = 2) +
coord_automap(feature_type = "nswgeo.lhd", inset = configure_inset(
centre = "Western Sydney", radius = 60, units = "km",
scale = 4, translation = c(500, 0)
)) +
labs(x = NULL, y = NULL) +
theme_void()