1 Introduction

• Spatial data provide insights

Spread of a disease, or

Situation of an outbreak

Where are the current disease hotspots?

• How have the hotspots changed over time?

• How is the access to health facilities?

• Today, why to use R to address these tasks.

2 Learning objectives

  1. Define what is a geospatial analysis.

  2. Identify the main analytical task that a GIS software need to solve.

  3. Identify the advantages of R as a GIS software.

3 Prerequisites

This lesson requires familiarity with basic R and {ggplot2}: if you need to brush up, have a look at our introductory course on R and data visualization.

if(!require('pacman')) install.packages('pacman')
pacman::p_load_gh("wmgeolab/rgeoboundaries")
pacman::p_load(tidyverse, 
               ggspatial, 
               leaflet, 
               mapview,
               raster,
               spData,
               stars, 
               tmap, 
               here,
               sf)

4 What is Geospatial analysis?

• Data with geographic locations or coordinates

• Related to positions on the Earth’s surface.

• Essential to epidemiology.

• Identify hot-spots and potential high-risk areas for communicable disease spread;

• Map of malaria prevalence predictions in The Gambia (Moraga, 2019)

• Let’s see how the code looks like!

# 👉 first, get packages:

if(!require('pacman')) install.packages('pacman')
pacman::p_load_gh("wmgeolab/rgeoboundaries")
pacman::p_load(tidyverse, ggspatial, leaflet, 
               raster, stars, here, prettymapr)
# 👉 second, get data:

# country boundaries
gambia_boundaries <- geoboundaries(country = "Gambia", adm_lvl = 1)
# malaria prevalence
gambia_prevalence <- read_rds(here("data", "gambia_prevalence.rds"))
# 👉 third, plot data:

ggplot() +
  # with a background
  annotation_map_tile(data = gambia_boundaries, zoomin = 0) +
  # plus a prevalence surface
  geom_stars(data = st_as_stars(gambia_prevalence)) +
  # with a color scale
  scale_fill_viridis_c(na.value = "transparent", alpha = 0.75) +
  # and a coordinate system
  coord_sf()

• Here, skills for geospatial visualization,

• To make accurate, elegant and informative maps.

5 R as a GIS

• Geospatial analysis needs a geographic information system (GIS).

Manage, analyze, and visualize spatial data.

• Popular platforms, ArcGIS and QGIS, are graphic-user-interface (GUI).

• So why use R for geospatial work?

• Here five of its merits:

5.1 (1/5) Reproducibility:

• Code is straightforward for anyone to re-run,

• Easily build on other people’s work

• Facilitates collaboration

• Paste this code and reproduce in your computer:

# 👉 packages
if(!require('pacman')) install.packages('pacman')
pacman::p_load(sf, ggplot2)

# 👉 data 
nc <- st_read(system.file("shape/nc.shp", package = "sf"),
              quiet = TRUE)
# 👉 plot
ggplot(data = nc) + 
  geom_sf(aes(fill = SID74)) +
  scale_fill_viridis_c()

5.2 (2/5)Reporting:

{Rmarkdown}, {flexdashboard} and {shiny} to generate reports and dashboards.

Interactive maps with {leaflet} instead of {ggplot2}:

# 👉 packages
if(!require('pacman')) install.packages('pacman')
pacman::p_load(sf, leaflet)

# 👉 data
nc <- st_read(system.file("shape/nc.shp", package = "sf"),
              quiet = TRUE)

# 👉 plot
pal <- colorNumeric("YlOrRd", domain = nc$SID74)
leaflet(nc) %>%
  addTiles() %>%
  addPolygons(color = "white", fillColor = ~ pal(SID74),
              fillOpacity = 1) %>%
  addLegend(pal = pal, values = ~SID74, opacity = 1)

5.3 (3/5) Rich ecosystem:

• R with rapidly growing libraries

• highly-active open-source community,

• ready-to-use packages or tutorials.

interactive map with one line of code!

{mapview} instead of {leaflet}:

# 👉 packages
if(!require('pacman')) install.packages('pacman')
pacman::p_load(sf, mapview)

# 👉 data
nc <- st_read(system.file("shape/nc.shp", package = "sf"),
              quiet = TRUE)

# 👉 plot
mapview(nc, zcol = "SID74")

5.4 (4/5) Convenience:

• You already know R!

• Explore new pieces of code.

As an example, we will use the {tmap} package and make minor modifications to it!

First, run this chunk:

# 👉 packages
if(!require('pacman')) install.packages('pacman')
pacman::p_load(tmap, spData)

# 👉 data
load(here("data/nz_elev.rda"))

# 👉 plot
tm_shape(nz_elev)  +
  tm_raster(title = "Elevation (m)",  # Add units to the legend title
            style = "cont",
            palette = "-BuGn") +
  tm_shape(nz) +
  tm_borders(col = "black", 
             lwd = 1) + # Reduce line width
  tm_scale_bar(breaks = c(0, 100, 200),
               text.size = 1) +
  tm_compass(position = c("RIGHT", "top"),
             type = "rose", 
             size = 2) +
  tm_credits(text = "O A Lawal, 2024") +
  tm_layout(main.title = "New Zealand",
            bg.color = "lightgreen",  # Change background color
            inner.margins = c(0, 0, 0, 0),  legend.title.size = 1.5) # Adjust legend title size

Now, apply any of the following suggestions to get used to how this package works:

  1. Change the map title from “My map” to “New Zealand”.
  2. Update the map credits with your own name and today’s date.
  3. Change the color palette to “BuGn”.
  4. Try other palettes from http://colorbrewer2.org/
  5. Put the north arrow in the top right corner of the map.
  6. Improve the legend title by adding the legend units.
  7. Increase the number of breaks in the scale bar.
  8. Change the borders’ color of the New Zealand’s regions to black.
  9. Decrease the line width.
  10. Change the background color to any color of your choice.

5.5 (5/5) Integrated workflow:

• Combine geospatial visualization and statistical analyses,

• all within a single script.

• For example, built 3D maps of the Monterey Bay using {rayshader}

• Tutorial available: https://www.tylermw.com/3d-maps-with-rayshader/

• Bivariate maps of unequal distribution of the income.

• Tutorial available: https://timogrossenbacher.ch/2019/04/bivariate-maps-with-ggplot2-and-sf/

6 Wrap up

• We learned why to use R as a GIS software,

• take advantage of its coding environment.

• But, which maps are we going to built?

Figure 1. Thematic maps: (A) Choropleth map, (B) Dot map, (C) Density map, and (D) Basemap for a dot map.
Figure 1. Thematic maps: (A) Choropleth map, (B) Dot map, (C) Density map, and (D) Basemap for a dot map.

• How to built -step by step- different types of Thematic maps using the {ggplot2} package,

• different data sources and illustrative annotations.

Figure 2. {ggplot2} map with text annotations, a scale bar and north arrow.
Figure 2. {ggplot2} map with text annotations, a scale bar and north arrow.

Contributors

The following team members contributed to this lesson:

References

Some material in this lesson was adapted from the following sources:

This work is licensed under the Creative Commons Attribution Share Alike license. Creative Commons License

LS0tDQp0aXRsZTogJ1IgZm9yIEdJUycNCmF1dGhvcjoNCiAgLSBuYW1lOiAiQW5kcmVlIFZhbGxlIENhbXBvcyINCiAgLSBuYW1lOiAiS2VuZSBEYXZpZCBOd29zdSINCmRhdGU6ICIyMDI0LTExLTIyIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIGNzczogIWV4cHIgaGVyZTo6aGVyZSgiZ2xvYmFsL3N0eWxlL3N0eWxlLmNzcyIpDQogICAgaGlnaGxpZ2h0OiBrYXRlDQogICAgcGFuZG9jX2FyZ3M6IC0tc2hpZnQtaGVhZGluZy1sZXZlbC1ieT0tMQ0KZWRpdG9yX29wdGlvbnM6DQogIG1hcmtkb3duOg0KICAgIHdyYXA6IDEwMA0KICBjYW5vbmljYWw6IHRydWUNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyLCBpbmNsdWRlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KIyBMb2FkIHBhY2thZ2VzIA0KaWYoIXJlcXVpcmUocGFjbWFuKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwga25pdHIsIGhlcmUpDQoNCiMgU291cmNlIGZ1bmN0aW9ucyANCnNvdXJjZShoZXJlKCJnbG9iYWwvZnVuY3Rpb25zL21pc2NfZnVuY3Rpb25zLlIiKSkNCg0KIyBrbml0ciBzZXR0aW5ncw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2xhc3Muc291cmNlID0gInRnYy1jb2RlLWJsb2NrIiwgZXJyb3IgPSBUKQ0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQpnZ3Bsb3QyOjp0aGVtZV9zZXQobmV3ID0gdGhlbWVfYncoKSkNCm9wdGlvbnMoc2NpcGVuPTEwMDAwKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo8IS0tICMgR2Vvc3BhdGlhbCBhbmFseXNpczogUiBmb3IgR0lTIC0tPg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0K4oCiIFNwYXRpYWwgZGF0YSBwcm92aWRlIGluc2lnaHRzDQoNCuKAoiAqU3ByZWFkKiBvZiBhIGRpc2Vhc2UsIG9yDQoNCuKAoiAqU2l0dWF0aW9uKiBvZiBhbiBvdXRicmVhaw0KDQrigKIgKipXaGVyZSoqIGFyZSB0aGUgY3VycmVudCBkaXNlYXNlIGhvdHNwb3RzPw0KDQrigKIgSG93IGhhdmUgdGhlIGhvdHNwb3RzICoqY2hhbmdlZCBvdmVyIHRpbWUqKj8NCg0K4oCiIEhvdyBpcyB0aGUgKiphY2Nlc3MqKiB0byBoZWFsdGggZmFjaWxpdGllcz8NCg0KIVtdKGltYWdlcy9naXNfaGVhZF9pbWFnZS5wbmcpDQoNCuKAoiBUb2RheSwgKip3aHkgdG8gdXNlIFIqKiB0byBhZGRyZXNzIHRoZXNlIHRhc2tzLg0KDQojIyBMZWFybmluZyBvYmplY3RpdmVzDQoNCjEuICBEZWZpbmUgd2hhdCBpcyBhICoqZ2Vvc3BhdGlhbCBhbmFseXNpcyoqLg0KDQoyLiAgSWRlbnRpZnkgdGhlIG1haW4gYW5hbHl0aWNhbCB0YXNrIHRoYXQgYSAqKkdJUyBzb2Z0d2FyZSoqIG5lZWQgdG8gc29sdmUuDQoNCjMuICBJZGVudGlmeSB0aGUgKiphZHZhbnRhZ2VzKiogb2YgUiBhcyBhIEdJUyBzb2Z0d2FyZS4NCg0KIyMgUHJlcmVxdWlzaXRlcw0KDQpUaGlzIGxlc3NvbiByZXF1aXJlcyBmYW1pbGlhcml0eSB3aXRoIGJhc2ljIFIgYW5kIGB7Z2dwbG90Mn1gOiBpZiB5b3UgbmVlZCB0byBicnVzaCB1cCwgaGF2ZSBhIGxvb2sgYXQgb3VyIGludHJvZHVjdG9yeSBjb3Vyc2Ugb24gUiBhbmQgZGF0YSB2aXN1YWxpemF0aW9uLg0KDQpgYGB7cixldmFsPVRSVUUsZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0V9DQppZighcmVxdWlyZSgncGFjbWFuJykpIGluc3RhbGwucGFja2FnZXMoJ3BhY21hbicpDQpwYWNtYW46OnBfbG9hZF9naCgid21nZW9sYWIvcmdlb2JvdW5kYXJpZXMiKQ0KcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCANCiAgICAgICAgICAgICAgIGdnc3BhdGlhbCwgDQogICAgICAgICAgICAgICBsZWFmbGV0LCANCiAgICAgICAgICAgICAgIG1hcHZpZXcsDQogICAgICAgICAgICAgICByYXN0ZXIsDQogICAgICAgICAgICAgICBzcERhdGEsDQogICAgICAgICAgICAgICBzdGFycywgDQogICAgICAgICAgICAgICB0bWFwLCANCiAgICAgICAgICAgICAgIGhlcmUsDQogICAgICAgICAgICAgICBzZikNCmBgYA0KDQojIyBXaGF0IGlzIEdlb3NwYXRpYWwgYW5hbHlzaXM/DQoNCuKAoiBEYXRhIHdpdGggKmdlb2dyYXBoaWMqIGxvY2F0aW9ucyBvciBjb29yZGluYXRlcw0KDQrigKIgUmVsYXRlZCB0byBwb3NpdGlvbnMgb24gdGhlIEVhcnRoJ3Mgc3VyZmFjZS4NCg0K4oCiIEVzc2VudGlhbCB0byBlcGlkZW1pb2xvZ3kuDQoNCuKAoiBJZGVudGlmeSAqKmhvdC1zcG90cyoqIGFuZCBwb3RlbnRpYWwgKipoaWdoLXJpc2sgYXJlYXMqKiBmb3IgY29tbXVuaWNhYmxlIGRpc2Vhc2Ugc3ByZWFkOw0KDQrigKIgTWFwIG9mIG1hbGFyaWEgcHJldmFsZW5jZSBwcmVkaWN0aW9ucyBpbiBUaGUgR2FtYmlhIChNb3JhZ2EsIDIwMTkpDQoNCiFbXShpbWFnZXMvbWFsYXJpYV9nYW1iaWFfMDEucG5nKQ0KDQrigKIgTGV0J3Mgc2VlIGhvdyB0aGUgY29kZSBsb29rcyBsaWtlIQ0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQojIPCfkYkgZmlyc3QsIGdldCBwYWNrYWdlczoNCg0KaWYoIXJlcXVpcmUoJ3BhY21hbicpKSBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KcGFjbWFuOjpwX2xvYWRfZ2goIndtZ2VvbGFiL3JnZW9ib3VuZGFyaWVzIikNCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgZ2dzcGF0aWFsLCBsZWFmbGV0LCANCiAgICAgICAgICAgICAgIHJhc3Rlciwgc3RhcnMsIGhlcmUsIHByZXR0eW1hcHIpDQpgYGANCg0KYGBge3J9DQojIPCfkYkgc2Vjb25kLCBnZXQgZGF0YToNCg0KIyBjb3VudHJ5IGJvdW5kYXJpZXMNCmdhbWJpYV9ib3VuZGFyaWVzIDwtIGdlb2JvdW5kYXJpZXMoY291bnRyeSA9ICJHYW1iaWEiLCBhZG1fbHZsID0gMSkNCiMgbWFsYXJpYSBwcmV2YWxlbmNlDQpnYW1iaWFfcHJldmFsZW5jZSA8LSByZWFkX3JkcyhoZXJlKCJkYXRhIiwgImdhbWJpYV9wcmV2YWxlbmNlLnJkcyIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyDwn5GJIHRoaXJkLCBwbG90IGRhdGE6DQoNCmdncGxvdCgpICsNCiAgIyB3aXRoIGEgYmFja2dyb3VuZA0KICBhbm5vdGF0aW9uX21hcF90aWxlKGRhdGEgPSBnYW1iaWFfYm91bmRhcmllcywgem9vbWluID0gMCkgKw0KICAjIHBsdXMgYSBwcmV2YWxlbmNlIHN1cmZhY2UNCiAgZ2VvbV9zdGFycyhkYXRhID0gc3RfYXNfc3RhcnMoZ2FtYmlhX3ByZXZhbGVuY2UpKSArDQogICMgd2l0aCBhIGNvbG9yIHNjYWxlDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hLnZhbHVlID0gInRyYW5zcGFyZW50IiwgYWxwaGEgPSAwLjc1KSArDQogICMgYW5kIGEgY29vcmRpbmF0ZSBzeXN0ZW0NCiAgY29vcmRfc2YoKQ0KYGBgDQoNCuKAoiBIZXJlLCBza2lsbHMgZm9yICoqZ2Vvc3BhdGlhbCB2aXN1YWxpemF0aW9uKiosDQoNCuKAoiBUbyBtYWtlICphY2N1cmF0ZSosICplbGVnYW50KiBhbmQgKmluZm9ybWF0aXZlKiBtYXBzLg0KDQojIyBSIGFzIGEgR0lTDQoNCuKAoiBHZW9zcGF0aWFsIGFuYWx5c2lzIG5lZWRzIGEgKipnZW9ncmFwaGljIGluZm9ybWF0aW9uIHN5c3RlbSAoR0lTKSoqLg0KDQrigKIgKk1hbmFnZSosICphbmFseXplKiwgYW5kICp2aXN1YWxpemUqIHNwYXRpYWwgZGF0YS4NCg0K4oCiIFBvcHVsYXIgcGxhdGZvcm1zLCAqKkFyY0dJUyoqIGFuZCAqKlFHSVMqKiwgYXJlICpncmFwaGljLXVzZXItaW50ZXJmYWNlIChHVUkpKi4NCg0K4oCiIFNvICoqd2h5IHVzZSBSIGZvciBnZW9zcGF0aWFsIHdvcms/KioNCg0K4oCiIEhlcmUgZml2ZSBvZiBpdHMgbWVyaXRzOg0KDQojIyMgKDEvNSkgUmVwcm9kdWNpYmlsaXR5Og0KDQrigKIgQ29kZSBpcyBzdHJhaWdodGZvcndhcmQgZm9yIGFueW9uZSB0byByZS1ydW4sDQoNCuKAoiBFYXNpbHkgYnVpbGQgb24gb3RoZXIgcGVvcGxlJ3Mgd29yaw0KDQrigKIgRmFjaWxpdGF0ZXMgY29sbGFib3JhdGlvbg0KDQrigKIgUGFzdGUgdGhpcyBjb2RlIGFuZCByZXByb2R1Y2UgaW4geW91ciBjb21wdXRlcjoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCiMg8J+RiSBwYWNrYWdlcw0KaWYoIXJlcXVpcmUoJ3BhY21hbicpKSBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KcGFjbWFuOjpwX2xvYWQoc2YsIGdncGxvdDIpDQoNCiMg8J+RiSBkYXRhIA0KbmMgPC0gc3RfcmVhZChzeXN0ZW0uZmlsZSgic2hhcGUvbmMuc2hwIiwgcGFja2FnZSA9ICJzZiIpLA0KICAgICAgICAgICAgICBxdWlldCA9IFRSVUUpDQojIPCfkYkgcGxvdA0KZ2dwbG90KGRhdGEgPSBuYykgKyANCiAgZ2VvbV9zZihhZXMoZmlsbCA9IFNJRDc0KSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpDQpgYGANCg0KIyMjICgyLzUpUmVwb3J0aW5nOg0KDQrigKIgYHtSbWFya2Rvd259YCwgYHtmbGV4ZGFzaGJvYXJkfWAgYW5kIGB7c2hpbnl9YCB0byBnZW5lcmF0ZSByZXBvcnRzIGFuZCAqZGFzaGJvYXJkcyouDQoNCuKAoiAqSW50ZXJhY3RpdmUqIG1hcHMgd2l0aCBge2xlYWZsZXR9YCBpbnN0ZWFkIG9mIGB7Z2dwbG90Mn1gOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFfQ0KIyDwn5GJIHBhY2thZ2VzDQppZighcmVxdWlyZSgncGFjbWFuJykpIGluc3RhbGwucGFja2FnZXMoJ3BhY21hbicpDQpwYWNtYW46OnBfbG9hZChzZiwgbGVhZmxldCkNCg0KIyDwn5GJIGRhdGENCm5jIDwtIHN0X3JlYWQoc3lzdGVtLmZpbGUoInNoYXBlL25jLnNocCIsIHBhY2thZ2UgPSAic2YiKSwNCiAgICAgICAgICAgICAgcXVpZXQgPSBUUlVFKQ0KDQojIPCfkYkgcGxvdA0KcGFsIDwtIGNvbG9yTnVtZXJpYygiWWxPclJkIiwgZG9tYWluID0gbmMkU0lENzQpDQpsZWFmbGV0KG5jKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUNCiAgYWRkUG9seWdvbnMoY29sb3IgPSAid2hpdGUiLCBmaWxsQ29sb3IgPSB+IHBhbChTSUQ3NCksDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMSkgJT4lDQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIHZhbHVlcyA9IH5TSUQ3NCwgb3BhY2l0eSA9IDEpDQpgYGANCg0KIyMjICgzLzUpIFJpY2ggZWNvc3lzdGVtOg0KDQrigKIgUiB3aXRoIHJhcGlkbHkgKmdyb3dpbmcgbGlicmFyaWVzKg0KDQrigKIgaGlnaGx5LWFjdGl2ZSBvcGVuLXNvdXJjZSBjb21tdW5pdHksDQoNCuKAoiByZWFkeS10by11c2UgcGFja2FnZXMgb3IgdHV0b3JpYWxzLg0KDQrigKIgKmludGVyYWN0aXZlKiBtYXAgd2l0aCBvbmUgbGluZSBvZiBjb2RlIQ0KDQrigKIgYHttYXB2aWV3fWAgaW5zdGVhZCBvZiBge2xlYWZsZXR9YDoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCiMg8J+RiSBwYWNrYWdlcw0KaWYoIXJlcXVpcmUoJ3BhY21hbicpKSBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KcGFjbWFuOjpwX2xvYWQoc2YsIG1hcHZpZXcpDQoNCiMg8J+RiSBkYXRhDQpuYyA8LSBzdF9yZWFkKHN5c3RlbS5maWxlKCJzaGFwZS9uYy5zaHAiLCBwYWNrYWdlID0gInNmIiksDQogICAgICAgICAgICAgIHF1aWV0ID0gVFJVRSkNCg0KIyDwn5GJIHBsb3QNCm1hcHZpZXcobmMsIHpjb2wgPSAiU0lENzQiKQ0KYGBgDQoNCiMjIyAoNC81KSBDb252ZW5pZW5jZToNCg0K4oCiIFlvdSBhbHJlYWR5IGtub3cgUiENCg0K4oCiIEV4cGxvcmUgbmV3IHBpZWNlcyBvZiBjb2RlLg0KDQo6OjogcnN0dWRpby1jbG91ZA0KDQpBcyBhbiBleGFtcGxlLCB3ZSB3aWxsIHVzZSB0aGUgYHt0bWFwfWAgcGFja2FnZSBhbmQgbWFrZSBtaW5vciBtb2RpZmljYXRpb25zIHRvIGl0IQ0KDQpGaXJzdCwgcnVuIHRoaXMgY2h1bms6DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCiMg8J+RiSBwYWNrYWdlcw0KaWYoIXJlcXVpcmUoJ3BhY21hbicpKSBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KcGFjbWFuOjpwX2xvYWQodG1hcCwgc3BEYXRhKQ0KDQojIPCfkYkgZGF0YQ0KbG9hZChoZXJlKCJkYXRhL256X2VsZXYucmRhIikpDQoNCiMg8J+RiSBwbG90DQp0bV9zaGFwZShuel9lbGV2KSAgKw0KICB0bV9yYXN0ZXIodGl0bGUgPSAiRWxldmF0aW9uIChtKSIsICAjIEFkZCB1bml0cyB0byB0aGUgbGVnZW5kIHRpdGxlDQogICAgICAgICAgICBzdHlsZSA9ICJjb250IiwNCiAgICAgICAgICAgIHBhbGV0dGUgPSAiLUJ1R24iKSArDQogIHRtX3NoYXBlKG56KSArDQogIHRtX2JvcmRlcnMoY29sID0gImJsYWNrIiwgDQogICAgICAgICAgICAgbHdkID0gMSkgKyAjIFJlZHVjZSBsaW5lIHdpZHRoDQogIHRtX3NjYWxlX2JhcihicmVha3MgPSBjKDAsIDEwMCwgMjAwKSwNCiAgICAgICAgICAgICAgIHRleHQuc2l6ZSA9IDEpICsNCiAgdG1fY29tcGFzcyhwb3NpdGlvbiA9IGMoIlJJR0hUIiwgInRvcCIpLA0KICAgICAgICAgICAgIHR5cGUgPSAicm9zZSIsIA0KICAgICAgICAgICAgIHNpemUgPSAyKSArDQogIHRtX2NyZWRpdHModGV4dCA9ICJPIEEgTGF3YWwsIDIwMjQiKSArDQogIHRtX2xheW91dChtYWluLnRpdGxlID0gIk5ldyBaZWFsYW5kIiwNCiAgICAgICAgICAgIGJnLmNvbG9yID0gImxpZ2h0Z3JlZW4iLCAgIyBDaGFuZ2UgYmFja2dyb3VuZCBjb2xvcg0KICAgICAgICAgICAgaW5uZXIubWFyZ2lucyA9IGMoMCwgMCwgMCwgMCksICBsZWdlbmQudGl0bGUuc2l6ZSA9IDEuNSkgIyBBZGp1c3QgbGVnZW5kIHRpdGxlIHNpemUNCmBgYA0KDQpOb3csIGFwcGx5IGFueSBvZiB0aGUgZm9sbG93aW5nIHN1Z2dlc3Rpb25zIHRvIGdldCB1c2VkIHRvIGhvdyB0aGlzIHBhY2thZ2Ugd29ya3M6DQoNCjEuICBDaGFuZ2UgdGhlICoqbWFwIHRpdGxlKiogZnJvbSAiTXkgbWFwIiB0byAiTmV3IFplYWxhbmQiLg0KMi4gIFVwZGF0ZSB0aGUgKiptYXAgY3JlZGl0cyoqIHdpdGggeW91ciBvd24gbmFtZSBhbmQgdG9kYXkncyBkYXRlLg0KMy4gIENoYW5nZSB0aGUgKipjb2xvciBwYWxldHRlKiogdG8gIkJ1R24iLg0KNC4gIFRyeSAqKm90aGVyIHBhbGV0dGVzKiogZnJvbSA8aHR0cDovL2NvbG9yYnJld2VyMi5vcmcvPg0KNS4gIFB1dCB0aGUgKipub3J0aCBhcnJvdyoqIGluIHRoZSB0b3AgcmlnaHQgY29ybmVyIG9mIHRoZSBtYXAuDQo2LiAgSW1wcm92ZSB0aGUgKipsZWdlbmQgdGl0bGUqKiBieSBhZGRpbmcgdGhlIGxlZ2VuZCB1bml0cy4NCjcuICBJbmNyZWFzZSB0aGUgbnVtYmVyIG9mIGJyZWFrcyBpbiB0aGUgKipzY2FsZSBiYXIqKi4NCjguICBDaGFuZ2UgdGhlICoqYm9yZGVycycgY29sb3IqKiBvZiB0aGUgTmV3IFplYWxhbmQncyByZWdpb25zIHRvIGJsYWNrLg0KOS4gIERlY3JlYXNlIHRoZSBsaW5lIHdpZHRoLg0KMTAuIENoYW5nZSB0aGUgKipiYWNrZ3JvdW5kIGNvbG9yKiogdG8gYW55IGNvbG9yIG9mIHlvdXIgY2hvaWNlLg0KOjo6DQoNCiMjIyAoNS81KSBJbnRlZ3JhdGVkIHdvcmtmbG93Og0KDQrigKIgQ29tYmluZSBnZW9zcGF0aWFsIHZpc3VhbGl6YXRpb24gYW5kIHN0YXRpc3RpY2FsIGFuYWx5c2VzLA0KDQrigKIgYWxsIHdpdGhpbiBhIHNpbmdsZSBzY3JpcHQuDQoNCuKAoiBGb3IgZXhhbXBsZSwgYnVpbHQgM0QgbWFwcyBvZiB0aGUgTW9udGVyZXkgQmF5IHVzaW5nIGB7cmF5c2hhZGVyfWANCg0K4oCiIFR1dG9yaWFsIGF2YWlsYWJsZTogPGh0dHBzOi8vd3d3LnR5bGVybXcuY29tLzNkLW1hcHMtd2l0aC1yYXlzaGFkZXIvPg0KDQohW10oaW1hZ2VzL21vbnRiYXlhYm92ZS5naWYpDQoNCuKAoiBCaXZhcmlhdGUgbWFwcyBvZiB1bmVxdWFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgaW5jb21lLg0KDQrigKIgVHV0b3JpYWwgYXZhaWxhYmxlOiA8aHR0cHM6Ly90aW1vZ3Jvc3NlbmJhY2hlci5jaC8yMDE5LzA0L2JpdmFyaWF0ZS1tYXBzLXdpdGgtZ2dwbG90Mi1hbmQtc2YvPg0KDQohW10oaW1hZ2VzL2JpdmFyaWF0ZS1tYXAtc3cucG5nKXt3aWR0aD0iNDk5In0NCg0KYGBge3J9DQoNCmBgYA0KDQojIyBXcmFwIHVwDQoNCuKAoiBXZSBsZWFybmVkIHdoeSB0byB1c2UgUiBhcyBhIEdJUyBzb2Z0d2FyZSwNCg0K4oCiIHRha2UgYWR2YW50YWdlIG9mIGl0cyBjb2RpbmcgZW52aXJvbm1lbnQuDQoNCuKAoiBCdXQsIHdoaWNoIG1hcHMgYXJlIHdlIGdvaW5nIHRvIGJ1aWx0Pw0KDQohW0ZpZ3VyZSAxLiBUaGVtYXRpYyBtYXBzOiAoQSkgQ2hvcm9wbGV0aCBtYXAsIChCKSBEb3QgbWFwLCAoQykgRGVuc2l0eSBtYXAsIGFuZCAoRCkgQmFzZW1hcCBmb3IgYSBkb3QgbWFwLl0oaW1hZ2VzL2ludHJvX3RoZW1hdGljX21hcF8wNi5wbmcpe3dpZHRoPSI0ODQifQ0KDQrigKIgSG93IHRvIGJ1aWx0IC1zdGVwIGJ5IHN0ZXAtIGRpZmZlcmVudCB0eXBlcyBvZiAqKlRoZW1hdGljIG1hcHMqKiB1c2luZyB0aGUgYHtnZ3Bsb3QyfWAgcGFja2FnZSwNCg0K4oCiIGRpZmZlcmVudCBkYXRhIHNvdXJjZXMgYW5kIGlsbHVzdHJhdGl2ZSBhbm5vdGF0aW9ucy4NCg0KIVtGaWd1cmUgMi4ge2dncGxvdDJ9IG1hcCB3aXRoIHRleHQgYW5ub3RhdGlvbnMsIGEgc2NhbGUgYmFyIGFuZCBub3J0aCBhcnJvdy5dKGltYWdlcy9tdWx0aWxheWVyX21hcF8wMS5wbmcpe3dpZHRoPSI0MDkifQ0KDQojIyBDb250cmlidXRvcnMgey51bmxpc3RlZCAudW5udW1iZXJlZH0NCg0KVGhlIGZvbGxvd2luZyB0ZWFtIG1lbWJlcnMgY29udHJpYnV0ZWQgdG8gdGhpcyBsZXNzb246DQoNCmByIHRnY19jb250cmlidXRvcnNfbGlzdChpZHMgPSBjKCJhdmFsbGVjYW0iLCAia2VuZGF2aWRuIikpYA0KDQojIyBSZWZlcmVuY2VzIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQoNClNvbWUgbWF0ZXJpYWwgaW4gdGhpcyBsZXNzb24gd2FzIGFkYXB0ZWQgZnJvbSB0aGUgZm9sbG93aW5nIHNvdXJjZXM6DQoNCi0gICAqQmF0cmEsIE5lYWxlLCBldCBhbC4gKDIwMjEpLiBUaGUgRXBpZGVtaW9sb2dpc3QgUiBIYW5kYm9vay4gQ2hhcHRlciAyODogR0lTIEJhc2ljcyouICgyMDIxKS4gUmV0cmlldmVkIDAxIEFwcmlsIDIwMjIsIGZyb20gPGh0dHBzOi8vZXBpcmhhbmRib29rLmNvbS9lbi9naXMtYmFzaWNzLmh0bWw+DQoNCi0gICAqQmF1bWVyLCBCZW5qYW1pbiBTLiwgS2FwbGFuLCBEYW5pZWwgVC4sIGFuZCBIb3J0b24sIE5pY2hvbGFzIEouIE1vZGVybiBEYXRhIFNjaWVuY2Ugd2l0aCBSLiBDaGFwdGVyIDE3OiBXb3JraW5nIHdpdGggZ2Vvc3BhdGlhbCBkYXRhKi4gKDIwMjEpLiBSZXRyaWV2ZWQgMDUgSnVuZSAyMDIyLCBmcm9tIDxodHRwczovL21kc3ItYm9vay5naXRodWIuaW8vbWRzcjJlL2NoLXNwYXRpYWwuaHRtbD4NCg0KLSAgICpMb3ZlbGFjZSwgUi4sIE5vd29zYWQsIEouLCAmIE11ZW5jaG93LCBKLiBHZW9jb21wdXRhdGlvbiB3aXRoIFIuIENoYXB0ZXIgMjogR2VvZ3JhcGhpYyBkYXRhIGluIFIuKiAoMjAxOSkuIFJldHJpZXZlZCAwMSBBcHJpbCAyMDIyLCBmcm9tIDxodHRwczovL2dlb2NvbXByLnJvYmlubG92ZWxhY2UubmV0L3NwYXRpYWwtY2xhc3MuaHRtbD4NCg0KLSAgICpNb3JhZ2EsIFBhdWxhLiBHZW9zcGF0aWFsIEhlYWx0aCBEYXRhOiBNb2RlbGluZyBhbmQgVmlzdWFsaXphdGlvbiB3aXRoIFItSU5MQSBhbmQgU2hpbnkuIENoYXB0ZXIgMTI6IEJ1aWxkaW5nIGEgZGFzaGJvYXJkIHRvIHZpc3VhbGl6ZSBzcGF0aWFsIGRhdGEgd2l0aCBmbGV4ZGFzaGJvYXJkKi4gKDIwMTkpLiBSZXRyaWV2ZWQgMTMgU2VwdGVtYmVyIDIwMjIsIGZyb20gPGh0dHBzOi8vd3d3LnBhdWxhbW9yYWdhLmNvbS9ib29rLWdlb3NwYXRpYWwvc2VjLWZsZXhkYXNoYm9hcmQuaHRtbD4NCg0KLSAgICpNb3Jlbm8sIE0uLCBhbmQgQmFzdGlsbGUsIE0uIERyYXdpbmcgYmVhdXRpZnVsIG1hcHMgcHJvZ3JhbW1hdGljYWxseSB3aXRoIFIsIHNmIGFuZCBnZ3Bsb3QyIC0tLSBQYXJ0IDE6IEJhc2ljcy4qICgyMDE4KS4gUmV0cmlldmVkIDEzIFNlcHRlbWJlciAyMDIyLCBmcm9tIDxodHRwczovL3Itc3BhdGlhbC5vcmcvci8yMDE4LzEwLzI1L2dncGxvdDItc2YuaHRtbC4+DQoNCi0gICAqTm93b3NhZCwgSi4gQmFzaWNzIG9mIFNwYXRpYWwgRGF0YSBBbmFseXNpcyBXb3Jrc2hvcC4qICgyMDE5KS4gUmV0cmlldmVkIDEzIFNlcHRlbWJlciAyMDIyLCBmcm9tIDxodHRwczovL2dpdGh1Yi5jb20vTm93b3NhZC93aHlyXzE5dy9ibG9iL21hc3Rlci9jb2RlL3NwYXRpYWxfdmlzLlI+DQoNCi0gICAqTm93b3NhZCwgSi4gVGhlIExhbmRzY2FwZSBvZiBTcGF0aWFsIERhdGEgQW5hbHlzaXMgaW4gUi4qICgyMDE5KS4gUmV0cmlldmVkIDEzIFNlcHRlbWJlciAyMDIyLCBmcm9tIDxodHRwczovL2pha3Vibm93b3NhZC5jb20vd2h5cl8xOS8jMT4NCg0KYHIgdGdjX2xpY2Vuc2UoKWANCg==