Capital Circles

Author
Published

November 30, 2024

I really enjoyed seeing other’s #30DayMapChallenge posts, and particularly those of Ansgar Wolsing. I spent a while thinking about this glorious post

And had the idea of creating a map of circles centered around cities with the radius being the shortest distance to another city above a certain population threshold. So let’s throw something together.

First I’ll load my packages, and Ansgar introduced me to {giscoR} for obtaining shapefiles. Previously, I’ve used {rnaturalearthdata} but think I might change the default going forwards.

library("tidyverse")
library("maps")
library("sf")
library("giscoR")
library("countrycode")

countries_sf <- gisco_get_countries() %>% 
  as_tibble() %>% 
  st_as_sf()

# Manually remove duplicate San Jose and Nicosia
cities_clean <- world.cities %>% 
  as_tibble() %>% 
  # filter(capital == 1) %>% 
  filter(!(name == "San Jose" & lat == 10.97),
         !(name == "Nicosia" & long == 33.37)) %>% 
  mutate(iso_3c = countrycode(country.etc, "country.name", "iso3c")) %>% 
  drop_na() %>% 
  st_as_sf(coords = c("long", "lat"), crs = 4326)

Let’s only look at cities with a population of at least 1 million

cities_of_interest <- cities_clean %>% 
  filter(pop >= 1000000)

Here’s a neat little function that finds the max and min distance to each row in the data

add_max_min_point_to_row <- function(data, row_n){
  target_row <- slice(data, row_n)
  
  all_others <- slice(data, setdiff(1:nrow(data), row_n))
  
  all_distances <- st_distance(st_as_sfc(target_row), st_as_sfc(all_others))
  
  min_iso_3c <- slice(all_others, which.min(as.numeric(all_distances))) %>% 
    pull(iso_3c)
  
  max_iso_3c <- slice(all_others, which.max(as.numeric(all_distances))) %>% 
    pull(iso_3c)
  
  target_row %>% 
    mutate(min_iso_3c = min_iso_3c,
           min_distance = min(all_distances),
           max_iso_3c = max_iso_3c,
           max_distance = max(all_distances))
}
cities_of_interest_distances <- 1:nrow(cities_of_interest) %>% 
  map_dfr(~add_max_min_point_to_row(cities_of_interest, .x))

And map!

gg_city_circles <- ggplot() +
  geom_sf(data = cities_of_interest_distances %>% 
            mutate(geometry = st_buffer(geometry, dist = min_distance, nQuadSegs = 100)) %>% 
            st_wrap_dateline() ,
          fill = "#663399",
          linewidth = 0) + 
  geom_sf(data = countries_sf %>% 
            filter(!NAME_ENGL %in% c("Antarctica")) %>%
            mutate(area = as.numeric(st_area(geometry))) %>% 
            filter(area >= 1E11),
          fill = "transparent",
          colour = colorspace::lighten("lightblue", amount = 0.2)) + 
  labs(title = "Closest cities with populations of least 1 million") +
  coord_sf(crs = "+proj=robin") +
  theme_void() +
  theme(panel.background = element_rect(fill = "lightblue")) +
  NULL

ggsave(quarto_here("gg_city_circles.png"),
       gg_city_circles,
       width = 2 * 4,
       height = 2 * 2.37)

Reuse

Citation

BibTeX citation:
@online{hadley2024,
  author = {Hadley, Charlie},
  title = {Capital {Circles}},
  date = {2024-11-30},
  url = {https://visibledata.co.uk/posts/2024-11-29_capital-circles/},
  langid = {en}
}
For attribution, please cite this work as:
Hadley, Charlie. 2024. “Capital Circles.” November 30, 2024. https://visibledata.co.uk/posts/2024-11-29_capital-circles/.