Introducción

En este cuaderno se ilustra como hacer mapas tematicos que nos muestren la participacion municipal de la producción de cultivos de hortalizas para el departamento de Boyacá. Se usa como fuente principal de datos, el archivo csv, guardado en el cuaderno EVA, así como el shapefile de los municipios obtenidos en clase.

Configuración

Se instalaran y cargaran las bibliotecas de R necesarias.

#run the following lines from the command line:
#install.packages('dplyr')
#install.packages('readxl')
#install.packages('sf)
library(sf)
library(dplyr)
library(readr)

Lectura de Archivos relacionados con cultivos, municipios y ciudades.

Teniendo en cuenta que el mucipio trabajado es bayacá, ses deben revisar las rutas de los archivos,los nombres de los archivos y los nombres de las variables. Se asegura que los siguientes archivos esten guardados en el computador: - Un shapefile con los municipios del departamento (Boyacá) - Un archivo csv con las estadísticas de EVA 2020 para el grupo de cultivos (Hortalizas)

Además de esto, se necesitaran un archivo con ciudades de Colombia, este se puede descargar desde https://simplemaps.com/data/co-cities. Este archivo descargado queda de forma co.csv y se lleva al el directorio de trabajo. Se enumeran los archivos disponibles, y se verifican cuales son los de tipo csv guardados en el directorio de trabajo.

list.files( pattern=c('csv'))
[1] "Boy_Hortalizas_2022.csv" "co.csv"                  "Eva_Boyaca.csv"          "worldcities.csv"        

Cuales son los shapefiles guardados en ese directorio.

#Se cambia la siguiente linea en el directorio de datos#
list.files('BM')
[1] "Boy_Mun.cpg" "Boy_Mun.dbf" "Boy_Mun.prj"
[4] "Boy_Mun.qmd" "Boy_Mun.shp" "Boy_Mun.shx"

Ahora, procedamos a leer los archivos basados en EVA obtenidos del primer cuaderno:

(Hortalizas = read_csv("Boy_Hortalizas_2022.csv",show_col_types = FALSE))

Este, lee los municipios de el departamento (Boyacá).

# este archivo shapefile debe tener un código EPSG 4326
# este archivo shapefile se obtuvo en clase usando QGIS
(mun.tmp =  st_read('BM/Boy_Mun.shp'))
Reading layer `Boy_Mun' from data source 
  `C:\Users\gagug\OneDrive\Escritorio\GEOMATICA-20230824T155303Z-001\GEOMATICA\GB2\CuaerdoEVA\BM\Boy_Mun.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 246 features and 8 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -74.66496 ymin: 4.655196 xmax: -71.94885 ymax: 7.055557
Geodetic CRS:  WGS 84
Simple feature collection with 246 features and 8 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -74.66496 ymin: 4.655196 xmax: -71.94885 ymax: 7.055557
Geodetic CRS:  WGS 84
First 10 features:
   fid DPTO_CCDGO MPIO_CCDGO MPIO_CNMBR MPIO_CDPMP      AREA  LATITUD  LONGITUD                       geometry
1    1         15        001      TUNJA      15001 119688918 5.518473 -73.37802 POLYGON ((-73.36346 5.55805...
2    2         15        022    ALMEIDA      15022  57672119 4.954825 -73.38813 POLYGON ((-73.36793 5.01349...
3    3         15        047  AQUITANIA      15047 942146563 5.437416 -72.87149 POLYGON ((-72.76242 5.63856...
4    4         15        051   ARCABUCO      15051 137898588 5.749565 -73.43888 POLYGON ((-73.50487 5.84347...
5    5         15        087      BELEN      15087 163088220 6.005059 -72.89370 POLYGON ((-72.91692 6.08612...
6    6         15        090     BERBEO      15090  58013016 5.232058 -73.10117 POLYGON ((-73.0677 5.27048,...
7    7         15        092  BETEITIVA      15092 101899548 5.920859 -72.84858 POLYGON ((-72.81796 5.97422...
8    8         15        097    BOAVITA      15097 145305291 6.337516 -72.62021 POLYGON ((-72.64907 6.43640...
9    9         15        104     BOYACA      15104  48022868 5.439856 -73.38137 POLYGON ((-73.34806 5.47411...
10  10         15        106    BRICENO      15106  64599703 5.675510 -73.90933 POLYGON ((-73.89118 5.73749...

Ahora, se seleccionan algunos atributos para limpiar el objeto:

# comprobar la salida del objeto en el último fragmento y
# cambiar los nombres de los atributos según sus propios datos
mun.tmp %>% select(MPIO_CCDGO, MPIO_CNMBR, AREA) -> municipios
municipios
Simple feature collection with 246 features and 3 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -74.66496 ymin: 4.655196 xmax: -71.94885 ymax: 7.055557
Geodetic CRS:  WGS 84
First 10 features:
   MPIO_CCDGO MPIO_CNMBR      AREA                       geometry
1         001      TUNJA 119688918 POLYGON ((-73.36346 5.55805...
2         022    ALMEIDA  57672119 POLYGON ((-73.36793 5.01349...
3         047  AQUITANIA 942146563 POLYGON ((-72.76242 5.63856...
4         051   ARCABUCO 137898588 POLYGON ((-73.50487 5.84347...
5         087      BELEN 163088220 POLYGON ((-72.91692 6.08612...
6         090     BERBEO  58013016 POLYGON ((-73.0677 5.27048,...
7         092  BETEITIVA 101899548 POLYGON ((-72.81796 5.97422...
8         097    BOAVITA 145305291 POLYGON ((-72.64907 6.43640...
9         104     BOYACA  48022868 POLYGON ((-73.34806 5.47411...
10        106    BRICENO  64599703 POLYGON ((-73.89118 5.73749...

Ahora, se lee el archivo cvs de las ciudades:

# Este archivo se descargó de simplemaps como se describe en la parte de arriba
(cities = read_csv("worldcities.csv"))
Rows: 44691 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (7): city, city_ascii, country, iso2, iso3, admin_name, capital
dbl (4): lat, lng, population, id
ℹ 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.
cities %>%
  filter(iso3== "COL")->col.cities
col.cities
col.cities %>%
  filter(admin_name== "Boyacá")->Boy.cities
Boy.cities

También que los valores de las coordenadas se almacenan en los atributos “lng” (latitud) y “lat” (longitud).

Para convertir ciudades en un objeto espacial (un objeto de característica simple, en este caso):

#Convertir data frame a un objeto de caracteristica simple
sf.cities <-  st_as_sf(x = Boy.cities, 
                        coords = c("lng", "lat"))
sf.cities
Simple feature collection with 29 features and 9 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -74.4167 ymin: 5.0056 xmax: -72.4167 ymax: 6.1667
CRS:           NA

¡Tenga en cuenta el CRS que falta!

Agreguemos el CRS usando el código EPSG:

st_crs(sf.cities) <- 4326

4. Datos de subconjunto relevantes para nuestro departamento.

Como estamos interesados en un solo departamento (Boyacá), necesitamos crear una unión espacial:

# buscar puntos (ciudades) dentro de polígonos (nuestros municipios)
sf.cities.joined <- st_join(sf.cities, municipios, join = st_within)

Para mostrar el objeto unido:

sf.cities.joined
Simple feature collection with 58 features and 12 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -74.4167 ymin: 5.0056 xmax: -72.4167 ymax: 6.1667
Geodetic CRS:  WGS 84

Tenga en cuenta que obtuvimos un marco de datos sf con cada fila de ciudades adjuntadas con las columnas de nuestros municipios. Las ciudades ubicadas en un departamento diferente tienen muchas NA.

Ahora filtramos las filas que corresponden a nuestro departamento (en este caso Boyacá):

Boy.ct = dplyr::filter(sf.cities.joined, admin_name=='Boyacá')
Boy.ct
Simple feature collection with 58 features and 12 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -74.4167 ymin: 5.0056 xmax: -72.4167 ymax: 6.1667
Geodetic CRS:  WGS 84

5. Hacer un Mapa para el grupo De cultivo Hortalizas como el que mas importancia le hemos dado.

# Se ejecutan las siguientes líneas desde la ventana de comandos:
#install.packages("tmap")
#install.packages("ggplot2")
#install.packages("classInt")
library(tmap)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
The legacy packages maptools, rgdal, and rgeos, underpinning the sp package,
which was just loaded, were retired in October 2023.
Please refer to R-spatial evolution reports for details, especially
https://r-spatial.org/r/2023/05/15/evolution4.html.
It may be desirable to make the sf package available;
package maintainers should consider adding sf to Suggests:.
Breaking News: tmap 3.x is retiring. Please test v4, e.g. with
remotes::install_github('r-tmap/tmap')
library(ggplot2)
library(ggrepel)
library(classInt)

Recordemos que el objeto municipios no tiene atributos EVA. En cambio, el objeto Hortalizas, que contiene estadísticas de cultivos, es un objeto no espacial. Nuestra siguiente tarea es unir el objeto de estadísticas a los objetos espaciales para tener los datos relevantes en un solo objeto. Para poder realizar la unión, necesitamos una clave compartida, es decir, un atributo común. En este caso lo tenemos en ambos objetos, ese es el código municipal. Sin embargo, tenga en cuenta que tanto sus nombres como sus tipos de datos son diferentes.

### Revisamos de acuerdo con nuestros datos propios
class(Hortalizas$COD_MUN)
[1] "numeric"
class(municipios$MPIO_CCDGO)
[1] "character"

Por lo tanto, necesitamos arreglar esto:

Hortalizas$COD_MUN = as.character(Hortalizas$COD_MUN)

Ahora estamos listos para unirlos:

municipios$str = "15"
municipios$codigo=paste(municipios$str, municipios$MPIO_CCDGO, sep = "")
head(municipios)
Simple feature collection with 6 features and 5 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -73.51402 ymin: 4.904038 xmax: -72.65243 ymax: 6.098348
Geodetic CRS:  WGS 84
  MPIO_CCDGO MPIO_CNMBR      AREA                       geometry str codigo
1        001      TUNJA 119688918 POLYGON ((-73.36346 5.55805...  15  15001
2        022    ALMEIDA  57672119 POLYGON ((-73.36793 5.01349...  15  15022
3        047  AQUITANIA 942146563 POLYGON ((-72.76242 5.63856...  15  15047
4        051   ARCABUCO 137898588 POLYGON ((-73.50487 5.84347...  15  15051
5        087      BELEN 163088220 POLYGON ((-72.91692 6.08612...  15  15087
6        090     BERBEO  58013016 POLYGON ((-73.0677 5.27048,...  15  15090
munic_Hortalizas = left_join(municipios, Hortalizas, by = c("codigo" = "COD_MUN"))

Y el resultado es:

munic_Hortalizas2
Simple feature collection with 246 features and 10 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -74.66496 ymin: 4.655196 xmax: -71.94885 ymax: 7.055557
Geodetic CRS:  WGS 84
First 10 features:
   MPIO_CCDGO MPIO_CNMBR      AREA str codigo MUNICIPIO      GRUPO max_prod                       geometry
1         001      TUNJA 119688918  15  15001     Tunja Hortalizas     5180 POLYGON ((-73.36346 5.55805...
2         022    ALMEIDA  57672119  15  15022   Almeida Hortalizas      180 POLYGON ((-73.36793 5.01349...
3         047  AQUITANIA 942146563  15  15047 Aquitania Hortalizas    65250 POLYGON ((-72.76242 5.63856...
4         051   ARCABUCO 137898588  15  15051  Arcabuco Hortalizas       40 POLYGON ((-73.50487 5.84347...
5         087      BELEN 163088220  15  15087     Belén Hortalizas      765 POLYGON ((-72.91692 6.08612...
6         090     BERBEO  58013016  15  15090    Berbeo Hortalizas      560 POLYGON ((-73.0677 5.27048,...
7         092  BETEITIVA 101899548  15  15092 Betéitiva Hortalizas      256 POLYGON ((-72.81796 5.97422...
8         097    BOAVITA 145305291  15  15097   Boavita Hortalizas      750 POLYGON ((-72.64907 6.43640...
9         104     BOYACA  48022868  15  15104    Boyacá Hortalizas     4500 POLYGON ((-73.34806 5.47411...
10        106    BRICENO  64599703  15  15106         0          0        0 POLYGON ((-73.89118 5.73749...
      faktor_class      Produccion
1  (1860 - 6062.5] (1860 - 6062.5]
2       [0 - 1860]      [0 - 1860]
3  (58305 - 65250] (58305 - 65250]
4       [0 - 1860]      [0 - 1860]
5       [0 - 1860]      [0 - 1860]
6       [0 - 1860]      [0 - 1860]
7       [0 - 1860]      [0 - 1860]
8       [0 - 1860]      [0 - 1860]
9  (1860 - 6062.5] (1860 - 6062.5]
10            <NA>            <NA>

Tenga en cuenta que munic_Hortalizas incluye ahora el atributo max_prod que representa la cantidad total de toneladas producidas por cultivos de Hortalizas en 2022.

El siguiente código personaliza los colores de los municipios. Más información [aquí] (https://stackoverflow.com/questions/57177312/trying-to-plot-in-tmap-shapefile-with-attribute).

munic_Hortalizas2 <- munic_Hortalizas %>% replace(is.na(.),0)
Warning: invalid factor level, NA generatedWarning: invalid factor level, NA generated
breaks <- classIntervals(munic_Hortalizas2$max_prod, n = 6, style = 'fisher')

#label breaks
lab_vec <- vector(length = length(breaks$brks)-1)
rounded_breaks <- round(breaks$brks,2)
lab_vec[1] <- paste0('[', rounded_breaks[1],' - ', rounded_breaks[2],']')
for(i in 2:(length(breaks$brks) - 1)){
  lab_vec[i] <- paste0('(',rounded_breaks[i], ' - ', rounded_breaks[i+1], ']')
}

Ahora para el mapa sintactico:

munic_Hortalizas2 <-  munic_Hortalizas2 %>%
  mutate(faktor_class = factor(cut(max_prod, breaks$brks, include.lowest = T), labels = lab_vec))
# Cambiar el nombre del atributo
munic_Hortalizas2$Produccion = munic_Hortalizas2$faktor_clas
# Este código crea un nuevo campo ("mind") con coordenadas centroides
munic_Hortalizas2$mid <- sf::st_centroid(munic_Hortalizas2$geometry)
# Obtener los valores de longitud
LONG = st_coordinates(munic_Hortalizas2$mid)[,1]
# Obtener los valores de latitud
LAT = st_coordinates(munic_Hortalizas2$mid)[,2]
ggplot(data = munic_Hortalizas2) +
   geom_sf(aes(fill = Produccion)) +
   geom_label_repel(aes(x = LONG, y = LAT, label = MPIO_CNMBR), 
                    label.padding =     unit(0.05,"lines"),  
                    label.r = unit(0.025, "lines"),
                    label.size = 0.05)

6. Haz un nuevo mapa para el segundo grupo más grande de cultivos.

# see example at https://r-tmap.github.io/tmap-book/layout.html
facet = "max_prod"
cereal_map =  
  tm_shape(munic_Hortalizas2) + tm_polygons(facet) + tm_text(text = "MPIO_CNMBR", size = 0.7, fontfamily = "sans") +
  tm_shape(cities) + tm_symbols(shape = 2, col = "red", size = 0.20) +
  tm_credits("Data source: UPRA (2020)", fontface = "bold") +
  tm_layout(main.title = "Produccion de oleaginosas en 2020",
            main.title.fontface = "bold.italic", 
            legend.title.fontfamily = "monospace") +
  tm_scale_bar(position = c("left", "bottom"))
tmap_mode("view")
tmap mode set to interactive viewing
LS0tDQp0aXRsZTogIk1hcGEgZGUgcHJvZHVjY2nDs24gZGUgSG9ydGFsaXphcyBlbiBlbCBkZXBhcnRhbWVudG8gZGUgQm95YWPDoSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIEludHJvZHVjY2nDs24NCg0KRW4gZXN0ZSBjdWFkZXJubyBzZSBpbHVzdHJhIGNvbW8gaGFjZXIgbWFwYXMgdGVtYXRpY29zIHF1ZSBub3MgbXVlc3RyZW4gbGEgcGFydGljaXBhY2lvbiBtdW5pY2lwYWwgZGUgbGEgcHJvZHVjY2nDs24gZGUgY3VsdGl2b3MgZGUgaG9ydGFsaXphcyBwYXJhIGVsIGRlcGFydGFtZW50byBkZSBCb3lhY8OhLiBTZSB1c2EgY29tbyBmdWVudGUgcHJpbmNpcGFsIGRlIGRhdG9zLCBlbCBhcmNoaXZvIGNzdiwgZ3VhcmRhZG8gZW4gZWwgY3VhZGVybm8gRVZBLCBhc8OtIGNvbW8gZWwgc2hhcGVmaWxlIGRlIGxvcyBtdW5pY2lwaW9zIG9idGVuaWRvcyBlbiBjbGFzZS4NCg0KIyMgQ29uZmlndXJhY2nDs24NCg0KU2UgaW5zdGFsYXJhbiB5IGNhcmdhcmFuIGxhcyBiaWJsaW90ZWNhcyBkZSBSIG5lY2VzYXJpYXMuDQoNCmBgYHtyfQ0KI3J1biB0aGUgZm9sbG93aW5nIGxpbmVzIGZyb20gdGhlIGNvbW1hbmQgbGluZToNCiNpbnN0YWxsLnBhY2thZ2VzKCdkcGx5cicpDQojaW5zdGFsbC5wYWNrYWdlcygncmVhZHhsJykNCiNpbnN0YWxsLnBhY2thZ2VzKCdzZikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShzZikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJlYWRyKQ0KYGBgDQoNCiMjIExlY3R1cmEgZGUgQXJjaGl2b3MgcmVsYWNpb25hZG9zIGNvbiBjdWx0aXZvcywgbXVuaWNpcGlvcyB5IGNpdWRhZGVzLg0KDQpUZW5pZW5kbyBlbiBjdWVudGEgcXVlIGVsIG11Y2lwaW8gdHJhYmFqYWRvIGVzIGJheWFjw6EsIHNlcyBkZWJlbiByZXZpc2FyIGxhcyBydXRhcyBkZSBsb3MgYXJjaGl2b3MsbG9zIG5vbWJyZXMgZGUgbG9zIGFyY2hpdm9zIHkgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcy4gU2UgYXNlZ3VyYSBxdWUgbG9zIHNpZ3VpZW50ZXMgYXJjaGl2b3MgZXN0ZW4gZ3VhcmRhZG9zIGVuIGVsIGNvbXB1dGFkb3I6IC0gVW4gc2hhcGVmaWxlIGNvbiBsb3MgbXVuaWNpcGlvcyBkZWwgZGVwYXJ0YW1lbnRvIChCb3lhY8OhKSAtIFVuIGFyY2hpdm8gY3N2IGNvbiBsYXMgZXN0YWTDrXN0aWNhcyBkZSBFVkEgMjAyMCBwYXJhIGVsIGdydXBvIGRlIGN1bHRpdm9zIChIb3J0YWxpemFzKQ0KDQpBZGVtw6FzIGRlIGVzdG8sIHNlIG5lY2VzaXRhcmFuIHVuIGFyY2hpdm8gY29uIGNpdWRhZGVzIGRlIENvbG9tYmlhLCBlc3RlIHNlIHB1ZWRlIGRlc2NhcmdhciBkZXNkZSA8aHR0cHM6Ly9zaW1wbGVtYXBzLmNvbS9kYXRhL2NvLWNpdGllcz4uIEVzdGUgYXJjaGl2byBkZXNjYXJnYWRvIHF1ZWRhIGRlIGZvcm1hIGNvLmNzdiB5IHNlIGxsZXZhIGFsIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqby4gU2UgZW51bWVyYW4gbG9zIGFyY2hpdm9zIGRpc3BvbmlibGVzLCB5IHNlIHZlcmlmaWNhbiBjdWFsZXMgc29uIGxvcyBkZSB0aXBvIGNzdiBndWFyZGFkb3MgZW4gZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvLg0KDQpgYGB7cn0NCmxpc3QuZmlsZXMoIHBhdHRlcm49YygnY3N2JykpDQoNCmBgYA0KDQpDdWFsZXMgc29uIGxvcyBzaGFwZWZpbGVzIGd1YXJkYWRvcyBlbiBlc2UgZGlyZWN0b3Jpby4NCg0KYGBge3J9DQojU2UgY2FtYmlhIGxhIHNpZ3VpZW50ZSBsaW5lYSBlbiBlbCBkaXJlY3RvcmlvIGRlIGRhdG9zIw0KbGlzdC5maWxlcygnQk0nKQ0KYGBgDQoNCkFob3JhLCBwcm9jZWRhbW9zIGEgbGVlciBsb3MgYXJjaGl2b3MgYmFzYWRvcyBlbiBFVkEgb2J0ZW5pZG9zIGRlbCBwcmltZXIgY3VhZGVybm86DQoNCmBgYHtyfQ0KKEhvcnRhbGl6YXMgPSByZWFkX2NzdigiQm95X0hvcnRhbGl6YXNfMjAyMi5jc3YiLHNob3dfY29sX3R5cGVzID0gRkFMU0UpKQ0KYGBgDQoNCkVzdGUsIGxlZSBsb3MgbXVuaWNpcGlvcyBkZSBlbCBkZXBhcnRhbWVudG8gKEJveWFjw6EpLg0KDQoNCmBgYHtyfQ0KIyBlc3RlIGFyY2hpdm8gc2hhcGVmaWxlIGRlYmUgdGVuZXIgdW4gY8OzZGlnbyBFUFNHIDQzMjYNCiMgZXN0ZSBhcmNoaXZvIHNoYXBlZmlsZSBzZSBvYnR1dm8gZW4gY2xhc2UgdXNhbmRvIFFHSVMNCihtdW4udG1wID0gIHN0X3JlYWQoJ0JNL0JveV9NdW4uc2hwJykpDQpgYGANCg0KQWhvcmEsIHNlIHNlbGVjY2lvbmFuIGFsZ3Vub3MgYXRyaWJ1dG9zIHBhcmEgbGltcGlhciBlbCBvYmpldG86IA0KDQpgYGB7cn0NCiMgY29tcHJvYmFyIGxhIHNhbGlkYSBkZWwgb2JqZXRvIGVuIGVsIMO6bHRpbW8gZnJhZ21lbnRvIHkNCiMgY2FtYmlhciBsb3Mgbm9tYnJlcyBkZSBsb3MgYXRyaWJ1dG9zIHNlZ8O6biBzdXMgcHJvcGlvcyBkYXRvcw0KbXVuLnRtcCAlPiUgc2VsZWN0KE1QSU9fQ0NER08sIE1QSU9fQ05NQlIsIEFSRUEpIC0+IG11bmljaXBpb3MNCmBgYA0KDQoNCmBgYHtyfQ0KbXVuaWNpcGlvcw0KYGBgDQoNCkFob3JhLCBzZSBsZWUgZWwgYXJjaGl2byBjdnMgZGUgbGFzIGNpdWRhZGVzOiANCg0KYGBge3J9DQojIEVzdGUgYXJjaGl2byBzZSBkZXNjYXJnw7MgZGUgc2ltcGxlbWFwcyBjb21vIHNlIGRlc2NyaWJlIGVuIGxhIHBhcnRlIGRlIGFycmliYQ0KKGNpdGllcyA9IHJlYWRfY3N2KCJ3b3JsZGNpdGllcy5jc3YiKSkNCmBgYA0KDQpgYGB7cn0NCmNpdGllcyAlPiUNCiAgZmlsdGVyKGlzbzM9PSAiQ09MIiktPmNvbC5jaXRpZXMNCg0KYGBgDQoNCg0KYGBge3J9DQpjb2wuY2l0aWVzDQpgYGANCmBgYHtyfQ0KY29sLmNpdGllcyAlPiUNCiAgZmlsdGVyKGFkbWluX25hbWU9PSAiQm95YWPDoSIpLT5Cb3kuY2l0aWVzDQpgYGANCg0KYGBge3J9DQpCb3kuY2l0aWVzDQpgYGANCg0KVGFtYmnDqW4gcXVlIGxvcyB2YWxvcmVzIGRlIGxhcyBjb29yZGVuYWRhcyBzZSBhbG1hY2VuYW4gZW4gbG9zIGF0cmlidXRvcyDigJxsbmfigJ0gKGxhdGl0dWQpIHkg4oCcbGF04oCdIChsb25naXR1ZCkuDQoNClBhcmEgY29udmVydGlyIGNpdWRhZGVzIGVuIHVuIG9iamV0byBlc3BhY2lhbCAodW4gb2JqZXRvIGRlIGNhcmFjdGVyw61zdGljYSBzaW1wbGUsIGVuIGVzdGUgY2Fzbyk6DQoNCmBgYHtyfQ0KI0NvbnZlcnRpciBkYXRhIGZyYW1lIGEgdW4gb2JqZXRvIGRlIGNhcmFjdGVyaXN0aWNhIHNpbXBsZQ0Kc2YuY2l0aWVzIDwtICBzdF9hc19zZih4ID0gQm95LmNpdGllcywgDQogICAgICAgICAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJsbmciLCAibGF0IikpDQpgYGANCg0KYGBge3J9DQpzZi5jaXRpZXMNCmBgYA0KDQrCoVRlbmdhIGVuIGN1ZW50YSBlbCBDUlMgcXVlIGZhbHRhIQ0KDQpBZ3JlZ3VlbW9zIGVsIENSUyB1c2FuZG8gZWwgY8OzZGlnbyBFUFNHOg0KDQpgYGB7cn0NCnN0X2NycyhzZi5jaXRpZXMpIDwtIDQzMjYNCmBgYA0KDQojIyA0LiBEYXRvcyBkZSBzdWJjb25qdW50byByZWxldmFudGVzIHBhcmEgbnVlc3RybyBkZXBhcnRhbWVudG8uDQpDb21vIGVzdGFtb3MgaW50ZXJlc2Fkb3MgZW4gdW4gc29sbyBkZXBhcnRhbWVudG8gKEJveWFjw6EpLCBuZWNlc2l0YW1vcyBjcmVhciB1bmEgdW5pw7NuIGVzcGFjaWFsOg0KDQpgYGB7cn0NCiMgYnVzY2FyIHB1bnRvcyAoY2l1ZGFkZXMpIGRlbnRybyBkZSBwb2zDrWdvbm9zIChudWVzdHJvcyBtdW5pY2lwaW9zKQ0Kc2YuY2l0aWVzLmpvaW5lZCA8LSBzdF9qb2luKHNmLmNpdGllcywgbXVuaWNpcGlvcywgam9pbiA9IHN0X3dpdGhpbikNCmBgYA0KUGFyYSBtb3N0cmFyIGVsIG9iamV0byB1bmlkbzoNCg0KYGBge3J9DQpzZi5jaXRpZXMuam9pbmVkDQpgYGANCg0KVGVuZ2EgZW4gY3VlbnRhIHF1ZSBvYnR1dmltb3MgdW4gbWFyY28gZGUgZGF0b3Mgc2YgY29uIGNhZGEgZmlsYSBkZSBjaXVkYWRlcyBhZGp1bnRhZGFzIGNvbiBsYXMgY29sdW1uYXMgZGUgbnVlc3Ryb3MgbXVuaWNpcGlvcy4gTGFzIGNpdWRhZGVzIHViaWNhZGFzIGVuIHVuIGRlcGFydGFtZW50byBkaWZlcmVudGUgdGllbmVuIG11Y2hhcyBOQS4NCg0KQWhvcmEgZmlsdHJhbW9zIGxhcyBmaWxhcyBxdWUgY29ycmVzcG9uZGVuIGEgbnVlc3RybyBkZXBhcnRhbWVudG8gKGVuIGVzdGUgY2FzbyBCb3lhY8OhKToNCg0KYGBge3J9DQpCb3kuY3QgPSBkcGx5cjo6ZmlsdGVyKHNmLmNpdGllcy5qb2luZWQsIGFkbWluX25hbWU9PSdCb3lhY8OhJykNCmBgYA0KYGBge3J9DQpCb3kuY3QNCmBgYA0KDQojIyA1LiBIYWNlciB1biBNYXBhIHBhcmEgZWwgZ3J1cG8gRGUgY3VsdGl2byBIb3J0YWxpemFzIGNvbW8gZWwgcXVlIG1hcyBpbXBvcnRhbmNpYSBsZSBoZW1vcyBkYWRvLg0KDQpgYGB7cn0NCiMgU2UgZWplY3V0YW4gbGFzIHNpZ3VpZW50ZXMgbMOtbmVhcyBkZXNkZSBsYSB2ZW50YW5hIGRlIGNvbWFuZG9zOg0KI2luc3RhbGwucGFja2FnZXMoInRtYXAiKQ0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KI2luc3RhbGwucGFja2FnZXMoImNsYXNzSW50IikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkodG1hcCkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoY2xhc3NJbnQpDQpgYGANCg0KUmVjb3JkZW1vcyBxdWUgZWwgb2JqZXRvIG11bmljaXBpb3Mgbm8gdGllbmUgYXRyaWJ1dG9zIEVWQS4gRW4gY2FtYmlvLCBlbCBvYmpldG8gSG9ydGFsaXphcywgcXVlIGNvbnRpZW5lIGVzdGFkw61zdGljYXMgZGUgY3VsdGl2b3MsIGVzIHVuIG9iamV0byBubyBlc3BhY2lhbC4gTnVlc3RyYSBzaWd1aWVudGUgdGFyZWEgZXMgdW5pciBlbCBvYmpldG8gZGUgZXN0YWTDrXN0aWNhcyBhIGxvcyBvYmpldG9zIGVzcGFjaWFsZXMgcGFyYSB0ZW5lciBsb3MgZGF0b3MgcmVsZXZhbnRlcyBlbiB1biBzb2xvIG9iamV0by4NClBhcmEgcG9kZXIgcmVhbGl6YXIgbGEgdW5pw7NuLCBuZWNlc2l0YW1vcyB1bmEgY2xhdmUgY29tcGFydGlkYSwgZXMgZGVjaXIsIHVuIGF0cmlidXRvIGNvbcO6bi4gRW4gZXN0ZSBjYXNvIGxvIHRlbmVtb3MgZW4gYW1ib3Mgb2JqZXRvcywgZXNlIGVzIGVsIGPDs2RpZ28gbXVuaWNpcGFsLiBTaW4gZW1iYXJnbywgdGVuZ2EgZW4gY3VlbnRhIHF1ZSB0YW50byBzdXMgbm9tYnJlcyBjb21vIHN1cyB0aXBvcyBkZSBkYXRvcyBzb24gZGlmZXJlbnRlcy4NCg0KYGBge3J9DQojIyMgUmV2aXNhbW9zIGRlIGFjdWVyZG8gY29uIG51ZXN0cm9zIGRhdG9zIHByb3Bpb3MNCmNsYXNzKEhvcnRhbGl6YXMkQ09EX01VTikNCmBgYA0KYGBge3J9DQpjbGFzcyhtdW5pY2lwaW9zJE1QSU9fQ0NER08pDQpgYGANClBvciBsbyB0YW50bywgbmVjZXNpdGFtb3MgYXJyZWdsYXIgZXN0bzoNCg0KDQpgYGB7cn0NCkhvcnRhbGl6YXMkQ09EX01VTiA9IGFzLmNoYXJhY3RlcihIb3J0YWxpemFzJENPRF9NVU4pDQpgYGANCg0KQWhvcmEgZXN0YW1vcyBsaXN0b3MgcGFyYSB1bmlybG9zOg0KDQoNCmBgYHtyfQ0KbXVuaWNpcGlvcyRzdHIgPSAiMTUiDQpgYGANCmBgYHtyfQ0KbXVuaWNpcGlvcyRjb2RpZ289cGFzdGUobXVuaWNpcGlvcyRzdHIsIG11bmljaXBpb3MkTVBJT19DQ0RHTywgc2VwID0gIiIpDQpgYGANCg0KYGBge3J9DQpoZWFkKG11bmljaXBpb3MpDQpgYGANCg0KDQpgYGB7cn0NCm11bmljX0hvcnRhbGl6YXMgPSBsZWZ0X2pvaW4obXVuaWNpcGlvcywgSG9ydGFsaXphcywgYnkgPSBjKCJjb2RpZ28iID0gIkNPRF9NVU4iKSkNCg0KYGBgDQoNCg0KWSBlbCByZXN1bHRhZG8gZXM6DQpgYGB7cn0NCm11bmljX0hvcnRhbGl6YXMyDQpgYGANCg0KVGVuZ2EgZW4gY3VlbnRhIHF1ZSBtdW5pY19Ib3J0YWxpemFzIGluY2x1eWUgYWhvcmEgZWwgYXRyaWJ1dG8gbWF4X3Byb2QgcXVlIHJlcHJlc2VudGEgbGEgY2FudGlkYWQgdG90YWwgZGUgdG9uZWxhZGFzIHByb2R1Y2lkYXMgcG9yIGN1bHRpdm9zIGRlIEhvcnRhbGl6YXMgZW4gMjAyMi4NCg0KRWwgc2lndWllbnRlIGPDs2RpZ28gcGVyc29uYWxpemEgbG9zIGNvbG9yZXMgZGUgbG9zIG11bmljaXBpb3MuIE3DoXMgaW5mb3JtYWNpw7NuIFthcXXDrV0gKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU3MTc3MzEyL3RyeWluZy10by1wbG90LWluLXRtYXAtc2hhcGVmaWxlLXdpdGgtYXR0cmlidXRlKS4NCg0KYGBge3J9DQptdW5pY19Ib3J0YWxpemFzMiA8LSBtdW5pY19Ib3J0YWxpemFzICU+JSByZXBsYWNlKGlzLm5hKC4pLDApDQpgYGANCg0KDQpgYGB7cn0NCmJyZWFrcyA8LSBjbGFzc0ludGVydmFscyhtdW5pY19Ib3J0YWxpemFzMiRtYXhfcHJvZCwgbiA9IDYsIHN0eWxlID0gJ2Zpc2hlcicpDQoNCiNsYWJlbCBicmVha3MNCmxhYl92ZWMgPC0gdmVjdG9yKGxlbmd0aCA9IGxlbmd0aChicmVha3MkYnJrcyktMSkNCnJvdW5kZWRfYnJlYWtzIDwtIHJvdW5kKGJyZWFrcyRicmtzLDIpDQpsYWJfdmVjWzFdIDwtIHBhc3RlMCgnWycsIHJvdW5kZWRfYnJlYWtzWzFdLCcgLSAnLCByb3VuZGVkX2JyZWFrc1syXSwnXScpDQpmb3IoaSBpbiAyOihsZW5ndGgoYnJlYWtzJGJya3MpIC0gMSkpew0KICBsYWJfdmVjW2ldIDwtIHBhc3RlMCgnKCcscm91bmRlZF9icmVha3NbaV0sICcgLSAnLCByb3VuZGVkX2JyZWFrc1tpKzFdLCAnXScpDQp9DQpgYGANCg0KQWhvcmEgcGFyYSBlbCBtYXBhIHNpbnRhY3RpY286DQoNCmBgYHtyfQ0KbXVuaWNfSG9ydGFsaXphczIgPC0gIG11bmljX0hvcnRhbGl6YXMyICU+JQ0KICBtdXRhdGUoZmFrdG9yX2NsYXNzID0gZmFjdG9yKGN1dChtYXhfcHJvZCwgYnJlYWtzJGJya3MsIGluY2x1ZGUubG93ZXN0ID0gVCksIGxhYmVscyA9IGxhYl92ZWMpKQ0KIyBDYW1iaWFyIGVsIG5vbWJyZSBkZWwgYXRyaWJ1dG8NCm11bmljX0hvcnRhbGl6YXMyJFByb2R1Y2Npb24gPSBtdW5pY19Ib3J0YWxpemFzMiRmYWt0b3JfY2xhcw0KYGBgDQoNCmBgYHtyfQ0KIyBFc3RlIGPDs2RpZ28gY3JlYSB1biBudWV2byBjYW1wbyAoIm1pbmQiKSBjb24gY29vcmRlbmFkYXMgY2VudHJvaWRlcw0KbXVuaWNfSG9ydGFsaXphczIkbWlkIDwtIHNmOjpzdF9jZW50cm9pZChtdW5pY19Ib3J0YWxpemFzMiRnZW9tZXRyeSkNCmBgYA0KDQpgYGB7cn0NCiMgT2J0ZW5lciBsb3MgdmFsb3JlcyBkZSBsb25naXR1ZA0KTE9ORyA9IHN0X2Nvb3JkaW5hdGVzKG11bmljX0hvcnRhbGl6YXMyJG1pZClbLDFdDQpgYGANCg0KYGBge3J9DQojIE9idGVuZXIgbG9zIHZhbG9yZXMgZGUgbGF0aXR1ZA0KTEFUID0gc3RfY29vcmRpbmF0ZXMobXVuaWNfSG9ydGFsaXphczIkbWlkKVssMl0NCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXVuaWNfSG9ydGFsaXphczIpICsNCiAgIGdlb21fc2YoYWVzKGZpbGwgPSBQcm9kdWNjaW9uKSkgKw0KICAgZ2VvbV9sYWJlbF9yZXBlbChhZXMoeCA9IExPTkcsIHkgPSBMQVQsIGxhYmVsID0gTVBJT19DTk1CUiksIA0KICAgICAgICAgICAgICAgICAgICBsYWJlbC5wYWRkaW5nID0gICAgIHVuaXQoMC4wNSwibGluZXMiKSwgIA0KICAgICAgICAgICAgICAgICAgICBsYWJlbC5yID0gdW5pdCgwLjAyNSwgImxpbmVzIiksDQogICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLjA1KQ0KDQpgYGANCg0KIyMgNi4gSGF6IHVuIG51ZXZvIG1hcGEgcGFyYSBlbCBzZWd1bmRvIGdydXBvIG3DoXMgZ3JhbmRlIGRlIGN1bHRpdm9zLg0KDQpgYGB7cn0NCiMgc2VlIGV4YW1wbGUgYXQgaHR0cHM6Ly9yLXRtYXAuZ2l0aHViLmlvL3RtYXAtYm9vay9sYXlvdXQuaHRtbA0KZmFjZXQgPSAibWF4X3Byb2QiDQpjZXJlYWxfbWFwID0gIA0KICB0bV9zaGFwZShtdW5pY19Ib3J0YWxpemFzMikgKyB0bV9wb2x5Z29ucyhmYWNldCkgKyB0bV90ZXh0KHRleHQgPSAiTVBJT19DTk1CUiIsIHNpemUgPSAwLjcsIGZvbnRmYW1pbHkgPSAic2FucyIpICsNCiAgdG1fc2hhcGUoY2l0aWVzKSArIHRtX3N5bWJvbHMoc2hhcGUgPSAyLCBjb2wgPSAicmVkIiwgc2l6ZSA9IDAuMjApICsNCiAgdG1fY3JlZGl0cygiRGF0YSBzb3VyY2U6IFVQUkEgKDIwMjApIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiUHJvZHVjY2lvbiBkZSBvbGVhZ2lub3NhcyBlbiAyMDIwIiwNCiAgICAgICAgICAgIG1haW4udGl0bGUuZm9udGZhY2UgPSAiYm9sZC5pdGFsaWMiLCANCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZS5mb250ZmFtaWx5ID0gIm1vbm9zcGFjZSIpICsNCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkNCnRtYXBfbW9kZSgidmlldyIpDQpgYGANCg0KDQpgYGB7cn0NCmBgYA0KDQoNCg==