Skip to content

Instantly share code, notes, and snippets.

@jmclawson
Last active September 19, 2023 13:20
Show Gist options
  • Save jmclawson/6bff76c7d671756ff56bd701fb7a25e5 to your computer and use it in GitHub Desktop.
Save jmclawson/6bff76c7d671756ff56bd701fb7a25e5 to your computer and use it in GitHub Desktop.
Shift or rotate states when mapping (good for simpler US maps)
# This process is adapted from https://sesync-ci.github.io/blog/transform-Alaska-Hawaii.html
# Here, it's offered as a function to simplify trial-and-error.
move_state <- function(
df, # spatial dataframe
choice, # value in "state" column
rotation, # eg -39 * pi/180
right, # amount to move eastward; use negative for left/west
up# amount to move northward; use negative for down/south
){
rot <- function(a){
matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2)
}
df_state <- df |>
filter(state == choice)
centroid <- df_state |>
st_geometry() |>
st_union() |>
st_centroid()
transformation <- (st_geometry(df_state) - centroid) * rot(rotation) + centroid + c(right, up)
df_state <- df_state |>
st_set_geometry(transformation) |>
st_set_crs(st_crs(df))
df |>
filter(state != choice) |>
rbind(df_state)
}
@jmclawson
Copy link
Author

jmclawson commented Sep 19, 2023

Mapping just the United States can result in an awkward map:

my_map <- 
  tigris::states(cb = TRUE) |> 
  filter(NAME %in% state.name) |> 
  st_transform("ESRI:102008")

my_map |> 
  ggplot() + 
  geom_sf(fill = "salmon")

Screenshot 2023-09-19 at 8 03 33 AM

Quentin Read shows a good process to move the spatial coordinates of states, including moving, rotating, and shrinking them here: https://sesync-ci.github.io/blog/transform-Alaska-Hawaii.html

I wasn't satisfied with Alaska's shrinking because it's more inaccurate than is needed. (See Claus Wilke's Fundamentals of Data Visualization chapter 15.) Keeping Alaska its original size necessitated playing around with the numbers to get it someplace not bumping into another state. This function simplifies that trial and error. Note that it expects the data frame to have a filtering column called state.

my_map2 <- my_map |> 
  rename(state = NAME) |> 
  move_state("Alaska", 
             rotation = -40 * pi/180,
             right = 1350000,
             up = -5500000) |> 
  move_state("Hawaii", 
             rotation = -35 * pi/180,
             right = 6800000,
             up = -2000000) 

my_map2 |>
  ggplot() +
  geom_sf(fill = "salmon") + 
  theme_void()

Screenshot 2023-09-19 at 8 08 56 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment