Inspirado num antigo post de BlakeRMills, criador do pacote {MetBrewer}, criei um mapa com a altitude das ruas em Brasília.
Em breve farei um post com tutorial detalhado e também pretendo replicar este tipo de mapa para outras cidades interessantes. O código para replicar o gráfico está abaixo.
Code
library(dplyr)library(sf)library(ggplot2)library(osmdata)library(purrr)sf::sf_use_s2(FALSE)sysfonts::font_add_google("Roboto Condensed", "Roboto Condensed")showtext::showtext_auto()url ="https://pt.wikipedia.org/wiki/Lista_de_municípios_do_Brasil_por_população"tab = xml2::read_html(url) |> rvest::html_table() |> purrr::pluck(2)as_numeric_char =Vectorize(function(x) { ls = stringr::str_extract_all(x, "[[:digit:]]") y =paste(ls[[1]], collapse ="")as.numeric(y)})clean_tab = tab |> janitor::clean_names() |>rename(code_muni = codigo_ibge,name_muni = municipio,rank = posicao,name_state = unidade_federativa,pop = populacao ) |>filter(name_muni !="Brasil") |>mutate(code_muni =as.numeric(code_muni),pop =as_numeric_char(pop),rank =rank(-pop) )top20 =slice_max(clean_tab, pop, n =20)get_border =function(city) {#> Encontra o código do município code_muni = top20 |>filter(name_muni == city) |>pull(code_muni) |>unique()stopifnot(length(code_muni) ==1)#> Baixa o shapefile do município border = geobr::read_municipality(code_muni, showProgress =FALSE)return(border)}get_streets =function(city, border) {#> Encontra o nome da Unidade Federativa nome_uf = top20 |>filter(name_muni == city) |>pull(name_state)#> Monta o nome do local name_place = stringr::str_glue("{city}, {nome_uf}, Brazil")#> Monta a query place =opq(bbox =getbb(name_place))#> Importa todas as principais vias da cidade# streets = add_osm_feature(# place,# key = "highway",# value = c(# "motorway", "primary", "motorway_link", "primary_link",# "secondary", "tertiary", "secondary_link", "tertiary_link",# "residential"# )# ) streets =add_osm_feature(place, key ="highway")#> Converte o dado streets = streets %>%osmdata_sf() %>% .$osm_lines %>%select(osm_id, name) %>%st_transform(crs =4674)#> Enconrtra a intersecção entre as estradas e o limites do município streets_border =st_intersection(streets, border)# out = list(streets = streets, streets_border = streets_border)return(streets_border)}get_elevation =function(border, z =8) { altitude = elevatr::get_elev_raster(border, z = z, clip ="bbox") altitude = raster::rasterToPolygons(altitude) altitude =st_as_sf(altitude)names(altitude)[1] ="elevation" altitude =st_transform(altitude, crs =4674) altitude =suppressWarnings(st_intersection(altitude, border)) altitude =filter(altitude, st_is_valid(altitude))return(altitude)}add_jenks_breaks =function(shp, k =7, round =TRUE, r =0) {#> Classifica os dados de altitude em k grupos segundo o algo. de Jenks jbreaks = BAMMtools::getJenksBreaks(shp$elevation, k = k)#> Arredonda os números para chegar numa legenda menos quebradaif (round) { jbreaks =round(jbreaks, r) }#> Cria a coluna 'jenks_group' que classifica cada valor num grupo shp =mutate(shp, jenks_group =cut(elevation, jbreaks))#> Verifica se todas as observações tem um grupo check =any(is.na(shp$jenks_group))if (check) {warning("Some observations have failed to be grouped") }#> Transforma os groups em legendas labels =get_jenks_labels(jbreaks)#> Retorna o output numa lista out =list(shp = shp, labels = labels)return(out)}get_jenks_labels =function(x) { labels =paste(x, x[-1], sep ="–") labels[1] =paste("<", x[2]) labels[length(labels)] =paste(">", max(x))return(labels)}get_streets_altitude =function(altitude, streets) {stopifnot(any(colnames(altitude) %in%"jenks_group"))#> Get all groups groups =levels(altitude$jenks_group)#> For each group get the full polygon and join with streets join_streets =function(group) { poly = altitude %>%filter(jenks_group == group) %>%st_union(.) %>%st_as_sf() %>%st_make_valid() joined =suppressWarnings(st_intersection(streets, poly))return(joined) }#> Apply the function to all groups street_levels = purrr::map(groups, join_streets)#> Bind all results together out =bind_rows(street_levels, .id ="level")return(out)}map_plot =function(shp, labels, title, showtext =TRUE) { colors = viridis::plasma(n =length(labels) +1) colors = colors[-length(colors)] font =ifelse(showtext ==TRUE, "Roboto Condensed", "sans") plot =ggplot(data = shp) +geom_sf(aes(color = level, fill = level), linewidth =0.2) +scale_color_manual(name ="Altitude",labels = labels,values = colors) +scale_fill_manual(name ="Altitude",labels = labels,values = colors) +guides(fill =guide_legend(nrow =1), color =guide_legend(nrow =1)) +ggtitle(title) + ggthemes::theme_map() +coord_sf() +theme(plot.title =element_text(size =30,hjust =0.5,family = font ),legend.title =element_text(size =20,family = font,color ="gray10" ),legend.text =element_text(size =14,family = font,color ="gray10" ),legend.position ="top",legend.direction ="horizontal",plot.background =element_rect(color =NA, fill ="#f6eee3"),panel.background =element_rect(color =NA, fill ="#f6eee3"),legend.background =element_rect(color =NA, fill ="#f6eee3") )return(plot)}map_altitude =function(city, k, z) {#> Importa o shape do limite do municípiomessage("Importando os limites do município: ", city) city_border =get_border(city)#> Importa as principais vias da cidade e junta com o limite do munimessage("Importando as vias.") city_street =get_streets(city, city_border)#> Importa a altitude da cidademessage("Importando a altitude.") city_elevation =suppressMessages(get_elevation(city_border, z = z))#> Classifica a altitude em gruposmessage("Classificando e juntando os shapefiles.") jenks =add_jenks_breaks(city_elevation, k = k) city_elevation = jenks[["shp"]] labels = jenks[["labels"]]#> Junta a altitude (agrupada) com as vias city_street_elevation =get_streets_altitude(city_elevation, city_street)#> Monta o mapa finalmessage("Gerando o mapa final.") plot =map_plot(city_street_elevation, labels = labels, title = city)message("Feito.")#> Retorna o output numa lista out =list(shp = city_street_elevation,streets = city_street,elevation = city_elevation,plot = plot )return(out)}export_citymap =function(city, w =14, h =16, ...) { plot =map_altitude(city, ...)$plotif (is.numeric(city)) { name_city = cities_brasil |>filter(code_muni == city) |>pull(name_muni) } elseif (is.character(city)) { name_city = city } name_file = glue::glue("elevation_{janitor::make_clean_names(name_city)}.svg" )ggsave( here::here("graphics/altitude", name_file), plot,width = w,height = h ) name_file = glue::glue("elevation_{janitor::make_clean_names(name_city)}.png" )ggsave( here::here("graphics/altitude", name_file), plot,width = w,height = h )}safe_export = purrr::safely(export_citymap)params =tribble(~city, ~z, ~k,#----------------#---#---#"São Paulo", 8, 7,"Rio de Janeiro", 9, 7,"Brasília", 8, 7,"Fortaleza", 11, 7,"Salvador", 10, 7,"Belo Horizonte", 10, 7,"Manaus", 6, 7,"Curitiba", 10, 7,"Recife", 10, 7,"Goiânia", 8, 7,"Porto Alegre", 8, 7,"Belém", 9, 7,"Guarulhos", 9, 7,"Campinas", 9, 7,"São Luís", 9, 7,"Maceió", 11, 8,"Campo Grande", 7, 7,"São Gonçalo", 8, 7,"Teresina", 10, 7,"João Pessoa", 9, 7,"Joinville", 9, 7 )#> Exportar todas as cidades listadas acima#pmap(params, safe_export)#> Exportar uma cidade em particular#safe_export("Brasília", z = 8, k = 7)