Trabajando con datos y mapas

Herramientas y técnicas de código abierto para visualizar datos en mapas personalizados

En la ciencia de datos, existe un área que trabaja con datos geoespaciales y especialmente en la generación de visualizaciones de mapas. Estos gráficos nos permiten comprender las métricas en distintas regiones geográficas.

“Recolectando y analizando datos hace lo invisible visible”

Melinda Gates

Creación de mapas con R

En R, es posible generar mapas utilizando los paquetes: ggplot2, leaflet,. Antes de adentrarnos en este paquete, es interesante conocer el funcionamiento del sistema de gráficos en R. Este sistema está conformado por:

  1. Paquetes gráficos.
  2. Sistemas gráficos.
  3. Motor de dispositivos gráficos.
  4. Paquetes de dispositivos gráficos.

Figura 1: Sistema de gráficos en R

El paquete ggplot2 permite elaborar gráficos a partir de un proceso de acumulación de capas. Tiene cierto grado de complejidad, pero permite obtener resultados muy profesionales.

Ventajas de crear mapas con R frente a hacerlo con software GIS

Las soluciones más comunes para generar mapas involucran software GIS como QGIS o ArcMap. Y entonces, ¿qué ventajas nos ofrece R frente al software GIS para generar nuestros mapas? Algunas de ellas son:

  • R es un software libre, lo que lo hace accesible para todos.
  • Los elementos de un mapa se pueden agregar o eliminar con facilidad.
  • Podemos reutilizar código rápidamente para generar el mismo tipo de mapa pero con otros datos.
  • Podemos automatizar rápidamente la creación de multitud de mapas.
  • Tiene un lenguaje interactivo, estructuras de datos, disponibilidad de gráficos, una comunidad desarrollada y la ventaja de agregar más funcionalidades a través de un ecosistema enorme de paquetes.

Código extraído de la investigación de Sara Acevedo

Es útil representar nuestros datos ambientales (en este proyecto: número de aves) de una forma simple y rápida. A continuación, se presenta el paso a paso de un proyecto de estudio que consta en la creación de un mapa de Santiago de Chile, en el cual se visualizan los puntos de mayor concentración de aves. Cabe resaltar que esta visualización se complementa con una capa de puntos sobre el mapa y su respectiva leyenda.

Librerias utilizadas

Se recomienda descargar las librerías en RStudio.

library(tidyverse) # Limpieza de datos
library(ggmap) # Mapas
library(sf)  # Dataframe a espacial
library(ggspatial) # Mapas
library(osmdata) # Mapa
library(vembedr)

knitr::opts_chunk$set(echo = TRUE)

Etapas

1. Cargar el bounding box de la ciudad a mapear.

Un bounding box (bbox) es un área definida por dos longitudes y dos latitudes, en el cuál el estandar es:

  • bbox = left, bottom, right, top
  • bbox = Longitud mínima, Latitud mínima, Longitud máxima, Latitud máxima.
stgo_bbox <- getbb("Santiago")

2. Crear el mapa desde Open Stret Map usando ggmap.

map <- get_stamenmap(
  bbox = c(
    left = stgo_bbox[1, 1],
    bottom = stgo_bbox[2, 1],
    right = stgo_bbox[1, 2],
    top = stgo_bbox[2, 2]
  ),
  zoom = 12,
  maptype = "terrain"
)

3. Crear un dataset de ejemplo ficticio, usando tribble.

En este proceso se ubicarán las variables latitud y longitud (para la geolocalización de puntos), así como el número de aves.

lugares_stgo <- tribble(
  ~ "lng", ~ "lat", ~ "numero_aves",
  -70.51861,
  -33.38371,
  21,
  -70.71982,
  -33.51318,
  33,
  -70.65768,
  -33.42345,
  12,
  -70.55768,
  -33.56177,
  15,
  -70.67279,
  -33.50156,
  52
)

Estos datos ahora son transformados a un objeto sf. Revisar siempre el CRS en esta etapa del proyecto.

4. El siguiente código representa el “coordinate reference system” de las coordenadas del dataframe original.

lugares_stgo_sf <-
  st_as_sf(lugares_stgo, coords = c("lng", "lat"), crs = 4326)

5. Por medio de ggmap() + geom_sf() graficar los puntos, incluyendo en aes(color) el número de aves para generar una leyenda.

mapa_urbano <- ggmap(map) +
  geom_sf(
    data = lugares_stgo_sf,
    aes(color = numero_aves),
    size = 3,
    inherit.aes = FALSE
  ) +
  labs(x = "", y = "", color = "número\nde aves") +
  annotation_scale(location = "br",
                   bar_cols = c("grey20", "white")) +
  ggtitle("Santiago de Chile") +
  annotation_north_arrow(
    location = "tl",
    which_north = "true",
    style = north_arrow_nautical(fill = c("grey40", "white"),
                                 line_col = "grey20")
  )
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
mapa_urbano

Siguientes pasos

A partir de este proyecto, se recomienda realizar proyectos similares de geolocalización en mapas, utilizando el lenguaje de programación Python.

Diferencias entre R y Python

Lenguaje R Lenguaje Python
De propósito general Lenguaje open source
Fácil de escribir y mantener tiene curva de aprendizaje
Flexibilidad y fácil lectura Usado para visualizaciones de datos
Dirigido principalmente a Machine Learning Dirigido principalmente hacia Ciencia de Datos

En Python, existen dos bibliotecas geoespaciales muy populares:

  1. Geopandas: extiende Pandas para permitir operaciones espaciales en tipos geométricos.

  2. Geoplot: una biblioteca de gráficos geoespaciales de alto nivel.

La segunda biblioteca es especialmente útil, ya que se basa en otras bibliotecas geoespaciales y permite simplificar la codificación. Entre otras bibliotecas, también se incluyen: cartopy, que a su vez aprovecha Cython, NumPy, GEOS, Shapely, pyshp, PROJ, Six y quizás algunos otros como mapclassify, según las funciones que se necesite usar.

Temas a revisar para próximos proyectos

En la siguiente lista se encuentran los términos más utilizados en el trabajo con datos geoespaciales:

  1. shapefile: formato de archivo de datos utilizado para representar elementos en un mapa.
  2. geometría: un vector que se utiliza para representar puntos, polígonos y otras formas o ubicaciones geométricas.
  3. polígono: un área.
  4. mapa base: la configuración de fondo de un mapa.
  5. proyección: representa una superficie 3D en 2D, utilizando un sistema de referencia de coordenadas.
  6. mapa de colores: elección de una paleta de colores para renderizar datos.
  7. cartograma: deformar el área relativa de los polígonos para representar niveles de datos.

No olvides practicar 😊

LS0tDQp0aXRsZTogIlZpc3VhbGl6YWNpw7NuIGRlIGRhdG9zIGdlb2VzcGFjaWFsZXMiDQphdXRob3I6ICJWYWxlcmlhIEJlbMOpbiBDZXJwYSBTYWxhcyINCmRhdGU6ICIyMDIyLzA2LzA1Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMjICoqVHJhYmFqYW5kbyBjb24gZGF0b3MgeSBtYXBhcyoqDQoNCiMjIyMgSGVycmFtaWVudGFzIHkgdMOpY25pY2FzIGRlIGPDs2RpZ28gYWJpZXJ0byBwYXJhIHZpc3VhbGl6YXIgZGF0b3MgZW4gbWFwYXMgcGVyc29uYWxpemFkb3MNCg0KRW4gbGEgY2llbmNpYSBkZSBkYXRvcywgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPmV4aXN0ZSB1biDDoXJlYSBxdWUgdHJhYmFqYSBjb24gZGF0b3MgZ2VvZXNwYWNpYWxlcyB5IGVzcGVjaWFsbWVudGUgZW4gbGEgZ2VuZXJhY2nDs24gZGUgdmlzdWFsaXphY2lvbmVzIGRlIG1hcGFzPC9zcGFuPi4gRXN0b3MgZ3LDoWZpY29zIG5vcyBwZXJtaXRlbiBjb21wcmVuZGVyIGxhcyBtw6l0cmljYXMgZW4gZGlzdGludGFzIHJlZ2lvbmVzIGdlb2dyw6FmaWNhcy4NCg0KPiAiUmVjb2xlY3RhbmRvIHkgYW5hbGl6YW5kbyBkYXRvcyBoYWNlIGxvIGludmlzaWJsZSB2aXNpYmxlIg0KPg0KPioqTWVsaW5kYSBHYXRlcyoqDQoNCg0KIyMgKipDcmVhY2nDs24gZGUgbWFwYXMgY29uIFIqKg0KDQpFbiBSLCBlcyBwb3NpYmxlIGdlbmVyYXIgbWFwYXMgdXRpbGl6YW5kbyBsb3MgcGFxdWV0ZXM6IFsqKmdncGxvdDIqKl0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSwgWyoqbGVhZmxldCoqXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pLC4gQW50ZXMgZGUgYWRlbnRyYXJub3MgZW4gZXN0ZSBwYXF1ZXRlLCBlcyBpbnRlcmVzYW50ZSBjb25vY2VyIGVsIGZ1bmNpb25hbWllbnRvIGRlbCBzaXN0ZW1hIGRlIGdyw6FmaWNvcyBlbiBSLiBFc3RlIHNpc3RlbWEgZXN0w6EgY29uZm9ybWFkbyBwb3I6DQogDQoxLiBQYXF1ZXRlcyBncsOhZmljb3MuDQoyLiBTaXN0ZW1hcyBncsOhZmljb3MuDQozLiBNb3RvciBkZSBkaXNwb3NpdGl2b3MgZ3LDoWZpY29zLg0KNC4gUGFxdWV0ZXMgZGUgZGlzcG9zaXRpdm9zIGdyw6FmaWNvcy4NCg0KPGNlbnRlcj4hWyoqRmlndXJhIDE6KiogU2lzdGVtYSBkZSBncsOhZmljb3MgZW4gUl0oaHR0cHM6Ly9tYXBwaW5nZ2lzLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxOS8wNy9JbWFnZW4yLnBuZyl7d2lkdGg9JzYwMHB4J308L2NlbnRlcj4NCg0KRWwgcGFxdWV0ZSAqKmdncGxvdDIqKiBwZXJtaXRlIGVsYWJvcmFyIGdyw6FmaWNvcyBhIHBhcnRpciBkZSB1biBwcm9jZXNvIGRlIGFjdW11bGFjacOzbiBkZSBjYXBhcy4gVGllbmUgY2llcnRvIGdyYWRvIGRlIGNvbXBsZWppZGFkLCBwZXJvIHBlcm1pdGUgb2J0ZW5lciByZXN1bHRhZG9zIG11eSBwcm9mZXNpb25hbGVzLg0KDQojIyMgKipWZW50YWphcyBkZSBjcmVhciBtYXBhcyBjb24gUiBmcmVudGUgYSBoYWNlcmxvIGNvbiBzb2Z0d2FyZSBHSVMqKg0KTGFzIHNvbHVjaW9uZXMgbcOhcyBjb211bmVzIHBhcmEgZ2VuZXJhciBtYXBhcyBpbnZvbHVjcmFuIHNvZnR3YXJlIEdJUyBjb21vICoqUUdJUyoqIG8gKipBcmNNYXAqKi4gWSBlbnRvbmNlcywgwr9xdcOpIHZlbnRhamFzIG5vcyBvZnJlY2UgUiBmcmVudGUgYWwgc29mdHdhcmUgR0lTIHBhcmEgZ2VuZXJhciBudWVzdHJvcyBtYXBhcz8gQWxndW5hcyBkZSBlbGxhcyBzb246DQoNCi0gUiBlcyB1biBzb2Z0d2FyZSBsaWJyZSwgbG8gcXVlIGxvIGhhY2UgYWNjZXNpYmxlIHBhcmEgdG9kb3MuDQotIExvcyBlbGVtZW50b3MgZGUgdW4gbWFwYSBzZSBwdWVkZW4gYWdyZWdhciBvIGVsaW1pbmFyIGNvbiBmYWNpbGlkYWQuDQotIFBvZGVtb3MgcmV1dGlsaXphciBjw7NkaWdvIHLDoXBpZGFtZW50ZSBwYXJhIGdlbmVyYXIgZWwgbWlzbW8gdGlwbyBkZSBtYXBhIHBlcm8gY29uIG90cm9zIGRhdG9zLg0KLSBQb2RlbW9zIGF1dG9tYXRpemFyIHLDoXBpZGFtZW50ZSBsYSBjcmVhY2nDs24gZGUgbXVsdGl0dWQgZGUgbWFwYXMuDQotIFRpZW5lIHVuIGxlbmd1YWplIGludGVyYWN0aXZvLCBlc3RydWN0dXJhcyBkZSBkYXRvcywgZGlzcG9uaWJpbGlkYWQgZGUgZ3LDoWZpY29zLCB1bmEgY29tdW5pZGFkIGRlc2Fycm9sbGFkYSB5IGxhIHZlbnRhamEgZGUgYWdyZWdhciBtw6FzIGZ1bmNpb25hbGlkYWRlcyBhIHRyYXbDqXMgZGUgdW4gZWNvc2lzdGVtYSBlbm9ybWUgZGUgcGFxdWV0ZXMuDQoNCiMjIyAqKkPDs2RpZ28gZXh0cmHDrWRvIGRlIGxhIGludmVzdGlnYWNpw7NuIGRlIFNhcmEgQWNldmVkbyoqDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCkVzIMO6dGlsIHJlcHJlc2VudGFyIG51ZXN0cm9zIGRhdG9zIGFtYmllbnRhbGVzIChlbiBlc3RlIHByb3llY3RvOiBuw7ptZXJvIGRlIGF2ZXMpIGRlIHVuYSBmb3JtYSBzaW1wbGUgeSByw6FwaWRhLiBBIGNvbnRpbnVhY2nDs24sIHNlIHByZXNlbnRhIGVsIHBhc28gYSBwYXNvIGRlIHVuIHByb3llY3RvIGRlIGVzdHVkaW8gcXVlIGNvbnN0YSBlbiBsYSBjcmVhY2nDs24gZGUgdW4gbWFwYSBkZSBTYW50aWFnbyBkZSBDaGlsZSwgZW4gZWwgY3VhbCBzZSB2aXN1YWxpemFuIGxvcyBwdW50b3MgZGUgbWF5b3IgY29uY2VudHJhY2nDs24gZGUgYXZlcy4gQ2FiZSByZXNhbHRhciBxdWUgZXN0YSB2aXN1YWxpemFjacOzbiBzZSBjb21wbGVtZW50YSBjb24gdW5hIGNhcGEgZGUgcHVudG9zIHNvYnJlIGVsIG1hcGEgeSBzdSByZXNwZWN0aXZhIGxleWVuZGEuDQoNCiMjIyMgTGlicmVyaWFzIHV0aWxpemFkYXMNClNlIHJlY29taWVuZGEgZGVzY2FyZ2FyIGxhcyBsaWJyZXLDrWFzIGVuIFJTdHVkaW8uDQoNCmBgYHtyIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpICMgTGltcGllemEgZGUgZGF0b3MNCmxpYnJhcnkoZ2dtYXApICMgTWFwYXMNCmxpYnJhcnkoc2YpICAjIERhdGFmcmFtZSBhIGVzcGFjaWFsDQpsaWJyYXJ5KGdnc3BhdGlhbCkgIyBNYXBhcw0KbGlicmFyeShvc21kYXRhKSAjIE1hcGENCmxpYnJhcnkodmVtYmVkcikNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMjIyAqKkV0YXBhcyoqIA0KDQojIyMjIDEuIENhcmdhciBlbCBib3VuZGluZyBib3ggZGUgbGEgY2l1ZGFkIGEgbWFwZWFyLg0KVW4gYm91bmRpbmcgYm94IChiYm94KSBlcyB1biDDoXJlYSBkZWZpbmlkYSBwb3IgZG9zIGxvbmdpdHVkZXMgeSBkb3MgbGF0aXR1ZGVzLCBlbiBlbCBjdcOhbCBlbCBlc3RhbmRhciBlczoNCg0KLSBiYm94ID0gbGVmdCwgYm90dG9tLCByaWdodCwgdG9wDQotIGJib3ggPSBMb25naXR1ZCBtw61uaW1hLCBMYXRpdHVkIG3DrW5pbWEsIExvbmdpdHVkIG3DoXhpbWEsIExhdGl0dWQgbcOheGltYS4NCg0KYGBge3J9DQpzdGdvX2Jib3ggPC0gZ2V0YmIoIlNhbnRpYWdvIikNCmBgYA0KDQoNCiMjIyMgMi4gQ3JlYXIgZWwgbWFwYSBkZXNkZSBPcGVuIFN0cmV0IE1hcCB1c2FuZG8gZ2dtYXAuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbWFwIDwtIGdldF9zdGFtZW5tYXAoDQogIGJib3ggPSBjKA0KICAgIGxlZnQgPSBzdGdvX2Jib3hbMSwgMV0sDQogICAgYm90dG9tID0gc3Rnb19iYm94WzIsIDFdLA0KICAgIHJpZ2h0ID0gc3Rnb19iYm94WzEsIDJdLA0KICAgIHRvcCA9IHN0Z29fYmJveFsyLCAyXQ0KICApLA0KICB6b29tID0gMTIsDQogIG1hcHR5cGUgPSAidGVycmFpbiINCikNCmBgYA0KDQoNCiMjIyMgMy4gQ3JlYXIgdW4gZGF0YXNldCBkZSBlamVtcGxvIGZpY3RpY2lvLCB1c2FuZG8gdHJpYmJsZS4NCkVuIGVzdGUgcHJvY2VzbyBzZSB1YmljYXLDoW4gbGFzIHZhcmlhYmxlcyBsYXRpdHVkIHkgbG9uZ2l0dWQgKHBhcmEgbGEgZ2VvbG9jYWxpemFjacOzbiBkZSBwdW50b3MpLCBhc8OtIGNvbW8gZWwgbsO6bWVybyBkZSBhdmVzLg0KDQpgYGB7cn0NCmx1Z2FyZXNfc3RnbyA8LSB0cmliYmxlKA0KICB+ICJsbmciLCB+ICJsYXQiLCB+ICJudW1lcm9fYXZlcyIsDQogIC03MC41MTg2MSwNCiAgLTMzLjM4MzcxLA0KICAyMSwNCiAgLTcwLjcxOTgyLA0KICAtMzMuNTEzMTgsDQogIDMzLA0KICAtNzAuNjU3NjgsDQogIC0zMy40MjM0NSwNCiAgMTIsDQogIC03MC41NTc2OCwNCiAgLTMzLjU2MTc3LA0KICAxNSwNCiAgLTcwLjY3Mjc5LA0KICAtMzMuNTAxNTYsDQogIDUyDQopDQpgYGANCg0KRXN0b3MgZGF0b3MgYWhvcmEgc29uIHRyYW5zZm9ybWFkb3MgYSB1biBvYmpldG8gc2YuIFJldmlzYXIgc2llbXByZSBlbCBDUlMgZW4gZXN0YSBldGFwYSBkZWwgcHJveWVjdG8uDQoNCiMjIyMgNC4gRWwgc2lndWllbnRlIGPDs2RpZ28gcmVwcmVzZW50YSBlbCAiY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtIiBkZSBsYXMgY29vcmRlbmFkYXMgZGVsIGRhdGFmcmFtZSBvcmlnaW5hbC4NCg0KYGBge3J9DQpsdWdhcmVzX3N0Z29fc2YgPC0NCiAgc3RfYXNfc2YobHVnYXJlc19zdGdvLCBjb29yZHMgPSBjKCJsbmciLCAibGF0IiksIGNycyA9IDQzMjYpDQpgYGANCg0KIyMjIyA1LiBQb3IgbWVkaW8gZGUgZ2dtYXAoKSArIGdlb21fc2YoKSBncmFmaWNhciBsb3MgcHVudG9zLCBpbmNsdXllbmRvIGVuIGFlcyhjb2xvcikgZWwgbsO6bWVybyBkZSBhdmVzIHBhcmEgZ2VuZXJhciB1bmEgbGV5ZW5kYS4NCg0KYGBge3J9DQptYXBhX3VyYmFubyA8LSBnZ21hcChtYXApICsNCiAgZ2VvbV9zZigNCiAgICBkYXRhID0gbHVnYXJlc19zdGdvX3NmLA0KICAgIGFlcyhjb2xvciA9IG51bWVyb19hdmVzKSwNCiAgICBzaXplID0gMywNCiAgICBpbmhlcml0LmFlcyA9IEZBTFNFDQogICkgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiLCBjb2xvciA9ICJuw7ptZXJvXG5kZSBhdmVzIikgKw0KICBhbm5vdGF0aW9uX3NjYWxlKGxvY2F0aW9uID0gImJyIiwNCiAgICAgICAgICAgICAgICAgICBiYXJfY29scyA9IGMoImdyZXkyMCIsICJ3aGl0ZSIpKSArDQogIGdndGl0bGUoIlNhbnRpYWdvIGRlIENoaWxlIikgKw0KICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KA0KICAgIGxvY2F0aW9uID0gInRsIiwNCiAgICB3aGljaF9ub3J0aCA9ICJ0cnVlIiwNCiAgICBzdHlsZSA9IG5vcnRoX2Fycm93X25hdXRpY2FsKGZpbGwgPSBjKCJncmV5NDAiLCAid2hpdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVfY29sID0gImdyZXkyMCIpDQogICkNCm1hcGFfdXJiYW5vDQpgYGANCg0KDQojIyMgKipTaWd1aWVudGVzIHBhc29zKioNCkEgcGFydGlyIGRlIGVzdGUgcHJveWVjdG8sIHNlIHJlY29taWVuZGEgcmVhbGl6YXIgcHJveWVjdG9zIHNpbWlsYXJlcyBkZSBnZW9sb2NhbGl6YWNpw7NuIGVuIG1hcGFzLCB1dGlsaXphbmRvIGVsIGxlbmd1YWplIGRlIHByb2dyYW1hY2nDs24gUHl0aG9uLg0KDQoNCiMjIyMgRGlmZXJlbmNpYXMgZW50cmUgUiB5IFB5dGhvbg0KDQpMZW5ndWFqZSBSIHwgTGVuZ3VhamUgUHl0aG9uDQotLSB8IC0tDQpEZSBwcm9ww7NzaXRvIGdlbmVyYWwgfCBMZW5ndWFqZSBvcGVuIHNvdXJjZQ0KRsOhY2lsIGRlIGVzY3JpYmlyIHkgbWFudGVuZXIgfCB0aWVuZSBjdXJ2YSBkZSBhcHJlbmRpemFqZQ0KRmxleGliaWxpZGFkIHkgZsOhY2lsIGxlY3R1cmEgfCBVc2FkbyBwYXJhIHZpc3VhbGl6YWNpb25lcyBkZSBkYXRvcw0KRGlyaWdpZG8gcHJpbmNpcGFsbWVudGUgYSBNYWNoaW5lIExlYXJuaW5nIHwgRGlyaWdpZG8gcHJpbmNpcGFsbWVudGUgaGFjaWEgQ2llbmNpYSBkZSBEYXRvcw0KDQoNCkVuIFB5dGhvbiwgZXhpc3RlbiBkb3MgYmlibGlvdGVjYXMgZ2VvZXNwYWNpYWxlcyBtdXkgcG9wdWxhcmVzOg0KDQoxLiBbKipHZW9wYW5kYXMqKl0oaHR0cHM6Ly9nZW9wYW5kYXMub3JnL2VuL3N0YWJsZS8pOiBleHRpZW5kZSBQYW5kYXMgcGFyYSBwZXJtaXRpciBvcGVyYWNpb25lcyBlc3BhY2lhbGVzIGVuIHRpcG9zIGdlb23DqXRyaWNvcy4NCg0KMi4gWyoqR2VvcGxvdCoqXShodHRwczovL3Jlc2lkZW50bWFyaW8uZ2l0aHViLmlvL2dlb3Bsb3QvaW5kZXguaHRtbCk6IHVuYSBiaWJsaW90ZWNhIGRlIGdyw6FmaWNvcyBnZW9lc3BhY2lhbGVzIGRlIGFsdG8gbml2ZWwuIA0KDQpMYSBzZWd1bmRhIGJpYmxpb3RlY2EgZXMgZXNwZWNpYWxtZW50ZSDDunRpbCwgeWEgcXVlIHNlIGJhc2EgZW4gb3RyYXMgYmlibGlvdGVjYXMgZ2VvZXNwYWNpYWxlcyB5IHBlcm1pdGUgc2ltcGxpZmljYXIgbGEgY29kaWZpY2FjacOzbi4gRW50cmUgb3RyYXMgYmlibGlvdGVjYXMsIHRhbWJpw6luIHNlIGluY2x1eWVuOiAqY2FydG9weSosIHF1ZSBhIHN1IHZleiBhcHJvdmVjaGEgKkN5dGhvbiosICpOdW1QeSosICpHRU9TKiwgKlNoYXBlbHkqLCAqcHlzaHAqLCAqUFJPSiosICpTaXgqIHkgcXVpesOhcyBhbGd1bm9zIG90cm9zIGNvbW8gKm1hcGNsYXNzaWZ5Kiwgc2Vnw7puIGxhcyBmdW5jaW9uZXMgcXVlIHNlIG5lY2VzaXRlIHVzYXIuDQoNCiMjIyAqKlRlbWFzIGEgcmV2aXNhciBwYXJhIHByw7N4aW1vcyBwcm95ZWN0b3MqKg0KRW4gbGEgc2lndWllbnRlIGxpc3RhIHNlIGVuY3VlbnRyYW4gbG9zIHTDqXJtaW5vcyBtw6FzIHV0aWxpemFkb3MgZW4gZWwgdHJhYmFqbyBjb24gZGF0b3MgZ2VvZXNwYWNpYWxlczoNCg0KMS4gKipzaGFwZWZpbGUqKjogZm9ybWF0byBkZSBhcmNoaXZvIGRlIGRhdG9zIHV0aWxpemFkbyBwYXJhIHJlcHJlc2VudGFyIGVsZW1lbnRvcyBlbiB1biBtYXBhLg0KMi4gKipnZW9tZXRyw61hKio6IHVuIHZlY3RvciBxdWUgc2UgdXRpbGl6YSBwYXJhIHJlcHJlc2VudGFyIHB1bnRvcywgcG9sw61nb25vcyB5IG90cmFzIGZvcm1hcyBvIHViaWNhY2lvbmVzIGdlb23DqXRyaWNhcy4NCjMuICoqcG9sw61nb25vKio6IHVuIMOhcmVhLg0KNC4gKiptYXBhIGJhc2UqKjogbGEgY29uZmlndXJhY2nDs24gZGUgZm9uZG8gZGUgdW4gbWFwYS4NCjUuICoqcHJveWVjY2nDs24qKjogcmVwcmVzZW50YSB1bmEgc3VwZXJmaWNpZSAzRCBlbiAyRCwgdXRpbGl6YW5kbyB1biBzaXN0ZW1hIGRlIHJlZmVyZW5jaWEgZGUgY29vcmRlbmFkYXMuDQo2LiAqKm1hcGEgZGUgY29sb3JlcyoqOiBlbGVjY2nDs24gZGUgdW5hIHBhbGV0YSBkZSBjb2xvcmVzIHBhcmEgcmVuZGVyaXphciBkYXRvcy4NCjcuICoqY2FydG9ncmFtYSoqOiBkZWZvcm1hciBlbCDDoXJlYSByZWxhdGl2YSBkZSBsb3MgcG9sw61nb25vcyBwYXJhIHJlcHJlc2VudGFyIG5pdmVsZXMgZGUgZGF0b3MuDQoNCiMjIyAqKk5vIG9sdmlkZXMgcHJhY3RpY2FyKiog8J+Yig0KDQohW10oaHR0cHM6Ly9taXJvLm1lZGl1bS5jb20vbWF4LzE0MDAvMSphLUhNZmVnNXctVzAyTnJ3MjFpUHRnLmdpZik=