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.
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).
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 ;)
Karney, Charles FF, 2013, Algorithms for geodesics, Journal of Geodesy 87(1), 43–55.↩︎