title: “Angelica Martinez” output: Datos de elevacion en R. 26 de marzo —

En este notebook se ilustraran diferentes funciones para obtener, procesar y visualizar modelos de elevacion digital.

Para lograrlos se deben instalar los siguientes paquetes.

#install.packages("rgdal")
# install.packages("raster")
# install.packages("elevatr")
#install.packages("rasterVis")
#install.packages("rgl")
#install.packages("lattice")
#install.packages("latticeExtra")

Una vez instalados se deben cargar.

library(raster)
library(rgl)
library(rgdal)
library(lattice)
library(latticeExtra)
library(sp)
library(raster)
library(rasterVis)
library(elevatr)

3. Get Raster Elevation Data

Se trabajara con el archivo del Censo del Dane, que fue descargado con antelación. Se listara el contenido del archivo llamado “ADMINISTRATIVO”

list.files("ADMINISTRATIVO")

Ahora leeremosel archivo con una utilidad del paquete raster.

(munic <-  shapefile("ADMINISTRATIVO/MGN_MPIO_POLITICO.shp"))

Visualicemos los atributos de munic

head(munic)

Se seleccionara lacapital del departamento de Cesar “Valledupar” y realizaremos un ploteo

valle<- munic[munic$MPIO_CNMBR=="VALLEDUPAR",]
plot(valle, main="Valledupar", axes=TRUE)
plot(munic, add=TRUE)
invisible(text(coordinates(munic), labels=as.character(munic$MPIO_CNMBR), cex=0.5))

Ahora se trabajara con el paquete elevation

elevation <- get_elev_raster(valle, z = 8)
#elevation <-  raster("./dem/elev_z8.tif")

Se visualizara el contenido de elevation

elevation

Es de resaltar que el sistema de coordenadas de refencia es el WGS84

plot(elevation, main="This the downloaded DEM [meters]")
plot(valle, add=TRUE)

4.Recorte los datos de elevación para que coincidan con la extensión del área de estudio.

#writeRaster(elevation, cesar1="./dem/elev_z8.tif", datatype='INT4S', overwrite=TRUE)

Cortaremos los datos correspondientes a Valledupar, y su abreviatura sera " valle"

elev_crop = crop(elevation, valle)
plot(elev_crop, main="Cropped Digital elevation model")
plot(valle, add=TRUE)

Se chequea el nuevo elemento.

elev_crop

5. Reproyectar los datos de elevación

En la pagina epsg se pueden obtener los datos de MAGNA Colombia Bogota zone projection. se necesita la definicion de PROJEC 4.

spatialref <- "+proj=tmerc +lat_0=4.596200416666666 +lon_0=-74.07750791666666 +k=1 +x_0=1000000 +y_0=1000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs" 

Podemos reproyectarlos datos de elevacion de WGS84 geographic coordinates a MAGNA Colombia Bogota zone.


pr3 <- projectExtent(elev_crop, spatialref)

res(pr3) <- 100

rep_elev <- projectRaster(elev_crop, pr3)

Lo que obtendriamos sera:

rep_elev

Ahora se reproyectara el SpatialPolygonsDataFrame de la capital del Cesar:

(rep_valle = spTransform(valle,spatialref))

Esta esla imagen que obtenemos al plotear

plot(rep_elev, main="Reprojected Digital elevation model")
plot(rep_valle, add=TRUE)

Con elsiguiente codigo guardemos el cambio.


#writeRaster(rep_elev, cesar1="./dem/rep_valle_elev.tif", datatype='INT4S', overwrite=TRUE)

6. Estadísticas básicas de datos de elevación

Haremos un histograma de las elevaciones , para revisar algo de estadistica.

# histograma
hist(rep_elev)
# para archivos grandes sepueden trabajar los siguientes codigos 
promedio <- cellStats(rep_elev, 'mean')
minimo <- cellStats(rep_elev, 'min')
maximo <- cellStats(rep_elev, 'max')
desviacion  <- cellStats(rep_elev, 'sd')
# create unidimensional vector
metricas <- c('mean', 'min', 'max', 'std')
valores <- c(promedio, minimo, maximo, desviacion)

Obtendremos estos datos

# creacion de data frame con estadisticas de elevacion [metros]
# el paréntesis externo sirve para "imprimir" el contenido de un objeto
(df_estadisticas <- data.frame(metricas, valores))

7. Estadísticas básicas de datos de dimensiones

Antes de introducirse en este tema se deben tener conocimientos previos del tema de pendientes

slope = terrain(rep_elev,opt='slope', unit='degrees')
aspect = terrain(rep_elev,opt='aspect',unit='degrees')
hill = hillShade(slope,aspect,40,315)
plot(rep_elev,main="DEM for Valledupar [meters]", col=terrain.colors(25,alpha=0.7))

En nuestros ploters , la pendiente se observa por los cambios de tonos y de colores

plot(slope,main="Slope for Valledupar [degrees]", col=topo.colors(25,alpha=0.7))
plot(aspect,main="Aspect for Valledupar [degrees]", col=rainbow(25,alpha=0.7))

plot(hill,
        col=grey(1:100/100),  
        legend=FALSE,         
        main="DEM for Valledupar",
        axes=FALSE)           
plot(rep_elev, 
        axes=FALSE,
        col=terrain.colors(12, alpha=0.35), add=TRUE) # color method

8. Mapeo de datos de elevación con rayshader

La biblioteca rayshader es un paquete de código abierto para producir visualizaciones de datos 2D y 3D en R. rayshader utiliza datos de elevación en una matriz base R y una combinación de trazado de rayos, mapeo de texturas esféricas, superposiciones y oclusión ambiental para generar hermosos mapas topográficos 2D y 3D . Además de los mapas, rayshader también permite al usuario traducir objetos ggplot2 en hermosas visualizaciones de datos 3D.

#install.packages("rayshader")
library(rayshader)
elmat = raster_to_matrix(rep_elev)

Mapear texturas


elmat %>%
  sphere_shade(texture = "imhof2") %>%
  plot_map()

Detectar agua


elmat %>%
  sphere_shade(texture = "desert") %>%
  add_water(detect_water(elmat), color = "desert") %>%
  plot_map()

Podemos agregar una capa de trazado de rayos desde esa dirección del sol:

elmat %>%
  sphere_shade(texture = "desert") %>%
  add_water(detect_water(elmat), color = "desert") %>%
  add_shadow(ray_shade(elmat), 0.5) %>%
  plot_map()

9. Otra forma de visualizacion

#install.packages("jpeg")
library(jpeg)

Función de sombreado de mapeo de entorno esférico


getv=function(i,a,s){
  ct = dim(i)[1:2]/2
  sx = values(s)/90 * ct[1]
  sy = values(s)/90 * ct[2]
  a = values(a) * 0.01745
  px = floor(ct[1] + sx * -sin(a))
  py = floor(ct[2] + sy * cos(a))
  
  
  template = brick(s,s,s)
  values(template)=NA
  
  cellr = px + py * ct[1]*2
  cellg = px + py * ct[1]*2 + (ct[1]*2*ct[2]*2)
  cellb = px + py * ct[1]*2 + 2*(ct[1]*2*ct[2]*2)
  
  template[[1]] = i[cellr]
  template[[2]] = i[cellg]
  template[[3]] = i[cellb]
  
  template = template * 256
  
  template
}

Ahora con una imagen jpeg


map=readJPEG("./9pvbHjN.jpg")


out = getv(map, aspect, slope)


plotRGB(out, main = "El Valle")
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KdGl0bGU6ICJBbmdlbGljYSBNYXJ0aW5leiIKb3V0cHV0OiBEYXRvcyBkZSBlbGV2YWNpb24gZW4gUi4gMjYgZGUgbWFyem8KLS0tCgpFbiBlc3RlIG5vdGVib29rIHNlIGlsdXN0cmFyYW4gZGlmZXJlbnRlcyBmdW5jaW9uZXMgcGFyYSBvYnRlbmVyLCBwcm9jZXNhciB5IHZpc3VhbGl6YXIgbW9kZWxvcyBkZSBlbGV2YWNpb24gZGlnaXRhbC4KClBhcmEgbG9ncmFybG9zIHNlIGRlYmVuIGluc3RhbGFyIGxvcyBzaWd1aWVudGVzIHBhcXVldGVzLgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoInJnZGFsIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJyYXN0ZXIiKQojIGluc3RhbGwucGFja2FnZXMoImVsZXZhdHIiKQojaW5zdGFsbC5wYWNrYWdlcygicmFzdGVyVmlzIikKI2luc3RhbGwucGFja2FnZXMoInJnbCIpCiNpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikKI2luc3RhbGwucGFja2FnZXMoImxhdHRpY2VFeHRyYSIpCgpgYGAKVW5hIHZleiBpbnN0YWxhZG9zICBzZSBkZWJlbiBjYXJnYXIuCgpgYGB7cn0KbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkocmdsKQpsaWJyYXJ5KHJnZGFsKQpsaWJyYXJ5KGxhdHRpY2UpCmxpYnJhcnkobGF0dGljZUV4dHJhKQpsaWJyYXJ5KHNwKQpsaWJyYXJ5KHJhc3RlcikKYGBgCgoKYGBge3J9CmxpYnJhcnkocmFzdGVyVmlzKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGVsZXZhdHIpCmBgYAoqKjMuIEdldCBSYXN0ZXIgRWxldmF0aW9uIERhdGEqKgoKIFNlIHRyYWJhamFyYSBjb24gZWwgYXJjaGl2byBkZWwgQ2Vuc28gZGVsIERhbmUsIHF1ZSBmdWUgZGVzY2FyZ2FkbyBjb24gYW50ZWxhY2nDs24uCiBTZSBsaXN0YXJhIGVsIGNvbnRlbmlkbyBkZWwgYXJjaGl2byBsbGFtYWRvICJBRE1JTklTVFJBVElWTyIgCgpgYGB7cn0KbGlzdC5maWxlcygiQURNSU5JU1RSQVRJVk8iKQpgYGAKQWhvcmEgbGVlcmVtb3NlbCBhcmNoaXZvIGNvbiB1bmEgdXRpbGlkYWQgZGVsIHBhcXVldGUgIHJhc3Rlci4KCmBgYHtyfQoobXVuaWMgPC0gIHNoYXBlZmlsZSgiQURNSU5JU1RSQVRJVk8vTUdOX01QSU9fUE9MSVRJQ08uc2hwIikpCmBgYAogVmlzdWFsaWNlbW9zIGxvcyBhdHJpYnV0b3MgZGUgbXVuaWMgCiAKYGBge3J9CmhlYWQobXVuaWMpCmBgYApTZSBzZWxlY2Npb25hcmEgbGFjYXBpdGFsIGRlbCBkZXBhcnRhbWVudG8gZGUgQ2VzYXIgIlZhbGxlZHVwYXIiIHkgcmVhbGl6YXJlbW9zIHVuIHBsb3RlbwoKYGBge3J9CnZhbGxlPC0gbXVuaWNbbXVuaWMkTVBJT19DTk1CUj09IlZBTExFRFVQQVIiLF0KcGxvdCh2YWxsZSwgbWFpbj0iVmFsbGVkdXBhciIsIGF4ZXM9VFJVRSkKcGxvdChtdW5pYywgYWRkPVRSVUUpCmludmlzaWJsZSh0ZXh0KGNvb3JkaW5hdGVzKG11bmljKSwgbGFiZWxzPWFzLmNoYXJhY3RlcihtdW5pYyRNUElPX0NOTUJSKSwgY2V4PTAuNSkpCmBgYAoKCkFob3JhIHNlIHRyYWJhamFyYSBjb24gZWwgcGFxdWV0ZSBlbGV2YXRpb24KCmBgYHtyfQplbGV2YXRpb24gPC0gZ2V0X2VsZXZfcmFzdGVyKHZhbGxlLCB6ID0gOCkKYGBgCgpgYGB7cn0KI2VsZXZhdGlvbiA8LSAgcmFzdGVyKCIuL2RlbS9lbGV2X3o4LnRpZiIpCmBgYAoKU2UgdmlzdWFsaXphcmEgZWwgY29udGVuaWRvIGRlIGVsZXZhdGlvbgoKYGBge3J9CmVsZXZhdGlvbgpgYGAKRXMgZGUgcmVzYWx0YXIgcXVlIGVsIHNpc3RlbWEgZGUgY29vcmRlbmFkYXMgZGUgcmVmZW5jaWEgZXMgZWwgV0dTODQKIApgYGB7cn0KcGxvdChlbGV2YXRpb24sIG1haW49IlRoaXMgdGhlIGRvd25sb2FkZWQgREVNIFttZXRlcnNdIikKcGxvdCh2YWxsZSwgYWRkPVRSVUUpCmBgYAoKKio0LlJlY29ydGUgbG9zIGRhdG9zIGRlIGVsZXZhY2nDs24gcGFyYSBxdWUgY29pbmNpZGFuIGNvbiBsYSBleHRlbnNpw7NuIGRlbCDDoXJlYSBkZSBlc3R1ZGlvLioqCgoKCmBgYHtyfQojd3JpdGVSYXN0ZXIoZWxldmF0aW9uLCBjZXNhcjE9Ii4vZGVtL2VsZXZfejgudGlmIiwgZGF0YXR5cGU9J0lOVDRTJywgb3ZlcndyaXRlPVRSVUUpCmBgYAoKQ29ydGFyZW1vcyBsb3MgZGF0b3MgY29ycmVzcG9uZGllbnRlcyBhIFZhbGxlZHVwYXIsIHkgc3UgYWJyZXZpYXR1cmEgc2VyYSAiIHZhbGxlIgoKYGBge3J9CmVsZXZfY3JvcCA9IGNyb3AoZWxldmF0aW9uLCB2YWxsZSkKcGxvdChlbGV2X2Nyb3AsIG1haW49IkNyb3BwZWQgRGlnaXRhbCBlbGV2YXRpb24gbW9kZWwiKQpwbG90KHZhbGxlLCBhZGQ9VFJVRSkKYGBgCgpTZSBjaGVxdWVhIGVsIG51ZXZvIGVsZW1lbnRvLgoKYGBge3J9CmVsZXZfY3JvcApgYGAKCioqNS4gUmVwcm95ZWN0YXIgbG9zIGRhdG9zIGRlIGVsZXZhY2nDs24qKgoKCkVuIGxhIHBhZ2luYSBlcHNnICBzZSBwdWVkZW4gb2J0ZW5lciBsb3MgZGF0b3MgZGUgTUFHTkEgQ29sb21iaWEgQm9nb3RhIHpvbmUgcHJvamVjdGlvbi4gc2UgbmVjZXNpdGEgbGEgZGVmaW5pY2lvbiBkZSBQUk9KRUMgNC4gCmBgYHtyfQpzcGF0aWFscmVmIDwtICIrcHJvaj10bWVyYyArbGF0XzA9NC41OTYyMDA0MTY2NjY2NjYgK2xvbl8wPS03NC4wNzc1MDc5MTY2NjY2NiAraz0xICt4XzA9MTAwMDAwMCAreV8wPTEwMDAwMDAgK2VsbHBzPUdSUzgwICt0b3dnczg0PTAsMCwwLDAsMCwwLDAgK3VuaXRzPW0gK25vX2RlZnMiIApgYGAKUG9kZW1vcyByZXByb3llY3RhcmxvcyBkYXRvcyBkZSBlbGV2YWNpb24gZGUgV0dTODQgZ2VvZ3JhcGhpYyBjb29yZGluYXRlcyBhIE1BR05BIENvbG9tYmlhIEJvZ290YSB6b25lLgoKYGBge3J9CgpwcjMgPC0gcHJvamVjdEV4dGVudChlbGV2X2Nyb3AsIHNwYXRpYWxyZWYpCgpyZXMocHIzKSA8LSAxMDAKCnJlcF9lbGV2IDwtIHByb2plY3RSYXN0ZXIoZWxldl9jcm9wLCBwcjMpCmBgYApMbyBxdWUgb2J0ZW5kcmlhbW9zIHNlcmE6CmBgYHtyfQpyZXBfZWxldgpgYGAKCkFob3JhIHNlIHJlcHJveWVjdGFyYSBlbCAgU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lICBkZSBsYSBjYXBpdGFsIGRlbCBDZXNhcjoKCmBgYHtyfQoocmVwX3ZhbGxlID0gc3BUcmFuc2Zvcm0odmFsbGUsc3BhdGlhbHJlZikpCmBgYAoKRXN0YSBlc2xhIGltYWdlbiBxdWUgb2J0ZW5lbW9zIGFsIHBsb3RlYXIKCmBgYHtyfQpwbG90KHJlcF9lbGV2LCBtYWluPSJSZXByb2plY3RlZCBEaWdpdGFsIGVsZXZhdGlvbiBtb2RlbCIpCnBsb3QocmVwX3ZhbGxlLCBhZGQ9VFJVRSkKYGBgCkNvbiBlbHNpZ3VpZW50ZSBjb2RpZ28gZ3VhcmRlbW9zIGVsIGNhbWJpby4KCmBgYHtyfQoKI3dyaXRlUmFzdGVyKHJlcF9lbGV2LCBjZXNhcjE9Ii4vZGVtL3JlcF92YWxsZV9lbGV2LnRpZiIsIGRhdGF0eXBlPSdJTlQ0UycsIG92ZXJ3cml0ZT1UUlVFKQpgYGAKCgoqKjYuIEVzdGFkw61zdGljYXMgYsOhc2ljYXMgZGUgZGF0b3MgZGUgZWxldmFjacOzbioqCgpIYXJlbW9zIHVuIGhpc3RvZ3JhbWEgZGUgbGFzIGVsZXZhY2lvbmVzICwgcGFyYSByZXZpc2FyIGFsZ28gZGUgZXN0YWRpc3RpY2EuCmBgYHtyfQojIGhpc3RvZ3JhbWEKaGlzdChyZXBfZWxldikKYGBgCgpgYGB7cn0KIyBwYXJhIGFyY2hpdm9zIGdyYW5kZXMgc2VwdWVkZW4gdHJhYmFqYXIgbG9zIHNpZ3VpZW50ZXMgY29kaWdvcyAKcHJvbWVkaW8gPC0gY2VsbFN0YXRzKHJlcF9lbGV2LCAnbWVhbicpCm1pbmltbyA8LSBjZWxsU3RhdHMocmVwX2VsZXYsICdtaW4nKQptYXhpbW8gPC0gY2VsbFN0YXRzKHJlcF9lbGV2LCAnbWF4JykKZGVzdmlhY2lvbiAgPC0gY2VsbFN0YXRzKHJlcF9lbGV2LCAnc2QnKQpgYGAKCmBgYHtyfQojIGNyZWF0ZSB1bmlkaW1lbnNpb25hbCB2ZWN0b3IKbWV0cmljYXMgPC0gYygnbWVhbicsICdtaW4nLCAnbWF4JywgJ3N0ZCcpCnZhbG9yZXMgPC0gYyhwcm9tZWRpbywgbWluaW1vLCBtYXhpbW8sIGRlc3ZpYWNpb24pCmBgYAoKCk9idGVuZHJlbW9zIGVzdG9zIGRhdG9zCmBgYHtyfQojIGNyZWFjaW9uIGRlIGRhdGEgZnJhbWUgY29uIGVzdGFkaXN0aWNhcyBkZSBlbGV2YWNpb24gW21ldHJvc10KIyBlbCBwYXLDqW50ZXNpcyBleHRlcm5vIHNpcnZlIHBhcmEgImltcHJpbWlyIiBlbCBjb250ZW5pZG8gZGUgdW4gb2JqZXRvCihkZl9lc3RhZGlzdGljYXMgPC0gZGF0YS5mcmFtZShtZXRyaWNhcywgdmFsb3JlcykpCmBgYAoKCioqNy4gRXN0YWTDrXN0aWNhcyBiw6FzaWNhcyBkZSBkYXRvcyBkZSBkaW1lbnNpb25lcyoqCgpBbnRlcyBkZSBpbnRyb2R1Y2lyc2UgZW4gZXN0ZSB0ZW1hIHNlIGRlYmVuIHRlbmVyIGNvbm9jaW1pZW50b3MgcHJldmlvcyBkZWwgdGVtYSBkZSBwZW5kaWVudGVzCmBgYHtyfQpzbG9wZSA9IHRlcnJhaW4ocmVwX2VsZXYsb3B0PSdzbG9wZScsIHVuaXQ9J2RlZ3JlZXMnKQphc3BlY3QgPSB0ZXJyYWluKHJlcF9lbGV2LG9wdD0nYXNwZWN0Jyx1bml0PSdkZWdyZWVzJykKaGlsbCA9IGhpbGxTaGFkZShzbG9wZSxhc3BlY3QsNDAsMzE1KQpgYGAKCmBgYHtyfQpwbG90KHJlcF9lbGV2LG1haW49IkRFTSBmb3IgVmFsbGVkdXBhciBbbWV0ZXJzXSIsIGNvbD10ZXJyYWluLmNvbG9ycygyNSxhbHBoYT0wLjcpKQpgYGAKCkVuIG51ZXN0cm9zIHBsb3RlcnMgLCBsYSBwZW5kaWVudGUgc2Ugb2JzZXJ2YSBwb3IgbG9zIGNhbWJpb3MgZGUgdG9ub3MgeSBkZSBjb2xvcmVzCmBgYHtyfQpwbG90KHNsb3BlLG1haW49IlNsb3BlIGZvciBWYWxsZWR1cGFyIFtkZWdyZWVzXSIsIGNvbD10b3BvLmNvbG9ycygyNSxhbHBoYT0wLjcpKQpgYGAKYGBge3J9CnBsb3QoYXNwZWN0LG1haW49IkFzcGVjdCBmb3IgVmFsbGVkdXBhciBbZGVncmVlc10iLCBjb2w9cmFpbmJvdygyNSxhbHBoYT0wLjcpKQpgYGAKCmBgYHtyfQoKcGxvdChoaWxsLAogICAgICAgIGNvbD1ncmV5KDE6MTAwLzEwMCksICAKICAgICAgICBsZWdlbmQ9RkFMU0UsICAgICAgICAgCiAgICAgICAgbWFpbj0iREVNIGZvciBWYWxsZWR1cGFyIiwKICAgICAgICBheGVzPUZBTFNFKSAgICAgICAgICAgCnBsb3QocmVwX2VsZXYsIAogICAgICAgIGF4ZXM9RkFMU0UsCiAgICAgICAgY29sPXRlcnJhaW4uY29sb3JzKDEyLCBhbHBoYT0wLjM1KSwgYWRkPVRSVUUpICMgY29sb3IgbWV0aG9kCmBgYAoKCioqOC4gTWFwZW8gZGUgZGF0b3MgZGUgZWxldmFjacOzbiBjb24gcmF5c2hhZGVyKioKCkxhIGJpYmxpb3RlY2EgcmF5c2hhZGVyIGVzIHVuIHBhcXVldGUgZGUgY8OzZGlnbyBhYmllcnRvIHBhcmEgcHJvZHVjaXIgdmlzdWFsaXphY2lvbmVzIGRlIGRhdG9zIDJEIHkgM0QgZW4gUi4gcmF5c2hhZGVyIHV0aWxpemEgZGF0b3MgZGUgZWxldmFjacOzbiBlbiB1bmEgbWF0cml6IGJhc2UgUiB5IHVuYSBjb21iaW5hY2nDs24gZGUgdHJhemFkbyBkZSByYXlvcywgbWFwZW8gZGUgdGV4dHVyYXMgZXNmw6lyaWNhcywgc3VwZXJwb3NpY2lvbmVzIHkgb2NsdXNpw7NuIGFtYmllbnRhbCBwYXJhIGdlbmVyYXIgaGVybW9zb3MgbWFwYXMgdG9wb2dyw6FmaWNvcyAyRCB5IDNEIC4gQWRlbcOhcyBkZSBsb3MgbWFwYXMsIHJheXNoYWRlciB0YW1iacOpbiBwZXJtaXRlIGFsIHVzdWFyaW8gdHJhZHVjaXIgb2JqZXRvcyBnZ3Bsb3QyIGVuIGhlcm1vc2FzIHZpc3VhbGl6YWNpb25lcyBkZSBkYXRvcyAzRC4KYGBge3J9CiNpbnN0YWxsLnBhY2thZ2VzKCJyYXlzaGFkZXIiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHJheXNoYWRlcikKYGBgCmBgYHtyfQplbG1hdCA9IHJhc3Rlcl90b19tYXRyaXgocmVwX2VsZXYpCmBgYApNYXBlYXIgdGV4dHVyYXMKCgpgYGB7cn0KCmVsbWF0ICU+JQogIHNwaGVyZV9zaGFkZSh0ZXh0dXJlID0gImltaG9mMiIpICU+JQogIHBsb3RfbWFwKCkKYGBgCkRldGVjdGFyIGFndWEKYGBge3J9CgplbG1hdCAlPiUKICBzcGhlcmVfc2hhZGUodGV4dHVyZSA9ICJkZXNlcnQiKSAlPiUKICBhZGRfd2F0ZXIoZGV0ZWN0X3dhdGVyKGVsbWF0KSwgY29sb3IgPSAiZGVzZXJ0IikgJT4lCiAgcGxvdF9tYXAoKQpgYGAKUG9kZW1vcyBhZ3JlZ2FyIHVuYSBjYXBhIGRlIHRyYXphZG8gZGUgcmF5b3MgZGVzZGUgZXNhIGRpcmVjY2nDs24gZGVsIHNvbDoKYGBge3J9CmVsbWF0ICU+JQogIHNwaGVyZV9zaGFkZSh0ZXh0dXJlID0gImRlc2VydCIpICU+JQogIGFkZF93YXRlcihkZXRlY3Rfd2F0ZXIoZWxtYXQpLCBjb2xvciA9ICJkZXNlcnQiKSAlPiUKICBhZGRfc2hhZG93KHJheV9zaGFkZShlbG1hdCksIDAuNSkgJT4lCiAgcGxvdF9tYXAoKQpgYGAKCioqOS4gT3RyYSBmb3JtYSBkZSB2aXN1YWxpemFjaW9uKioKYGBge3J9CiNpbnN0YWxsLnBhY2thZ2VzKCJqcGVnIikKYGBgCmBgYHtyfQpsaWJyYXJ5KGpwZWcpCmBgYApGdW5jacOzbiBkZSBzb21icmVhZG8gZGUgbWFwZW8gZGUgZW50b3JubyBlc2bDqXJpY28KYGBge3J9CgpnZXR2PWZ1bmN0aW9uKGksYSxzKXsKICBjdCA9IGRpbShpKVsxOjJdLzIKICBzeCA9IHZhbHVlcyhzKS85MCAqIGN0WzFdCiAgc3kgPSB2YWx1ZXMocykvOTAgKiBjdFsyXQogIGEgPSB2YWx1ZXMoYSkgKiAwLjAxNzQ1CiAgcHggPSBmbG9vcihjdFsxXSArIHN4ICogLXNpbihhKSkKICBweSA9IGZsb29yKGN0WzJdICsgc3kgKiBjb3MoYSkpCiAgCiAgCiAgdGVtcGxhdGUgPSBicmljayhzLHMscykKICB2YWx1ZXModGVtcGxhdGUpPU5BCiAgCiAgY2VsbHIgPSBweCArIHB5ICogY3RbMV0qMgogIGNlbGxnID0gcHggKyBweSAqIGN0WzFdKjIgKyAoY3RbMV0qMipjdFsyXSoyKQogIGNlbGxiID0gcHggKyBweSAqIGN0WzFdKjIgKyAyKihjdFsxXSoyKmN0WzJdKjIpCiAgCiAgdGVtcGxhdGVbWzFdXSA9IGlbY2VsbHJdCiAgdGVtcGxhdGVbWzJdXSA9IGlbY2VsbGddCiAgdGVtcGxhdGVbWzNdXSA9IGlbY2VsbGJdCiAgCiAgdGVtcGxhdGUgPSB0ZW1wbGF0ZSAqIDI1NgogIAogIHRlbXBsYXRlCn0KYGBgCiBBaG9yYSBjb24gdW5hIGltYWdlbiBqcGVnIAoKYGBge3J9CgptYXA9cmVhZEpQRUcoIi4vOXB2YkhqTi5qcGciKQoKCm91dCA9IGdldHYobWFwLCBhc3BlY3QsIHNsb3BlKQoKCnBsb3RSR0Iob3V0LCBtYWluID0gIkVsIFZhbGxlIikKYGBgCgoKCg==