knitr::opts_chunk$set(fig.align = "center",
                      warning = FALSE,
                      message = FALSE,
                      fig.width = 9)

Generalidades

  • Datos
    • Datos de captura remota (satelites lidar)
    • Datos modelados (SDM)
    • Datos de expertos
    • Planes futuros
  • Formatos
    • Datos vectoriales (.shp –> Shapefiles)
      • Puntos –> Ciudades, eventos, sitio de muestreo
      • Líneas
      • Polígonos
    • Datos en grilla (raster)

Shapefiles

Data countriesHigh

library(sf)
library(tidyverse)
library(rworldxtra)
data("countriesHigh")
class(countriesHigh)
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"

Objeto sf

  • Transformación a objeto sf (simple feature): este tipo de objeto es más moderno que el tipo sp para manejar datos espaciales. También facilita la conversión a data.frame, de tal manera que se facilita el manejo a través del tidyverse.
class(mundo)
[1] "sf"         "data.frame"

Gráficos

  • Gráfico con gggplot2:
mundo %>% 
  ggplot(aes(fill = POP_EST)) +
  geom_sf() +
  labs(title = "Población mundial")

  • Filtrando sólo África:
library(viridis)
mundo %>% 
  filter(continent == "Africa") %>% 
  ggplot(aes(fill = POP_EST)) +
  geom_sf() +
  scale_fill_viridis_c()

Exportando shape

  • Exportando subconjunto de datos como .shp:
mundo %>% 
  filter(continent == "Africa") %>% 
  dplyr::select(NAME, POP_EST, GDP_MD_EST, GLOCAF) ->
  africa
write_sf(africa, "ejemplo_africa.shp")
  • Lectura de archivo shape:
# Función biblioteca raster
africa2 <- shapefile("ejemplo_africa.shp")
class(africa2)
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"
#Función biblioteca sf
africa3 <- read_sf("ejemplo_africa.shp") 
class(africa3)
[1] "sf"         "tbl_df"     "tbl"        "data.frame"
  • Gráficos por defecto con la función plot():
plot(africa2)

plot(africa3)

Obtención de shapes con R

  • Los nombres ISO-3 de cada país se pueden obtener de la siguiente manera:
getData(name = "ISO3")
  • Obtención de datos con función getData() para Colombia:

    • level = 0: división a nivel de país.
    • level = 1: división regional (departamentos)
    • level = 2: división municipal (municipios)
colombia <- getData(name = "GADM", country = "COL", level = 0)
colombia_sf <- st_as_sf(colombia)
colombia_sf %>% 
  ggplot() + 
  geom_sf()

  • Proyección del sistema de coordenadas de objetos “colombia” y “colombia_sf”:
crs(colombia)
CRS arguments:
 +proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs 
crs(colombia_sf)
[1] "+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs"
  • Añadiendo punto al mapa. Con el formato sf es posible hacer esto facilmente con ggplot2. Es necesario ingresar a la función st_as_sf() la posición de las columnas longitud y latitud, respectivamente. Además es necesario incoporar la proyección del sistema de coordenadas.
prueba <- data.frame(lon = -70, lat = 5, punto = "ejemplo")

prueba %>% 
  st_as_sf(coords = c(1, 2),
           crs = "+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs") ->
  prueba_sf
prueba_sf
Simple feature collection with 1 feature and 1 field
geometry type:  POINT
dimension:      XY
bbox:           xmin: -70 ymin: 5 xmax: -70 ymax: 5
CRS:            +proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs
    punto      geometry
1 ejemplo POINT (-70 5)
  • Gráfico de shape + punto creado previamete:
ggplot() + 
  geom_sf(data = colombia_sf)  +
  geom_sf(data = prueba_sf, color = "red", size = 4)

Raster

  • Datos en grilla, similares a una imagen (pixeles). Poseen características de resolución, proyección, unidades (m, km, …)

Datos CHELSA

  • Individuales:
raster1 <- raster("images-tif/CHELSA_Bio1_4km2.tif")
raster2 <- raster("images-tif/CHELSA_Bio6_4km2.tif")
raster1
class      : RasterLayer 
dimensions : 3900, 4980, 19422000  (nrow, ncol, ncell)
resolution : 0.016666, 0.016666  (x, y)
extent     : -112.0029, -29.00618, -39.99518, 25.00222  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs 
source     : D:/DocumentosEdimer/Github/Spatial-Data-Science/SIG_R/images-tif/CHELSA_Bio1_4km2.tif 
names      : CHELSA_Bio1_4km2 
values     : -169.4425, 293.2272  (min, max)
raster2
class      : RasterLayer 
dimensions : 3900, 4980, 19422000  (nrow, ncol, ncell)
resolution : 0.016666, 0.016666  (x, y)
extent     : -112.0029, -29.00618, -39.99518, 25.00222  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs 
source     : D:/DocumentosEdimer/Github/Spatial-Data-Science/SIG_R/images-tif/CHELSA_Bio6_4km2.tif 
names      : CHELSA_Bio6_4km2 
values     : -286.5303, 264  (min, max)
  • Stack de raster: es posible almacenar varios raster en un sólo objeto.
mi_stack <- stack(raster1, raster2)
mi_stack
class      : RasterStack 
dimensions : 3900, 4980, 19422000, 2  (nrow, ncol, ncell, nlayers)
resolution : 0.016666, 0.016666  (x, y)
extent     : -112.0029, -29.00618, -39.99518, 25.00222  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs 
names      : CHELSA_Bio1_4km2, CHELSA_Bio6_4km2 
min values :        -169.4425,        -286.5303 
max values :         293.2272,         264.0000 

Gráficos

plot(mi_stack, colNA = "black")

  • Escalas equivalentes: a través de la biblioteca rasterVis es posible generar mapas que compartan la misma leyenda.
library(rasterVis)
levelplot(mi_stack)

Operaciones simples

  • Suma de raster:
total <- mi_stack[[1]] + mi_stack[[2]]
levelplot(total)

  • Promedios:
promedios <- mean(mi_stack)
levelplot(promedios)

Corte de raster

  • Es posible cortar el raster para una región específica, en este caso sólo para Colombia. La función crop() permitirá extraer el cuadrante donde se encuenta la región de interés.
raster_colombia <- promedios %>% crop(colombia_sf) # también funciona: crop(colombia)
levelplot(raster_colombia)

  • Gráfico con la biblioteca base:
plot(raster_colombia, colNA = "black")

  • Corte de sólo Colombia: además de la función crop() es necesario utilizar la función mask(), ambas de la biblioteca raster.
solo_colombia <- promedios %>% 
  crop(colombia_sf) %>% 
  mask(colombia_sf)
Discarded datum Unknown based on WGS84 ellipsoid in CRS definition,
 but +towgs84= values preserved
levelplot(solo_colombia)

  • Gráfico con la biblioteca base:
plot(solo_colombia, colNA = "black")

Extracción de valores

  • Con los datos creados previamente para representar un punto dentro del mapa, se ejemplifica la extracción de valores del raster (temperatura en este caso) correspondiente a la respectiva longitud y latitud. En este caso equivale a 236. Nota: recordar que los valores de las variables bioclimáticas están multiplicados por 10, de tal manera que la temperatura para el punto ficticio es 236/10 = 23.6°C.
extract(solo_colombia, prueba_sf)
[1] 236
  • Se podría añadir este valor a la base de datos:
prueba_sf
Simple feature collection with 1 feature and 2 fields
geometry type:  POINT
dimension:      XY
bbox:           xmin: -70 ymin: 5 xmax: -70 ymax: 5
CRS:            +proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs
    punto      geometry temperatura
1 ejemplo POINT (-70 5)         236
  • También es posible aplicar el proceso anterior sobre un stack de raster:
extract(mi_stack, prueba_sf)
     CHELSA_Bio1_4km2 CHELSA_Bio6_4km2
[1,]              251              221
cbind(prueba_sf, extract(mi_stack, prueba_sf)) %>% 
  as.data.frame()

Exportar raster

writeRaster(solo_colombia, "colombia.grd", overwrite = TRUE)
class      : RasterLayer 
dimensions : 1209, 898, 1085682  (nrow, ncol, ncell)
resolution : 0.016666, 0.016666  (x, y)
extent     : -81.8374, -66.87133, -4.229944, 15.91925  (xmin, xmax, ymin, ymax)
crs        : +proj=longlat +datum=WGS84 +no_defs 
source     : D:/DocumentosEdimer/Github/Spatial-Data-Science/SIG_R/colombia.grd 
names      : layer 
values     : -47.5102, 269.0904  (min, max)
  • Lectura del raster:
nuevo_col <- raster("colombia.grd")
plot(nuevo_col, colNA = "black")

Proyecciones

  • Importante cuando el área del pixel ingresa en los calculos del análisis, por ejemplo, cuando se está obteniendo abundancia (relativa) de especies. Lugares cercanos al ecuador podrían tener tamaños más grandes de pixel y ocasionar sesgo si no se controla la proyección. En la página web de Projection Wizard es posible obtener proyecciones para lugares específicos, garantizando así que tenemos un mapa de igual área. En este caso para colombia la proyección se muestra en la siguiente imagen:


  • Para más información acerca de proyecciones geográficas, recomiendo ver el siguiente video.
  • Proyección inicial:
proj4string(solo_colombia)
[1] "+proj=longlat +datum=WGS84 +no_defs"
  • Cambiando proyección:
colombia_igual <- projectRaster(
  solo_colombia,
  crs = "+proj=laea +lon_0=-74.0917969 +lat_0=0 +datum=WGS84 +units=m +no_defs"
)
plot(colombia_igual, colNA = "black")

Shape + Raster

  • Conversión de raster a base de datos (data.frame).
colombia_data <- solo_colombia %>% 
  as("SpatialPixelsDataFrame") %>% 
  as.data.frame()
head(colombia_data)
  • Mapa con gggplot2:
# Opcional: cambiando nombre de variable layer
colombia_data <- colombia_data %>% dplyr::rename(Temp = layer)

# Gráfico
ggplot() +
  geom_tile(data = colombia_data, aes(x = x, y = y, fill = Temp)) +
  geom_sf(data = colombia_sf, alpha = 0) +
  scale_fill_viridis_c() +
  theme_bw()

LS0tDQp0aXRsZTogIlNpc3RlbWFzIGRlIEluZm9ybWFjacOzbiBHZW9ncsOhZmljYSBjb24gUiINCnN1YnRpdGxlOiAiSW50cm9kdWNjacOzbiINCmF1dGhvcjogIkVkaW1lciBEYXZpZCBKYXJhbWlsbG8iDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KLS0tDQoNCmBgYHtyfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOSkNCmBgYA0KDQoNCi0gTm90YXMgdG9tYWRhcyBkZWwgY3Vyc28gWyJTaXN0ZW1hcyBkZSBJbmZvcm1hY2nDs24gR2VvZ3LDoWZpY2EgZW4gUiAoU0lHIGVuIFIpIiBjb24gRGVyZWsgQ29yY29yYW4uXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9jaGFubmVsL1VDT2ItMTJ3dVdWdHJXZnFJM3A4SEw0dykNCg0KPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC92NzFwQzd5ZGRvMCIgZnJhbWVib3JkZXI9IjAiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4NCg0KIyBHZW5lcmFsaWRhZGVzDQoNCi0gRGF0b3MNCiAgLSBEYXRvcyBkZSBjYXB0dXJhIHJlbW90YSAoc2F0ZWxpdGVzIGxpZGFyKQ0KICAtIERhdG9zIG1vZGVsYWRvcyAoU0RNKQ0KICAtIERhdG9zIGRlIGV4cGVydG9zDQogIC0gUGxhbmVzIGZ1dHVyb3MNCi0gRm9ybWF0b3MNCiAgLSBEYXRvcyB2ZWN0b3JpYWxlcyAoLnNocCAtLT4gU2hhcGVmaWxlcykNCiAgICAtIFB1bnRvcyAtLT4gQ2l1ZGFkZXMsIGV2ZW50b3MsIHNpdGlvIGRlIG11ZXN0cmVvDQogICAgLSBMw61uZWFzDQogICAgLSBQb2zDrWdvbm9zDQogIC0gRGF0b3MgZW4gZ3JpbGxhIChyYXN0ZXIpDQoNCiMgU2hhcGVmaWxlcw0KICANCiMjIERhdGEgY291bnRyaWVzSGlnaA0KDQpgYGB7cn0NCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocndvcmxkeHRyYSkNCmRhdGEoImNvdW50cmllc0hpZ2giKQ0KY2xhc3MoY291bnRyaWVzSGlnaCkNCmBgYA0KDQojIyBPYmpldG8gc2YNCg0KLSAqKlRyYW5zZm9ybWFjacOzbiBhIG9iamV0byBzZiAoc2ltcGxlIGZlYXR1cmUpOioqIGVzdGUgdGlwbyBkZSBvYmpldG8gZXMgbcOhcyBtb2Rlcm5vIHF1ZSBlbCB0aXBvICpzcCogcGFyYSBtYW5lamFyIGRhdG9zIGVzcGFjaWFsZXMuIFRhbWJpw6luIGZhY2lsaXRhIGxhIGNvbnZlcnNpw7NuIGEgZGF0YS5mcmFtZSwgZGUgdGFsIG1hbmVyYSBxdWUgc2UgZmFjaWxpdGEgZWwgbWFuZWpvIGEgdHJhdsOpcyBkZWwgKnRpZHl2ZXJzZSouDQoNCmBgYHtyfQ0KbXVuZG8gPC0gc3RfYXNfc2YoY291bnRyaWVzSGlnaCkNCmNsYXNzKG11bmRvKQ0KYGBgDQoNCiMjIEdyw6FmaWNvcw0KDQotICoqR3LDoWZpY28gY29uIGdnZ3Bsb3QyOioqDQoNCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9DQptdW5kbyAlPiUgDQogIGdncGxvdChhZXMoZmlsbCA9IFBPUF9FU1QpKSArDQogIGdlb21fc2YoKSArDQogIGxhYnModGl0bGUgPSAiUG9ibGFjacOzbiBtdW5kaWFsIikNCmBgYA0KDQotICoqRmlsdHJhbmRvIHPDs2xvIMOBZnJpY2E6KioNCg0KYGBge3J9DQpsaWJyYXJ5KHZpcmlkaXMpDQptdW5kbyAlPiUgDQogIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIpICU+JSANCiAgZ2dwbG90KGFlcyhmaWxsID0gUE9QX0VTVCkpICsNCiAgZ2VvbV9zZigpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKQ0KYGBgDQoNCiMjIEV4cG9ydGFuZG8gc2hhcGUNCg0KLSAqKkV4cG9ydGFuZG8gc3ViY29uanVudG8gZGUgZGF0b3MgY29tbyAuc2hwOioqDQoNCmBgYHtyfQ0KbXVuZG8gJT4lIA0KICBmaWx0ZXIoY29udGluZW50ID09ICJBZnJpY2EiKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoTkFNRSwgUE9QX0VTVCwgR0RQX01EX0VTVCwgR0xPQ0FGKSAtPg0KICBhZnJpY2ENCndyaXRlX3NmKGFmcmljYSwgImVqZW1wbG9fYWZyaWNhLnNocCIpDQpgYGANCg0KLSAqKkxlY3R1cmEgZGUgYXJjaGl2byBzaGFwZToqKg0KDQpgYGB7cn0NCiMgRnVuY2nDs24gYmlibGlvdGVjYSByYXN0ZXINCmFmcmljYTIgPC0gc2hhcGVmaWxlKCJlamVtcGxvX2FmcmljYS5zaHAiKQ0KY2xhc3MoYWZyaWNhMikNCg0KI0Z1bmNpw7NuIGJpYmxpb3RlY2Egc2YNCmFmcmljYTMgPC0gcmVhZF9zZigiZWplbXBsb19hZnJpY2Euc2hwIikgDQpjbGFzcyhhZnJpY2EzKQ0KYGBgDQoNCi0gKipHcsOhZmljb3MgcG9yIGRlZmVjdG8gY29uIGxhIGZ1bmNpw7NuIHBsb3QoKToqKg0KDQpgYGB7cn0NCnBsb3QoYWZyaWNhMikNCnBsb3QoYWZyaWNhMykNCmBgYA0KIyMgT2J0ZW5jacOzbiBkZSBzaGFwZXMgY29uIFINCg0KLSBMb3Mgbm9tYnJlcyBJU08tMyBkZSBjYWRhIHBhw61zIHNlIHB1ZWRlbiBvYnRlbmVyIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6DQoNCmBgYHtyfQ0KZ2V0RGF0YShuYW1lID0gIklTTzMiKQ0KYGBgDQoNCi0gKipPYnRlbmNpw7NuIGRlIGRhdG9zIGNvbiBmdW5jacOzbiBnZXREYXRhKCkgcGFyYSBDb2xvbWJpYToqKg0KDQogIC0gYGxldmVsID0gMGA6IGRpdmlzacOzbiBhIG5pdmVsIGRlIHBhw61zLg0KICAtIGBsZXZlbCA9IDFgOiBkaXZpc2nDs24gcmVnaW9uYWwgKGRlcGFydGFtZW50b3MpDQogIC0gYGxldmVsID0gMmA6IGRpdmlzacOzbiBtdW5pY2lwYWwgKG11bmljaXBpb3MpDQoNCmBgYHtyfQ0KY29sb21iaWEgPC0gZ2V0RGF0YShuYW1lID0gIkdBRE0iLCBjb3VudHJ5ID0gIkNPTCIsIGxldmVsID0gMCkNCmNvbG9tYmlhX3NmIDwtIHN0X2FzX3NmKGNvbG9tYmlhKQ0KY29sb21iaWFfc2YgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKCkNCmBgYA0KDQotIFByb3llY2Npw7NuIGRlbCBzaXN0ZW1hIGRlIGNvb3JkZW5hZGFzIGRlIG9iamV0b3MgImNvbG9tYmlhIiB5ICJjb2xvbWJpYV9zZiI6DQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KY3JzKGNvbG9tYmlhKQ0KY3JzKGNvbG9tYmlhX3NmKQ0KYGBgDQoNCg0KDQotIEHDsWFkaWVuZG8gcHVudG8gYWwgbWFwYS4gQ29uIGVsIGZvcm1hdG8gc2YgZXMgcG9zaWJsZSBoYWNlciBlc3RvIGZhY2lsbWVudGUgY29uIGdncGxvdDIuIEVzIG5lY2VzYXJpbyBpbmdyZXNhciBhIGxhIGZ1bmNpw7NuIGBzdF9hc19zZigpYCBsYSBwb3NpY2nDs24gZGUgbGFzIGNvbHVtbmFzIGxvbmdpdHVkIHkgbGF0aXR1ZCwgcmVzcGVjdGl2YW1lbnRlLiBBZGVtw6FzIGVzIG5lY2VzYXJpbyBpbmNvcG9yYXIgbGEgcHJveWVjY2nDs24gZGVsIHNpc3RlbWEgZGUgY29vcmRlbmFkYXMuDQoNCmBgYHtyfQ0KcHJ1ZWJhIDwtIGRhdGEuZnJhbWUobG9uID0gLTcwLCBsYXQgPSA1LCBwdW50byA9ICJlamVtcGxvIikNCg0KcHJ1ZWJhICU+JSANCiAgc3RfYXNfc2YoY29vcmRzID0gYygxLCAyKSwNCiAgICAgICAgICAgY3JzID0gIitwcm9qPWxvbmdsYXQgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwLDAsMCwwLDAgK25vX2RlZnMiKSAtPg0KICBwcnVlYmFfc2YNCnBydWViYV9zZg0KYGBgDQoNCi0gR3LDoWZpY28gZGUgc2hhcGUgKyBwdW50byBjcmVhZG8gcHJldmlhbWV0ZToNCg0KYGBge3J9DQpnZ3Bsb3QoKSArIA0KICBnZW9tX3NmKGRhdGEgPSBjb2xvbWJpYV9zZikgICsNCiAgZ2VvbV9zZihkYXRhID0gcHJ1ZWJhX3NmLCBjb2xvciA9ICJyZWQiLCBzaXplID0gNCkNCmBgYA0KIyBSYXN0ZXINCg0KLSBEYXRvcyBlbiBncmlsbGEsIHNpbWlsYXJlcyBhIHVuYSBpbWFnZW4gKHBpeGVsZXMpLiBQb3NlZW4gY2FyYWN0ZXLDrXN0aWNhcyBkZSByZXNvbHVjacOzbiwgcHJveWVjY2nDs24sIHVuaWRhZGVzIChtLCBrbSwgLi4uKQ0KDQojIyBEYXRvcyBDSEVMU0ENCg0KLSBJbmRpdmlkdWFsZXM6DQoNCmBgYHtyfQ0KcmFzdGVyMSA8LSByYXN0ZXIoImltYWdlcy10aWYvQ0hFTFNBX0JpbzFfNGttMi50aWYiKQ0KcmFzdGVyMiA8LSByYXN0ZXIoImltYWdlcy10aWYvQ0hFTFNBX0JpbzZfNGttMi50aWYiKQ0KcmFzdGVyMQ0KcmFzdGVyMg0KYGBgDQoNCi0gU3RhY2sgZGUgcmFzdGVyOiBlcyBwb3NpYmxlIGFsbWFjZW5hciB2YXJpb3MgcmFzdGVyIGVuIHVuIHPDs2xvIG9iamV0by4NCg0KYGBge3J9DQptaV9zdGFjayA8LSBzdGFjayhyYXN0ZXIxLCByYXN0ZXIyKQ0KbWlfc3RhY2sNCmBgYA0KDQojIyBHcsOhZmljb3MNCg0KYGBge3J9DQpwbG90KG1pX3N0YWNrLCBjb2xOQSA9ICJibGFjayIpDQpgYGANCi0gKipFc2NhbGFzIGVxdWl2YWxlbnRlczoqKiBhIHRyYXbDqXMgZGUgbGEgYmlibGlvdGVjYSByYXN0ZXJWaXMgZXMgcG9zaWJsZSBnZW5lcmFyIG1hcGFzIHF1ZSBjb21wYXJ0YW4gbGEgbWlzbWEgbGV5ZW5kYS4NCg0KYGBge3J9DQpsaWJyYXJ5KHJhc3RlclZpcykNCmxldmVscGxvdChtaV9zdGFjaykNCmBgYA0KDQojIyBPcGVyYWNpb25lcyBzaW1wbGVzDQoNCi0gU3VtYSBkZSByYXN0ZXI6DQoNCmBgYHtyLCBmaWcud2lkdGg9OX0NCnRvdGFsIDwtIG1pX3N0YWNrW1sxXV0gKyBtaV9zdGFja1tbMl1dDQpsZXZlbHBsb3QodG90YWwpDQpgYGANCg0KLSBQcm9tZWRpb3M6DQoNCmBgYHtyLCBmaWcud2lkdGg9OX0NCnByb21lZGlvcyA8LSBtZWFuKG1pX3N0YWNrKQ0KbGV2ZWxwbG90KHByb21lZGlvcykNCmBgYA0KDQojIyBDb3J0ZSBkZSByYXN0ZXINCg0KLSBFcyBwb3NpYmxlIGNvcnRhciBlbCByYXN0ZXIgcGFyYSB1bmEgcmVnacOzbiBlc3BlY8OtZmljYSwgZW4gZXN0ZSBjYXNvIHPDs2xvIHBhcmEgQ29sb21iaWEuIExhIGZ1bmNpw7NuIGNyb3AoKSBwZXJtaXRpcsOhIGV4dHJhZXIgZWwgY3VhZHJhbnRlIGRvbmRlIHNlIGVuY3VlbnRhIGxhIHJlZ2nDs24gZGUgaW50ZXLDqXMuDQoNCmBgYHtyLCBmaWcud2lkdGg9OX0NCnJhc3Rlcl9jb2xvbWJpYSA8LSBwcm9tZWRpb3MgJT4lIGNyb3AoY29sb21iaWFfc2YpICMgdGFtYmnDqW4gZnVuY2lvbmE6IGNyb3AoY29sb21iaWEpDQpsZXZlbHBsb3QocmFzdGVyX2NvbG9tYmlhKQ0KYGBgDQoNCi0gR3LDoWZpY28gY29uIGxhIGJpYmxpb3RlY2EgYmFzZToNCg0KYGBge3J9DQpwbG90KHJhc3Rlcl9jb2xvbWJpYSwgY29sTkEgPSAiYmxhY2siKQ0KYGBgDQoNCi0gKipDb3J0ZSBkZSBzw7NsbyBDb2xvbWJpYToqKiBhZGVtw6FzIGRlIGxhIGZ1bmNpw7NuIGNyb3AoKSBlcyBuZWNlc2FyaW8gdXRpbGl6YXIgbGEgZnVuY2nDs24gbWFzaygpLCBhbWJhcyBkZSBsYSBiaWJsaW90ZWNhIHJhc3Rlci4NCg0KYGBge3IsIGZpZy53aWR0aD05fQ0Kc29sb19jb2xvbWJpYSA8LSBwcm9tZWRpb3MgJT4lIA0KICBjcm9wKGNvbG9tYmlhX3NmKSAlPiUgDQogIG1hc2soY29sb21iaWFfc2YpDQpsZXZlbHBsb3Qoc29sb19jb2xvbWJpYSkNCmBgYA0KLSBHcsOhZmljbyBjb24gbGEgYmlibGlvdGVjYSBiYXNlOg0KDQpgYGB7cn0NCnBsb3Qoc29sb19jb2xvbWJpYSwgY29sTkEgPSAiYmxhY2siKQ0KYGBgDQoNCiMjIEV4dHJhY2Npw7NuIGRlIHZhbG9yZXMNCg0KLSBDb24gbG9zIGRhdG9zIGNyZWFkb3MgcHJldmlhbWVudGUgcGFyYSByZXByZXNlbnRhciB1biBwdW50byBkZW50cm8gZGVsIG1hcGEsIHNlIGVqZW1wbGlmaWNhIGxhIGV4dHJhY2Npw7NuIGRlIHZhbG9yZXMgZGVsIHJhc3RlciAodGVtcGVyYXR1cmEgZW4gZXN0ZSBjYXNvKSBjb3JyZXNwb25kaWVudGUgYSBsYSByZXNwZWN0aXZhIGxvbmdpdHVkIHkgbGF0aXR1ZC4gRW4gZXN0ZSBjYXNvIGVxdWl2YWxlIGEgMjM2LiAqKk5vdGE6KiogcmVjb3JkYXIgcXVlIGxvcyB2YWxvcmVzIGRlIGxhcyB2YXJpYWJsZXMgYmlvY2xpbcOhdGljYXMgZXN0w6FuIG11bHRpcGxpY2Fkb3MgcG9yIDEwLCBkZSB0YWwgbWFuZXJhIHF1ZSBsYSB0ZW1wZXJhdHVyYSBwYXJhIGVsIHB1bnRvIGZpY3RpY2lvIGVzIDIzNi8xMCA9IDIzLjbCsEMuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZXh0cmFjdChzb2xvX2NvbG9tYmlhLCBwcnVlYmFfc2YpDQpgYGANCg0KLSBTZSBwb2Ryw61hIGHDsWFkaXIgZXN0ZSB2YWxvciBhIGxhIGJhc2UgZGUgZGF0b3M6DQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHJ1ZWJhX3NmJHRlbXBlcmF0dXJhIDwtIGV4dHJhY3Qoc29sb19jb2xvbWJpYSwgcHJ1ZWJhX3NmKQ0KcHJ1ZWJhX3NmDQpgYGANCg0KLSBUYW1iacOpbiBlcyBwb3NpYmxlIGFwbGljYXIgZWwgcHJvY2VzbyBhbnRlcmlvciBzb2JyZSB1biBzdGFjayBkZSByYXN0ZXI6DQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZXh0cmFjdChtaV9zdGFjaywgcHJ1ZWJhX3NmKQ0KY2JpbmQocHJ1ZWJhX3NmLCBleHRyYWN0KG1pX3N0YWNrLCBwcnVlYmFfc2YpKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKQ0KYGBgDQojIyBFeHBvcnRhciByYXN0ZXINCg0KYGBge3J9DQp3cml0ZVJhc3Rlcihzb2xvX2NvbG9tYmlhLCAiY29sb21iaWEuZ3JkIiwgb3ZlcndyaXRlID0gVFJVRSkNCmBgYA0KDQotICoqTGVjdHVyYSBkZWwgcmFzdGVyOioqDQoNCmBgYHtyfQ0KbnVldm9fY29sIDwtIHJhc3RlcigiY29sb21iaWEuZ3JkIikNCnBsb3QobnVldm9fY29sLCBjb2xOQSA9ICJibGFjayIpDQpgYGANCg0KIyBQcm95ZWNjaW9uZXMNCg0KLSBJbXBvcnRhbnRlIGN1YW5kbyBlbCDDoXJlYSBkZWwgcGl4ZWwgaW5ncmVzYSBlbiBsb3MgY2FsY3Vsb3MgZGVsIGFuw6FsaXNpcywgcG9yIGVqZW1wbG8sIGN1YW5kbyBzZSBlc3TDoSBvYnRlbmllbmRvIGFidW5kYW5jaWEgKHJlbGF0aXZhKSBkZSBlc3BlY2llcy4gTHVnYXJlcyBjZXJjYW5vcyBhbCBlY3VhZG9yIHBvZHLDrWFuIHRlbmVyIHRhbWHDsW9zIG3DoXMgZ3JhbmRlcyBkZSBwaXhlbCB5IG9jYXNpb25hciBzZXNnbyBzaSBubyBzZSBjb250cm9sYSBsYSBwcm95ZWNjacOzbi4gRW4gbGEgcMOhZ2luYSB3ZWIgZGUgW1Byb2plY3Rpb24gV2l6YXJkXShodHRwczovL3Byb2plY3Rpb253aXphcmQub3JnLykgZXMgcG9zaWJsZSBvYnRlbmVyIHByb3llY2Npb25lcyBwYXJhIGx1Z2FyZXMgZXNwZWPDrWZpY29zLCBnYXJhbnRpemFuZG8gYXPDrSBxdWUgdGVuZW1vcyB1biBtYXBhIGRlIGlndWFsIMOhcmVhLiBFbiBlc3RlIGNhc28gcGFyYSBjb2xvbWJpYSBsYSBwcm95ZWNjacOzbiBzZSBtdWVzdHJhIGVuIGxhIHNpZ3VpZW50ZSBpbWFnZW46DQoNCjxjZW50ZXI+DQo8aW1nIHNyYyA9ICJpbWcvcHJvamVjdGlvbi13aXphcmQuUE5HIiAvPg0KPC9jZW50ZXI+DQoNCjxicj4gDQoNCi0gUGFyYSBtw6FzIGluZm9ybWFjacOzbiBhY2VyY2EgZGUgcHJveWVjY2lvbmVzIGdlb2dyw6FmaWNhcywgcmVjb21pZW5kbyB2ZXIgZWwgc2lndWllbnRlIHZpZGVvLg0KDQo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL3NFYWtkOEF5bHY0IiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPg0KDQotICoqUHJveWVjY2nDs24gaW5pY2lhbDoqKg0KDQpgYGB7cn0NCnByb2o0c3RyaW5nKHNvbG9fY29sb21iaWEpDQpgYGANCg0KLSAqKkNhbWJpYW5kbyBwcm95ZWNjacOzbjoqKg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmNvbG9tYmlhX2lndWFsIDwtIHByb2plY3RSYXN0ZXIoDQogIHNvbG9fY29sb21iaWEsDQogIGNycyA9ICIrcHJvaj1sYWVhICtsb25fMD0tNzQuMDkxNzk2OSArbGF0XzA9MCArZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiDQopDQpwbG90KGNvbG9tYmlhX2lndWFsLCBjb2xOQSA9ICJibGFjayIpDQoNCmBgYA0KDQojIFNoYXBlICsgUmFzdGVyDQoNCi0gQ29udmVyc2nDs24gZGUgcmFzdGVyIGEgYmFzZSBkZSBkYXRvcyAoZGF0YS5mcmFtZSkuDQoNCmBgYHtyfQ0KY29sb21iaWFfZGF0YSA8LSBzb2xvX2NvbG9tYmlhICU+JSANCiAgYXMoIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKQ0KaGVhZChjb2xvbWJpYV9kYXRhKQ0KYGBgDQoNCi0gTWFwYSBjb24gZ2dncGxvdDI6DQoNCmBgYHtyfQ0KIyBPcGNpb25hbDogY2FtYmlhbmRvIG5vbWJyZSBkZSB2YXJpYWJsZSBsYXllcg0KY29sb21iaWFfZGF0YSA8LSBjb2xvbWJpYV9kYXRhICU+JSBkcGx5cjo6cmVuYW1lKFRlbXAgPSBsYXllcikNCg0KIyBHcsOhZmljbw0KZ2dwbG90KCkgKw0KICBnZW9tX3RpbGUoZGF0YSA9IGNvbG9tYmlhX2RhdGEsIGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBUZW1wKSkgKw0KICBnZW9tX3NmKGRhdGEgPSBjb2xvbWJpYV9zZiwgYWxwaGEgPSAwKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICB0aGVtZV9idygpDQpgYGAgDQoNCg0KIyBSZWN1cnNvcyByZWNvbWVuZGFkb3MNCg0KLSBbRGl2YS1HSVNdKGh0dHBzOi8vd3d3LmRpdmEtZ2lzLm9yZy9nZGF0YSkNCi0gW1NwYXRpYWwgRGF0YSBTY2llbmNlIHdpdGggUl0oaHR0cHM6Ly9yc3BhdGlhbC5vcmcvKQ0KLSBbV29ybGRjbGltXShodHRwczovL3dvcmxkY2xpbS5vcmcvZGF0YS92MS40L2Zvcm1hdHMuaHRtbCkNCi0gW0NIRUxTQV0oaHR0cHM6Ly9jaGVsc2EtY2xpbWF0ZS5vcmcvKQ0KLSBbUHJvamVjdGlvbiBXaXphcmRdKGh0dHBzOi8vcHJvamVjdGlvbndpemFyZC5vcmcvKQ0KDQoNCg0KDQo=