Introdução

A caracterização da região de estudo e das estações meteorológicas (EM) empregadas em pesquisas ou aplicações de meteorologia (ou áreas afins) é imprescindível para o melhor entendimento e interpretação dos resultados de uma análise observacional. Neste tutorial demonstra-se como produzir um mapa temático da região de estudo incluindo a localização das EM (que proveram os dados utilizados na pesquisa ou trabalho) e um atributo associado as EM (e.g. a temperatura média do ar climatológica da EM).

O mapa temático permitirá a visualização de informações relativas a:

Pré-requisitos

Pacotes e funções necessárias.

## para instalar um pacote use
# install.packages("raster")
## limpando espaço de trabalho
rm(list = ls()) 
library(devtools)
## função load_pcks
source("../R/load_pcks.R")
## pacotes requeridos
pcks <- c("dplyr", "ggplot2","viridis", "lubridate", "scales", "raster", "viridis", "ggrepel")
load_pcks(pcks)
source_url('https://gist.githubusercontent.com/jdtatsch/5ec17e6624c97a7bbb767b07b46174ab/raw/47b38e36cc582ecc426dbc395edeb540db53caba/gg_bubble.R')
options(stringsAsFactors = TRUE)

Dados

Delimitação dos estados brasileiros

A delimitação1 dos estados brasileiros pode ser obtida a partir de um arquivo shapefile fornecido pelo IBGE com os limites dos estados na escala de 1:250000. Abaixo mostra-se o procedimento para baixar e importar esses dados no R.

limites2015 <- "ftp://geoftp.ibge.gov.br/cartas_e_mapas/bases_cartograficas_continuas/bc250/versao2015/Shapefile/Limites_v2015_20160809.zip"
# nome e caminho para o arquivo que será baixado, altere se necessário
(zip_file <- paste0("../data/", basename(limites2015)))
[1] "../data/Limites_v2015_20160809.zip"
# baixando arquivo compactado
download.file(limites2015, destfile = zip_file)
# diretório para descompactar
extract_dir <- gsub("\\.zip", "", zip_file)
extract_dir
[1] "../data/Limites_v2015_20160809"
# descompactando arquivo
unzip(zip_file, exdir = extract_dir)
# lista dos shapefiles contidos no arquivo compactado
shapefiles_list <- list.files(extract_dir,
                              pattern = "shp$", 
                              recursive = TRUE, 
                              full.names = TRUE)
shapefiles_list
 [1] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Municipio_A.shp"                   
 [2] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Outras_Unid_Protegidas_A.shp"      
 [3] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Pais_A.shp"                        
 [4] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Terra_Indigena_A.shp"              
 [5] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Terra_Indigena_P.shp"              
 [6] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Terra_Publica_A.shp"               
 [7] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Conservacao_Nao_Snuc_A.shp"
 [8] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Federacao_A.shp"           
 [9] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Protecao_Integral_A.shp"   
[10] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Protecao_Integral_P.shp"   
[11] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Uso_Sustentavel_A.shp"     
[12] "../data/Limites_v2015_20160809/Limite_v2015_2016-08-03/LIM_Unidade_Uso_Sustentavel_P.shp"     

Importando arquivo shapefile com delimitação dos estados.

# nome do arquivo shapefile dos estados
br_states_file <- grep(x = shapefiles_list, 
                       pattern = "Unidade_Federacao", 
                       value = TRUE)
# importa shape
br_states <- shapefile(br_states_file)
Z-dimension discarded
# projeta para lonlat
br_states <- spTransform(br_states, CRSobj = CRS("+proj=longlat +ellps=WGS84"))
plot(br_states, axes = TRUE)

Para selecionar a delimitação de alguns estados podemos fazer o seguinte:

sul <- br_states[br_states@data$NOME %in% c("Rio Grande do Sul", "Santa Catarina", "Paraná"), ]

Para uso dessa informação na função que gera o gráfico temático da região de estudo precisamos converter os dados armazenados na forma de um SpatialGridDataFrame para um dataframe, o que pode ser feito com a função fortify() do pacote ggplot2.

# 'fortificando' os dados (conversão para dataframe)
class(sul)
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"
sul_df <- fortify(sul)
Regions defined for each Polygons
head(sul_df)
class(sul_df)
[1] "data.frame"

Altitude do terreno

A altitude da região de interesse pode ser obtida através da função getData() do pacote raster que faz o download do modelo digital de elevação do terreno da base de dados do

dem <- getData(name = "alt", download=TRUE, path = "../data", country = "BRA")
dem
class       : RasterLayer 
dimensions  : 4716, 5388, 25409808  (nrow, ncol, ncell)
resolution  : 0.008333333, 0.008333333  (x, y)
extent      : -74.1, -29.2, -33.9, 5.4  (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +ellps=WGS84 
data source : /home/hidrometeorologista/UFSM/orientacoes/iniciacaoCientifica/caroline/work/inmet_Caroline/data/BRA_msk_alt.grd 
names       : BRA_msk_alt 
values      : -127, 2665  (min, max)

[1] 0.008333333 0.008333333

O objeto dem precisa ser convertido de raster para um data.frame, análogo ao que foi feito com função fortify() no objeto sul, em que as colunas contém as coordenadas espaciais do raster dem (lon e lat) e os valores de altitude armazenados nas células do dem. Este dataframe é um dado de entrada da função criada para produção do mapa temático com a função gg_bubble(), mostrada a seguir.

class(dem)
[1] "RasterLayer"
attr(,"package")
[1] "raster"
# extent(dem)
# minha_extent <- extent(-50, -40, -20, -10)
# plot(dem); plot(minha_extent, add = TRUE)
dem_sul <- crop(dem, sul)
# mascará para os estados
dem_sul <- mask(dem, sul)
#plot(dem_sul); plot(sul, add = TRUE)
dem_sul_df <- data.frame(lon = xFromCell(dem_sul, cell = 1:ncell(dem_sul)),
                         lat = yFromCell(dem_sul, cell = 1:ncell(dem_sul)),
                         alt = values(dem_sul))
#summary(dem_sul_df)
# removendo linhas de dados faltantes
dem_sul_df <- dem_sul_df[complete.cases(dem_sul_df), ]
head(dem_sul_df)
class(dem_sul_df)
[1] "data.frame"

Eventualmente a região de estudo pode cobrir mais de um país. Nesse caso precisaremos baixar os dados de altitude do terreno para outros países2 e juntá-los em um único raster. Esse procedimento é facilmente realizado com a função mosaic() do pacote raster. Veja ?mosaic para mais detalhes.

Informações das EM

As informações das estações meteorológicas devem incluir as coordenadas lon, lat e algum atributo de interesse, como por exemplo, a disponibilidade de dados, a temperatura média do ar, etc.

Nesse exemplo usaremos um dataframe preparado previamente e salvo em arquivo rds3.

info <- readRDS(file = "../output/info_sumary_tair_sul.rds")
info
# estrutura dos dados
str(info)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   82 obs. of  17 variables:
 $ site    : chr  "A801" "A802" "A803" "A805" ...
 $ tmax_med: num  24.8 22.1 24.8 24.7 24.5 ...
 $ tmin_med: num  16.1 15.3 14.8 15.1 18 ...
 $ dtr_med : num  8.66 6.77 9.94 9.6 6.49 ...
 $ sdate   : Date, format: "2000-09-22" "2001-11-16" ...
 $ edate   : Date, format: "2015-12-31" "2015-12-31" ...
 $ period  : num  15.3 14.1 14.1 14.1 12.9 12.9 9.6 9.3 9.1 8.9 ...
 $ max_tair: num  40 38 40 36 39 34 41 40 39 37 ...
 $ min_tair: num  0 0 -2 -2 4 0 0 -3 -4 -2 ...
 $ missing : num  4.7 13.1 4.4 17.5 10.6 27.3 3.5 1.7 13 3.3 ...
 $ long_gap: num  305 9177 1110 9097 6639 ...
 $ sdate_lg: POSIXct, format: "2001-05-23 06:00:00" "2004-05-07 09:00:00" ...
 $ name    : chr  "PORTO ALEGRE" "RIO GRANDE" "SANTA MARIA" "SANTO AUGUSTO" ...
 $ state   : chr  "RS" "RS" "RS" "RS" ...
 $ lon     : num  -51.2 -52.1 -53.7 -53.8 -48.6 ...
 $ lat     : num  -30.1 -32 -29.7 -27.9 -27.6 ...
 $ alt     : num  46.97 2.46 95 550 1.8 ...
# nome das variáveis do data.frame (tabela de dados)
names(info)
 [1] "site"     "tmax_med" "tmin_med" "dtr_med"  "sdate"    "edate"   
 [7] "period"   "max_tair" "min_tair" "missing"  "long_gap" "sdate_lg"
[13] "name"     "state"    "lon"      "lat"      "alt"     

Os dados armazenados no arquivo RDS poderiam ser obtidos a partir de um arquivo texto contendo as informações das EM. Os dados dessa tabela poderiam ser importados como mostrado abaixo.

info <- read.table(file = "mydata.txt", 
                   header = FALSE,  # dados tem cabeçalho TRUE/FALSE
                   sep = " ",       # separador das colunas (",", "\t", " ")
                   na.strings = "-999.9") # string repreentando dados faltantes
# se os dados não tiverem cabeçalho (header = FALSE)
names(info) <- c("site", "tmax_med", "tmin_med", ...)
# salvando como RDS para importação mais rápida
saveRDS(info, "info_sumary_tair_sul.rds")

Gráfico com período de dados

Para demonstrar o uso a da função gg_bubble() faremos dois mapas temáticos. O primeiro representará a variação da média da temperatura mínima do ar para cada EM. No plano de fundo será mostrado a altitude do terreno, sobreposto pelo contorno dos estados.

# mapa da % de dados faltantes em cada EM
tmin_plot <- gg_bubble(data = info      # tabela de info das EM
                       ,z = "tmin_med"         # coluna da tabela data
                       ,colors_z = viridis     # paleta para variação de z
                       ,limites = sul_df       # dataframe com contorno da região (gerado de algum shapefile)
                       ,raster_bg = dem_sul_df # raster de background (altitude, decividade, etc)
                       ,colors_bg = gray.colors # paleta de cores do raster 
                       
                       ,z_legend = "Tmin (°C)" # texto para legenda
                       ,text_color = "red")     # cor do texto para o identificador ("site") das estações 
tmin_plot

No segundo mapa temático, alteramos o atributo das EM para o período de dados disponível de cada EM e não especificaremos os argumentos raster_bg e colors_bg. Pelo mapa resultante observa-se que os polígonos dos estados foram coloridos com uma mesma cor (definida pelo argumento color_fill com valor default burlywood3). O tamanho do label usado para indicar o código das EM foi aumentado para ilustrar o posicionamento otimizado dos labels.

# para período de dados
period_plot <-  gg_bubble(data = info
                          ,z = "period"
                          ,colors_z = viridis
                          ,limites = sul_df 
                          #,raster_bg = mdet_rs_df 
                          #,colors_bg = gray.colors
                          ,z_legend = "Período (anos)"
                          ,text_color = "black"
                          ,text_size = 4) 
period_plot

Pronto! Agora você tem um mapa temático de alta qualidade para caracterização de sua região de estudo.


  1. A delimitação das regiões administrativas de qualquer país pode ser obtida através da função getData() do pacote raster que faz download da base de dados limites administrativos globais (GADM). Para obter os polígonos dos estados devemos definir o argumento level = 1. Esse argumento indica o nível de sub-divisão administrativa (level = 0 para o polígono do país, 1 para estados e 2 para municípios).

  2. Repetimos a operação realizada com a função getData() alterando o argumento country conforme os países de interesse.

  3. arquivo de armazenamento de dados em formato binário utilizado pelo R.

LS0tCnRpdGxlOiAiTWFwYSB0ZW3DoXRpY28gZGEgcmVnacOjbyBkZSBlc3R1ZG8iCnN1YnRpdGxlOiAiSW5jbHVpbmRvIGEgbG9jYWxpemHDp8OjbyBkYXMgZXN0YcOnw7VlcyBtZXRlb3JvbMOzZ2ljYXMiCmF1dGhvcjogIkpvbmF0YW4gVGF0c2NoIC0gVUZTTSIKZGF0ZTogJ2ByIFN5cy5EYXRlKClgJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKLS0tCgoKCiMgSW50cm9kdcOnw6NvCgpBIGNhcmFjdGVyaXphw6fDo28gZGEgcmVnacOjbyBkZSBlc3R1ZG8gZSBkYXMgZXN0YcOnw7VlcyBtZXRlb3JvbMOzZ2ljYXMgKEVNKSBlbXByZWdhZGFzIGVtIHBlc3F1aXNhcyBvdSBhcGxpY2HDp8O1ZXMgZGUgbWV0ZW9yb2xvZ2lhIChvdSDDoXJlYXMgYWZpbnMpIMOpIGltcHJlc2NpbmTDrXZlbCBwYXJhIG8gbWVsaG9yIGVudGVuZGltZW50byBlIGludGVycHJldGHDp8OjbyBkb3MgcmVzdWx0YWRvcyBkZSB1bWEgYW7DoWxpc2Ugb2JzZXJ2YWNpb25hbC4gTmVzdGUgdHV0b3JpYWwgZGVtb25zdHJhLXNlIGNvbW8gcHJvZHV6aXIgdW0gbWFwYSB0ZW3DoXRpY28gZGEgcmVnacOjbyBkZSBlc3R1ZG8gaW5jbHVpbmRvIGEgbG9jYWxpemHDp8OjbyBkYXMgRU0gKHF1ZSBwcm92ZXJhbSBvcyBkYWRvcyB1dGlsaXphZG9zIG5hIHBlc3F1aXNhIG91IHRyYWJhbGhvKSBlIHVtIGF0cmlidXRvIGFzc29jaWFkbyBhcyBFTSAoZS5nLiBhIHRlbXBlcmF0dXJhIG3DqWRpYSBkbyBhciBjbGltYXRvbMOzZ2ljYSBkYSBFTSkuCgpPIG1hcGEgdGVtw6F0aWNvIHBlcm1pdGlyw6EgYSB2aXN1YWxpemHDp8OjbyBkZSBpbmZvcm1hw6fDtWVzIHJlbGF0aXZhcyBhOgoKLSBsb2NhbGl6YcOnw6NvIGRhcyBFTQotIGRlbGltaXRhw6fDo28gZG9zIGVzdGFkb3MgYnJhc2lsZWlyb3MKLSBhbHRpdHVkZSBkbyB0ZXJyZW5vCi0gYWxndW0gYXRyaWJ1dG8gYXNzb2NpYWRvIGFzIEVNICAKCiMgUHLDqS1yZXF1aXNpdG9zCgpQYWNvdGVzIGUgZnVuw6fDtWVzIG5lY2Vzc8Ohcmlhcy4KCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIyBwYXJhIGluc3RhbGFyIHVtIHBhY290ZSB1c2UKIyBpbnN0YWxsLnBhY2thZ2VzKCJyYXN0ZXIiKQojIyBsaW1wYW5kbyBlc3Bhw6dvIGRlIHRyYWJhbGhvCnJtKGxpc3QgPSBscygpKSAKbGlicmFyeShkZXZ0b29scykKIyMgZnVuw6fDo28gbG9hZF9wY2tzCnNvdXJjZSgiLi4vUi9sb2FkX3Bja3MuUiIpCiMjIHBhY290ZXMgcmVxdWVyaWRvcwpwY2tzIDwtIGMoImRwbHlyIiwgImdncGxvdDIiLCJ2aXJpZGlzIiwgImx1YnJpZGF0ZSIsICJzY2FsZXMiLCAicmFzdGVyIiwgInZpcmlkaXMiLCAiZ2dyZXBlbCIpCmxvYWRfcGNrcyhwY2tzKQpzb3VyY2VfdXJsKCdodHRwczovL2dpc3QuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2pkdGF0c2NoLzVlYzE3ZTY2MjRjOTdhN2JiYjc2N2IwN2I0NjE3NGFiL3Jhdy80N2IzOGUzNmNjNTgyZWNjNDI2ZGJjMzk1ZWRlYjU0MGRiNTNjYWJhL2dnX2J1YmJsZS5SJykKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSkKYGBgCgoKCiMgRGFkb3MKCiMjIERlbGltaXRhw6fDo28gZG9zIGVzdGFkb3MgYnJhc2lsZWlyb3MKCkEgZGVsaW1pdGHDp8Ojb1teMV0gZG9zIGVzdGFkb3MgYnJhc2lsZWlyb3MgcG9kZSBzZXIgb2J0aWRhIGEgcGFydGlyIGRlIHVtIGFycXVpdm8gW3NoYXBlZmlsZV0oKSBmb3JuZWNpZG8gcGVsbyBbSUJHRV0oZnRwOi8vZ2VvZnRwLmliZ2UuZ292LmJyLykgY29tIG9zIGxpbWl0ZXMgZG9zIGVzdGFkb3MgbmEgZXNjYWxhIGRlIDE6MjUwMDAwLiBBYmFpeG8gbW9zdHJhLXNlIG8gcHJvY2VkaW1lbnRvIHBhcmEgYmFpeGFyIGUgaW1wb3J0YXIgZXNzZXMgZGFkb3Mgbm8gUi4KClteMV06IEEgZGVsaW1pdGHDp8OjbyBkYXMgcmVnacO1ZXMgYWRtaW5pc3RyYXRpdmFzIGRlIHF1YWxxdWVyIHBhw61zIHBvZGUgc2VyIG9idGlkYSBhdHJhdsOpcyBkYSBmdW7Dp8OjbyBgZ2V0RGF0YSgpYCBkbyBwYWNvdGUgcmFzdGVyIHF1ZSBmYXogKmRvd25sb2FkKiBkYSBiYXNlIGRlIGRhZG9zIGxpbWl0ZXMgYWRtaW5pc3RyYXRpdm9zIGdsb2JhaXMgKEdBRE0pLiBQYXJhIG9idGVyIG9zIHBvbMOtZ29ub3MgZG9zIGVzdGFkb3MgZGV2ZW1vcyBkZWZpbmlyIG8gYXJndW1lbnRvIGBsZXZlbCA9IDFgLiBFc3NlIGFyZ3VtZW50byBpbmRpY2EgbyBuw612ZWwgZGUgc3ViLWRpdmlzw6NvIGFkbWluaXN0cmF0aXZhIChsZXZlbCA9IDAgcGFyYSBvIHBvbMOtZ29ubyBkbyBwYcOtcywgMSBwYXJhIGVzdGFkb3MgZSAyIHBhcmEgbXVuaWPDrXBpb3MpLgoKYGBge3J9CmxpbWl0ZXMyMDE1IDwtICJmdHA6Ly9nZW9mdHAuaWJnZS5nb3YuYnIvY2FydGFzX2VfbWFwYXMvYmFzZXNfY2FydG9ncmFmaWNhc19jb250aW51YXMvYmMyNTAvdmVyc2FvMjAxNS9TaGFwZWZpbGUvTGltaXRlc192MjAxNV8yMDE2MDgwOS56aXAiCiMgbm9tZSBlIGNhbWluaG8gcGFyYSBvIGFycXVpdm8gcXVlIHNlcsOhIGJhaXhhZG8sIGFsdGVyZSBzZSBuZWNlc3PDoXJpbwooemlwX2ZpbGUgPC0gcGFzdGUwKCIuLi9kYXRhLyIsIGJhc2VuYW1lKGxpbWl0ZXMyMDE1KSkpCmBgYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBiYWl4YW5kbyBhcnF1aXZvIGNvbXBhY3RhZG8KZG93bmxvYWQuZmlsZShsaW1pdGVzMjAxNSwgZGVzdGZpbGUgPSB6aXBfZmlsZSkKYGBgCgpgYGB7cn0KIyBkaXJldMOzcmlvIHBhcmEgZGVzY29tcGFjdGFyCmV4dHJhY3RfZGlyIDwtIGdzdWIoIlxcLnppcCIsICIiLCB6aXBfZmlsZSkKZXh0cmFjdF9kaXIKIyBkZXNjb21wYWN0YW5kbyBhcnF1aXZvCnVuemlwKHppcF9maWxlLCBleGRpciA9IGV4dHJhY3RfZGlyKQojIGxpc3RhIGRvcyBzaGFwZWZpbGVzIGNvbnRpZG9zIG5vIGFycXVpdm8gY29tcGFjdGFkbwpzaGFwZWZpbGVzX2xpc3QgPC0gbGlzdC5maWxlcyhleHRyYWN0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJzaHAkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY3Vyc2l2ZSA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkKc2hhcGVmaWxlc19saXN0CmBgYAoKSW1wb3J0YW5kbyBhcnF1aXZvICpzaGFwZWZpbGUqIGNvbSBkZWxpbWl0YcOnw6NvIGRvcyBlc3RhZG9zLgoKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBmaWcuYWxpZ249J2NlbnRlcicgfQojIG5vbWUgZG8gYXJxdWl2byBzaGFwZWZpbGUgZG9zIGVzdGFkb3MKYnJfc3RhdGVzX2ZpbGUgPC0gZ3JlcCh4ID0gc2hhcGVmaWxlc19saXN0LCAKICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIlVuaWRhZGVfRmVkZXJhY2FvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBUUlVFKQojIGltcG9ydGEgc2hhcGUKYnJfc3RhdGVzIDwtIHNoYXBlZmlsZShicl9zdGF0ZXNfZmlsZSkKIyBwcm9qZXRhIHBhcmEgbG9ubGF0CmJyX3N0YXRlcyA8LSBzcFRyYW5zZm9ybShicl9zdGF0ZXMsIENSU29iaiA9IENSUygiK3Byb2o9bG9uZ2xhdCArZWxscHM9V0dTODQiKSkKcGxvdChicl9zdGF0ZXMsIGF4ZXMgPSBUUlVFKQpgYGAKClBhcmEgc2VsZWNpb25hciBhIGRlbGltaXRhw6fDo28gZGUgYWxndW5zIGVzdGFkb3MgcG9kZW1vcyBmYXplciBvIHNlZ3VpbnRlOgoKYGBge3J9CnN1bCA8LSBicl9zdGF0ZXNbYnJfc3RhdGVzQGRhdGEkTk9NRSAlaW4lIGMoIlJpbyBHcmFuZGUgZG8gU3VsIiwgIlNhbnRhIENhdGFyaW5hIiwgIlBhcmFuw6EiKSwgXQpgYGAKClBhcmEgdXNvIGRlc3NhIGluZm9ybWHDp8OjbyBuYSBmdW7Dp8OjbyBxdWUgZ2VyYSBvIGdyw6FmaWNvIHRlbcOhdGljbyBkYSByZWdpw6NvIGRlIGVzdHVkbyBwcmVjaXNhbW9zIGNvbnZlcnRlciBvcyBkYWRvcyBhcm1hemVuYWRvcyBuYSBmb3JtYSBkZSB1bSBgU3BhdGlhbEdyaWREYXRhRnJhbWVgIHBhcmEgdW0gIGBkYXRhZnJhbWVgLCBvIHF1ZSBwb2RlIHNlciBmZWl0byBjb20gYSBmdW7Dp8OjbyBgZm9ydGlmeSgpYCBkbyBwYWNvdGUgW2dncGxvdDJdKGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnL2N1cnJlbnQvKS4KCmBgYHtyfQojICdmb3J0aWZpY2FuZG8nIG9zIGRhZG9zIChjb252ZXJzw6NvIHBhcmEgZGF0YWZyYW1lKQpjbGFzcyhzdWwpCnN1bF9kZiA8LSBmb3J0aWZ5KHN1bCkKaGVhZChzdWxfZGYpCmNsYXNzKHN1bF9kZikKYGBgCgoKIyMgQWx0aXR1ZGUgZG8gdGVycmVubwoKQSBhbHRpdHVkZSBkYSByZWdpw6NvIGRlIGludGVyZXNzZSBwb2RlIHNlciBvYnRpZGEgYXRyYXbDqXMgZGEgZnVuw6fDo28gYGdldERhdGEoKWAgZG8gcGFjb3RlIGByYXN0ZXJgIHF1ZSBmYXogbyAqZG93bmxvYWQqIGRvIG1vZGVsbyBkaWdpdGFsIGRlIGVsZXZhw6fDo28gZG8gdGVycmVubyBkYSBiYXNlIGRlIGRhZG9zIGRvIAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmRlbSA8LSBnZXREYXRhKG5hbWUgPSAiYWx0IiwgZG93bmxvYWQ9VFJVRSwgcGF0aCA9ICIuLi9kYXRhIiwgY291bnRyeSA9ICJCUkEiKQpkZW0KYGBgCgpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInIH0KIyBkaWdpdGFsIGVsZXZhdGlvbiBtb2RlbApkZW0gPC0gcmFzdGVyKCIuLi9kYXRhL0JSQV9tc2tfYWx0LmdyZCIpCmRlbQpyZXMoZGVtKSAjIH45MDAgbSAoMcK6IH4xMDAga20pCiMgc3Vic3RpdHVpbmRvIGFsdCA8IDAgcG9yIDAKZGVtW2RlbSA8IDBdIDwtIDAKcGxvdChkZW0pCmBgYAoKTyBvYmpldG8gYGRlbWAgcHJlY2lzYSBzZXIgY29udmVydGlkbyBkZSBgcmFzdGVyYCBwYXJhIHVtIGBkYXRhLmZyYW1lYCwgYW7DoWxvZ28gYW8gcXVlIGZvaSBmZWl0byBjb20gZnVuw6fDo28gYGZvcnRpZnkoKWAgbm8gb2JqZXRvIGBzdWxgLCBlbSBxdWUgYXMgY29sdW5hcyBjb250w6ltIGFzIGNvb3JkZW5hZGFzIGVzcGFjaWFpcyBkbyBgcmFzdGVyYCBgZGVtYCAoYGxvbmAgZSBgbGF0YCkgZSBvcyB2YWxvcmVzIGRlIGFsdGl0dWRlIGFybWF6ZW5hZG9zIG5hcyBjw6lsdWxhcyBkbyBgZGVtYC4gRXN0ZSBgZGF0YWZyYW1lYCDDqSB1bSBkYWRvIGRlIGVudHJhZGEgZGEgZnVuw6fDo28gY3JpYWRhIHBhcmEgcHJvZHXDp8OjbyBkbyBtYXBhIHRlbcOhdGljbyBjb20gYSBmdW7Dp8OjbyBgZ2dfYnViYmxlKClgLCBtb3N0cmFkYSBhIHNlZ3Vpci4KCmBgYHtyfQpjbGFzcyhkZW0pCiMgZXh0ZW50KGRlbSkKIyBtaW5oYV9leHRlbnQgPC0gZXh0ZW50KC01MCwgLTQwLCAtMjAsIC0xMCkKIyBwbG90KGRlbSk7IHBsb3QobWluaGFfZXh0ZW50LCBhZGQgPSBUUlVFKQpkZW1fc3VsIDwtIGNyb3AoZGVtLCBzdWwpCiMgbWFzY2Fyw6EgcGFyYSBvcyBlc3RhZG9zCmRlbV9zdWwgPC0gbWFzayhkZW0sIHN1bCkKI3Bsb3QoZGVtX3N1bCk7IHBsb3Qoc3VsLCBhZGQgPSBUUlVFKQpkZW1fc3VsX2RmIDwtIGRhdGEuZnJhbWUobG9uID0geEZyb21DZWxsKGRlbV9zdWwsIGNlbGwgPSAxOm5jZWxsKGRlbV9zdWwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxhdCA9IHlGcm9tQ2VsbChkZW1fc3VsLCBjZWxsID0gMTpuY2VsbChkZW1fc3VsKSksCiAgICAgICAgICAgICAgICAgICAgICAgICBhbHQgPSB2YWx1ZXMoZGVtX3N1bCkpCiNzdW1tYXJ5KGRlbV9zdWxfZGYpCiMgcmVtb3ZlbmRvIGxpbmhhcyBkZSBkYWRvcyBmYWx0YW50ZXMKZGVtX3N1bF9kZiA8LSBkZW1fc3VsX2RmW2NvbXBsZXRlLmNhc2VzKGRlbV9zdWxfZGYpLCBdCmhlYWQoZGVtX3N1bF9kZikKY2xhc3MoZGVtX3N1bF9kZikKYGBgCgpFdmVudHVhbG1lbnRlIGEgcmVnacOjbyBkZSBlc3R1ZG8gcG9kZSBjb2JyaXIgbWFpcyBkZSB1bSBwYcOtcy4gTmVzc2UgY2FzbyBwcmVjaXNhcmVtb3MgYmFpeGFyIG9zIGRhZG9zIGRlIGFsdGl0dWRlIGRvIHRlcnJlbm8gcGFyYSBvdXRyb3MgcGHDrXNlc1teMl0gZSBqdW50w6EtbG9zIGVtIHVtIMO6bmljbyByYXN0ZXIuIEVzc2UgcHJvY2VkaW1lbnRvIMOpIGZhY2lsbWVudGUgcmVhbGl6YWRvIGNvbSBhIGZ1bsOnw6NvIGBtb3NhaWMoKWAgZG8gcGFjb3RlIGByYXN0ZXJgLiBWZWphIGA/bW9zYWljYCBwYXJhIG1haXMgZGV0YWxoZXMuCgpbXjJdOiBSZXBldGltb3MgYSBvcGVyYcOnw6NvIHJlYWxpemFkYSBjb20gYSBmdW7Dp8OjbyBgZ2V0RGF0YSgpYCBhbHRlcmFuZG8gbyBhcmd1bWVudG8gYGNvdW50cnlgICBjb25mb3JtZSBvcyBwYcOtc2VzIGRlIGludGVyZXNzZS4gCgojIyBJbmZvcm1hw6fDtWVzIGRhcyBFTQoKQXMgaW5mb3JtYcOnw7VlcyBkYXMgZXN0YcOnw7VlcyBtZXRlb3JvbMOzZ2ljYXMgZGV2ZW0gaW5jbHVpciBhcyBjb29yZGVuYWRhcyBgbG9uYCwgYGxhdGAgZSBhbGd1bSBhdHJpYnV0byBkZSBpbnRlcmVzc2UsIGNvbW8gcG9yIGV4ZW1wbG8sIGEgZGlzcG9uaWJpbGlkYWRlIGRlIGRhZG9zLCBhIHRlbXBlcmF0dXJhIG3DqWRpYSBkbyBhciwgZXRjLgoKTmVzc2UgZXhlbXBsbyB1c2FyZW1vcyB1bSBgZGF0YWZyYW1lYCBwcmVwYXJhZG8gcHJldmlhbWVudGUgZSBzYWx2byBlbSBhcnF1aXZvIHJkc1teM10uIAoKCmBgYHtyLCBlY2hvID0gVFJVRX0KaW5mbyA8LSByZWFkUkRTKGZpbGUgPSAiLi4vb3V0cHV0L2luZm9fc3VtYXJ5X3RhaXJfc3VsLnJkcyIpCmluZm8KIyBlc3RydXR1cmEgZG9zIGRhZG9zCnN0cihpbmZvKQojIG5vbWUgZGFzIHZhcmnDoXZlaXMgZG8gZGF0YS5mcmFtZSAodGFiZWxhIGRlIGRhZG9zKQpuYW1lcyhpbmZvKQpgYGAKCgpPcyBkYWRvcyBhcm1hemVuYWRvcyBubyBhcnF1aXZvIFJEUyBwb2RlcmlhbSBzZXIgb2J0aWRvcyBhIHBhcnRpciBkZSB1bSBhcnF1aXZvIHRleHRvIGNvbnRlbmRvIGFzIGluZm9ybWHDp8O1ZXMgZGFzIEVNLiBPcyBkYWRvcyBkZXNzYSB0YWJlbGEgcG9kZXJpYW0gc2VyIGltcG9ydGFkb3MgY29tbyBtb3N0cmFkbyBhYmFpeG8uCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQppbmZvIDwtIHJlYWQudGFibGUoZmlsZSA9ICJteWRhdGEudHh0IiwgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBGQUxTRSwgICMgZGFkb3MgdGVtIGNhYmXDp2FsaG8gVFJVRS9GQUxTRQogICAgICAgICAgICAgICAgICAgc2VwID0gIiAiLCAgICAgICAjIHNlcGFyYWRvciBkYXMgY29sdW5hcyAoIiwiLCAiXHQiLCAiICIpCiAgICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIi05OTkuOSIpICMgc3RyaW5nIHJlcHJlZW50YW5kbyBkYWRvcyBmYWx0YW50ZXMKIyBzZSBvcyBkYWRvcyBuw6NvIHRpdmVyZW0gY2FiZcOnYWxobyAoaGVhZGVyID0gRkFMU0UpCm5hbWVzKGluZm8pIDwtIGMoInNpdGUiLCAidG1heF9tZWQiLCAidG1pbl9tZWQiLCAuLi4pCiMgc2FsdmFuZG8gY29tbyBSRFMgcGFyYSBpbXBvcnRhw6fDo28gbWFpcyByw6FwaWRhCnNhdmVSRFMoaW5mbywgImluZm9fc3VtYXJ5X3RhaXJfc3VsLnJkcyIpCmBgYAoKClteM106IGFycXVpdm8gZGUgYXJtYXplbmFtZW50byBkZSBkYWRvcyBlbSBmb3JtYXRvIGJpbsOhcmlvIHV0aWxpemFkbyBwZWxvIFIuCgoKIyBHcsOhZmljbyBjb20gcGVyw61vZG8gZGUgZGFkb3MKClBhcmEgZGVtb25zdHJhciBvIHVzbyBhIGRhIGZ1bsOnw6NvIGBnZ19idWJibGUoKWAgZmFyZW1vcyBkb2lzIG1hcGFzIHRlbcOhdGljb3MuIE8gcHJpbWVpcm8gcmVwcmVzZW50YXLDoSBhIHZhcmlhw6fDo28gZGEgbcOpZGlhIGRhIHRlbXBlcmF0dXJhIG3DrW5pbWEgZG8gYXIgcGFyYSBjYWRhICBFTS4gTm8gcGxhbm8gZGUgZnVuZG8gc2Vyw6EgbW9zdHJhZG8gYSBhbHRpdHVkZSBkbyB0ZXJyZW5vLCBzb2JyZXBvc3RvIHBlbG8gY29udG9ybm8gZG9zIGVzdGFkb3MuIAoKYGBge3IgZ3JhZjEsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgbWFwYSBkYSAlIGRlIGRhZG9zIGZhbHRhbnRlcyBlbSBjYWRhIEVNCnRtaW5fcGxvdCA8LSBnZ19idWJibGUoZGF0YSA9IGluZm8gICAgICAjIHRhYmVsYSBkZSBpbmZvIGRhcyBFTQogICAgICAgICAgICAgICAgICAgICAgICx6ID0gInRtaW5fbWVkIiAgICAgICAgICMgY29sdW5hIGRhIHRhYmVsYSBkYXRhCiAgICAgICAgICAgICAgICAgICAgICAgLGNvbG9yc196ID0gdmlyaWRpcyAgICAgIyBwYWxldGEgcGFyYSB2YXJpYcOnw6NvIGRlIHoKICAgICAgICAgICAgICAgICAgICAgICAsbGltaXRlcyA9IHN1bF9kZiAgICAgICAjIGRhdGFmcmFtZSBjb20gY29udG9ybm8gZGEgcmVnacOjbyAoZ2VyYWRvIGRlIGFsZ3VtIHNoYXBlZmlsZSkKICAgICAgICAgICAgICAgICAgICAgICAscmFzdGVyX2JnID0gZGVtX3N1bF9kZiAjIHJhc3RlciBkZSBiYWNrZ3JvdW5kIChhbHRpdHVkZSwgZGVjaXZpZGFkZSwgZXRjKQogICAgICAgICAgICAgICAgICAgICAgICxjb2xvcnNfYmcgPSBncmF5LmNvbG9ycyAjIHBhbGV0YSBkZSBjb3JlcyBkbyByYXN0ZXIgCiAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgLHpfbGVnZW5kID0gIlRtaW4gKMKwQykiICMgdGV4dG8gcGFyYSBsZWdlbmRhCiAgICAgICAgICAgICAgICAgICAgICAgLHRleHRfY29sb3IgPSAicmVkIikgICAgICMgY29yIGRvIHRleHRvIHBhcmEgbyBpZGVudGlmaWNhZG9yICgic2l0ZSIpIGRhcyBlc3Rhw6fDtWVzIAp0bWluX3Bsb3QKYGBgCgpObyBzZWd1bmRvIG1hcGEgdGVtw6F0aWNvLCBhbHRlcmFtb3MgbyBhdHJpYnV0byBkYXMgRU0gcGFyYSBvIHBlcsOtb2RvIGRlIGRhZG9zIGRpc3BvbsOtdmVsIGRlIGNhZGEgRU0gZSBuw6NvIGVzcGVjaWZpY2FyZW1vcyBvcyBhcmd1bWVudG9zIGByYXN0ZXJfYmdgIGUgYGNvbG9yc19iZ2AuIFBlbG8gbWFwYSByZXN1bHRhbnRlIG9ic2VydmEtc2UgcXVlIG9zIHBvbMOtZ29ub3MgZG9zIGVzdGFkb3MgZm9yYW0gY29sb3JpZG9zIGNvbSB1bWEgbWVzbWEgY29yIChkZWZpbmlkYSBwZWxvIGFyZ3VtZW50byBgY29sb3JfZmlsbGAgY29tIHZhbG9yICpkZWZhdWx0KiBgYnVybHl3b29kM2ApLiBPIHRhbWFuaG8gZG8gKmxhYmVsKiB1c2FkbyBwYXJhIGluZGljYXIgbyBjw7NkaWdvIGRhcyBFTSBmb2kgYXVtZW50YWRvIHBhcmEgaWx1c3RyYXIgbyBwb3NpY2lvbmFtZW50byBvdGltaXphZG8gZG9zICpsYWJlbHMqLgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJyB9CiMgcGFyYSBwZXLDrW9kbyBkZSBkYWRvcwpwZXJpb2RfcGxvdCA8LSAgZ2dfYnViYmxlKGRhdGEgPSBpbmZvCiAgICAgICAgICAgICAgICAgICAgICAgICAgLHogPSAicGVyaW9kIgogICAgICAgICAgICAgICAgICAgICAgICAgICxjb2xvcnNfeiA9IHZpcmlkaXMKICAgICAgICAgICAgICAgICAgICAgICAgICAsbGltaXRlcyA9IHN1bF9kZiAKICAgICAgICAgICAgICAgICAgICAgICAgICAjLHJhc3Rlcl9iZyA9IG1kZXRfcnNfZGYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyxjb2xvcnNfYmcgPSBncmF5LmNvbG9ycwogICAgICAgICAgICAgICAgICAgICAgICAgICx6X2xlZ2VuZCA9ICJQZXLDrW9kbyAoYW5vcykiCiAgICAgICAgICAgICAgICAgICAgICAgICAgLHRleHRfY29sb3IgPSAiYmxhY2siCiAgICAgICAgICAgICAgICAgICAgICAgICAgLHRleHRfc2l6ZSA9IDQpIApwZXJpb2RfcGxvdApgYGAgCgpQcm9udG8hIEFnb3JhIHZvY8OqIHRlbSB1bSBtYXBhIHRlbcOhdGljbyBkZSBhbHRhIHF1YWxpZGFkZSBwYXJhIGNhcmFjdGVyaXphw6fDo28gZGUgc3VhIHJlZ2nDo28gZGUgZXN0dWRvLgoKCgo=