Qual a capital mais distante de Brasília?

Hoje acordei curioso para saber qual era a capital mais distante de Brasília. Essa não é uma questão tão trivial quanto parece, pois, como a Terra é esférica (apesar de todas as controvérsias), podemos chegar em um ponto por mais de um caminho. Por isso, resolvi calcular todas as distâncias e montar um mapinha!

Encontrando as coordenadas geográficas

O primeiro passo para essa aventura foi encontrar as coordenadas geográficas das capitais de todo o mundo. Encontrei nesse post uma forma de fazer isso em R. O post estava desatualizado, então resolvi reescrevê-lo da maneira tidy.

Começamos usando o maravilhoso pacote {httr} para obter o HTML da página que contém os dados. Precisei modificar o User-Agent pois, sem ele, a requisição retornava com código 406.

## Download a partir do site
r_capitals <- httr::GET(
  "https://lab.lmnixon.org/4th/worldcapitals.html",
  httr::user_agent("Mozilla/5.0 (X11; Linux x86_64)")
)

Em seguida, usamos nosso queridinho {xml2} para encontrar a tabela, e o não tão queridinho {rvest} para transformar essa tabela (temos uma discussão sobre isso aqui). Os pacotes {tibble} e {janitor} foram usados para deixar a tabela formatada.

## Parse do resultado do site
da_countries_raw <- r_capitals %>% 
  xml2::read_html() %>% 
  xml2::xml_find_first("//table") %>% 
  rvest::html_table(header = TRUE) %>% 
  tibble::as_tibble() %>% 
  janitor::clean_names()

Arrumando os dados

Como bom faxineiro de dados, eu não poderia deixar se mostrar a parte mais divertida da ciência de dados: organizar os dados brutos! Primeiro, as coordenadas de latitude e longitude estavam em texto e, ao invés de mostrar valores positivos e negativos, mostrava os valores N (norte), S (sul), E (leste), W (oeste). Além disso, a latitude e longitude de Jerusalém (Israel) estava incorreta.

library(tidyverse)

da_countries_tidy <- da_countries_raw %>% 
  filter(country != "") %>% 
  # transforma (N,S) (E,W) em (1,-1), (1,-1)
  mutate(
    lat_num = str_detect(latitude, "N") * 2 - 1,
    lng_num = str_detect(longitude, "E") * 2 - 1
  ) %>% 
  # transforma em numérico
  mutate(
    across(c(latitude, longitude), parse_number),
    lat = latitude * lat_num,
    lng = longitude * lng_num
  ) %>% 
  # arruma Israel
  mutate(
    lat = if_else(country == "Israel", 31.7683, lat),
    lng = if_else(country == "Israel", 35.2137, lng)
  ) %>% 
  select(country, capital, lat, lng)

Transformando os dados

Com os dados arrumados em mãos, calculei as distâncias através da distância geodésica1, usando latitude e longitude como base e o maravilhoso pacote {sf}. São duas funções principais: a sf::st_point() cria um objeto especial do tipo ponto, e a sf::st_distance() calcula a distância entre dois pontos. Utilizamos map2() e map() do pacote {purrr} para fazer aplicar essas operações em todos os países. No final, temos a base ordenada pelas distâncias. As distâncias são calculadas em metros, que transformamos para quilômetros.

da_countries <- da_countries_tidy %>% 
  # cria pontos com base em lat lng e sf::st_point()
  # sf::st_sfc() transforma a lista num objeto POINT do {sf}
  # crs = 4326 serve para o {sf} saber que
  #   são coordenadas no planeta Terra.
  mutate(pt = sf::st_sfc(
    map2(lng, lat, ~sf::st_point(c(.x, .y, 1))),
    crs = 4326
  )) %>%
  mutate(
    across(c(lat, lng), list(br = ~.x[country == "Brazil"])),
    pt_br = sf::st_sfc(
      list(sf::st_point(c(lng_br[1], lat_br[1], 1))), 
      crs = 4326
    )
  ) %>% 
  mutate(
    dist_br = sf::st_distance(pt, pt_br, by_element = TRUE),
    dist_br = as.numeric(dist_br / 1000)
  ) %>% 
  # ordena a base pelas distâncias
  arrange(dist_br)

Visualizando

As capitais mais próximas estão na Tabela 1. Sem muitas surpresas aqui: como Brasília fica na região central do país, a capital mais próxima é a do Paraguai, seguida por outros países da América do Sul.

Tabela 1: Capitais mais próximas de Brasília.
País Capital Distância (km)
Paraguay Asuncion 1473
Bolivia La Paz (administrative) / Sucre (legislative) 2202
Uruguay Montevideo 2276
French Guiana Cayenne 2326
Suriname Paramaribo 2464
Argentina Buenos Aires 2618
Guyana Georgetown 2695
Chile Santiago 3028
Peru Lima 3206
Barbados Bridgetown 3409

As coisas ficam mais interessantes quando visualizamos as capitais mais distantes, na Tabela 2. E temos nosso resultado: Koror (Palau) é a capital mais distante da capital, Brasília, seguida por Manila (Filipinas) e Saipan (Ilhas Mariana do Norte).

Tabela 2: Capitais mais distantes de Brasília.
País Capital Distância (km)
Palau Koror 19069
Philippines Manila 18801
Northern Mariana Islands Saipan 18639
Macao, China Macau 17887
Brunei Darussalam Bandar Seri Begawan 17765
Republic of Korea Seoul 17517
North Korea Pyongyang 17296
East Timor Dili 17264
Viet Nam Hanoi 17126
Micronesia (Federated States of) Palikir 17066

Mas será mesmo? Vamos usar o pacote {leaflet} para visualizar:

library(leaflet)

# Cria as labels dos popups
make_label <- function(pais, capital) {
  txt <- stringr::str_glue(
    "<b>País</b>: {pais}<br>",
    "<b>Capital</b>: {capital}"
  )
  htmltools::HTML(txt)
}

p_leaflet <- da_countries %>% 
  mutate(lab = map2(country, capital, make_label)) %>% 
  # cria mapa
  leaflet() %>% 
  # adiciona a casquinha
  addTiles() %>% 
  # adiciona os pontos
  addMarkers(
    clusterOptions = markerClusterOptions(), 
    lat = ~lat, lng = ~lng, popup = ~lab
  )

p_leaflet

Acesse o mapa dinâmico neste link.

Olhando o mapa (e considerando que a terra é esférica), parece mesmo que esses países estão bem longe, mesmo tentando acessar pelo leste ou pelo oeste.

E, já que a terra é esférica, que tal criar um mapa 3D? Fiz isso usando o {plotly}, com base no tutorial disponível neste link.

library(plotly)

p_plotly <- plot_ly(height = 1000) %>%
  # adiciona o mapa mundi
  add_sf(
    data = world, 
    x = ~coord_x(x, y),
    y = ~coord_y(x, y),
    z = ~coord_z(y),
    color = I("gray80"), 
    size = I(2),
    hoverinfo = "none"
  ) %>% 
  # adiciona as linhas
  add_sf(
    data = da_lines_sf,
    name = "linhas",
    x = ~coord_x(x, y),
    y = ~coord_y(x, y),
    z = ~coord_z(y),
    color = ~dist_br,
    size = I(3),
    text = ~label_plotly(country, capital, dist_br),
    hoverinfo = "text"
  ) %>% 
  layout(showlegend = FALSE)

p_plotly

Acesse o mapa dinâmico neste link.

Wrap-up

Nesse post vimos como usar ferramentas do {tidyverse}, {sf} para transformação de dados para mapas, além de utilizar o {leaflet} e o {plotly} para visualizações interativas.

O código completo para construir as visualizações do zero está neste link.

É isso pessoal. Happy coding ;)


  1. Karney, Charles FF, 2013, Algorithms for geodesics, Journal of Geodesy 87(1), 43–55.↩︎

comments powered by Disqus