Introducción

Las librerias stars, geostat y automap sirven como alternativa a la libreria geoR para realizar la interpolación espacial de datos puntuales.

Librerias

library(sf)
Linking to GEOS 3.9.1, GDAL 3.3.2, PROJ 7.2.1; sf_use_s2() is TRUE
library(stars)
Loading required package: abind
library(gstat)
library(automap)
library(leaflet)
library(leafem)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:raster’:

    intersect, select, union

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Lectura del shapefile del área de estudio

mun.tmp = st_read("C:\\Users\\ynata\\OneDrive\\Documentos\\GGB2022\\Datos\\DPTOCASANARE.shp")
Reading layer `DPTOCASANARE' from data source 
  `C:\Users\ynata\OneDrive\Documentos\GGB2022\Datos\DPTOCASANARE.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 19 features and 11 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -73.07777 ymin: 4.287476 xmax: -69.83591 ymax: 6.346111
Geodetic CRS:  WGS 84
mun.tmp %>% dplyr::select( MPIO_CCNCT, MPIO_CNMBR) -> casanare
rename(casanare, MPIO_CCDGO = MPIO_CCNCT)
Simple feature collection with 19 features and 2 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -73.07777 ymin: 4.287476 xmax: -69.83591 ymax: 6.346111
Geodetic CRS:  WGS 84
First 10 features:
   MPIO_CCDGO MPIO_CNMBR                       geometry
1       85001      YOPAL POLYGON ((-72.39513 5.56853...
2       85010    AGUAZUL POLYGON ((-72.56545 5.36972...
3       85015    CHÁMEZA POLYGON ((-72.81017 5.36659...
4       85136  LA SALINA POLYGON ((-72.33885 6.34470...
5       85139       MANÍ POLYGON ((-72.34155 5.06495...
6       85162  MONTERREY POLYGON ((-72.89989 5.03465...
7       85225    NUNCHÍA POLYGON ((-72.19558 5.71924...
8       85230     OROCUÉ POLYGON ((-71.50965 5.21730...
9       85263       PORE POLYGON ((-72.04587 5.83819...
10      85279    RECETOR POLYGON ((-72.80501 5.38092...

Lectura del raster de precipitación

archivo <- ("C:\\Users\\ynata\\OneDrive\\Documentos\\GGB2022\\R\\precip.tif")
(precip <- read_stars(archivo))
stars object with 2 dimensions and 1 attribute
attribute(s):
            Min. 1st Qu. Median     Mean 3rd Qu.  Max.
precip.tif   0.2     2.4    7.2 14.30432    15.7 161.1
dimension(s):
precip.mask <- st_crop(precip, casanare)
precip.mask 
stars object with 2 dimensions and 1 attribute
attribute(s):
            Min. 1st Qu. Median     Mean 3rd Qu.  Max. NA's
precip.tif   0.2     1.7    3.1 6.656184     6.1 122.2 1892
dimension(s):
m <- leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      precip.mask,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("red", "orange", "#fcc000","yellow", "cyan", "blue", "#3240cd"), 
                                  domain = 4:83)
    ) 
m

Vectorización de la capa de precipitación

precip.pts <- st_as_sf(precip.mask, as_points = TRUE, merge = FALSE, long = TRUE)
precip.pts
Simple feature collection with 2086 features and 1 field
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -73.0625 ymin: 4.270833 xmax: -69.85417 ymax: 6.354167
Geodetic CRS:  WGS 84
First 10 features:
   precip.tif                   geometry
1        26.8 POINT (-72.39583 6.270833)
2         0.5 POINT (-71.27083 6.270833)
3         0.5 POINT (-71.22917 6.270833)
4         0.5  POINT (-71.1875 6.270833)
5         1.1  POINT (-70.4375 6.270833)
6         1.0 POINT (-70.39583 6.270833)
7         0.9 POINT (-70.35417 6.270833)
8         1.3  POINT (-70.3125 6.270833)
9         1.1 POINT (-70.27083 6.270833)
10        1.0 POINT (-70.22917 6.270833)

Datos de entrenamiento y prueba

dt = sort(sample(2086, 2086*.7))
train<-precip.pts[dt,]
test<-precip.pts[-dt,]
train
Simple feature collection with 1460 features and 1 field
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -73.0625 ymin: 4.3125 xmax: -69.85417 ymax: 6.270833
Geodetic CRS:  WGS 84
First 10 features:
   precip.tif                   geometry
1        26.8 POINT (-72.39583 6.270833)
2         0.5 POINT (-71.27083 6.270833)
3         0.5 POINT (-71.22917 6.270833)
6         1.0 POINT (-70.39583 6.270833)
7         0.9 POINT (-70.35417 6.270833)
10        1.0 POINT (-70.22917 6.270833)
11        1.4  POINT (-70.1875 6.270833)
13       23.1 POINT (-72.35417 6.229167)
14        0.3  POINT (-71.4375 6.229167)
15        0.3 POINT (-71.39583 6.229167)

Visualización de lugares de muestreo

longit <- st_coordinates(train)[,1]
latit <- st_coordinates(train)[,2]
rain <- train$precip.tif
id <- seq(1,1460,1) 
(sitios <- data.frame(id, longit, latit, rain))
m <- leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      precip.mask,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("red", "orange", "#fcc000","yellow", "cyan", "blue", "#3240cd"), 
                                  domain = 4:83)
    ) %>%
  addMarkers(lng=sitios$longit,lat=sitios$latit, popup=sitios$rain, clusterOptions = markerClusterOptions())
m

Lectura del modelo digital de elevación

## file.choose()
(dem = read_stars("C:\\Users\\ynata\\OneDrive\\Documentos\\GGB2022\\Datos\\ELV_CASANARE.tif"))
stars object with 2 dimensions and 1 attribute
attribute(s):
                  Min. 1st Qu. Median     Mean 3rd Qu. Max.  NA's
ELV_CASANARE.tif    84     128    157 259.2479     197 4208 43383
dimension(s):
pal.dem <- colorNumeric(palette = c("forestgreen", "green", "yellow", "brown", "lightcyan"), domain = 84:4208 , na.color = "transparent")
m <- leaflet() %>%
  addTiles() %>%  
  leafem:::addGeoRaster(
      dem,
      opacity = 0.7,                
      colorOptions = colorOptions(palette = c("forestgreen", "green", "yellow",  "brown",         "lightcyan"), 
                                  domain = 84:4208)
    ) %>%
  addMarkers(lng=sitios$longit,lat=sitios$latit, popup=sitios$rain, clusterOptions = markerClusterOptions()) %>%
  addLegend("bottomright", pal = pal.dem, values= dem$ELV_CASANARE.tif,
    title = "Elevation")
m

Extracción de los valores de elavación en los sitios de entrenamiento

st_crs(dem) <- st_crs(train)
(train = st_join(train, st_as_sf(dem)))
Simple feature collection with 1460 features and 2 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -73.0625 ymin: 4.3125 xmax: -69.85417 ymax: 6.270833
Geodetic CRS:  WGS 84
First 10 features:
   precip.tif ELV_CASANARE.tif                   geometry
1        26.8             3965 POINT (-72.39583 6.270833)
2         0.5              152 POINT (-71.27083 6.270833)
3         0.5              153 POINT (-71.22917 6.270833)
6         1.0              105 POINT (-70.39583 6.270833)
7         0.9              104 POINT (-70.35417 6.270833)
10        1.0              101 POINT (-70.22917 6.270833)
11        1.4              102  POINT (-70.1875 6.270833)
13       23.1             2648 POINT (-72.35417 6.229167)
14        0.3              166  POINT (-71.4375 6.229167)
15        0.3              165 POINT (-71.39583 6.229167)
train = train[!is.na(train$ELV_CASANARE.tif), ]
names(train) <- c("precip", "elev", "geometry")
train
Simple feature collection with 1460 features and 2 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -73.0625 ymin: 4.3125 xmax: -69.85417 ymax: 6.270833
Geodetic CRS:  WGS 84
First 10 features:
   precip elev                   geometry
1    26.8 3965 POINT (-72.39583 6.270833)
2     0.5  152 POINT (-71.27083 6.270833)
3     0.5  153 POINT (-71.22917 6.270833)
6     1.0  105 POINT (-70.39583 6.270833)
7     0.9  104 POINT (-70.35417 6.270833)
10    1.0  101 POINT (-70.22917 6.270833)
11    1.4  102  POINT (-70.1875 6.270833)
13   23.1 2648 POINT (-72.35417 6.229167)
14    0.3  166  POINT (-71.4375 6.229167)
15    0.3  165 POINT (-71.39583 6.229167)

Interpolación espacial

Creación del objeto gstat

Para interpolar, primero tenemos que crear un objeto de la clase gstat, utilizando una función del mismo nombre: gstat.

Un objeto gstat contiene toda la información necesaria para realizar la interpolación espacial, a saber.

  • La definición del modelo
  • Los datos de calibración

Basándose en sus argumentos, la función gstat “entiende” qué tipo de modelo de interpolación queremos utilizar:

  • Sin modelo de variograma → IDW
  • Modelo de variograma, sin covariables → Kriging ordinario
  • Modelo de variograma, con covariables → Kriging universal

Vamos a utilizar tres parámetros de la función gstat:

  • fórmula: la “fórmula” de predicción que especifica las variables dependientes e independientes (también llamadas covariables)
  • datos: los datos de calibración (es decir, los datos del tren)
  • modelo: el modelo del variograma

IDW

g1 = gstat(formula = precip ~ 1, data = train)

Ahora que nuestro modelo de interpolación g1 está definido, podemos utilizar la función predecir para interpolar realmente, es decir, para estimar los valores de precipitación.

La función predecir acepta:

El raster sirve para dos propósitos:

Por ejemplo, la siguiente expresión interpola los valores de las precipitaciones según el modelo definido en g1 y el modelo de trama definido en dem:

 ## [inverse distance weighted interpolation]
z1 = predict(g1, dem)
z1
stars object with 2 dimensions and 2 attributes
attribute(s):
           Min.  1st Qu.   Median     Mean  3rd Qu.  Max.  NA's
var1.pred   0.3 2.375452 3.839834 6.464584 6.091785 118.3 43383
var1.var     NA       NA       NA      NaN       NA    NA 95448
dimension(s):

Podemos subconjuntar sólo el primer atributo y renombrarlo como “precipitación”:


names(z1) = "precipitation"
z1 = z1["precipitation",,]

En la siguiente figura se muestra el ráster de precipitaciones interpolado mediante IDW:

b = seq(1, 100, 1)
plot(z1, breaks = b, col = hcl.colors(length(b)-1, "Spectral"), reset = FALSE)
plot(st_geometry(train), pch = 3, add = TRUE)
contour(z1, breaks = b, add = TRUE)

Kriging ordinario Los métodos de Kriging requieren un modelo de variograma. El modelo de variograma es una forma objetiva de cuantificar el patrón de autocorrelación en los datos, y asignar pesos en consecuencia al hacer predicciones.

Como primer paso, podemos calcular y examinar el variograma empírico utilizando la función variograma.

La función requiere dos argumentos

Por ejemplo, la siguiente expresión calcula el variograma empírico, sin covariables:

v_emp_ok = variogram(precip ~ 1, data=train)
plot(v_emp_ok)

Hay varias formas de ajustar un modelo de variograma a un variograma empírico. Utilizaremos la más sencilla: el ajuste automático mediante la función autofitVariogram del paquete automap:

v_mod_ok = autofitVariogram(precip ~ 1, as(train, "Spatial"))

La función elige el tipo de modelo que mejor se ajusta, y también afina sus parámetros. Puede utilizar show.vgms() para mostrar los tipos de modelos de variograma.

Tenga en cuenta que la función autofitVariogram no funciona en objetos sf, por lo que convertimos el objeto en un SpatialPointsDataFrame (paquete sp).

El modelo ajustado puede representarse con plot:

plot(v_mod_ok)

El objeto resultante es en realidad una lista con varios componentes, incluyendo el variograma empírico y el modelo de variograma ajustado. El componente $var_model del objeto resultante contiene el modelo real:

v_mod_ok$var_model
NA

El modelo del variograma puede entonces pasarse a la función gstat, y podemos continuar con la interpolación Kriging ordinaria:

## [using ordinary kriging]
g2 = gstat(formula = precip ~ 1, model = v_mod_ok$var_model, data = train)
z2= predict(g2, dem)

Una vez más, vamos a subconjuntar el atributo de valores predichos y a renombrarlo:

names(z2) = "precip"

Las predicciones de Kriging ordinario se muestran en la siguiente figura:

b = seq(1, 100, 1)
plot(z2, breaks = b, col = hcl.colors(length(b)-1, "Spectral"), reset = FALSE)
plot(st_geometry(train), pch = 3, add = TRUE)
contour(z2, breaks = b, add = TRUE)

Universal Kriging

v_emp_uk = variogram(precip ~ elev, train)
train.sp = as(train, "Spatial")
v_mod_uk = autofitVariogram(precip ~ elev, train.sp)
plot(v_mod_uk)

plot(v_emp_ok, model = v_mod_ok$var_model, ylim = c(0, 150), main = "OK")

plot(v_emp_uk, model = v_mod_uk$var_model, ylim = c(0, 70), main = "UK")

g3 = gstat(formula = precip ~ elev, model = v_mod_uk$var_model, data = train.sp)
names(dem)
[1] "ELV_CASANARE.tif"
names(dem) <- "elev"
z3 = predict(g3, dem)
z3 = z3["var1.pred",,]
names(z3) = "precipitation"
b = seq(1, 100, 1)
plot(z3, breaks = b, col = hcl.colors(length(b)-1, "Spectral"), reset = FALSE)
plot(st_geometry(train), pch = 3, add = TRUE)
contour(z3, breaks = b, add = TRUE)

4. Evaluación cualitativa de los resultados

paleta <- colorNumeric(palette = c("coral2", "coral1","coral", "orange", "#fcc000","yellow", "cyan", "blue", "#3240cd"), domain = 10:100, na.color = "transparent")
colores <- colorOptions(palette = c("red", "orange", "#fcc000","yellow", "cyan", "blue", "#3240cd"), domain = 10:100, na.color = "transparent")
m <- leaflet() %>%
  addTiles() %>%  
  addGeoRaster(z1, opacity = 0.7, colorOptions = colores, group="IDW") %>%
  addGeoRaster(z2, colorOptions = colores, opacity = 0.7, group= "OK")  %>%
  addGeoRaster(z3, colorOptions = colores, opacity = 0.7, group= "UK")  %>%
  # Add layers controls
  addLayersControl(
    overlayGroups = c("UK", "OK", "IDW"),
    options = layersControlOptions(collapsed = FALSE)
  ) %>% 
  
    addLegend("bottomright", pal = paleta, values= z1$precipitation,
    title = "Precipitation (2019)"
  )
Warning in pal(c(r[1], cuts, r[2])) :
  Some values were outside the color scale and will be treated as NA
Warning in write_stars.stars(x, dsn = fl) :
  all but first attribute are ignored
m

5. Validación cruzada

bubble(as(cv1[, "residual"], "Spatial"))

sqrt(sum((cv1$var1.pred - cv1$observed)^2) / nrow(cv1))
[1] 5.564086
bubble(as(cv2[, "residual"], "Spatial"))

sqrt(sum((cv2$var1.pred - cv2$observed)^2) / nrow(cv2))
[1] 2.287359
bubble(as(cv3[, "residual"], "Spatial"))

sqrt(sum((cv3$var1.pred - cv3$observed)^2) / nrow(cv3))
[1] 2.661829
LS0tDQp0aXRsZTogIkludGVycG9sYWNpw7NuIGVzcGFjaWFsIGRlIGRhdG9zIGNsaW3DoXRpY29zICINCm91dHB1dDogIGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgSW50cm9kdWNjacOzbg0KDQpMYXMgbGlicmVyaWFzIHN0YXJzLCBnZW9zdGF0IHkgYXV0b21hcCBzaXJ2ZW4gY29tbyBhbHRlcm5hdGl2YSBhIGxhIGxpYnJlcmlhIGdlb1IgcGFyYSByZWFsaXphciBsYSBpbnRlcnBvbGFjacOzbiBlc3BhY2lhbCBkZSBkYXRvcyBwdW50dWFsZXMuDQoNCiMjIyBMaWJyZXJpYXMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHN0YXJzKQ0KbGlicmFyeShnc3RhdCkNCmxpYnJhcnkoYXV0b21hcCkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkobGVhZmVtKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQojIyMgTGVjdHVyYSBkZWwgc2hhcGVmaWxlIGRlbCDDoXJlYSBkZSBlc3R1ZGlvDQoNCmBgYHtyfQ0KbXVuLnRtcCA9IHN0X3JlYWQoIkM6XFxVc2Vyc1xceW5hdGFcXE9uZURyaXZlXFxEb2N1bWVudG9zXFxHR0IyMDIyXFxEYXRvc1xcRFBUT0NBU0FOQVJFLnNocCIpDQptdW4udG1wICU+JSBkcGx5cjo6c2VsZWN0KCBNUElPX0NDTkNULCBNUElPX0NOTUJSKSAtPiBjYXNhbmFyZQ0KcmVuYW1lKGNhc2FuYXJlLCBNUElPX0NDREdPID0gTVBJT19DQ05DVCkNCmBgYA0KDQojIyMgTGVjdHVyYSBkZWwgcmFzdGVyIGRlIHByZWNpcGl0YWNpw7NuDQoNCmBgYHtyfQ0KYXJjaGl2byA8LSAoIkM6XFxVc2Vyc1xceW5hdGFcXE9uZURyaXZlXFxEb2N1bWVudG9zXFxHR0IyMDIyXFxSXFxwcmVjaXAudGlmIikNCihwcmVjaXAgPC0gcmVhZF9zdGFycyhhcmNoaXZvKSkNCmBgYA0KDQpgYGB7cn0NCnByZWNpcC5tYXNrIDwtIHN0X2Nyb3AocHJlY2lwLCBjYXNhbmFyZSkNCmBgYA0KDQpgYGB7cn0NCnByZWNpcC5tYXNrIA0KYGBgDQoNCmBgYHtyfQ0KbSA8LSBsZWFmbGV0KCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lICANCiAgbGVhZmVtOjo6YWRkR2VvUmFzdGVyKA0KICAgICAgcHJlY2lwLm1hc2ssDQogICAgICBvcGFjaXR5ID0gMC43LCAgICAgICAgICAgICAgICANCiAgICAgIGNvbG9yT3B0aW9ucyA9IGNvbG9yT3B0aW9ucyhwYWxldHRlID0gYygicmVkIiwgIm9yYW5nZSIsICIjZmNjMDAwIiwieWVsbG93IiwgImN5YW4iLCAiYmx1ZSIsICIjMzI0MGNkIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbiA9IDQ6ODMpDQogICAgKSANCm0NCmBgYA0KDQojIyMgVmVjdG9yaXphY2nDs24gZGUgbGEgY2FwYSBkZSBwcmVjaXBpdGFjacOzbg0KDQpgYGB7cn0NCnByZWNpcC5wdHMgPC0gc3RfYXNfc2YocHJlY2lwLm1hc2ssIGFzX3BvaW50cyA9IFRSVUUsIG1lcmdlID0gRkFMU0UsIGxvbmcgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KcHJlY2lwLnB0cw0KYGBgDQoNCiMjIyBEYXRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQoNCmBgYHtyfQ0KZHQgPSBzb3J0KHNhbXBsZSgyMDg2LCAyMDg2Ki43KSkNCnRyYWluPC1wcmVjaXAucHRzW2R0LF0NCnRlc3Q8LXByZWNpcC5wdHNbLWR0LF0NCmBgYA0KDQpgYGB7cn0NCnRyYWluDQpgYGANCg0KIyMjIFZpc3VhbGl6YWNpw7NuIGRlIGx1Z2FyZXMgZGUgbXVlc3RyZW8NCg0KYGBge3J9DQpsb25naXQgPC0gc3RfY29vcmRpbmF0ZXModHJhaW4pWywxXQ0KbGF0aXQgPC0gc3RfY29vcmRpbmF0ZXModHJhaW4pWywyXQ0KcmFpbiA8LSB0cmFpbiRwcmVjaXAudGlmDQpgYGANCg0KYGBge3J9DQppZCA8LSBzZXEoMSwxNDYwLDEpIA0KYGBgDQoNCmBgYHtyfQ0KKHNpdGlvcyA8LSBkYXRhLmZyYW1lKGlkLCBsb25naXQsIGxhdGl0LCByYWluKSkNCmBgYA0KDQpgYGB7cn0NCm0gPC0gbGVhZmxldCgpICU+JQ0KICBhZGRUaWxlcygpICU+JSAgDQogIGxlYWZlbTo6OmFkZEdlb1Jhc3RlcigNCiAgICAgIHByZWNpcC5tYXNrLA0KICAgICAgb3BhY2l0eSA9IDAuNywgICAgICAgICAgICAgICAgDQogICAgICBjb2xvck9wdGlvbnMgPSBjb2xvck9wdGlvbnMocGFsZXR0ZSA9IGMoInJlZCIsICJvcmFuZ2UiLCAiI2ZjYzAwMCIsInllbGxvdyIsICJjeWFuIiwgImJsdWUiLCAiIzMyNDBjZCIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSA0OjgzKQ0KICAgICkgJT4lDQogIGFkZE1hcmtlcnMobG5nPXNpdGlvcyRsb25naXQsbGF0PXNpdGlvcyRsYXRpdCwgcG9wdXA9c2l0aW9zJHJhaW4sIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkNCm0NCmBgYA0KDQojIyMgTGVjdHVyYSBkZWwgbW9kZWxvIGRpZ2l0YWwgZGUgZWxldmFjacOzbg0KDQpgYGB7cn0NCiMjIGZpbGUuY2hvb3NlKCkNCmBgYA0KDQpgYGB7cn0NCihkZW0gPSByZWFkX3N0YXJzKCJDOlxcVXNlcnNcXHluYXRhXFxPbmVEcml2ZVxcRG9jdW1lbnRvc1xcR0dCMjAyMlxcRGF0b3NcXEVMVl9DQVNBTkFSRS50aWYiKSkNCmBgYA0KDQpgYGB7cn0NCnBhbC5kZW0gPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSBjKCJmb3Jlc3RncmVlbiIsICJncmVlbiIsICJ5ZWxsb3ciLCAiYnJvd24iLCAibGlnaHRjeWFuIiksIGRvbWFpbiA9IDg0OjQyMDggLCBuYS5jb2xvciA9ICJ0cmFuc3BhcmVudCIpDQpgYGANCg0KYGBge3J9DQptIDwtIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUgIA0KICBsZWFmZW06OjphZGRHZW9SYXN0ZXIoDQogICAgICBkZW0sDQogICAgICBvcGFjaXR5ID0gMC43LCAgICAgICAgICAgICAgICANCiAgICAgIGNvbG9yT3B0aW9ucyA9IGNvbG9yT3B0aW9ucyhwYWxldHRlID0gYygiZm9yZXN0Z3JlZW4iLCAiZ3JlZW4iLCAieWVsbG93IiwgICJicm93biIsICAgICAgICAgImxpZ2h0Y3lhbiIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSA4NDo0MjA4KQ0KICAgICkgJT4lDQogIGFkZE1hcmtlcnMobG5nPXNpdGlvcyRsb25naXQsbGF0PXNpdGlvcyRsYXRpdCwgcG9wdXA9c2l0aW9zJHJhaW4sIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSBwYWwuZGVtLCB2YWx1ZXM9IGRlbSRFTFZfQ0FTQU5BUkUudGlmLA0KICAgIHRpdGxlID0gIkVsZXZhdGlvbiIpDQpgYGANCg0KYGBge3J9DQptDQpgYGANCg0KIyMjIEV4dHJhY2Npw7NuIGRlIGxvcyB2YWxvcmVzIGRlIGVsYXZhY2nDs24gZW4gbG9zIHNpdGlvcyBkZSBlbnRyZW5hbWllbnRvDQoNCmBgYHtyfQ0Kc3RfY3JzKGRlbSkgPC0gc3RfY3JzKHRyYWluKQ0KYGBgDQoNCmBgYHtyfQ0KKHRyYWluID0gc3Rfam9pbih0cmFpbiwgc3RfYXNfc2YoZGVtKSkpDQpgYGANCg0KYGBge3J9DQp0cmFpbiA9IHRyYWluWyFpcy5uYSh0cmFpbiRFTFZfQ0FTQU5BUkUudGlmKSwgXQ0KDQpgYGANCg0KYGBge3J9DQpuYW1lcyh0cmFpbikgPC0gYygicHJlY2lwIiwgImVsZXYiLCAiZ2VvbWV0cnkiKQ0KYGBgDQoNCmBgYHtyfQ0KdHJhaW4NCmBgYA0KDQojIyMgSW50ZXJwb2xhY2nDs24gZXNwYWNpYWwNCg0KIyMjIyBDcmVhY2nDs24gZGVsIG9iamV0byBnc3RhdA0KDQpQYXJhIGludGVycG9sYXIsIHByaW1lcm8gdGVuZW1vcyBxdWUgY3JlYXIgdW4gb2JqZXRvIGRlIGxhIGNsYXNlIGdzdGF0LCB1dGlsaXphbmRvIHVuYSBmdW5jacOzbiBkZWwgbWlzbW8gbm9tYnJlOiBnc3RhdC4NCg0KVW4gb2JqZXRvIGdzdGF0IGNvbnRpZW5lIHRvZGEgbGEgaW5mb3JtYWNpw7NuIG5lY2VzYXJpYSBwYXJhIHJlYWxpemFyIGxhIGludGVycG9sYWNpw7NuIGVzcGFjaWFsLCBhIHNhYmVyLg0KDQotICAgTGEgZGVmaW5pY2nDs24gZGVsIG1vZGVsbw0KLSAgIExvcyBkYXRvcyBkZSBjYWxpYnJhY2nDs24NCg0KQmFzw6FuZG9zZSBlbiBzdXMgYXJndW1lbnRvcywgbGEgZnVuY2nDs24gZ3N0YXQgImVudGllbmRlIiBxdcOpIHRpcG8gZGUgbW9kZWxvIGRlIGludGVycG9sYWNpw7NuIHF1ZXJlbW9zIHV0aWxpemFyOg0KDQotICAgU2luIG1vZGVsbyBkZSB2YXJpb2dyYW1hIOKGkiBJRFcNCi0gICBNb2RlbG8gZGUgdmFyaW9ncmFtYSwgc2luIGNvdmFyaWFibGVzIOKGkiBLcmlnaW5nIG9yZGluYXJpbw0KLSAgIE1vZGVsbyBkZSB2YXJpb2dyYW1hLCBjb24gY292YXJpYWJsZXMg4oaSIEtyaWdpbmcgdW5pdmVyc2FsDQoNClZhbW9zIGEgdXRpbGl6YXIgdHJlcyBwYXLDoW1ldHJvcyBkZSBsYSBmdW5jacOzbiBnc3RhdDoNCg0KLSAgIGbDs3JtdWxhOiBsYSAiZsOzcm11bGEiIGRlIHByZWRpY2Npw7NuIHF1ZSBlc3BlY2lmaWNhIGxhcyB2YXJpYWJsZXMgZGVwZW5kaWVudGVzIGUgaW5kZXBlbmRpZW50ZXMgKHRhbWJpw6luIGxsYW1hZGFzIGNvdmFyaWFibGVzKQ0KLSAgIGRhdG9zOiBsb3MgZGF0b3MgZGUgY2FsaWJyYWNpw7NuIChlcyBkZWNpciwgbG9zIGRhdG9zIGRlbCB0cmVuKQ0KLSAgIG1vZGVsbzogZWwgbW9kZWxvIGRlbCB2YXJpb2dyYW1hDQoNCiMjIyBJRFcNCg0KYGBge3J9DQpnMSA9IGdzdGF0KGZvcm11bGEgPSBwcmVjaXAgfiAxLCBkYXRhID0gdHJhaW4pDQoNCmBgYA0KDQpBaG9yYSBxdWUgbnVlc3RybyBtb2RlbG8gZGUgaW50ZXJwb2xhY2nDs24gZzEgZXN0w6EgZGVmaW5pZG8sIHBvZGVtb3MgdXRpbGl6YXIgbGEgZnVuY2nDs24gcHJlZGVjaXIgcGFyYSBpbnRlcnBvbGFyIHJlYWxtZW50ZSwgZXMgZGVjaXIsIHBhcmEgZXN0aW1hciBsb3MgdmFsb3JlcyBkZSBwcmVjaXBpdGFjacOzbi4NCg0KTGEgZnVuY2nDs24gcHJlZGVjaXIgYWNlcHRhOg0KDQotICAgVW4gb2JqZXRvIHJhc3Rlci1zdGFycywgY29tbyBkZW0NCi0gICBVbiBvYmpldG8gbW9kZWwtZ3N0YXQsIGNvbW8gZzENCg0KRWwgcmFzdGVyIHNpcnZlIHBhcmEgZG9zIHByb3DDs3NpdG9zOg0KDQotICAgRXNwZWNpZmljYXIgbGFzIHViaWNhY2lvbmVzIGVuIGxhcyBxdWUgcXVlcmVtb3MgaGFjZXIgcHJlZGljY2lvbmVzIChlbiB0b2RvcyBsb3MgbcOpdG9kb3MpDQotICAgRXNwZWNpZmljYXIgbG9zIHZhbG9yZXMgZGUgbGFzIGNvdmFyaWFibGVzIChzw7NsbyBlbiBLcmlnaW5nIFVuaXZlcnNhbCkNCg0KUG9yIGVqZW1wbG8sIGxhIHNpZ3VpZW50ZSBleHByZXNpw7NuIGludGVycG9sYSBsb3MgdmFsb3JlcyBkZSBsYXMgcHJlY2lwaXRhY2lvbmVzIHNlZ8O6biBlbCBtb2RlbG8gZGVmaW5pZG8gZW4gZzEgeSBlbCBtb2RlbG8gZGUgdHJhbWEgZGVmaW5pZG8gZW4gZGVtOg0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJ30NCiAjIyBbaW52ZXJzZSBkaXN0YW5jZSB3ZWlnaHRlZCBpbnRlcnBvbGF0aW9uXQ0KejEgPSBwcmVkaWN0KGcxLCBkZW0pDQpgYGANCg0KYGBge3J9DQp6MQ0KYGBgDQoNClBvZGVtb3Mgc3ViY29uanVudGFyIHPDs2xvIGVsIHByaW1lciBhdHJpYnV0byB5IHJlbm9tYnJhcmxvIGNvbW8gInByZWNpcGl0YWNpw7NuIjoNCg0KYGBge3J9DQoNCm5hbWVzKHoxKSA9ICJwcmVjaXBpdGF0aW9uIg0KejEgPSB6MVsicHJlY2lwaXRhdGlvbiIsLF0NCmBgYA0KDQpFbiBsYSBzaWd1aWVudGUgZmlndXJhIHNlIG11ZXN0cmEgZWwgcsOhc3RlciBkZSBwcmVjaXBpdGFjaW9uZXMgaW50ZXJwb2xhZG8gbWVkaWFudGUgSURXOg0KDQpgYGB7cn0NCmIgPSBzZXEoMSwgMTAwLCAxKQ0KcGxvdCh6MSwgYnJlYWtzID0gYiwgY29sID0gaGNsLmNvbG9ycyhsZW5ndGgoYiktMSwgIlNwZWN0cmFsIiksIHJlc2V0ID0gRkFMU0UpDQpwbG90KHN0X2dlb21ldHJ5KHRyYWluKSwgcGNoID0gMywgYWRkID0gVFJVRSkNCmNvbnRvdXIoejEsIGJyZWFrcyA9IGIsIGFkZCA9IFRSVUUpDQpgYGANCg0KS3JpZ2luZyBvcmRpbmFyaW8gTG9zIG3DqXRvZG9zIGRlIEtyaWdpbmcgcmVxdWllcmVuIHVuIG1vZGVsbyBkZSB2YXJpb2dyYW1hLiBFbCBtb2RlbG8gZGUgdmFyaW9ncmFtYSBlcyB1bmEgZm9ybWEgb2JqZXRpdmEgZGUgY3VhbnRpZmljYXIgZWwgcGF0csOzbiBkZSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyBkYXRvcywgeSBhc2lnbmFyIHBlc29zIGVuIGNvbnNlY3VlbmNpYSBhbCBoYWNlciBwcmVkaWNjaW9uZXMuDQoNCkNvbW8gcHJpbWVyIHBhc28sIHBvZGVtb3MgY2FsY3VsYXIgeSBleGFtaW5hciBlbCB2YXJpb2dyYW1hIGVtcMOtcmljbyB1dGlsaXphbmRvIGxhIGZ1bmNpw7NuIHZhcmlvZ3JhbWEuDQoNCkxhIGZ1bmNpw7NuIHJlcXVpZXJlIGRvcyBhcmd1bWVudG9zDQoNCi0gICBGw7NybXVsYTogZXNwZWNpZmljYSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSB5IGxhcyBjb3ZhcmlhYmxlcywgYWwgaWd1YWwgcXVlIGVuIGdzdGF0DQotICAgRGF0b3M6IGxhIGNhcGEgZGUgcHVudG9zIGNvbiBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSB5IGxhcyBjb3ZhcmlhYmxlcyBjb21vIGF0cmlidXRvcyBkZSBsb3MgcHVudG9zDQoNClBvciBlamVtcGxvLCBsYSBzaWd1aWVudGUgZXhwcmVzacOzbiBjYWxjdWxhIGVsIHZhcmlvZ3JhbWEgZW1ww61yaWNvLCBzaW4gY292YXJpYWJsZXM6DQoNCmBgYHtyfQ0Kdl9lbXBfb2sgPSB2YXJpb2dyYW0ocHJlY2lwIH4gMSwgZGF0YT10cmFpbikNCnBsb3Qodl9lbXBfb2spDQpgYGANCg0KSGF5IHZhcmlhcyBmb3JtYXMgZGUgYWp1c3RhciB1biBtb2RlbG8gZGUgdmFyaW9ncmFtYSBhIHVuIHZhcmlvZ3JhbWEgZW1ww61yaWNvLiBVdGlsaXphcmVtb3MgbGEgbcOhcyBzZW5jaWxsYTogZWwgYWp1c3RlIGF1dG9tw6F0aWNvIG1lZGlhbnRlIGxhIGZ1bmNpw7NuIGF1dG9maXRWYXJpb2dyYW0gZGVsIHBhcXVldGUgYXV0b21hcDoNCg0KYGBge3J9DQp2X21vZF9vayA9IGF1dG9maXRWYXJpb2dyYW0ocHJlY2lwIH4gMSwgYXModHJhaW4sICJTcGF0aWFsIikpDQpgYGANCg0KTGEgZnVuY2nDs24gZWxpZ2UgZWwgdGlwbyBkZSBtb2RlbG8gcXVlIG1lam9yIHNlIGFqdXN0YSwgeSB0YW1iacOpbiBhZmluYSBzdXMgcGFyw6FtZXRyb3MuIFB1ZWRlIHV0aWxpemFyIHNob3cudmdtcygpIHBhcmEgbW9zdHJhciBsb3MgdGlwb3MgZGUgbW9kZWxvcyBkZSB2YXJpb2dyYW1hLg0KDQpUZW5nYSBlbiBjdWVudGEgcXVlIGxhIGZ1bmNpw7NuIGF1dG9maXRWYXJpb2dyYW0gbm8gZnVuY2lvbmEgZW4gb2JqZXRvcyBzZiwgcG9yIGxvIHF1ZSBjb252ZXJ0aW1vcyBlbCBvYmpldG8gZW4gdW4gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSAocGFxdWV0ZSBzcCkuDQoNCkVsIG1vZGVsbyBhanVzdGFkbyBwdWVkZSByZXByZXNlbnRhcnNlIGNvbiBwbG90Og0KDQpgYGB7cn0NCnBsb3Qodl9tb2Rfb2spDQpgYGANCg0KRWwgb2JqZXRvIHJlc3VsdGFudGUgZXMgZW4gcmVhbGlkYWQgdW5hIGxpc3RhIGNvbiB2YXJpb3MgY29tcG9uZW50ZXMsIGluY2x1eWVuZG8gZWwgdmFyaW9ncmFtYSBlbXDDrXJpY28geSBlbCBtb2RlbG8gZGUgdmFyaW9ncmFtYSBhanVzdGFkby4gRWwgY29tcG9uZW50ZSBcJHZhcl9tb2RlbCBkZWwgb2JqZXRvIHJlc3VsdGFudGUgY29udGllbmUgZWwgbW9kZWxvIHJlYWw6DQoNCmBgYHtyfQ0Kdl9tb2Rfb2skdmFyX21vZGVsDQoNCmBgYA0KDQpFbCBtb2RlbG8gZGVsIHZhcmlvZ3JhbWEgcHVlZGUgZW50b25jZXMgcGFzYXJzZSBhIGxhIGZ1bmNpw7NuIGdzdGF0LCB5IHBvZGVtb3MgY29udGludWFyIGNvbiBsYSBpbnRlcnBvbGFjacOzbiBLcmlnaW5nIG9yZGluYXJpYToNCg0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQojIyBbdXNpbmcgb3JkaW5hcnkga3JpZ2luZ10NCmcyID0gZ3N0YXQoZm9ybXVsYSA9IHByZWNpcCB+IDEsIG1vZGVsID0gdl9tb2Rfb2skdmFyX21vZGVsLCBkYXRhID0gdHJhaW4pDQp6Mj0gcHJlZGljdChnMiwgZGVtKQ0KYGBgDQoNClVuYSB2ZXogbcOhcywgdmFtb3MgYSBzdWJjb25qdW50YXIgZWwgYXRyaWJ1dG8gZGUgdmFsb3JlcyBwcmVkaWNob3MgeSBhIHJlbm9tYnJhcmxvOg0KDQpgYGB7cn0NCm5hbWVzKHoyKSA9ICJwcmVjaXAiDQpgYGANCg0KTGFzIHByZWRpY2Npb25lcyBkZSBLcmlnaW5nIG9yZGluYXJpbyBzZSBtdWVzdHJhbiBlbiBsYSBzaWd1aWVudGUgZmlndXJhOg0KDQpgYGB7cn0NCmIgPSBzZXEoMSwgMTAwLCAxKQ0KcGxvdCh6MiwgYnJlYWtzID0gYiwgY29sID0gaGNsLmNvbG9ycyhsZW5ndGgoYiktMSwgIlNwZWN0cmFsIiksIHJlc2V0ID0gRkFMU0UpDQpwbG90KHN0X2dlb21ldHJ5KHRyYWluKSwgcGNoID0gMywgYWRkID0gVFJVRSkNCmNvbnRvdXIoejIsIGJyZWFrcyA9IGIsIGFkZCA9IFRSVUUpDQpgYGANCg0KIyMjIFVuaXZlcnNhbCBLcmlnaW5nDQoNCmBgYHtyfQ0Kdl9lbXBfdWsgPSB2YXJpb2dyYW0ocHJlY2lwIH4gZWxldiwgdHJhaW4pDQp0cmFpbi5zcCA9IGFzKHRyYWluLCAiU3BhdGlhbCIpDQp2X21vZF91ayA9IGF1dG9maXRWYXJpb2dyYW0ocHJlY2lwIH4gZWxldiwgdHJhaW4uc3ApDQpgYGANCmBgYHtyfQ0KcGxvdCh2X21vZF91aykNCmBgYA0KDQpgYGB7cn0NCnBsb3Qodl9lbXBfb2ssIG1vZGVsID0gdl9tb2Rfb2skdmFyX21vZGVsLCB5bGltID0gYygwLCAxNTApLCBtYWluID0gIk9LIikNCmBgYA0KDQpgYGB7cn0NCnBsb3Qodl9lbXBfdWssIG1vZGVsID0gdl9tb2RfdWskdmFyX21vZGVsLCB5bGltID0gYygwLCA3MCksIG1haW4gPSAiVUsiKQ0KYGBgDQoNCmBgYHtyfQ0KZzMgPSBnc3RhdChmb3JtdWxhID0gcHJlY2lwIH4gZWxldiwgbW9kZWwgPSB2X21vZF91ayR2YXJfbW9kZWwsIGRhdGEgPSB0cmFpbi5zcCkNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGRlbSkNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGRlbSkgPC0gImVsZXYiDQpgYGANCg0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQp6MyA9IHByZWRpY3QoZzMsIGRlbSkNCmBgYA0KDQpgYGB7cn0NCnozID0gejNbInZhcjEucHJlZCIsLF0NCm5hbWVzKHozKSA9ICJwcmVjaXBpdGF0aW9uIg0KYGBgDQoNCmBgYHtyfQ0KYiA9IHNlcSgxLCAxMDAsIDEpDQpwbG90KHozLCBicmVha3MgPSBiLCBjb2wgPSBoY2wuY29sb3JzKGxlbmd0aChiKS0xLCAiU3BlY3RyYWwiKSwgcmVzZXQgPSBGQUxTRSkNCnBsb3Qoc3RfZ2VvbWV0cnkodHJhaW4pLCBwY2ggPSAzLCBhZGQgPSBUUlVFKQ0KY29udG91cih6MywgYnJlYWtzID0gYiwgYWRkID0gVFJVRSkNCmBgYA0KDQojIyMgNC4gRXZhbHVhY2nDs24gY3VhbGl0YXRpdmEgZGUgbG9zIHJlc3VsdGFkb3MNCg0KYGBge3J9DQpwYWxldGEgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSBjKCJyZWQiLCAib3JhbmdlIiwgIiNmY2MwMDAiLCJ5ZWxsb3ciLCAiY3lhbiIsICJibHVlIiwgIiMzMjQwY2QiKSwgZG9tYWluID0gMTA6MTAwLCBuYS5jb2xvciA9ICJ0cmFuc3BhcmVudCIpDQpgYGANCg0KYGBge3J9DQpjb2xvcmVzIDwtIGNvbG9yT3B0aW9ucyhwYWxldHRlID0gYygicmVkIiwgIm9yYW5nZSIsICIjZmNjMDAwIiwieWVsbG93IiwgImN5YW4iLCAiYmx1ZSIsICIjMzI0MGNkIiksIGRvbWFpbiA9IDEwOjEwMCwgbmEuY29sb3IgPSAidHJhbnNwYXJlbnQiKQ0KYGBgDQoNCmBgYHtyfQ0KbSA8LSBsZWFmbGV0KCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lICANCiAgYWRkR2VvUmFzdGVyKHoxLCBvcGFjaXR5ID0gMC43LCBjb2xvck9wdGlvbnMgPSBjb2xvcmVzLCBncm91cD0iSURXIikgJT4lDQogIGFkZEdlb1Jhc3Rlcih6MiwgY29sb3JPcHRpb25zID0gY29sb3Jlcywgb3BhY2l0eSA9IDAuNywgZ3JvdXA9ICJPSyIpICAlPiUNCiAgYWRkR2VvUmFzdGVyKHozLCBjb2xvck9wdGlvbnMgPSBjb2xvcmVzLCBvcGFjaXR5ID0gMC43LCBncm91cD0gIlVLIikgICU+JQ0KICAjIEFkZCBsYXllcnMgY29udHJvbHMNCiAgYWRkTGF5ZXJzQ29udHJvbCgNCiAgICBvdmVybGF5R3JvdXBzID0gYygiVUsiLCAiT0siLCAiSURXIiksDQogICAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IEZBTFNFKQ0KICApICU+JSANCiAgDQogICAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHBhbGV0YSwgdmFsdWVzPSB6MSRwcmVjaXBpdGF0aW9uLA0KICAgIHRpdGxlID0gIlByZWNpcGl0YXRpb24gKDIwMTkpIg0KICApDQptDQpgYGANCg0KIyMjIDUuIFZhbGlkYWNpw7NuIGNydXphZGENCg0KYGBge3IgcmVzdWx0cz0naGlkZSd9DQpjdjEgPSBnc3RhdC5jdihnMSkNCmN2MSA9IHN0X2FzX3NmKGN2MSkNCmBgYA0KDQpgYGB7cn0NCmJ1YmJsZShhcyhjdjFbLCAicmVzaWR1YWwiXSwgIlNwYXRpYWwiKSkNCmBgYA0KDQpgYGB7cn0NCnNxcnQoc3VtKChjdjEkdmFyMS5wcmVkIC0gY3YxJG9ic2VydmVkKV4yKSAvIG5yb3coY3YxKSkNCmBgYA0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJ30NCmN2MiA9IGdzdGF0LmN2KGcyKQ0KY3YyID0gc3RfYXNfc2YoY3YyKQ0KYGBgDQoNCmBgYHtyfQ0KYnViYmxlKGFzKGN2MlssICJyZXNpZHVhbCJdLCAiU3BhdGlhbCIpKQ0KYGBgDQoNCmBgYHtyfQ0Kc3FydChzdW0oKGN2MiR2YXIxLnByZWQgLSBjdjIkb2JzZXJ2ZWQpXjIpIC8gbnJvdyhjdjIpKQ0KYGBgDQoNCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQ0KY3YzID0gZ3N0YXQuY3YoZzMpDQpjdjMgPSBzdF9hc19zZihjdjMpDQpgYGANCg0KYGBge3J9DQpidWJibGUoYXMoY3YzWywgInJlc2lkdWFsIl0sICJTcGF0aWFsIikpDQpgYGANCg0KYGBge3J9DQpzcXJ0KHN1bSgoY3YzJHZhcjEucHJlZCAtIGN2MyRvYnNlcnZlZCleMikgLyBucm93KGN2MykpDQpgYGANCg==