-
Notifications
You must be signed in to change notification settings - Fork 309
Moving Hawaii and Alaksa
Submitted by Claus Wilke
Many visualizations of the U.S. show the lower 48 states plus Alaska and Hawaii moved from their original position to a position underneath California, Arizona, New Mexico, and Texas (see e.g. the geoAlbersUsa map in D3). In these visualizations, Alaska is usually scaled to a much smaller area than it actually ocupies. The animation demonstrated here shows the movement of Hawaii and Alaska from their original positions to south of the southern U.S. border, and it also keeps Alaska at its original size. This demonstrates how big Alaska actually is relative to the size at which it is frequently shown.
We need shape files, which are provided by the US Census Bureau:
https://www.census.gov/geo/maps-data/data/cbf/cbf_counties.html
There are three different versions, at different resolutions: 500k, 5m, 20m. The 500k files have the most details and rendering can be slow. The 20m files are fine. Thus, download cb_2017_us_county_20m.zip
and then unzip into a folder called US_shapes
. The code below assumes this has been done.
library(sf)
library(dplyr)
library(ggplot2)
library(gganimate)
## The data
# download shapefiles from:
# https://www.census.gov/geo/maps-data/data/cbf/cbf_counties.html
# then unzip in folder "US_shapes"
us_counties_sp <- rgdal::readOGR(dsn = "US_shapes", layer = "cb_2017_us_county_20m")
# we need appropriate projections for the lower 48 states, for Alaska, and for Hawaii
# ESRI:102003
crs_lower48 <- "+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"
# EPSG:3338
crs_alaska <- "+proj=aea +lat_1=55 +lat_2=65 +lat_0=50 +lon_0=-154 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs "
# ESRI:102007
crs_hawaii <- "+proj=aea +lat_1=8 +lat_2=18 +lat_0=13 +lon_0=-157 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs"
# aggregate individual counties into states
us_states_sp <- rgeos::gUnaryUnion(us_counties_sp, us_counties_sp$STATEFP)
# collect fips codes; they are the names of the objects after aggregation
us_states_sp$fips_state <- names(us_states_sp)
# convert to sf and project
us_states <- as(us_states_sp, "sf") %>%
st_transform(crs_lower48) %>%
filter(fips_state != "72") # remove Puerto Rico
# now remove Alaska and Hawaii for lower 48, and calculate bounding box
us_lower48 <- filter(us_states, !fips_state %in% c("02", "15"))
bb <- st_bbox(us_lower48)
# to move Alaska and Hawaii, we need a helper function that can place an SF geometry object
# at a desired position and scale; the scale option is not used in this code example
place_geometry <- function(geometry, position, scale = 1) {
(geometry - st_centroid(geometry)) * scale +
st_sfc(st_point(position))
}
# move Alaska
us_alaska <- filter(us_states, fips_state == "02")
us_alaska2 <- st_transform(us_alaska, crs_alaska)
st_geometry(us_alaska2) <- place_geometry(
st_geometry(us_alaska2),
c(bb$xmin - 0*(bb$xmax - bb$xmin),
bb$ymin - 0*(bb$ymax - bb$ymin))
)
# we're cheating here by assigning a projection that isn't correct; we
# need to do this because the final compound map needs to have a single
# projection
st_crs(us_alaska2) <- crs_lower48
# move Hawaii
us_hawaii <- filter(us_states, fips_state == "15")
us_hawaii2 <- st_transform(us_hawaii, crs_hawaii)
st_geometry(us_hawaii2) <- place_geometry(
st_geometry(us_hawaii2),
c(bb$xmin + 0.3*(bb$xmax - bb$xmin),
bb$ymin + 0.*(bb$ymax - bb$ymin))
)
# cheating, as before
st_crs(us_hawaii2) <- crs_lower48
# now make four data frames:
# 1. original map
# 2. map with Hawaii moved
# 3. map with Hawaii and Alaska moved
# 4. like 3, to generate an additional state in gganimate
# we use prefixes "a_", "b_", etc in the `type` variable as a
# simple cheat to get the animation order right in gganimate
x1 <- us_states
x1$type = "a_original"
x2 <- rbind(us_lower48, us_alaska, us_hawaii2)
x2$type = "b_hawaii"
x3 <- rbind(us_lower48, us_alaska2, us_hawaii2)
x3$type = "c_final"
x4 <- x3
x4$type = "d_final"
x <- rbind(x1, x2, x3, x4)
# bounding boxes for map 1 and 3, needed for zoom
bb1 <- st_bbox(x1)
bb2 <- st_bbox(x3)
# now animate
ggplot(x, aes(group = fips_state)) +
geom_sf(fill = "#56B4E9", color = "grey30", size = 0.3, alpha = 0.5) +
transition_states(type, 2, 1) +
view_zoom_manual(
2, 1, pause_first = FALSE,
xmin = c(bb1$xmin, bb1$xmin, bb1$xmin, bb2$xmin),
ymin = c(bb2$ymin, bb2$ymin, bb2$ymin, bb2$ymin),
xmax = c(bb1$xmax, bb1$xmax, bb1$xmax, bb2$xmax),
ymax = c(bb1$ymax, bb1$ymax, bb1$ymax, bb2$ymax)
) +
ease_aes('cubic-in-out')
This visualization requires some trickery with map projects. First, we project all 50 states using ESRI:102003, which is an appropriate area-preserving projection for the lower 48 states. Then, we separately project Alaska using EPSG:3338 and Hawaii using ESRI:102007. Then, we move the projected Alaska and Hawaii shapes to their new location and pretend they were projected using the original ESRI:102003 projection. This is cheating, but it is required because the final geometry collection needs to have one single coordinate system. Note that we are using area-preserving projections at all stages, so the areas shown for each state are correct, even if the shapes are somewhat distorted.
For the animation, we use the trick of adding a fourth state that is identical to the third state. This enables us to zoom via view_zoom_manual()
into the final map after Hawaii and Alaska have been moved.
Install gganimate using devtools::install_github('thomasp85/gganimate')
The Grammar
Misc
Examples