Licença

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

License: CC BY-SA 4.0
License: CC BY-SA 4.0

As ideias aqui expressas são de responsabilidade exclusiva do autor, e não representam as opiniões da instituição a que pertence.

Citação

Sugestão para citação: FIGUEIREDO, Adriano Marcos Rodrigues. Mapas em R com geobr e tmap - dados de competitividade municipal CLP`. Campo Grande-MS,Brasil: RStudio/Rpubs, 2025. Disponível em https://rpubs.com/amrofi/mapas-com-geobr-tmap/.

1 Introdução

Para realizar seu mapa em R, inicialmente o leitor deve baixar os programas e pacotes necessários ao projeto. Neste caso, sugere-se que utilize o RStudio e o R atualizados, a partir de: http://cran.r-project.org/bin/windows/base/ e https://www.rstudio.com/products/rstudio/download3/. Quando esta revisão foi feita, a versão do RStudio era a RStudio 2025.05.0 Build 496 e do R version 4.5.1 for Windows (32/64 bit).

Primeiro instale o R e posteriormente o RStudio, de modo que o segundo reconheça automaticamente o primeiro. Se tudo estiver perfeito, a tela inicial do RStudio mostrará corretamente a versão do R.

2 Mapas em R

Agora a meta é gerar um mapa simples em R. Para tanto, primeiro define-se a malha municipal desejada. Para o presente caso, utilizam-se as malhas digitais do pacote geobr.

Os pacotes (previamente instalados) são aqui carregados:

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(geobr)
## Carregando namespace exigido: sf
library(ggplot2) # Para manipulação de dados espaciais
library(sf) # Para manipulação de dados espaciais
## Linking to GEOS 3.13.1, GDAL 3.10.2, PROJ 9.5.1; sf_use_s2() is TRUE
library("ggspatial") # Para visualização espacial
library(rgeoda)
## Carregando pacotes exigidos: digest
library(tmap)

O pacote geobr tem as rotinas para o download dos mapas com divisões territoriais variadas. Faremos para os municípios brasileiros, malha com a estrutura municipal de 2024, contendo 5.571 municípios. É importante o leitor ter essa estrutura em mente, pois ao longo dos anos, municípios são criados a partir de desmembramentos de outros, não necessariamente respeitando limites de distritos ou outros atributos previamente definidos.

library(geobr)
options(timeout= 4000000)
mapa_br <- read_municipality(code_muni = "all", year = 2024)
saveRDS(mapa_br,"mapa_br.rds")
mapa_br <- readRDS("F:/disciplinas/economia regional/laboratorio_R/competitividade_CLP/mapa_br.rds")
names(mapa_br) # Verificar nomes das colunas do mapa
## [1] "code_muni"    "name_muni"    "code_state"   "abbrev_state" "name_state"  
## [6] "code_region"  "name_region"  "geom"
class(mapa_br) # [1] "sf"         "data.frame"
## [1] "sf"         "data.frame"
# [1] "code_muni"    "name_muni"    "code_state"   "abbrev_state" "name_state"   "code_region"  "name_region"  "geom" 

É possível verificar que esse objeto é da classe sf e data.frame. Um objeto sf contém uma coleção de recursos incluindo atributos e geometrias que contemplam os aspectos espaciais para desenhar o mapa.

Primeiro vou trabalhar com os dados para plotar com ggplot2. Vou carregar os dados de competitividade do CLP, disponíveis em: https://rankingdecompetitividade.org.br/municipios/.

# Dados de municipios_ranking-geral.csv
municipios<- read_csv("municipios_ranking-geral.csv", skip = 1)
## Rows: 418 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Nome
## dbl (4): Código IBGE, Posição, Variação, Nota Normalizada
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# juntar mapa_br com municipios, preservando formato sf
mapa_ranking <- left_join(mapa_br, municipios, by=c("code_muni"="Código IBGE"))
class(mapa_ranking) # [1] "sf"         "data.frame"
## [1] "sf"         "data.frame"
# Verificar se houve perda de linhas
nrow(mapa_br) # 5571 ok
## [1] 5571
nrow(municipios) # 418
## [1] 418
nrow(mapa_ranking) # 5571 ok
## [1] 5571
# Verificar nomes das colunas
names(mapa_ranking)
##  [1] "code_muni"        "name_muni"        "code_state"       "abbrev_state"    
##  [5] "name_state"       "code_region"      "name_region"      "Nome"            
##  [9] "Posição"          "Variação"         "Nota Normalizada" "geom"
# [1] "code_muni"        "name_muni"        "code_state"       "abbrev_state"     "name_state"       "code_region"     
# [7] "name_region"      "Nome"             "Posição"          "Variação"         "Nota Normalizada" "geom"  


# Mapa colorido com classes de ranking ----
min(mapa_ranking$`Nota Normalizada`, na.rm=TRUE) # [1] 30.4
## [1] 30.4
max(mapa_ranking$`Nota Normalizada`, na.rm=TRUE) # [1] 62.47
## [1] 62.47
# fazer classes de cores para o mapa, de 30 até 65 de 5 em 5, para variável mapa_ranking$`Nota Normalizada`
mapa_ranking <- mapa_ranking %>%
  mutate(classe_ranking = case_when(
    `Nota Normalizada` < 35 ~ "[30-35(",
    `Nota Normalizada` >= 35 & `Nota Normalizada` < 40 ~ "[35-40(",
    `Nota Normalizada` >= 40 & `Nota Normalizada` < 45 ~ "[40-44(",
    `Nota Normalizada` >= 45 & `Nota Normalizada` < 50 ~ "[45-49(",
    `Nota Normalizada` >= 50 & `Nota Normalizada` < 55 ~ "[50-54(",
    `Nota Normalizada` >= 55 & `Nota Normalizada` < 60 ~ "[55-59(",
    `Nota Normalizada` >= 60 ~ "[60-65(",
    TRUE ~ NA_character_
  ))
# Mapa do Brasil com classes de ranking ----
ggplot() +
  geom_sf(data=mapa_ranking, 
          aes(fill=`classe_ranking`), 
          color= "black", size=.15)+
  labs(title="Competitividade dos Municipíos Brasileiros - CLP",
       caption='Fonte: Elaboração própria com dados básicos da CLP', size=8)+
  scale_fill_discrete(name="Classes de Ranking")+
  theme_minimal()+ 
  annotation_north_arrow(location = "bl", # norte magnetico no mapa
                         which_north = "true", 
                         pad_x = unit(0.65, "in"), 
                         pad_y = unit(0.3, "in"), 
                         style = north_arrow_fancy_orienteering) +
  annotation_scale(location = "bl", width_hint = 0.3) # barra de escala no mapa
## Scale on map varies by more than 10%, scale bar may be inaccurate

Agora vou criar um shapefile que pode ser utilizado em outros programas como o Geoda, QGIS, ArcGIS, PhilCarto etc.

# Gerar shapefile do mapa_ranking ----
# antes de criar shapefile vou mudar code_muni para character
mapa_ranking.shp <- mapa_ranking %>%
  mutate(code_muni = as.character(code_muni))
st_write(mapa_ranking.shp, "mapa_ranking.shp", delete_dsn = TRUE)
## Warning in abbreviate(fld_names, minlength = 7): abbreaviate usado com
## caracteres não-ASCII
## Warning in abbreviate_shapefile_names(obj): Field names abbreviated for ESRI
## Shapefile driver
## Deleting source `mapa_ranking.shp' using driver `ESRI Shapefile'
## Writing layer `mapa_ranking' to data source 
##   `mapa_ranking.shp' using driver `ESRI Shapefile'
## Writing 5571 features with 12 fields and geometry type Multi Polygon.
#
# Usar rgeoda para fazer o mapa de quebras naturais (natural breaks) ----
# com a variável NtNrmlz do shapefile mapa_ranking.shp
# o pacote já está instalado e carregado
# carregar shapefile
mapa_ranking.shp2 <- st_read("mapa_ranking.shp")
## Reading layer `mapa_ranking' from data source 
##   `F:\disciplinas\economia regional\laboratorio_R\competitividade_CLP\mapa_ranking.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 5571 features and 12 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -73.98681 ymin: -33.75108 xmax: -28.84778 ymax: 5.26962
## Geodetic CRS:  SIRGAS 2000
# Usar rgeoda para fazer o mapa de quebras naturais (natural breaks) ----
# com a variável NtNrmlz do shapefile mapa_ranking.shp
# o pacote já está instalado e carregado
# mapa_ranking.shp2 carregado
names(mapa_ranking.shp2)
##  [1] "code_mn"  "name_mn"  "cod_stt"  "abbrv_s"  "nam_stt"  "cod_rgn" 
##  [7] "nam_rgn"  "Nome"     "Posição"  "Variaçã"  "NtNrmlz"  "clss_rn" 
## [13] "geometry"
# [1] "code_mn"  "name_mn"  "cod_stt"  "abbrv_s"  "nam_stt"  "cod_rgn"  "nam_rgn" 
# [8] "Nome"     "Posição"  "Variaçã"  "NtNrmlz"  "clss_rn"  "geometry"
# [1] 30.40 36.24 41.08 45.92 50.76 55.60 60.44 62.47

# https://geodacenter.github.io/opioid-environment-toolkit/visualizeArealData-tutorial.html

Agora fazendo o mapa pelo tmap a partir do shapefile.

tmap_mode('plot') # modo plot
## ℹ tmap mode set to "plot".
p1 <- tm_shape(mapa_ranking.shp2) +
  tm_fill(
    fill = "NtNrmlz",
    fill.scale = tm_scale_intervals(
      breaks = c(-Inf, 30.400, 36.720, 43.930, 47.450, 49.760, 53.680, Inf),
      values = c("#FFFFCC","#FADC86","#F9C652","#E48629",
                 "#CC5C0D","#A44507","#732000"),
      value.na = "#FFFFCC"  # cor para os NAs
    ),
    fill.legend = tm_legend(
      title = "Ranking Normalizado: Quebras Naturais"
    )
  ) +
  tm_borders() +
  tm_title("Ranking CLP - Jenks - Quebras Naturais", size = 0.9) +
  tm_layout(
    frame = FALSE,
    legend.outside = TRUE,
    legend.outside.position = "right",
    legend.title.size = 0.9
  ) +
  # 👉 Norte geográfico (estrela)
  tm_compass(type = "8star", position = c("right", "bottom"), size = 2) +
  # 👉 Barra de escala (v4 usa tm_scalebar)
  tm_scalebar(position = c("left", "bottom"))


p1
## Scale bar set for latitude km and will be different at the top and bottom of the map.

tmap_save(p1, 'CLP_Rank_Norm_Jenks.png')# save the map in a .png file
## Scale bar set for latitude km and will be different at the top and bottom of the map.
## Map saved to F:\disciplinas\economia regional\laboratorio_R\competitividade_CLP\CLP_Rank_Norm_Jenks.png
## Resolution: 2223.67 by 1983.208 pixels
## Size: 7.412235 by 6.610692 inches (300 dpi)

Vou chamar o mapa salvo como ‘CLP_Rank_Norm_Jenks.png’.

LS0tDQp0aXRsZTogIk1hcGFzIGVtIFIgY29tIGBnZW9icmAgZSBgdG1hcGAgLSBkYWRvcyBkZSBjb21wZXRpdGl2aWRhZGUgbXVuaWNpcGFsIENMUCINCmF1dGhvcjogJ0Fkcmlhbm8gTWFyY29zIFJvZHJpZ3VlcyBGaWd1ZWlyZWRvLCAqZS1tYWlsOiBhZHJpYW5vLmZpZ3VlaXJlZG9AdWZtcy5icionDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQphYnN0cmFjdDogIkluIHRoaXMgZXhlcmNpc2UgSSB1c2UgUiBhbmQgdG1hcCBhbmQgZ2VvYnIgcGFja2FnZXMgdG8gbWFwIHRoZSBDb21wZXRpdGl2ZW5lc3MgaW5kZXhlcyBmb3IgbXVuaWNpcGFsaXRpZXMgaW4gQnJhemlsLiBJdCdzIHdyaXR0ZW4gaW4gcG9ydHVndWVzZSBhbmQsIHNvLCBJIGVuY291cmFnZSBub24tcG9ydHVndWVzZSBzcGVha2VycyB0byBvcGVuIHRoZSBodG1sIHBvc3QgaW4gR29vZ2xlIENocm9tZSBvciBzaW1pbGFyIHdpdGggdGhlIHRyYW5zbGF0ZSBvcHRpb24uIg0KLS0tDQoNCiMgTGljZW7Dp2EgeyNMaWNlbsOnYSAudW5udW1iZXJlZH0NCg0KVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLVNoYXJlQWxpa2UgNC4wIEludGVybmF0aW9uYWwgTGljZW5zZS4gVG8gdmlldyBhIGNvcHkgb2YgdGhpcyBsaWNlbnNlLCB2aXNpdCA8aHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLz4gb3Igc2VuZCBhIGxldHRlciB0byBDcmVhdGl2ZSBDb21tb25zLCBQTyBCb3ggMTg2NiwgTW91bnRhaW4gVmlldywgQ0EgOTQwNDIsIFVTQS4NCg0KIVtMaWNlbnNlOiBDQyBCWS1TQSA0LjBdKGh0dHBzOi8vbWlycm9ycy5jcmVhdGl2ZWNvbW1vbnMub3JnL3ByZXNza2l0L2J1dHRvbnMvODh4MzEvcG5nL2J5LXNhLnBuZyl7d2lkdGg9IjI1JSJ9DQoNCkFzIGlkZWlhcyBhcXVpIGV4cHJlc3NhcyBzw6NvIGRlIHJlc3BvbnNhYmlsaWRhZGUgZXhjbHVzaXZhIGRvIGF1dG9yLCBlIG7Do28gcmVwcmVzZW50YW0gYXMgb3BpbmnDtWVzIGRhIGluc3RpdHVpw6fDo28gYSBxdWUgcGVydGVuY2UuDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCkNpdGHDp8OjbyB7LSNDaXRhw6fDo299DQo9PT09PT09PT09PT09PT09PT09DQoNClN1Z2VzdMOjbyBwYXJhIGNpdGHDp8OjbzogDQpGSUdVRUlSRURPLCBBZHJpYW5vIE1hcmNvcyBSb2RyaWd1ZXMuIE1hcGFzIGVtIFIgY29tIGBnZW9icmAgZSBgdG1hcGAgLSBkYWRvcyBkZSBjb21wZXRpdGl2aWRhZGUgbXVuaWNpcGFsIENMUGAuIENhbXBvIEdyYW5kZS1NUyxCcmFzaWw6IFJTdHVkaW8vUnB1YnMsIDIwMjUuIERpc3BvbsOtdmVsIGVtIDxodHRwczovL3JwdWJzLmNvbS9hbXJvZmkvbWFwYXMtY29tLWdlb2JyLXRtYXAvPi4gDQoNCg0KSW50cm9kdcOnw6NvIA0KPT09PT09PT09PT09PT09PT09DQoNClBhcmEgcmVhbGl6YXIgc2V1IG1hcGEgZW0gUiwgaW5pY2lhbG1lbnRlIG8gbGVpdG9yIGRldmUgYmFpeGFyIG9zIHByb2dyYW1hcyBlIHBhY290ZXMgbmVjZXNzw6FyaW9zIGFvIHByb2pldG8uIE5lc3RlIGNhc28sIHN1Z2VyZS1zZSBxdWUgdXRpbGl6ZSBvIFJTdHVkaW8gZSBvIFIgYXR1YWxpemFkb3MsIGEgcGFydGlyIGRlOiBodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL2Jpbi93aW5kb3dzL2Jhc2UvIGUgaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZDMvLiBRdWFuZG8gZXN0YSByZXZpc8OjbyBmb2kgZmVpdGEsIGEgdmVyc8OjbyBkbyBSU3R1ZGlvIGVyYSBhICpSU3R1ZGlvIDIwMjUuMDUuMCBCdWlsZCA0OTYqIGUgZG8gKlIgdmVyc2lvbiA0LjUuMSBmb3IgV2luZG93cyAoMzIvNjQgYml0KSouIA0KDQpQcmltZWlybyBpbnN0YWxlIG8gUiBlIHBvc3Rlcmlvcm1lbnRlIG8gUlN0dWRpbywgZGUgbW9kbyBxdWUgbyBzZWd1bmRvIHJlY29uaGXDp2EgYXV0b21hdGljYW1lbnRlIG8gcHJpbWVpcm8uIFNlIHR1ZG8gZXN0aXZlciBwZXJmZWl0bywgYSB0ZWxhIGluaWNpYWwgZG8gUlN0dWRpbyBtb3N0cmFyw6EgY29ycmV0YW1lbnRlIGEgdmVyc8OjbyBkbyBSLg0KDQoNCk1hcGFzIGVtIFINCj09PT09PT09PT09PT09PT09PQ0KDQpBZ29yYSBhIG1ldGEgw6kgZ2VyYXIgdW0gbWFwYSBzaW1wbGVzIGVtIFIuIFBhcmEgdGFudG8sIHByaW1laXJvIGRlZmluZS1zZSBhIG1hbGhhIG11bmljaXBhbCBkZXNlamFkYS4gUGFyYSBvIHByZXNlbnRlIGNhc28sIHV0aWxpemFtLXNlIGFzIG1hbGhhcyBkaWdpdGFpcyBkbyBwYWNvdGUgYGdlb2JyYC4gICAgIA0KDQpPcyBwYWNvdGVzIChwcmV2aWFtZW50ZSBpbnN0YWxhZG9zKSBzw6NvIGFxdWkgY2FycmVnYWRvczoNCg0KYGBge3IgcGFjb3RlcywgZWNobyA9IFQsIHJlc3VsdHMgPSAnaGlkZScsIHdhcm5pbmdzPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdlb2JyKQ0KbGlicmFyeShnZ3Bsb3QyKSAjIFBhcmEgbWFuaXB1bGHDp8OjbyBkZSBkYWRvcyBlc3BhY2lhaXMNCmxpYnJhcnkoc2YpICMgUGFyYSBtYW5pcHVsYcOnw6NvIGRlIGRhZG9zIGVzcGFjaWFpcw0KbGlicmFyeSgiZ2dzcGF0aWFsIikgIyBQYXJhIHZpc3VhbGl6YcOnw6NvIGVzcGFjaWFsDQpsaWJyYXJ5KHJnZW9kYSkNCmxpYnJhcnkodG1hcCkNCmBgYA0KDQpPIHBhY290ZSBgZ2VvYnJgIHRlbSBhcyByb3RpbmFzIHBhcmEgbyBkb3dubG9hZCBkb3MgbWFwYXMgY29tIGRpdmlzw7VlcyB0ZXJyaXRvcmlhaXMgdmFyaWFkYXMuIEZhcmVtb3MgcGFyYSBvcyBtdW5pY8OtcGlvcyBicmFzaWxlaXJvcywgbWFsaGEgY29tIGEgZXN0cnV0dXJhIG11bmljaXBhbCBkZSAyMDI0LCBjb250ZW5kbyA1LjU3MSBtdW5pY8OtcGlvcy4gw4kgaW1wb3J0YW50ZSBvIGxlaXRvciB0ZXIgZXNzYSBlc3RydXR1cmEgZW0gbWVudGUsIHBvaXMgYW8gbG9uZ28gZG9zIGFub3MsIG11bmljw61waW9zIHPDo28gY3JpYWRvcyBhIHBhcnRpciBkZSBkZXNtZW1icmFtZW50b3MgZGUgb3V0cm9zLCBuw6NvIG5lY2Vzc2FyaWFtZW50ZSByZXNwZWl0YW5kbyBsaW1pdGVzIGRlIGRpc3RyaXRvcyBvdSBvdXRyb3MgYXRyaWJ1dG9zIHByZXZpYW1lbnRlIGRlZmluaWRvcy4gICAgIA0KICAgIA0KDQpgYGB7ciBnZW9iciwgZXZhbD1GLCBlY2hvID0gVCwgcmVzdWx0cyA9ICdoaWRlJ30NCmxpYnJhcnkoZ2VvYnIpDQpvcHRpb25zKHRpbWVvdXQ9IDQwMDAwMDApDQptYXBhX2JyIDwtIHJlYWRfbXVuaWNpcGFsaXR5KGNvZGVfbXVuaSA9ICJhbGwiLCB5ZWFyID0gMjAyNCkNCnNhdmVSRFMobWFwYV9iciwibWFwYV9ici5yZHMiKQ0KYGBgDQoNCmBgYHtyIG1hcGFfYnJ9DQptYXBhX2JyIDwtIHJlYWRSRFMoIkY6L2Rpc2NpcGxpbmFzL2Vjb25vbWlhIHJlZ2lvbmFsL2xhYm9yYXRvcmlvX1IvY29tcGV0aXRpdmlkYWRlX0NMUC9tYXBhX2JyLnJkcyIpDQpuYW1lcyhtYXBhX2JyKSAjIFZlcmlmaWNhciBub21lcyBkYXMgY29sdW5hcyBkbyBtYXBhDQpjbGFzcyhtYXBhX2JyKSAjIFsxXSAic2YiICAgICAgICAgImRhdGEuZnJhbWUiDQojIFsxXSAiY29kZV9tdW5pIiAgICAibmFtZV9tdW5pIiAgICAiY29kZV9zdGF0ZSIgICAiYWJicmV2X3N0YXRlIiAibmFtZV9zdGF0ZSIgICAiY29kZV9yZWdpb24iICAibmFtZV9yZWdpb24iICAiZ2VvbSIgDQpgYGANCg0KDQrDiSBwb3Nzw612ZWwgdmVyaWZpY2FyIHF1ZSBlc3NlIG9iamV0byDDqSBkYSBjbGFzc2UgYHNmYCBlIGBkYXRhLmZyYW1lYC4gVW0gb2JqZXRvIGBzZmAgY29udMOpbSB1bWEgY29sZcOnw6NvIGRlIHJlY3Vyc29zIGluY2x1aW5kbyBhdHJpYnV0b3MgZSBnZW9tZXRyaWFzIHF1ZSBjb250ZW1wbGFtIG9zIGFzcGVjdG9zIGVzcGFjaWFpcyBwYXJhIGRlc2VuaGFyIG8gbWFwYS4NCiAgICAgIA0KUHJpbWVpcm8gdm91IHRyYWJhbGhhciBjb20gb3MgZGFkb3MgcGFyYSBwbG90YXIgY29tIGdncGxvdDIuDQpWb3UgY2FycmVnYXIgb3MgZGFkb3MgZGUgY29tcGV0aXRpdmlkYWRlIGRvIENMUCwgZGlzcG9uw612ZWlzIGVtOiBodHRwczovL3JhbmtpbmdkZWNvbXBldGl0aXZpZGFkZS5vcmcuYnIvbXVuaWNpcGlvcy8uDQoNCmBgYHtyIG11bmljaXBpb3N9DQojIERhZG9zIGRlIG11bmljaXBpb3NfcmFua2luZy1nZXJhbC5jc3YNCm11bmljaXBpb3M8LSByZWFkX2NzdigibXVuaWNpcGlvc19yYW5raW5nLWdlcmFsLmNzdiIsIHNraXAgPSAxKQ0KYGBgDQoNCg0KYGBge3IganVudGFyfQ0KIyBqdW50YXIgbWFwYV9iciBjb20gbXVuaWNpcGlvcywgcHJlc2VydmFuZG8gZm9ybWF0byBzZg0KbWFwYV9yYW5raW5nIDwtIGxlZnRfam9pbihtYXBhX2JyLCBtdW5pY2lwaW9zLCBieT1jKCJjb2RlX211bmkiPSJDw7NkaWdvIElCR0UiKSkNCmNsYXNzKG1hcGFfcmFua2luZykgIyBbMV0gInNmIiAgICAgICAgICJkYXRhLmZyYW1lIg0KIyBWZXJpZmljYXIgc2UgaG91dmUgcGVyZGEgZGUgbGluaGFzDQpucm93KG1hcGFfYnIpICMgNTU3MSBvaw0KbnJvdyhtdW5pY2lwaW9zKSAjIDQxOA0KbnJvdyhtYXBhX3JhbmtpbmcpICMgNTU3MSBvaw0KIyBWZXJpZmljYXIgbm9tZXMgZGFzIGNvbHVuYXMNCm5hbWVzKG1hcGFfcmFua2luZykNCiMgWzFdICJjb2RlX211bmkiICAgICAgICAibmFtZV9tdW5pIiAgICAgICAgImNvZGVfc3RhdGUiICAgICAgICJhYmJyZXZfc3RhdGUiICAgICAibmFtZV9zdGF0ZSIgICAgICAgImNvZGVfcmVnaW9uIiAgICAgDQojIFs3XSAibmFtZV9yZWdpb24iICAgICAgIk5vbWUiICAgICAgICAgICAgICJQb3Npw6fDo28iICAgICAgICAgICJWYXJpYcOnw6NvIiAgICAgICAgICJOb3RhIE5vcm1hbGl6YWRhIiAiZ2VvbSIgIA0KDQoNCiMgTWFwYSBjb2xvcmlkbyBjb20gY2xhc3NlcyBkZSByYW5raW5nIC0tLS0NCm1pbihtYXBhX3JhbmtpbmckYE5vdGEgTm9ybWFsaXphZGFgLCBuYS5ybT1UUlVFKSAjIFsxXSAzMC40DQptYXgobWFwYV9yYW5raW5nJGBOb3RhIE5vcm1hbGl6YWRhYCwgbmEucm09VFJVRSkgIyBbMV0gNjIuNDcNCiMgZmF6ZXIgY2xhc3NlcyBkZSBjb3JlcyBwYXJhIG8gbWFwYSwgZGUgMzAgYXTDqSA2NSBkZSA1IGVtIDUsIHBhcmEgdmFyacOhdmVsIG1hcGFfcmFua2luZyRgTm90YSBOb3JtYWxpemFkYWANCm1hcGFfcmFua2luZyA8LSBtYXBhX3JhbmtpbmcgJT4lDQogIG11dGF0ZShjbGFzc2VfcmFua2luZyA9IGNhc2Vfd2hlbigNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPCAzNSB+ICJbMzAtMzUoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gMzUgJiBgTm90YSBOb3JtYWxpemFkYWAgPCA0MCB+ICJbMzUtNDAoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gNDAgJiBgTm90YSBOb3JtYWxpemFkYWAgPCA0NSB+ICJbNDAtNDQoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gNDUgJiBgTm90YSBOb3JtYWxpemFkYWAgPCA1MCB+ICJbNDUtNDkoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gNTAgJiBgTm90YSBOb3JtYWxpemFkYWAgPCA1NSB+ICJbNTAtNTQoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gNTUgJiBgTm90YSBOb3JtYWxpemFkYWAgPCA2MCB+ICJbNTUtNTkoIiwNCiAgICBgTm90YSBOb3JtYWxpemFkYWAgPj0gNjAgfiAiWzYwLTY1KCIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKSkNCiMgTWFwYSBkbyBCcmFzaWwgY29tIGNsYXNzZXMgZGUgcmFua2luZyAtLS0tDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YT1tYXBhX3JhbmtpbmcsIA0KICAgICAgICAgIGFlcyhmaWxsPWBjbGFzc2VfcmFua2luZ2ApLCANCiAgICAgICAgICBjb2xvcj0gImJsYWNrIiwgc2l6ZT0uMTUpKw0KICBsYWJzKHRpdGxlPSJDb21wZXRpdGl2aWRhZGUgZG9zIE11bmljaXDDrW9zIEJyYXNpbGVpcm9zIC0gQ0xQIiwNCiAgICAgICBjYXB0aW9uPSdGb250ZTogRWxhYm9yYcOnw6NvIHByw7NwcmlhIGNvbSBkYWRvcyBiw6FzaWNvcyBkYSBDTFAnLCBzaXplPTgpKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9IkNsYXNzZXMgZGUgUmFua2luZyIpKw0KICB0aGVtZV9taW5pbWFsKCkrIA0KICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KGxvY2F0aW9uID0gImJsIiwgIyBub3J0ZSBtYWduZXRpY28gbm8gbWFwYQ0KICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoX25vcnRoID0gInRydWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC42NSwgImluIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBhZF95ID0gdW5pdCgwLjMsICJpbiIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9IG5vcnRoX2Fycm93X2ZhbmN5X29yaWVudGVlcmluZykgKw0KICBhbm5vdGF0aW9uX3NjYWxlKGxvY2F0aW9uID0gImJsIiwgd2lkdGhfaGludCA9IDAuMykgIyBiYXJyYSBkZSBlc2NhbGEgbm8gbWFwYQ0KYGBgDQoNCkFnb3JhIHZvdSBjcmlhciB1bSBzaGFwZWZpbGUgcXVlIHBvZGUgc2VyIHV0aWxpemFkbyBlbSBvdXRyb3MgcHJvZ3JhbWFzIGNvbW8gbyBHZW9kYSwgUUdJUywgQXJjR0lTLCBQaGlsQ2FydG8gZXRjLg0KDQpgYGB7ciBzaGFwZWZpbGV9DQojIEdlcmFyIHNoYXBlZmlsZSBkbyBtYXBhX3JhbmtpbmcgLS0tLQ0KIyBhbnRlcyBkZSBjcmlhciBzaGFwZWZpbGUgdm91IG11ZGFyIGNvZGVfbXVuaSBwYXJhIGNoYXJhY3Rlcg0KbWFwYV9yYW5raW5nLnNocCA8LSBtYXBhX3JhbmtpbmcgJT4lDQogIG11dGF0ZShjb2RlX211bmkgPSBhcy5jaGFyYWN0ZXIoY29kZV9tdW5pKSkNCnN0X3dyaXRlKG1hcGFfcmFua2luZy5zaHAsICJtYXBhX3Jhbmtpbmcuc2hwIiwgZGVsZXRlX2RzbiA9IFRSVUUpDQojDQojIFVzYXIgcmdlb2RhIHBhcmEgZmF6ZXIgbyBtYXBhIGRlIHF1ZWJyYXMgbmF0dXJhaXMgKG5hdHVyYWwgYnJlYWtzKSAtLS0tDQojIGNvbSBhIHZhcmnDoXZlbCBOdE5ybWx6IGRvIHNoYXBlZmlsZSBtYXBhX3Jhbmtpbmcuc2hwDQojIG8gcGFjb3RlIGrDoSBlc3TDoSBpbnN0YWxhZG8gZSBjYXJyZWdhZG8NCiMgY2FycmVnYXIgc2hhcGVmaWxlDQptYXBhX3Jhbmtpbmcuc2hwMiA8LSBzdF9yZWFkKCJtYXBhX3Jhbmtpbmcuc2hwIikNCiMgVXNhciByZ2VvZGEgcGFyYSBmYXplciBvIG1hcGEgZGUgcXVlYnJhcyBuYXR1cmFpcyAobmF0dXJhbCBicmVha3MpIC0tLS0NCiMgY29tIGEgdmFyacOhdmVsIE50TnJtbHogZG8gc2hhcGVmaWxlIG1hcGFfcmFua2luZy5zaHANCiMgbyBwYWNvdGUgasOhIGVzdMOhIGluc3RhbGFkbyBlIGNhcnJlZ2Fkbw0KIyBtYXBhX3Jhbmtpbmcuc2hwMiBjYXJyZWdhZG8NCm5hbWVzKG1hcGFfcmFua2luZy5zaHAyKQ0KIyBbMV0gImNvZGVfbW4iICAibmFtZV9tbiIgICJjb2Rfc3R0IiAgImFiYnJ2X3MiICAibmFtX3N0dCIgICJjb2RfcmduIiAgIm5hbV9yZ24iIA0KIyBbOF0gIk5vbWUiICAgICAiUG9zacOnw6NvIiAgIlZhcmlhw6fDoyIgICJOdE5ybWx6IiAgImNsc3Nfcm4iICAiZ2VvbWV0cnkiDQojIFsxXSAzMC40MCAzNi4yNCA0MS4wOCA0NS45MiA1MC43NiA1NS42MCA2MC40NCA2Mi40Nw0KDQojIGh0dHBzOi8vZ2VvZGFjZW50ZXIuZ2l0aHViLmlvL29waW9pZC1lbnZpcm9ubWVudC10b29sa2l0L3Zpc3VhbGl6ZUFyZWFsRGF0YS10dXRvcmlhbC5odG1sDQoNCmBgYA0KDQpBZ29yYSBmYXplbmRvIG8gbWFwYSBwZWxvIHRtYXAgYSBwYXJ0aXIgZG8gc2hhcGVmaWxlLg0KDQpgYGB7ciB0bWFwMX0NCnRtYXBfbW9kZSgncGxvdCcpICMgbW9kbyBwbG90DQpwMSA8LSB0bV9zaGFwZShtYXBhX3Jhbmtpbmcuc2hwMikgKw0KICB0bV9maWxsKA0KICAgIGZpbGwgPSAiTnROcm1seiIsDQogICAgZmlsbC5zY2FsZSA9IHRtX3NjYWxlX2ludGVydmFscygNCiAgICAgIGJyZWFrcyA9IGMoLUluZiwgMzAuNDAwLCAzNi43MjAsIDQzLjkzMCwgNDcuNDUwLCA0OS43NjAsIDUzLjY4MCwgSW5mKSwNCiAgICAgIHZhbHVlcyA9IGMoIiNGRkZGQ0MiLCIjRkFEQzg2IiwiI0Y5QzY1MiIsIiNFNDg2MjkiLA0KICAgICAgICAgICAgICAgICAiI0NDNUMwRCIsIiNBNDQ1MDciLCIjNzMyMDAwIiksDQogICAgICB2YWx1ZS5uYSA9ICIjRkZGRkNDIiAgIyBjb3IgcGFyYSBvcyBOQXMNCiAgICApLA0KICAgIGZpbGwubGVnZW5kID0gdG1fbGVnZW5kKA0KICAgICAgdGl0bGUgPSAiUmFua2luZyBOb3JtYWxpemFkbzogUXVlYnJhcyBOYXR1cmFpcyINCiAgICApDQogICkgKw0KICB0bV9ib3JkZXJzKCkgKw0KICB0bV90aXRsZSgiUmFua2luZyBDTFAgLSBKZW5rcyAtIFF1ZWJyYXMgTmF0dXJhaXMiLCBzaXplID0gMC45KSArDQogIHRtX2xheW91dCgNCiAgICBmcmFtZSA9IEZBTFNFLA0KICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwNCiAgICBsZWdlbmQub3V0c2lkZS5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgbGVnZW5kLnRpdGxlLnNpemUgPSAwLjkNCiAgKSArDQogICMg8J+RiSBOb3J0ZSBnZW9ncsOhZmljbyAoZXN0cmVsYSkNCiAgdG1fY29tcGFzcyh0eXBlID0gIjhzdGFyIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgc2l6ZSA9IDIpICsNCiAgIyDwn5GJIEJhcnJhIGRlIGVzY2FsYSAodjQgdXNhIHRtX3NjYWxlYmFyKQ0KICB0bV9zY2FsZWJhcihwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIikpDQoNCg0KcDENCg0KdG1hcF9zYXZlKHAxLCAnQ0xQX1JhbmtfTm9ybV9KZW5rcy5wbmcnKSMgc2F2ZSB0aGUgbWFwIGluIGEgLnBuZyBmaWxlDQoNCmBgYA0KDQpWb3UgY2hhbWFyIG8gbWFwYSBzYWx2byBjb21vICdDTFBfUmFua19Ob3JtX0plbmtzLnBuZycuDQoNCmBgYHtyIGltYWdlbSwgZWNobz1GQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJDTFBfUmFua19Ob3JtX0plbmtzLnBuZyIpDQpgYGANCg==