Objetivo

El objetivo de este trabajo es obtener, procesar y visuaizar el modelo digital de la elevación del departamento de Caquetá - Colombia.

Introducción a elevatr

Los datos de elevación juegan un papel importante aplicaciones como hidrografía, modelado geográfico, visualización, entre otros. En R, el paquete elevatr es útil ya que permite el acceso a los datos de elevación desde las API web.

library(rasterVis)
library(raster)
library(rgl)
library(rgdal)
library(elevatr)

Obtener datos de elevación raster.

Usando get_elev_raster () para acceder a los Terrain Tiles en AWS.

list.files("/cloud/project/Caquetá/ADMINISTRATIVO")
 [1] "MGN_DPTO_POLITICO.cpg"    
 [2] "MGN_DPTO_POLITICO.dbf"    
 [3] "MGN_DPTO_POLITICO.prj"    
 [4] "MGN_DPTO_POLITICO.sbn"    
 [5] "MGN_DPTO_POLITICO.sbx"    
 [6] "MGN_DPTO_POLITICO.shp"    
 [7] "MGN_DPTO_POLITICO.shp.xml"
 [8] "MGN_DPTO_POLITICO.shx"    
 [9] "MGN_MPIO_POLITICO.cpg"    
[10] "MGN_MPIO_POLITICO.dbf"    
[11] "MGN_MPIO_POLITICO.prj"    
[12] "MGN_MPIO_POLITICO.sbn"    
[13] "MGN_MPIO_POLITICO.sbx"    
[14] "MGN_MPIO_POLITICO.shp"    
[15] "MGN_MPIO_POLITICO.shp.xml"
[16] "MGN_MPIO_POLITICO.shx"    
(munic <-  shapefile("/cloud/project/Caquetá/ADMINISTRATIVO/MGN_MPIO_POLITICO.shp"))
class       : SpatialPolygonsDataFrame 
features    : 16 
extent      : -76.30622, -71.25385, -0.70584, 2.964148  (xmin, xmax, ymin, ymax)
crs         : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
variables   : 9
names       : DPTO_CCDGO, MPIO_CCDGO, MPIO_CNMBR,                           MPIO_CRSLC,    MPIO_NAREA, MPIO_NANO, DPTO_CNMBR,    Shape_Leng,      Shape_Area 
min values  :         18,      18001,    ALBANIA, Decreto 1678 de Septiembre 7 de 1967,  403.35827433,      2017,    CAQUETÁ,  1.1128288978, 0.0327393194813 
max values  :         18,      18860, VALPARAÍSO,  Ordenanza 3 de Noviembre 12 de 1985, 42312.7997376,      2017,    CAQUETÁ, 20.0458589874,   3.43579026269 

¿Cuáles son los atributos del objeto munich?

head(munic)

Seleccionemos solo la ciudad capital de este departamento.


medallo <- munic[munic$MPIO_CNMBR=="FLORENCIA",]
plot(medallo, main="Florencia", axes=TRUE)
plot(munic, add=TRUE)
invisible(text(coordinates(munic), labels=as.character(munic$MPIO_CNMBR), cex=0.8))

elevation <- get_elev_raster(medallo, z = 8)

Downloading DEMs [========>------------------]  33% eta:  1s
Downloading DEMs [=============>-------------]  50% eta:  1s
Downloading DEMs [=================>---------]  67% eta:  0s
Downloading DEMs [=====================>-----]  83% eta:  0s
Downloading DEMs [===========================] 100% eta:  0s
Merging DEMs
Reprojecting DEM to original projection
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -InfNote: Elevation units are in meters.
Note: The coordinate reference system is:
 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0

¿Qué hay dentro del objeto de elevación?

elevation
class      : RasterLayer 
dimensions : 1544, 1033, 1594952  (nrow, ncol, ncell)
resolution : 0.00275, 0.00275  (x, y)
extent     : -75.95125, -73.1105, -1.420879, 2.825121  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : -611.0625, 3796.604  (min, max)

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

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

writeRaster(elevation, filename="./dem/elev_z8.tif", datatype='INT4S', overwrite=TRUE)
elev_crop = crop(elevation, medallo)
plot(elev_crop, main="Cropped Digital elevation model")
plot(medallo, add=TRUE)

elev_crop
class      : RasterLayer 
dimensions : 318, 169, 53742  (nrow, ncol, ncell)
resolution : 0.00275, 0.00275  (x, y)
extent     : -75.8055, -75.34075, 1.320871, 2.195371  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : layer 
values     : 216.2132, 3375.029  (min, max)

Reproyectar los datos de elevación

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" 
pr3 <- projectExtent(elev_crop, spatialref)
res(pr3) <- 100
rep_elev <- projectRaster(elev_crop, pr3)
rep_elev
class      : RasterLayer 
dimensions : 968, 518, 501424  (nrow, ncol, ncell)
resolution : 100, 100  (x, y)
extent     : 807662.2, 859462.2, 637831.4, 734631.4  (xmin, xmax, ymin, ymax)
crs        : +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 
source     : memory
names      : layer 
values     : 215.1738, 3368.633  (min, max)
(rep_medallo = spTransform(medallo,spatialref))
class       : SpatialPolygonsDataFrame 
features    : 1 
extent      : 807669.7, 859391.2, 637973.3, 734552.1  (xmin, xmax, ymin, ymax)
crs         : +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 
variables   : 9
names       : DPTO_CCDGO, MPIO_CCDGO, MPIO_CNMBR,                      MPIO_CRSLC,    MPIO_NAREA, MPIO_NANO, DPTO_CNMBR,    Shape_Leng,     Shape_Area 
value       :         18,      18001,  FLORENCIA, Decreto 642 de Junio 17 de 1912, 2547.63842143,      2017,    CAQUETÁ, 2.94250780503, 0.206927769189 
plot(rep_elev, main="Reprojected Digital elevation model")
plot(rep_medallo, add=TRUE)

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

hist(rep_elev)

promedio <- cellStats(rep_elev, 'mean')
minimo <- cellStats(rep_elev, 'min')
maximo <- cellStats(rep_elev, 'max')
desviacion  <- cellStats(rep_elev, 'sd')
metricas <- c('mean', 'min', 'max', 'std')
valores <- c(promedio, minimo, maximo, desviacion)
(df_estadisticas <- data.frame(metricas, valores))

Obtención de variables geomorfométricas.

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 Caquetá [meters]", col=terrain.colors(25,alpha=0.7))

plot(slope,main="Slope for Caquetá [degrees]", col=topo.colors(25,alpha=0.7))

plot(aspect,main="Aspect for Caquetá [degrees]", col=rainbow(25,alpha=0.7))

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

Mapeo de datos de elevación con sombreador de rayos

install.packages("rayshader")
library(rayshader)
elmat = raster_to_matrix(rep_elev)
[1] "Dimensions of matrix are: 518x968."
elmat %>%
  sphere_shade(texture = "imhof2") %>%
  plot_map()

Calculating Surface Normal [---------------] ETA:  0s
Calculating Surface Normal [==============-] ETA:  0s
Calculating Surface Normal [===============] ETA:  0s
                                                     

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

Calculating Surface Normal [---------------] ETA:  0s
Calculating Surface Normal [===============] ETA:  0s
                                                     

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

Calculating Surface Normal [---------------] ETA:  0s
Calculating Surface Normal [===============] ETA:  0s
                                                     

Raytracing [-------------------------------] ETA:  0s
Raytracing [====---------------------------] ETA:  1s
Raytracing [=====--------------------------] ETA:  1s
Raytracing [======-------------------------] ETA:  1s
Raytracing [=======------------------------] ETA:  1s
Raytracing [========-----------------------] ETA:  1s
Raytracing [=========----------------------] ETA:  1s
Raytracing [==========---------------------] ETA:  1s
Raytracing [===========--------------------] ETA:  1s
Raytracing [============-------------------] ETA:  1s
Raytracing [=============------------------] ETA:  1s
Raytracing [==============-----------------] ETA:  1s
Raytracing [===============----------------] ETA:  1s
Raytracing [================---------------] ETA:  1s
Raytracing [=================--------------] ETA:  1s
Raytracing [==================-------------] ETA:  1s
Raytracing [===================------------] ETA:  1s
Raytracing [====================-----------] ETA:  1s
Raytracing [=====================----------] ETA:  1s
Raytracing [=====================----------] ETA:  0s
Raytracing [======================---------] ETA:  0s
Raytracing [=======================--------] ETA:  0s
Raytracing [========================-------] ETA:  0s
Raytracing [=========================------] ETA:  0s
Raytracing [==========================-----] ETA:  0s
Raytracing [===========================----] ETA:  0s
Raytracing [============================---] ETA:  0s
Raytracing [=============================--] ETA:  0s
Raytracing [==============================-] ETA:  0s
Raytracing [===============================] ETA:  0s
                                                     

Otra forma de visualización

install.packages("jpeg")
library(jpeg)
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
}
map=readJPEG("./9pvbHjN.jpg")

out = getv(map, aspect, slope)
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Infno non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
plotRGB(out, main = "Supposedly pretty mountains in Caquetá")

LS0tCnRpdGxlOiAiRWxldmFjacOzbiBDYXF1ZXTDoSIKYXV0aG9yOiAiU2FuZHJhIEthdHRlcnluZSBSb2Ryw61ndWV6IEh1cnRhZG8iCmRhdGU6ICIyOS4wMy4yMDIwIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMjIyMgT2JqZXRpdm8KCkVsIG9iamV0aXZvIGRlIGVzdGUgdHJhYmFqbyBlcyBvYnRlbmVyLCBwcm9jZXNhciB5IHZpc3VhaXphciBlbCBtb2RlbG8gZGlnaXRhbCBkZSBsYSBlbGV2YWNpw7NuIGRlbCBkZXBhcnRhbWVudG8gZGUgQ2FxdWV0w6EgLSBDb2xvbWJpYS4gCgojIyMjIEludHJvZHVjY2nDs24gYSBlbGV2YXRyCgpMb3MgZGF0b3MgZGUgZWxldmFjacOzbiBqdWVnYW4gdW4gcGFwZWwgaW1wb3J0YW50ZSBhcGxpY2FjaW9uZXMgY29tbyBoaWRyb2dyYWbDrWEsIG1vZGVsYWRvIGdlb2dyw6FmaWNvLCB2aXN1YWxpemFjacOzbiwgZW50cmUgb3Ryb3MuIEVuIFIsIGVsIHBhcXVldGUgZWxldmF0ciBlcyDDunRpbCB5YSBxdWUgcGVybWl0ZSBlbCBhY2Nlc28gYSBsb3MgZGF0b3MgZGUgZWxldmFjacOzbiBkZXNkZSBsYXMgQVBJIHdlYi4KCmBgYHtyfQpsaWJyYXJ5KHJhc3RlclZpcykKYGBgCmBgYHtyfQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShyZ2wpCmxpYnJhcnkocmdkYWwpCmBgYApgYGB7cn0KbGlicmFyeShlbGV2YXRyKQpgYGAKIyMjIyBPYnRlbmVyIGRhdG9zIGRlIGVsZXZhY2nDs24gcmFzdGVyLgpVc2FuZG8gZ2V0X2VsZXZfcmFzdGVyICgpIHBhcmEgYWNjZWRlciBhIGxvcyBUZXJyYWluIFRpbGVzIGVuIEFXUy4KCmBgYHtyfQpsaXN0LmZpbGVzKCIvY2xvdWQvcHJvamVjdC9DYXF1ZXTDoS9BRE1JTklTVFJBVElWTyIpCmBgYAoKYGBge3J9CihtdW5pYyA8LSAgc2hhcGVmaWxlKCIvY2xvdWQvcHJvamVjdC9DYXF1ZXTDoS9BRE1JTklTVFJBVElWTy9NR05fTVBJT19QT0xJVElDTy5zaHAiKSkKYGBgCiMjIyMgwr9DdcOhbGVzIHNvbiBsb3MgYXRyaWJ1dG9zIGRlbCBvYmpldG8gbXVuaWNoPwpgYGB7cn0KaGVhZChtdW5pYykKYGBgClNlbGVjY2lvbmVtb3Mgc29sbyBsYSBjaXVkYWQgY2FwaXRhbCBkZSBlc3RlIGRlcGFydGFtZW50by4KYGBge3J9CgptZWRhbGxvIDwtIG11bmljW211bmljJE1QSU9fQ05NQlI9PSJGTE9SRU5DSUEiLF0KcGxvdChtZWRhbGxvLCBtYWluPSJGbG9yZW5jaWEiLCBheGVzPVRSVUUpCnBsb3QobXVuaWMsIGFkZD1UUlVFKQppbnZpc2libGUodGV4dChjb29yZGluYXRlcyhtdW5pYyksIGxhYmVscz1hcy5jaGFyYWN0ZXIobXVuaWMkTVBJT19DTk1CUiksIGNleD0wLjgpKQpgYGAKYGBge3J9CmVsZXZhdGlvbiA8LSBnZXRfZWxldl9yYXN0ZXIobWVkYWxsbywgeiA9IDgpCmBgYAojIyMjIMK/UXXDqSBoYXkgZGVudHJvIGRlbCBvYmpldG8gZGUgZWxldmFjacOzbj8KYGBge3J9CmVsZXZhdGlvbgpgYGAKIyMjI1JlY29ydGUgbG9zIGRhdG9zIGRlIGVsZXZhY2nDs24gcGFyYSBxdWUgY29pbmNpZGFuIGNvbiBsYSBleHRlbnNpw7NuIGRlbCDDoXJlYSBkZSBlc3R1ZGlvLgpgYGB7cn0KcGxvdChlbGV2YXRpb24sIG1haW49IlRoaXMgdGhlIGRvd25sb2FkZWQgREVNIFttZXRlcnNdIikKcGxvdChtZWRhbGxvLCBhZGQ9VFJVRSkKYGBgCmBgYHtyfQp3cml0ZVJhc3RlcihlbGV2YXRpb24sIGZpbGVuYW1lPSIuL2RlbS9lbGV2X3o4LnRpZiIsIGRhdGF0eXBlPSdJTlQ0UycsIG92ZXJ3cml0ZT1UUlVFKQpgYGAKCgpgYGB7cn0KZWxldl9jcm9wID0gY3JvcChlbGV2YXRpb24sIG1lZGFsbG8pCnBsb3QoZWxldl9jcm9wLCBtYWluPSJDcm9wcGVkIERpZ2l0YWwgZWxldmF0aW9uIG1vZGVsIikKcGxvdChtZWRhbGxvLCBhZGQ9VFJVRSkKYGBgCmBgYHtyfQplbGV2X2Nyb3AKYGBgCiMjIyMgUmVwcm95ZWN0YXIgbG9zIGRhdG9zIGRlIGVsZXZhY2nDs24KYGBge3J9CnNwYXRpYWxyZWYgPC0gIitwcm9qPXRtZXJjICtsYXRfMD00LjU5NjIwMDQxNjY2NjY2NiArbG9uXzA9LTc0LjA3NzUwNzkxNjY2NjY2ICtrPTEgK3hfMD0xMDAwMDAwICt5XzA9MTAwMDAwMCArZWxscHM9R1JTODAgK3Rvd2dzODQ9MCwwLDAsMCwwLDAsMCArdW5pdHM9bSArbm9fZGVmcyIgCmBgYAoKYGBge3J9CnByMyA8LSBwcm9qZWN0RXh0ZW50KGVsZXZfY3JvcCwgc3BhdGlhbHJlZikKYGBgCgpgYGB7cn0KcmVzKHByMykgPC0gMTAwCmBgYApgYGB7cn0KcmVwX2VsZXYgPC0gcHJvamVjdFJhc3RlcihlbGV2X2Nyb3AsIHByMykKYGBgCgpgYGB7cn0KcmVwX2VsZXYKYGBgCgpgYGB7cn0KKHJlcF9tZWRhbGxvID0gc3BUcmFuc2Zvcm0obWVkYWxsbyxzcGF0aWFscmVmKSkKYGBgCgpgYGB7cn0KcGxvdChyZXBfZWxldiwgbWFpbj0iUmVwcm9qZWN0ZWQgRGlnaXRhbCBlbGV2YXRpb24gbW9kZWwiKQpwbG90KHJlcF9tZWRhbGxvLCBhZGQ9VFJVRSkKYGBgCiMjIyMgRXN0YWTDrXN0aWNhcyBiw6FzaWNhcyBkZSBkYXRvcyBkZSBlbGV2YWNpw7NuCmBgYHtyfQpoaXN0KHJlcF9lbGV2KQpgYGAKYGBge3J9CnByb21lZGlvIDwtIGNlbGxTdGF0cyhyZXBfZWxldiwgJ21lYW4nKQptaW5pbW8gPC0gY2VsbFN0YXRzKHJlcF9lbGV2LCAnbWluJykKbWF4aW1vIDwtIGNlbGxTdGF0cyhyZXBfZWxldiwgJ21heCcpCmRlc3ZpYWNpb24gIDwtIGNlbGxTdGF0cyhyZXBfZWxldiwgJ3NkJykKYGBgCgpgYGB7cn0KbWV0cmljYXMgPC0gYygnbWVhbicsICdtaW4nLCAnbWF4JywgJ3N0ZCcpCnZhbG9yZXMgPC0gYyhwcm9tZWRpbywgbWluaW1vLCBtYXhpbW8sIGRlc3ZpYWNpb24pCmBgYAoKYGBge3J9CihkZl9lc3RhZGlzdGljYXMgPC0gZGF0YS5mcmFtZShtZXRyaWNhcywgdmFsb3JlcykpCmBgYAojIyMjIE9idGVuY2nDs24gZGUgdmFyaWFibGVzIGdlb21vcmZvbcOpdHJpY2FzLgpgYGB7cn0Kc2xvcGUgPSB0ZXJyYWluKHJlcF9lbGV2LG9wdD0nc2xvcGUnLCB1bml0PSdkZWdyZWVzJykKYXNwZWN0ID0gdGVycmFpbihyZXBfZWxldixvcHQ9J2FzcGVjdCcsdW5pdD0nZGVncmVlcycpCmhpbGwgPSBoaWxsU2hhZGUoc2xvcGUsYXNwZWN0LDQwLDMxNSkKYGBgCgpgYGB7cn0KcGxvdChyZXBfZWxldixtYWluPSJERU0gZm9yIENhcXVldMOhIFttZXRlcnNdIiwgY29sPXRlcnJhaW4uY29sb3JzKDI1LGFscGhhPTAuNykpCmBgYApgYGB7cn0KcGxvdChzbG9wZSxtYWluPSJTbG9wZSBmb3IgQ2FxdWV0w6EgW2RlZ3JlZXNdIiwgY29sPXRvcG8uY29sb3JzKDI1LGFscGhhPTAuNykpCmBgYApgYGB7cn0KcGxvdChhc3BlY3QsbWFpbj0iQXNwZWN0IGZvciBDYXF1ZXTDoSBbZGVncmVlc10iLCBjb2w9cmFpbmJvdygyNSxhbHBoYT0wLjcpKQpgYGAKYGBge3J9CnBsb3QoaGlsbCwKICAgICAgICBjb2w9Z3JleSgxOjEwMC8xMDApLAogICAgICAgIGxlZ2VuZD1GQUxTRSwKICAgICAgICBtYWluPSJERU0gZm9yIENhcXVldMOhIiwKICAgICAgICBheGVzPUZBTFNFKSAgICAgICAgICAgCnBsb3QocmVwX2VsZXYsIAogICAgICAgIGF4ZXM9RkFMU0UsCiAgICAgICAgY29sPXRlcnJhaW4uY29sb3JzKDEyLCBhbHBoYT0wLjM1KSwgYWRkPVRSVUUpCmBgYAojIyMjIE1hcGVvIGRlIGRhdG9zIGRlIGVsZXZhY2nDs24gY29uIHNvbWJyZWFkb3IgZGUgcmF5b3MKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoInJheXNoYWRlciIpCmBgYApgYGB7cn0KbGlicmFyeShyYXlzaGFkZXIpCmBgYAoKYGBge3J9CmVsbWF0ID0gcmFzdGVyX3RvX21hdHJpeChyZXBfZWxldikKYGBgCmBgYHtyfQplbG1hdCAlPiUKICBzcGhlcmVfc2hhZGUodGV4dHVyZSA9ICJpbWhvZjIiKSAlPiUKICBwbG90X21hcCgpCmBgYApgYGB7cn0KZWxtYXQgJT4lCiAgc3BoZXJlX3NoYWRlKHRleHR1cmUgPSAiZGVzZXJ0IikgJT4lCiAgYWRkX3dhdGVyKGRldGVjdF93YXRlcihlbG1hdCksIGNvbG9yID0gImRlc2VydCIpICU+JQogIHBsb3RfbWFwKCkKCmBgYApgYGB7cn0KZWxtYXQgJT4lCiAgc3BoZXJlX3NoYWRlKHRleHR1cmUgPSAiZGVzZXJ0IikgJT4lCiAgYWRkX3dhdGVyKGRldGVjdF93YXRlcihlbG1hdCksIGNvbG9yID0gImRlc2VydCIpICU+JQogIGFkZF9zaGFkb3cocmF5X3NoYWRlKGVsbWF0KSwgMC41KSAlPiUKICBwbG90X21hcCgpCgpgYGAKIyMjIyBPdHJhIGZvcm1hIGRlIHZpc3VhbGl6YWNpw7NuCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJqcGVnIikKYGBgCgpgYGB7cn0KbGlicmFyeShqcGVnKQpgYGAKCmBgYHtyfQpnZXR2PWZ1bmN0aW9uKGksYSxzKXsKICBjdCA9IGRpbShpKVsxOjJdLzIKICBzeCA9IHZhbHVlcyhzKS85MCAqIGN0WzFdCiAgc3kgPSB2YWx1ZXMocykvOTAgKiBjdFsyXQogIGEgPSB2YWx1ZXMoYSkgKiAwLjAxNzQ1CiAgcHggPSBmbG9vcihjdFsxXSArIHN4ICogLXNpbihhKSkKICBweSA9IGZsb29yKGN0WzJdICsgc3kgKiBjb3MoYSkpCiAgCiAgCiAgdGVtcGxhdGUgPSBicmljayhzLHMscykKICB2YWx1ZXModGVtcGxhdGUpPU5BCiAgCiAgY2VsbHIgPSBweCArIHB5ICogY3RbMV0qMgogIGNlbGxnID0gcHggKyBweSAqIGN0WzFdKjIgKyAoY3RbMV0qMipjdFsyXSoyKQogIGNlbGxiID0gcHggKyBweSAqIGN0WzFdKjIgKyAyKihjdFsxXSoyKmN0WzJdKjIpCiAgCiAgdGVtcGxhdGVbWzFdXSA9IGlbY2VsbHJdCiAgdGVtcGxhdGVbWzJdXSA9IGlbY2VsbGddCiAgdGVtcGxhdGVbWzNdXSA9IGlbY2VsbGJdCiAgCiAgdGVtcGxhdGUgPSB0ZW1wbGF0ZSAqIDI1NgogIAogIHRlbXBsYXRlCn0KYGBgCgpgYGB7cn0KbWFwPXJlYWRKUEVHKCIuLzlwdmJIak4uanBnIikKCm91dCA9IGdldHYobWFwLCBhc3BlY3QsIHNsb3BlKQpwbG90UkdCKG91dCwgbWFpbiA9ICJTdXBwb3NlZGx5IHByZXR0eSBtb3VudGFpbnMgaW4gQ2FxdWV0w6EiKQpgYGAKCg==