<- filter(storms, year == 1975) storms75
18 Simple Features Maps
In the preceding chapter we described a fairly basic way to make maps of storms with packages "maps"
and "ggplot2"
. While this approach may let us get the job done for simple maps, one of its main limitations has to do with the distortions caused when zooming-in to certain areas of a given map.
A better approach to make maps of storm locations with "ggplot2"
is to use it jointly with the packages "sf"
(simple features) and "rnaturalearth"
. Instead of using map datasets from "maps"
we are going to use maps from "rnaturalearth"
. And instead of using the geom_polygon()
layer, we are going to use the geom_sf()
layer.
The code in this chapter requires the following packages:
library(tidyverse) # for syntactic manipulation of tables
library(sf) # provides classes and functions for vector data
library(rnaturalearth) # map data from Natural Earth
library(plotly) # interactive plots
and the following table for storms in 1975:
18.1 Basic World Map
Like we did it in the previous chapter, let’s start with a basic world map. "rnaturalearth"
provides a couple of world maps:
world coastline map, returned by
ne_coastline()
world country polygons, returned by
ne_countries()
18.1.1 World Coastline Map
One very simple world map involves the coastline map. As mentioned above, this map is returned by the ne_coastline()
function. In the following command, we specify a medium
scale resolution, and a returned object of class "sf"
.
# natural earth world coastline
= ne_coastline(scale = "medium", returnclass = "sf")
world_coast
class(world_coast)
[1] "sf" "data.frame"
Because we are going use the geom_sf()
layer, it is important to specify the argument returnclass = "sf"
so that we obtain an object of class "sf"
. This kind of object is a simple features object, but it’s also a data frame suitable for ggplot()
.
We pass world_coast
to ggplot()
, and use geom_sf()
which is the function that allows us to visualize the polygons of simple features objects "sf"
.
ggplot(data = world_coast) +
geom_sf()
18.1.2 World Countries Map
Another world map involves the countries map. The data for this map is returned by ne_countries()
. Like in the previous example, make sure you set the argument returnclass = "sf"
to get a simple features tabular object that is also a data.frame
:
# natural earth world country polygons
= ne_countries(returnclass = "sf")
world_countries
ggplot(data = world_countries) +
geom_sf()
When making maps with ggplot()
I like to modify the background to obtain simpler and cleaner visualizations. One option to do this is with the theme()
function:
ggplot(data = world_countries) +
geom_sf() +
theme(panel.background = element_blank())
One of the nice aspects of "rnaturalearth"
maps is that we can zoom-in without having distorted polygons. To focus on a specific region, we set the x-axis and y-axis limits with the coord_sf()
function, for example:
ggplot(data = world_countries) +
geom_sf() +
coord_sf(xlim = c(-110, 0), ylim = c(5, 65)) +
theme(panel.background = element_blank())
18.1.3 North America Map
Similarly, the ne_countries()
function lets you obtain polygons data for a certain continent of the world: e.g. "Europe"
, "Africa"
, "Asia"
, "North America"
, "South America"
, "Oceania"
, etc.
# natural earth world country polygons in North America
= ne_countries(continent = "North America", returnclass = "sf")
north_america
ggplot(data = north_america) +
geom_sf() +
theme(panel.background = element_blank())
18.2 Plotting Storms
Now that we know how to get polygons from natural earth, and how to plot them—via ggplot()
—with geom_sf()
and coord_sf()
, let’s add the longitude and latitude locations of storms in 1975.
Because the storm locations come from the tibble storms75
, we pass this data to geom_point()
ggplot(data = north_america) +
geom_sf() +
geom_point(data = storms75,
aes(x = long, y = lat, group = name)) +
theme(panel.background = element_blank())
Instead of points, we can connect the locations with lines through geom_path()
ggplot(data = north_america) +
geom_sf() +
geom_path(data = storms75,
aes(x = long, y = lat, group = name)) +
theme(panel.background = element_blank())
And then enhance it with colors mapping the name
column to the color
aesthetic of geom_path()
:
ggplot(data = north_america) +
geom_sf() +
geom_path(data = storms75,
aes(x = long, y = lat, group = name, color = name)) +
theme(panel.background = element_blank())
Interactive map with ggplotly()
For your amusement, you can take a ggplot object and pass it to ggplotly()
in order to get an interactive map:
= ggplot(data = north_america) +
gg geom_sf() +
geom_path(data = storms75,
aes(x = long, y = lat, group = name, color = name)) +
theme(panel.background = element_blank())
ggplotly(gg)
18.3 Hurricanes in 1975
Hurricanes in 1975
# vector of hurricane names
<- storms75 |>
hurr_names75 filter(wind >= 64) |>
distinct(name) |>
pull(name)
hurr_names75
[1] "Blanche" "Caroline" "Doris" "Eloise" "Faye" "Gladys"
We can then filter hurricanes:
<- storms75 |>
hurricanes75 filter(name %in% hurr_names75)
We’ll use hurricanes75
to super impose their longitude and latitude coordinates onto the world_countries
map:
ggplot(data = world_countries) +
geom_sf() +
geom_path(
data = hurricanes75,
aes(x = long, y = lat, group = name, color = name, linewidth = wind),
lineend = "round", alpha = 0.6) +
coord_sf(xlim = c(-110, 0), ylim = c(5, 65), expand = FALSE) +
theme(panel.background = element_blank()) +
labs(title = "Hurricanes in 1975")
A slightly more curated map
ggplot(data = world_countries) +
geom_sf(fill = "#FFFBED") +
geom_path(
data = hurricanes75,
aes(x = long, y = lat, group = name, color = name, linewidth = wind),
lineend = "round", alpha = 0.6) +
coord_sf(xlim = c(-110, 0), ylim = c(5, 65), expand = FALSE) +
theme(panel.background = element_rect(fill = "aliceblue")) +
labs(title = "North Atlantic Hurricanes",
subtitle = "Season 1975")
18.4 More Maps
As a simple experiment, let’s graph storms between 1975 and 1980 (six years). First we create a dedicated data table storms_set
to select the rows we are interested in:
# 2nd map (coloring storms individually)
= 1975:1980
which_years
# Subset storms (for a given year)
= storms |>
storms_set filter(year %in% which_years)
And then we can use facet_wrap(~ year)
to graph storms by year:
ggplot(data = world_countries) +
geom_sf() +
coord_sf(xlim = c(-110, 0), ylim = c(5, 65), expand = FALSE) +
geom_path(data = storms_set,
aes(x = long, y = lat, group = name),
lineend = "round") +
theme(panel.background = element_blank()) +
facet_wrap(~ year) +
labs(title = "Storms in North Atlantic, 1975-1980")