1. Introducción

Este cuaderno se encuentra el anexo número dos del informe final correspondiente a la asignatura geomática básica.En este documento se pretende ilustrar el método de interpolación con polígonos de Thiessen para los datos de precipitación en el Departameno de Vichada

3. Preprocesamiento de datos

Los puntos correspondientes a los datos de precipitación fueron almacenados en un archivo geojson, por ende, es necesario leerlos.

(precip.points <- geojsonio::geojson_read("C:/Users/LUISA CARRION/Documents/chirps/ppoints.geojson11.geojson", what="sp"))
Registered S3 method overwritten by 'geojsonsf':
  method        from   
  print.geojson geojson
class       : SpatialPointsDataFrame 
features    : 677 
extent      : -77.475, -75.725, 3.124999, 5.024999  (xmin, xmax, ymin, ymax)
crs         : +proj=longlat +datum=WGS84 +no_defs 
variables   : 1
names       :           lluvia 
min values  : 17.5390129089355 
max values  : 182.807846069336 

Ahora, se lee el archivo shapefile correspondiente del departamento.

(valle <- shapefile("C:/Users/LUISA CARRION/Downloads/76_VALLE_DEL_CAUCA/ADMINISTRATIVO/MGN_MPIO_POLITICO.shp"))
class       : SpatialPolygonsDataFrame 
features    : 42 
extent      : -77.54977, -75.70724, 3.091239, 5.047394  (xmin, xmax, ymin, ymax)
crs         : +proj=longlat +datum=WGS84 +no_defs 
variables   : 9
names       : DPTO_CCDGO, MPIO_CCDGO, MPIO_CNMBR,                    MPIO_CRSLC,    MPIO_NAREA, MPIO_NANO,      DPTO_CNMBR,     Shape_Leng,       Shape_Area 
min values  :         76,      76001,    ALCALÁ,                          1536,   41.86090736,      2017, VALLE DEL CAUCA, 0.453826056161, 0.00340901158098 
max values  :         76,      76895,     ZARZAL, Ordenanza 9 de Diciembre 1954, 6292.50083741,      2017, VALLE DEL CAUCA,  6.59527269127,   0.510778519244 

Se convierte a un objeto sf empleando la función st_as_sf

valle_sf <-  sf::st_as_sf(valle)
(border_sf <- valle_sf %>%summarise(area = sum(MPIO_NAREA)))
Simple feature collection with 1 feature and 1 field
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -77.54977 ymin: 3.091239 xmax: -75.70724 ymax: 5.047394
Geodetic CRS:  WGS 84
     area                       geometry
1 20665.6 MULTIPOLYGON (((-77.2346 4....

Se convierte el objeto sf a un Dataframe Espacial.

 (border <- as(border_sf, 'Spatial')) 
class       : SpatialPolygonsDataFrame 
features    : 1 
extent      : -77.54977, -75.70724, 3.091239, 5.047394  (xmin, xmax, ymin, ymax)
crs         : +proj=longlat +datum=WGS84 +no_defs 
variables   : 1
names       :           area 
value       : 20665.59885853 

Al objeto creado y denominado VAlle_sf se le agregan las nuevas columnas “MUNIC” y “CODIGO”, que contienen la misma información que “MPIO_CNMBR” y “MPIO_CCDGO” respectivamente.

(valle.sf <- st_as_sf(valle) %>% mutate(MUNIC = MPIO_CNMBR, CODIGO = MPIO_CCDGO) %>% select(MUNIC, CODIGO))
Simple feature collection with 42 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -77.54977 ymin: 3.091239 xmax: -75.70724 ymax: 5.047394
Geodetic CRS:  WGS 84
First 10 features:
         MUNIC CODIGO                       geometry
0         CALI  76001 MULTIPOLYGON (((-76.59175 3...
1 ANDALUCÃ\u008dA  76036 MULTIPOLYGON (((-76.22406 4...
2 ANSERMANUEVO  76041 MULTIPOLYGON (((-76.01558 4...
3      ARGELIA  76054 MULTIPOLYGON (((-76.14316 4...
4         BUGA  76111 MULTIPOLYGON (((-76.31608 3...
5 BUGALAGRANDE  76113 MULTIPOLYGON (((-76.15131 4...
6   CAICEDONIA  76122 MULTIPOLYGON (((-75.8539 4....
7       CALIMA  76126 MULTIPOLYGON (((-76.51747 4...
8   CANDELARIA  76130 MULTIPOLYGON (((-76.30455 3...
9      CARTAGO  76147 MULTIPOLYGON (((-75.94518 4...

Los puntos de datos de precipitación se convierten en un objeto sf

p.sf <- st_as_sf(precip.points)

Empleando la función “st_intersect” se realiza una interseccióon de los poligonos con los puntos.

(precip.sf = st_intersection(valle.sf, p.sf))
Warning: attribute variables are assumed to be spatially constant throughout all geometries
Simple feature collection with 677 features and 3 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -77.475 ymin: 3.124999 xmax: -75.725 ymax: 5.024999
Geodetic CRS:  WGS 84
First 10 features:
            MUNIC CODIGO   lluvia                 geometry
10   EL Ã\u0081GUILA  76243 59.29984 POINT (-76.075 5.024999)
10.1 EL Ã\u0081GUILA  76243 65.32109 POINT (-76.075 4.974999)
10.2 EL Ã\u0081GUILA  76243 68.39664 POINT (-76.075 4.924999)
10.3 EL Ã\u0081GUILA  76243 65.94273 POINT (-76.025 4.924999)
10.4 EL Ã\u0081GUILA  76243 64.68098 POINT (-76.125 4.874999)
10.5 EL Ã\u0081GUILA  76243 66.65178 POINT (-76.075 4.874999)
10.6 EL Ã\u0081GUILA  76243 64.36810 POINT (-76.025 4.874999)
11       EL CAIRO  76246 71.65340 POINT (-76.175 4.824999)
2    ANSERMANUEVO  76041 58.66259 POINT (-76.125 4.824999)
2.1  ANSERMANUEVO  76041 63.25397 POINT (-76.075 4.824999)

Ahora, se hace una reproyección en ambos objetos.

p.sf.magna <- st_transform(precip.sf, crs=3116)
valle.sf.magna <- st_transform(valle.sf, crs=3116)

Se realiza una conversión en el objeto “p.sf.magma”

(precip2 <- as(p.sf.magna, 'Spatial'))
class       : SpatialPointsDataFrame 
features    : 677 
extent      : 622166.5, 817183.3, 837632.5, 1047756  (xmin, xmax, ymin, ymax)
crs         : +proj=tmerc +lat_0=4.59620041666667 +lon_0=-74.0775079166667 +k=1 +x_0=1000000 +y_0=1000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 
variables   : 3
names       :   MUNIC, CODIGO,           lluvia 
min values  : ALCALÁ,  76001, 17.5390129089355 
max values  :  ZARZAL,  76895, 182.807846069336 

Se guarda el conjunto de datos obtenido en la carpeta chirps con el fin de tener una copia que pueda ser usada después.

shapefile(precip2, filename='chirps/precip2.shp', overwrite=TRUE)

Ahora, se redondearán los valores de la variable “lluvia” y dichos resultados, se almacenaran en la columna “precipitación”

precip2$precipitacion <- round(precip2$lluvia, 1)
precip2
class       : SpatialPointsDataFrame 
features    : 677 
extent      : 622166.5, 817183.3, 837632.5, 1047756  (xmin, xmax, ymin, ymax)
crs         : +proj=tmerc +lat_0=4.59620041666667 +lon_0=-74.0775079166667 +k=1 +x_0=1000000 +y_0=1000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 
variables   : 4
names       :   MUNIC, CODIGO,           lluvia, precipitacion 
min values  : ALCALÁ,  76001, 17.5390129089355,          17.5 
max values  :  ZARZAL,  76895, 182.807846069336,         182.8 

Posteriormente, se realiza una conversión al objeto sf (“valle.sf.magma”) ,

(valle2 <- as(valle.sf.magna, 'Spatial'))
class       : SpatialPolygonsDataFrame 
features    : 42 
extent      : 613842.8, 819150.3, 833918.1, 1050235  (xmin, xmax, ymin, ymax)
crs         : +proj=tmerc +lat_0=4.59620041666667 +lon_0=-74.0775079166667 +k=1 +x_0=1000000 +y_0=1000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 
variables   : 2
names       :   MUNIC, CODIGO 
min values  : ALCALÁ,  76001 
max values  :  ZARZAL,  76895 
shapefile(valle2, filename='chirps/valle2.shp', overwrite=TRUE)

Con el fin de verificar que las dos extensiones coincidan, se reemplaza la extensión del limite de los puntos con la del objeto “valle 2”

precip2@bbox <- valle2@bbox

Finalmente, se grafican los datos obtenidos luego del preprocesamiento

tm_shape(valle2) + tm_polygons() +tm_shape(precip2) +tm_dots(col="precipitacion", palette = "Dark2", midpoint = 42, title="Muestras de precipitacion \n(mm)", size=0.08) + tm_legend(legend.outside=TRUE)
Warning in sp::proj4string(obj) :
  CRS object has comment, which is lost in output

4. Interpolación con polígonos de Thiessen

Se representa el patron de puntos bidimensionales como formato ppp para que se pueda usar la función dirichlet(). Esta función es la que permite crear la superficie teselada de los polígonos de Thiessen.

library(spatstat)
th  <-  as(dirichlet(as.ppp(precip2)), "SpatialPolygons")
crs(th) <- crs(precip2)
crs(valle2) <- crs(precip2)

Se emplea la función over() para unir los atributos del punto a la superficie mediante una unión espacial. Tambien es necesario recortar la superficie al tamaño del departamento.

th.z     <- over(th, precip2, fn=mean)
th.spdf  <-  SpatialPolygonsDataFrame(th, th.z)
th.clp   <- raster::intersect(valle2,th.spdf)

Se grafica la información obtenida.

tm_shape(th.clp) + tm_polygons(col="precipitacion", palette="Spectral", midpoint=43.0, title="Polígonos de Thiessen \n para precipitación \n(mm)") + tm_legend(legend.outside=TRUE,title.size=1.2, text.size= 0.8)
Warning in sp::proj4string(obj) :
  CRS object has comment, which is lost in output

5. Otra forma de interpolación con poígonos de Thiessen

Se toma aleatoramente la mitad de los datos del objeto “precip2” con la función “sample()”.

train_index <- sample(1:nrow(precip2), 0.7 * nrow(precip2))
test_index <- setdiff(1:nrow(precip2), train_index)
ptos_train <- precip2[train_index, ]
ptos_test  <- precip2[test_index,]
ptrain <- spTransform(ptos_train, crs(precip.mask))
ptest <- spTransform(ptos_test, crs(precip.mask))

Se crea un mapa que permite de la visualización de los 2 conjuntos de datos.

library(htmltools)
library(leaflet.extras)
lplot <- leaflet(data = precip2) %>% 
  addProviderTiles("CartoDB.Positron") %>% 
  addRasterImage(precip.mask, colors = pal, opacity = 0.6) %>%
  addCircleMarkers(data = ptrain, 
                   radius = 1,
                   fillOpacity = .7,
                   stroke = FALSE,
                   popup = ~htmlEscape(precipitacion),
                   color = pal(ptos_train$precipitacion), 
                   clusterOptions = markerClusterOptions(),
                   group = "Training") %>% 
  addCircleMarkers(data = ptest, # second group
                   radius = 10,
                   fillOpacity = .7,
                   stroke = FALSE,
                   popup = ~htmlEscape(precipitacion),
                   color = pal(ptos_test$precipitacion), 
                   clusterOptions = markerClusterOptions(),
                   group = "Test") %>% 
  addLegend(position = "bottomright",
            values = ~precipitacion,
            opacity = .7,
            pal = pal, 
            title = "Precipitacion") %>% 
  leaflet::addLayersControl(overlayGroups = c("Training", "Test"),
                   options = layersControlOptions(collapsed = FALSE)) %>% 
  addResetMapButton()
lplot

Se crea la superficie teselada.

thiessen  <-  as(dirichlet(as.ppp(ptos_train)), "SpatialPolygons")

Se añade la información de proyección.

crs(thiessen) <- crs(ptos_train)
crs(valle2) <- crs(ptos_train)

Se unen los atributos

thiessen.z     <- over(thiessen, ptos_train, fn=mean)
thiessen.spdf  <-  SpatialPolygonsDataFrame(thiessen, thiessen.z)

Se recorta la superficie al tamaño del departamento.

thiessen.clp   <- raster::intersect(valle2,thiessen.spdf)

Se grafican los polígonos de Thiessen del departamento del Valle del Cauca.

tm_shape(thiessen.clp) + tm_polygons(col="precipitacion", palette="RdYlGn" , midpoint=43.0, title="Poligonos de Thiessen \npara precipitación \n(mm)") + tm_legend(legend.outside=TRUE, title.size=1.2, text.size= 0.8)
Warning in sp::proj4string(obj) :
  CRS object has comment, which is lost in output

LS0tDQp0aXRsZTogIkFuZXhvIDI6IEludGVycG9sYWNpw7NuIHBvbGlnb25vcyBkZSBUaGllc3NlbiBwYXJhIGRhdG9zIGRlIHByZWNpcGl0YWNpw7NuIGVuIFZhbGxlIGRlbCBDYXVjYSINCmF1dGhvcjogTHVpc2EgRmVybmFuZGEgQ2FycmnDs24gUmFtw61yZXogeSBNaWd1ZWwgU2FudGlhZ28gTW9yYWxlcyBSdcOteg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMjIDEuIEludHJvZHVjY2nDs24NCkVzdGUgY3VhZGVybm8gc2UgZW5jdWVudHJhIGVsIGFuZXhvIG7Dum1lcm8gZG9zIGRlbCBpbmZvcm1lIGZpbmFsIGNvcnJlc3BvbmRpZW50ZSBhIGxhIGFzaWduYXR1cmEgZ2VvbcOhdGljYSBiw6FzaWNhLkVuIGVzdGUgZG9jdW1lbnRvIHNlIHByZXRlbmRlIGlsdXN0cmFyIGVsIG3DqXRvZG8gZGUgaW50ZXJwb2xhY2nDs24gY29uIHBvbMOtZ29ub3MgZGUgVGhpZXNzZW4gcGFyYSBsb3MgZGF0b3MgZGUgcHJlY2lwaXRhY2nDs24gZW4gZWwgRGVwYXJ0YW1lbm8gZGUgVmljaGFkYQ0KDQojIyMgMy4gUHJlcHJvY2VzYW1pZW50byBkZSBkYXRvcw0KDQpMb3MgcHVudG9zIGNvcnJlc3BvbmRpZW50ZXMgYSBsb3MgZGF0b3MgZGUgcHJlY2lwaXRhY2nDs24gZnVlcm9uIGFsbWFjZW5hZG9zIGVuIHVuIGFyY2hpdm8gZ2VvanNvbiwgcG9yIGVuZGUsIGVzIG5lY2VzYXJpbyBsZWVybG9zLg0KDQpgYGB7cn0NCihwcmVjaXAucG9pbnRzIDwtIGdlb2pzb25pbzo6Z2VvanNvbl9yZWFkKCJDOi9Vc2Vycy9MVUlTQSBDQVJSSU9OL0RvY3VtZW50cy9jaGlycHMvcHBvaW50cy5nZW9qc29uMTEuZ2VvanNvbiIsIHdoYXQ9InNwIikpDQpgYGANCkFob3JhLCBzZSBsZWUgZWwgYXJjaGl2byBzaGFwZWZpbGUgY29ycmVzcG9uZGllbnRlIGRlbCBkZXBhcnRhbWVudG8uDQoNCmBgYHtyfQ0KKHZhbGxlIDwtIHNoYXBlZmlsZSgiQzovVXNlcnMvTFVJU0EgQ0FSUklPTi9Eb3dubG9hZHMvNzZfVkFMTEVfREVMX0NBVUNBL0FETUlOSVNUUkFUSVZPL01HTl9NUElPX1BPTElUSUNPLnNocCIpKQ0KYGBgDQpTZSBjb252aWVydGUgYSB1biBvYmpldG8gc2YgZW1wbGVhbmRvIGxhIGZ1bmNpw7NuIHN0X2FzX3NmDQoNCmBgYHtyfQ0KdmFsbGVfc2YgPC0gIHNmOjpzdF9hc19zZih2YWxsZSkNCihib3JkZXJfc2YgPC0gdmFsbGVfc2YgJT4lc3VtbWFyaXNlKGFyZWEgPSBzdW0oTVBJT19OQVJFQSkpKQ0KYGBgDQoNClNlIGNvbnZpZXJ0ZSBlbCBvYmpldG8gc2YgYSB1biBEYXRhZnJhbWUgRXNwYWNpYWwuDQoNCmBgYHtyfQ0KIChib3JkZXIgPC0gYXMoYm9yZGVyX3NmLCAnU3BhdGlhbCcpKSANCmBgYA0KDQpBbCBvYmpldG8gY3JlYWRvIHkgZGVub21pbmFkbyBWQWxsZV9zZiBzZSBsZSBhZ3JlZ2FuIGxhcyBudWV2YXMgY29sdW1uYXMg4oCcTVVOSUPigJ0geSDigJxDT0RJR0/igJ0sIHF1ZSBjb250aWVuZW4gbGEgbWlzbWEgaW5mb3JtYWNpw7NuIHF1ZSDigJxNUElPX0NOTUJS4oCdIHkg4oCcTVBJT19DQ0RHT+KAnSByZXNwZWN0aXZhbWVudGUuDQoNCmBgYHtyfQ0KKHZhbGxlLnNmIDwtIHN0X2FzX3NmKHZhbGxlKSAlPiUgbXV0YXRlKE1VTklDID0gTVBJT19DTk1CUiwgQ09ESUdPID0gTVBJT19DQ0RHTykgJT4lIHNlbGVjdChNVU5JQywgQ09ESUdPKSkNCmBgYA0KDQpMb3MgcHVudG9zIGRlIGRhdG9zIGRlIHByZWNpcGl0YWNpw7NuIHNlIGNvbnZpZXJ0ZW4gZW4gdW4gb2JqZXRvIHNmDQoNCmBgYHtyfQ0KcC5zZiA8LSBzdF9hc19zZihwcmVjaXAucG9pbnRzKQ0KYGBgDQoNCkVtcGxlYW5kbyBsYSBmdW5jacOzbiAic3RfaW50ZXJzZWN0IiBzZSByZWFsaXphIHVuYSBpbnRlcnNlY2Npw7NvbiBkZSBsb3MgcG9saWdvbm9zIGNvbiBsb3MgcHVudG9zLiANCg0KYGBge3J9DQoocHJlY2lwLnNmID0gc3RfaW50ZXJzZWN0aW9uKHZhbGxlLnNmLCBwLnNmKSkNCmBgYA0KDQpBaG9yYSwgc2UgaGFjZSB1bmEgcmVwcm95ZWNjacOzbiBlbiBhbWJvcyBvYmpldG9zLg0KDQpgYGB7cn0NCnAuc2YubWFnbmEgPC0gc3RfdHJhbnNmb3JtKHByZWNpcC5zZiwgY3JzPTMxMTYpDQp2YWxsZS5zZi5tYWduYSA8LSBzdF90cmFuc2Zvcm0odmFsbGUuc2YsIGNycz0zMTE2KQ0KYGBgDQoNClNlIHJlYWxpemEgdW5hIGNvbnZlcnNpw7NuIGVuIGVsIG9iamV0byDigJxwLnNmLm1hZ21h4oCdDQoNCmBgYHtyfQ0KKHByZWNpcDIgPC0gYXMocC5zZi5tYWduYSwgJ1NwYXRpYWwnKSkNCmBgYA0KU2UgZ3VhcmRhIGVsIGNvbmp1bnRvIGRlIGRhdG9zIG9idGVuaWRvIGVuIGxhIGNhcnBldGEgY2hpcnBzIGNvbiBlbCBmaW4gZGUgdGVuZXIgdW5hIGNvcGlhIHF1ZSBwdWVkYSBzZXIgdXNhZGEgZGVzcHXDqXMuDQoNCmBgYHtyfQ0Kc2hhcGVmaWxlKHByZWNpcDIsIGZpbGVuYW1lPSdjaGlycHMvcHJlY2lwMi5zaHAnLCBvdmVyd3JpdGU9VFJVRSkNCmBgYA0KDQpBaG9yYSwgc2UgcmVkb25kZWFyw6FuIGxvcyB2YWxvcmVzIGRlIGxhIHZhcmlhYmxlICJsbHV2aWEiIHkgZGljaG9zIHJlc3VsdGFkb3MsIHNlIGFsbWFjZW5hcmFuIGVuIGxhIGNvbHVtbmEgInByZWNpcGl0YWNpw7NuIg0KDQpgYGB7cn0NCnByZWNpcDIkcHJlY2lwaXRhY2lvbiA8LSByb3VuZChwcmVjaXAyJGxsdXZpYSwgMSkNCnByZWNpcDINCmBgYA0KUG9zdGVyaW9ybWVudGUsIHNlIHJlYWxpemEgdW5hIGNvbnZlcnNpw7NuIGFsIG9iamV0byBzZiAo4oCcdmFsbGUuc2YubWFnbWHigJ0pICwNCg0KYGBge3J9DQoodmFsbGUyIDwtIGFzKHZhbGxlLnNmLm1hZ25hLCAnU3BhdGlhbCcpKQ0KYGBgDQoNCmBgYHtyfQ0Kc2hhcGVmaWxlKHZhbGxlMiwgZmlsZW5hbWU9J2NoaXJwcy92YWxsZTIuc2hwJywgb3ZlcndyaXRlPVRSVUUpDQpgYGANCg0KQ29uIGVsIGZpbiBkZSB2ZXJpZmljYXIgcXVlIGxhcyBkb3MgZXh0ZW5zaW9uZXMgY29pbmNpZGFuLCBzZSByZWVtcGxhemEgbGEgZXh0ZW5zacOzbiBkZWwgbGltaXRlIGRlIGxvcyBwdW50b3MgY29uIGxhIGRlbCBvYmpldG8g4oCcdmFsbGUgMuKAnQ0KDQoNCmBgYHtyfQ0KcHJlY2lwMkBiYm94IDwtIHZhbGxlMkBiYm94DQpgYGANCg0KRmluYWxtZW50ZSwgc2UgZ3JhZmljYW4gbG9zIGRhdG9zIG9idGVuaWRvcyBsdWVnbyBkZWwgcHJlcHJvY2VzYW1pZW50bw0KDQpgYGB7cn0NCnRtX3NoYXBlKHZhbGxlMikgKyB0bV9wb2x5Z29ucygpICt0bV9zaGFwZShwcmVjaXAyKSArdG1fZG90cyhjb2w9InByZWNpcGl0YWNpb24iLCBwYWxldHRlID0gIkRhcmsyIiwgbWlkcG9pbnQgPSA0MiwgdGl0bGU9Ik11ZXN0cmFzIGRlIHByZWNpcGl0YWNpb24gXG4obW0pIiwgc2l6ZT0wLjA4KSArIHRtX2xlZ2VuZChsZWdlbmQub3V0c2lkZT1UUlVFKQ0KYGBgDQoNCiMjIyA0LiBJbnRlcnBvbGFjacOzbiBjb24gcG9sw61nb25vcyBkZSBUaGllc3Nlbg0KDQpTZSByZXByZXNlbnRhIGVsIHBhdHJvbiBkZSBwdW50b3MgYmlkaW1lbnNpb25hbGVzIGNvbW8gZm9ybWF0byBwcHAgcGFyYSBxdWUgc2UgcHVlZGEgdXNhciBsYSBmdW5jacOzbiBkaXJpY2hsZXQoKS4gRXN0YSBmdW5jacOzbiBlcyBsYSBxdWUgcGVybWl0ZSBjcmVhciBsYSBzdXBlcmZpY2llIHRlc2VsYWRhIGRlIGxvcyBwb2zDrWdvbm9zIGRlIFRoaWVzc2VuLg0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHNwYXRzdGF0KQ0KdGggIDwtICBhcyhkaXJpY2hsZXQoYXMucHBwKHByZWNpcDIpKSwgIlNwYXRpYWxQb2x5Z29ucyIpDQpjcnModGgpIDwtIGNycyhwcmVjaXAyKQ0KY3JzKHZhbGxlMikgPC0gY3JzKHByZWNpcDIpDQpgYGANCg0KU2UgZW1wbGVhIGxhIGZ1bmNpw7NuIG92ZXIoKSBwYXJhIHVuaXIgbG9zIGF0cmlidXRvcyBkZWwgcHVudG8gYSBsYSBzdXBlcmZpY2llIG1lZGlhbnRlIHVuYSB1bmnDs24gZXNwYWNpYWwuIFRhbWJpZW4gZXMgbmVjZXNhcmlvIHJlY29ydGFyIGxhIHN1cGVyZmljaWUgYWwgdGFtYcOxbyBkZWwgZGVwYXJ0YW1lbnRvLg0KDQpgYGB7cn0NCnRoLnogICAgIDwtIG92ZXIodGgsIHByZWNpcDIsIGZuPW1lYW4pDQp0aC5zcGRmICA8LSAgU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lKHRoLCB0aC56KQ0KdGguY2xwICAgPC0gcmFzdGVyOjppbnRlcnNlY3QodmFsbGUyLHRoLnNwZGYpDQpgYGANCg0KU2UgZ3JhZmljYSBsYSBpbmZvcm1hY2nDs24gb2J0ZW5pZGEuDQoNCmBgYHtyfQ0KdG1fc2hhcGUodGguY2xwKSArIHRtX3BvbHlnb25zKGNvbD0icHJlY2lwaXRhY2lvbiIsIHBhbGV0dGU9IlNwZWN0cmFsIiwgbWlkcG9pbnQ9NDMuMCwgdGl0bGU9IlBvbMOtZ29ub3MgZGUgVGhpZXNzZW4gXG4gcGFyYSBwcmVjaXBpdGFjacOzbiBcbihtbSkiKSArIHRtX2xlZ2VuZChsZWdlbmQub3V0c2lkZT1UUlVFLHRpdGxlLnNpemU9MS4yLCB0ZXh0LnNpemU9IDAuOCkNCmBgYA0KIyMjIDUuIE90cmEgZm9ybWEgZGUgaW50ZXJwb2xhY2nDs24gY29uIHBvw61nb25vcyBkZSBUaGllc3Nlbg0KDQpTZSB0b21hIGFsZWF0b3JhbWVudGUgbGEgbWl0YWQgZGUgbG9zIGRhdG9zIGRlbCBvYmpldG8gInByZWNpcDIiIGNvbiBsYSBmdW5jacOzbiAic2FtcGxlKCkiLiANCg0KYGBge3J9DQp0cmFpbl9pbmRleCA8LSBzYW1wbGUoMTpucm93KHByZWNpcDIpLCAwLjcgKiBucm93KHByZWNpcDIpKQ0KdGVzdF9pbmRleCA8LSBzZXRkaWZmKDE6bnJvdyhwcmVjaXAyKSwgdHJhaW5faW5kZXgpDQpwdG9zX3RyYWluIDwtIHByZWNpcDJbdHJhaW5faW5kZXgsIF0NCnB0b3NfdGVzdCAgPC0gcHJlY2lwMlt0ZXN0X2luZGV4LF0NCmBgYA0KDQpgYGB7cn0NCnB0cmFpbiA8LSBzcFRyYW5zZm9ybShwdG9zX3RyYWluLCBjcnMocHJlY2lwLm1hc2spKQ0KcHRlc3QgPC0gc3BUcmFuc2Zvcm0ocHRvc190ZXN0LCBjcnMocHJlY2lwLm1hc2spKQ0KYGBgDQoNClNlIGNyZWEgdW4gbWFwYSBxdWUgcGVybWl0ZSBkZSBsYSB2aXN1YWxpemFjacOzbiBkZSBsb3MgMiBjb25qdW50b3MgZGUgZGF0b3MuDQoNCmBgYHtyfQ0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KGxlYWZsZXQuZXh0cmFzKQ0KYGBgDQoNCmBgYHtyfQ0KbHBsb3QgPC0gbGVhZmxldChkYXRhID0gcHJlY2lwMikgJT4lIA0KICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCLlBvc2l0cm9uIikgJT4lIA0KICBhZGRSYXN0ZXJJbWFnZShwcmVjaXAubWFzaywgY29sb3JzID0gcGFsLCBvcGFjaXR5ID0gMC42KSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gcHRyYWluLCANCiAgICAgICAgICAgICAgICAgICByYWRpdXMgPSAxLA0KICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjcsDQogICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+aHRtbEVzY2FwZShwcmVjaXBpdGFjaW9uKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHBhbChwdG9zX3RyYWluJHByZWNpcGl0YWNpb24pLCANCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyT3B0aW9ucyA9IG1hcmtlckNsdXN0ZXJPcHRpb25zKCksDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiVHJhaW5pbmciKSAlPiUgDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHB0ZXN0LCAjIHNlY29uZCBncm91cA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IDEwLA0KICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjcsDQogICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+aHRtbEVzY2FwZShwcmVjaXBpdGFjaW9uKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHBhbChwdG9zX3Rlc3QkcHJlY2lwaXRhY2lvbiksIA0KICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJUZXN0IikgJT4lIA0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLA0KICAgICAgICAgICAgdmFsdWVzID0gfnByZWNpcGl0YWNpb24sDQogICAgICAgICAgICBvcGFjaXR5ID0gLjcsDQogICAgICAgICAgICBwYWwgPSBwYWwsIA0KICAgICAgICAgICAgdGl0bGUgPSAiUHJlY2lwaXRhY2lvbiIpICU+JSANCiAgbGVhZmxldDo6YWRkTGF5ZXJzQ29udHJvbChvdmVybGF5R3JvdXBzID0gYygiVHJhaW5pbmciLCAiVGVzdCIpLA0KICAgICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkpICU+JSANCiAgYWRkUmVzZXRNYXBCdXR0b24oKQ0KYGBgDQoNCmBgYHtyfQ0KbHBsb3QNCmBgYA0KDQpTZSBjcmVhIGxhIHN1cGVyZmljaWUgdGVzZWxhZGEuDQoNCmBgYHtyfQ0KdGhpZXNzZW4gIDwtICBhcyhkaXJpY2hsZXQoYXMucHBwKHB0b3NfdHJhaW4pKSwgIlNwYXRpYWxQb2x5Z29ucyIpDQpgYGANCg0KU2UgYcOxYWRlIGxhIGluZm9ybWFjacOzbiBkZSBwcm95ZWNjacOzbi4NCg0KYGBge3J9DQpjcnModGhpZXNzZW4pIDwtIGNycyhwdG9zX3RyYWluKQ0KY3JzKHZhbGxlMikgPC0gY3JzKHB0b3NfdHJhaW4pDQpgYGANCg0KU2UgdW5lbiBsb3MgYXRyaWJ1dG9zDQoNCmBgYHtyfQ0KdGhpZXNzZW4ueiAgICAgPC0gb3Zlcih0aGllc3NlbiwgcHRvc190cmFpbiwgZm49bWVhbikNCnRoaWVzc2VuLnNwZGYgIDwtICBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUodGhpZXNzZW4sIHRoaWVzc2VuLnopDQpgYGANCg0KU2UgcmVjb3J0YSBsYSBzdXBlcmZpY2llIGFsIHRhbWHDsW8gZGVsIGRlcGFydGFtZW50by4NCg0KYGBge3J9DQp0aGllc3Nlbi5jbHAgICA8LSByYXN0ZXI6OmludGVyc2VjdCh2YWxsZTIsdGhpZXNzZW4uc3BkZikNCmBgYA0KDQpTZSBncmFmaWNhbiBsb3MgcG9sw61nb25vcyBkZSBUaGllc3NlbiBkZWwgZGVwYXJ0YW1lbnRvIGRlbCBWYWxsZSBkZWwgQ2F1Y2EuDQoNCmBgYHtyfQ0KdG1fc2hhcGUodGhpZXNzZW4uY2xwKSArIHRtX3BvbHlnb25zKGNvbD0icHJlY2lwaXRhY2lvbiIsIHBhbGV0dGU9IlJkWWxHbiIgLCBtaWRwb2ludD00My4wLCB0aXRsZT0iUG9saWdvbm9zIGRlIFRoaWVzc2VuIFxucGFyYSBwcmVjaXBpdGFjacOzbiBcbihtbSkiKSArIHRtX2xlZ2VuZChsZWdlbmQub3V0c2lkZT1UUlVFLCB0aXRsZS5zaXplPTEuMiwgdGV4dC5zaXplPSAwLjgpDQpgYGANCg0K