This workshop will introduce drawing maps and integrating statistical information in maps.

Maps can be plottet in a various of different ways, however, the easiest way is using ggplot and a function called ggplot.

We will use the ggmap to create maps.

There are 2 basic steps to making a map using ggmap:

  • Part 1: Download map raster
  • Part 2: Plot raster and overlay data

PART 1 - Downloading the map raster

First we will install and load the necessary packages:

install.packages("tibble")
install.packages("dplyr")
install.packages("magrittr")
install.packages("ggmap")
install.packages("tidyverse")
install.packages("maps")
install.packages("mapproj")
library(tibble)  # The "tibble" datastructure.
library(dplyr)  # Working with the tibble datastructure.
library(magrittr)  # Need this for the %>% operator.
library(ggmap) # ggmap plots the raster objects
library(ggrepel)
library(tidyverse) # ggplot2 (visualisation), dplyr (manipulation), tidyr (tidying), readr (import) and more packages loaded in one
library(maps)
library(mapproj)
library(devtools)  # For printing the session info at the end of the notebook.
library(gridExtra)
library(viridis)
library(knitr)

Map sources

The get_map function provides a general approach for quickly obtaining maps from multiple sources.

There are different sources of map data.

  • stamen: easily accesible and widely used. maptype = c(“terrain”, “toner”, “watercolor”)

  • google: an api key and a account must be obtained. However it is free if you use less than 25.000 views / month maptype = c(“roadmap”, “terrain”, “satellite”, “hybrid”)

  • osm: open street map

  • cloudmade: an api key must be obtained, and it is free for the first 100.000 views, but there are detailed map information.

Location

There are three different ways to define location:

  • location/address myLocation <- “Faroe Islands”

  • lat/long

myLocation <- c(lon = -6.9118061, lat = 61.8926353)
  • bounding box lowerleftlon, lowerleftlat, upperrightlon, upperrightlat
myLocation <- c(left = -7.866927, bottom =  61.230244, right = -5.91136, top = 62.605639)

Define and load a map using stamen maps

mapFO <- get_stamenmap(bbox = c(left = -7.866927, bottom =  61.230244, right = -5.91136, top = 62.605639), zoom=8, maptype = "terrain", crop=FALSE) # crop = FALSE because otherwise the map is slightly shifted when we overlay data.
ggmap(mapFO)

Define map zoom, type, and color

Fine tune the scale or resolution of the map using zoom. The get_map function takes a guess at the zoom level, but you can alter it:

  • zoom = integer from 3-21
  • 3 = continent, 10=city, 21=building (openstreetmap limit of 18)

We choose zoom 9 to display Faroe Islands.

mapFO <- get_stamenmap(bbox = c(left = -7.866927, bottom =  61.230244, right = -5.91136, top = 62.605639), zoom = 9, maptype = "watercolor", crop=FALSE) # crop = FALSE because otherwise the map is slightly shifted when we overlay data.
ggmap(mapFO)

Here we changed the layout to look like a watercolor map. All styles can be seen at https://stamen.com. All maps can also be displayed in black and white

color = “bw”

Display a simple map

UK <- map_data("world") %>% filter(region=="UK")
ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
theme_void() + ylim(50,59) + coord_map() 

2 . Add points with latitude/longitude coordinates:

Using an open data sheet with latitude/longitude of all world cities and population data we can plot these data on the map.

The data contains information on name of city, country, population, latitude/longitude and if it is the capitol or not:

head(world.cities)

We filter the data son only UK cities are included:

dataUK=world.cities %>% filter(country.etc=="UK")

And then we plot the cities on the map:

ggplot() +
geom_polygon(data = UK, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
geom_point( data=dataUK, aes(x=long, y=lat)) +
theme_void() + ylim(50,59) + coord_map() 

If we go back to Faroe Islands

FO <- map_data("world") %>% filter(region=="Faroe Islands") # To raster the map
ggplot() +
geom_polygon(data = FO, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
theme_void() + ylim(61,63) + xlim (-8,-6) + coord_map() 

We see that this way of displaying the map is inadequate in resolution. This world database comes from a thinned cleaned-up version of the CIA World Data Bank II data and contains approximately 30,000 points representing the world coastlines and national boundaries. Apperently they did not save enough points to include all faroese islands. However, we will use it for plotting cities:

FO <- map_data("world") %>% filter(region=="Faroe Islands") # To raster the map
dataFO=world.cities %>% filter(country.etc=="Faroe Islands") # To filter cities from the world.cities database
ggplot() +
geom_polygon(data = FO, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
geom_point( data=dataFO, aes(x=long, y=lat)) +
theme_void() + ylim(61,63) + xlim (-8,-6) + coord_map() 

We will change back to the stamen maps to continue to explore the possibilities:

Raster Faroe Islands with a zoom level of 9 in a water color mode:

map <- get_stamenmap( bbox = c(left = -7.866927, bottom =  61.230244, right = -5.91136, top = 62.605639), zoom = 9, maptype = "watercolor")

Plot Faroe Islands

ggmap(map) + 
    theme_void() + 
    theme(
        plot.title = element_text(colour = "orange"), 
        panel.border = element_rect(colour = "grey", fill=NA, size=2)
    )

Plot cities:

ggmap(map) + geom_point( data=dataFO, aes(x=long, y=lat, alpha=pop)) +
    geom_point( data=dataFO, aes(x=long, y=lat), color="black", size=2) +
    theme_void() + coord_map() +
    theme_void() + 
    theme(
        plot.title = element_text(colour = "orange"), 
        panel.border = element_rect(colour = "grey", fill=NA, size=2) 
    )
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Plot top 10 largest cities with city names:

dataFO10 <- dataFO %>% arrange(pop) %>% tail(10)
ggmap(map) + geom_point( data=dataFO, aes(x=long, y=lat, alpha=pop)) +
    geom_text_repel( data=dataFO10, aes(x=long, y=lat, label=name), size=4) +
    geom_point( data=dataFO, aes(x=long, y=lat), color="black", size=1) +
    geom_point( data=dataFO10, aes(x=long, y=lat), color="red", size=3) +
    theme_void() + coord_map() +
    theme(legend.position="none") +
    theme(
        plot.title = element_text(colour = "orange"), 
        panel.border = element_rect(colour = "grey", fill=NA, size=2) 
    )
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Map cities as variables to size:

ggmap(map) + geom_point( data=dataFO, aes(x=long, y=lat, alpha=pop)) +
    geom_point( data=dataFO, aes(x=long, y=lat, size=pop)) +
    scale_size_continuous(range=c(1,12)) +
    theme_void() + coord_map() +
    theme(legend.position="none") +
    theme(
        plot.title = element_text(colour = "orange"), 
        panel.border = element_rect(colour = "grey", fill=NA, size=2) 
)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Map cities as variables to colour:

ggmap(map) + geom_point( data=dataFO, aes(x=long, y=lat, alpha=pop)) +
    geom_point( data=dataFO, aes(x=long, y=lat, color=pop, size=3)) +
    theme_void() + coord_map() +
    theme(legend.position="none") +
    theme(plot.title = element_text(colour = "orange"), 
    panel.border = element_rect(colour = "grey", fill=NA, size=2) 
)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

devtools::session_info()
─ Session info ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 setting  value                       
 version  R version 3.4.4 (2018-03-15)
 os       Ubuntu 16.04.6 LTS          
 system   x86_64, linux-gnu           
 ui       RStudio                     
 language en_US                       
 collate  en_US.UTF-8                 
 ctype    en_US.UTF-8                 
 tz       Atlantic/Faroe              
 date     2019-08-26                  

─ Packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 package     * version  date       lib source        
 aod         * 1.3.1    2019-01-26 [1] CRAN (R 3.4.4)
 assertthat    0.2.1    2019-03-21 [1] CRAN (R 3.4.4)
 backports     1.1.4    2019-04-10 [1] CRAN (R 3.4.4)
 base64enc     0.1-3    2015-07-28 [1] CRAN (R 3.4.4)
 bitops        1.0-6    2013-08-17 [1] CRAN (R 3.4.4)
 broom         0.5.2    2019-04-07 [1] CRAN (R 3.4.4)
 callr         3.3.1    2019-07-18 [1] CRAN (R 3.4.4)
 cellranger    1.1.0    2016-07-27 [1] CRAN (R 3.4.4)
 cli           1.1.0    2019-03-19 [1] CRAN (R 3.4.4)
 colorspace    1.4-1    2019-03-18 [1] CRAN (R 3.4.4)
 crayon        1.3.4    2017-09-16 [1] CRAN (R 3.4.4)
 curl          4.0      2019-07-22 [1] CRAN (R 3.4.4)
 desc          1.2.0    2018-05-01 [1] CRAN (R 3.4.4)
 devtools    * 2.1.0    2019-07-06 [1] CRAN (R 3.4.4)
 digest        0.6.20   2019-07-04 [1] CRAN (R 3.4.4)
 dplyr       * 0.8.3    2019-07-04 [1] CRAN (R 3.4.4)
 evaluate      0.14     2019-05-28 [1] CRAN (R 3.4.4)
 fansi         0.4.0    2018-10-05 [1] CRAN (R 3.4.4)
 forcats     * 0.4.0    2019-02-17 [1] CRAN (R 3.4.4)
 fs            1.3.1    2019-05-06 [1] CRAN (R 3.4.4)
 generics      0.0.2    2018-11-29 [1] CRAN (R 3.4.4)
 ggmap       * 3.0.0    2019-02-05 [1] CRAN (R 3.4.4)
 ggplot2     * 3.2.1    2019-08-10 [1] CRAN (R 3.4.4)
 ggrepel     * 0.8.1    2019-05-07 [1] CRAN (R 3.4.4)
 glue          1.3.1    2019-03-12 [1] CRAN (R 3.4.4)
 gridExtra   * 2.3      2017-09-09 [1] CRAN (R 3.4.4)
 gtable        0.3.0    2019-03-25 [1] CRAN (R 3.4.4)
 haven         2.1.1    2019-07-04 [1] CRAN (R 3.4.4)
 hms           0.5.1    2019-08-23 [1] CRAN (R 3.4.4)
 htmltools     0.3.6    2017-04-28 [1] CRAN (R 3.4.4)
 httr          1.4.1    2019-08-05 [1] CRAN (R 3.4.4)
 jpeg          0.1-8    2014-01-23 [1] CRAN (R 3.4.4)
 jsonlite      1.6      2018-12-07 [1] CRAN (R 3.4.4)
 knitr       * 1.24     2019-08-08 [1] CRAN (R 3.4.4)
 labeling      0.3      2014-08-23 [1] CRAN (R 3.4.4)
 lattice       0.20-38  2018-11-04 [4] CRAN (R 3.4.4)
 lazyeval      0.2.2    2019-03-15 [1] CRAN (R 3.4.4)
 lubridate   * 1.7.4    2018-04-11 [1] CRAN (R 3.4.4)
 magrittr    * 1.5      2014-11-22 [1] CRAN (R 3.4.4)
 mapproj     * 1.2.6    2018-03-29 [1] CRAN (R 3.4.4)
 maps        * 3.3.0    2018-04-03 [1] CRAN (R 3.4.4)
 MASS          7.3-50   2018-04-30 [4] CRAN (R 3.4.4)
 Matrix        1.2-14   2018-04-09 [4] CRAN (R 3.4.4)
 memoise       1.1.0    2017-04-21 [1] CRAN (R 3.4.4)
 modelr        0.1.5    2019-08-08 [1] CRAN (R 3.4.4)
 munsell       0.5.0    2018-06-12 [1] CRAN (R 3.4.4)
 nlme          3.1-137  2018-04-07 [4] CRAN (R 3.4.4)
 pillar        1.4.2    2019-06-29 [1] CRAN (R 3.4.4)
 pkgbuild      1.0.4    2019-08-05 [1] CRAN (R 3.4.4)
 pkgconfig     2.0.2    2018-08-16 [1] CRAN (R 3.4.4)
 pkgload       1.0.2    2018-10-29 [1] CRAN (R 3.4.4)
 plyr          1.8.4    2016-06-08 [1] CRAN (R 3.4.4)
 png           0.1-7    2013-12-03 [1] CRAN (R 3.4.4)
 prettyunits   1.0.2    2015-07-13 [1] CRAN (R 3.4.4)
 processx      3.4.1    2019-07-18 [1] CRAN (R 3.4.4)
 ps            1.3.0    2018-12-21 [1] CRAN (R 3.4.4)
 purrr       * 0.3.2    2019-03-15 [1] CRAN (R 3.4.4)
 R6            2.4.0    2019-02-14 [1] CRAN (R 3.4.4)
 Rcpp          1.0.2    2019-07-25 [1] CRAN (R 3.4.4)
 readr       * 1.3.1    2018-12-21 [1] CRAN (R 3.4.4)
 readxl      * 1.3.1    2019-03-13 [1] CRAN (R 3.4.4)
 remotes       2.1.0    2019-06-24 [1] CRAN (R 3.4.4)
 RgoogleMaps   1.4.4    2019-08-20 [1] CRAN (R 3.4.4)
 rjson         0.2.20   2018-06-08 [1] CRAN (R 3.4.4)
 rlang         0.4.0    2019-06-25 [1] CRAN (R 3.4.4)
 rmarkdown     1.15     2019-08-21 [1] CRAN (R 3.4.4)
 rprojroot     1.3-2    2018-01-03 [1] CRAN (R 3.4.4)
 rstudioapi    0.10     2019-03-19 [1] CRAN (R 3.4.4)
 rvest         0.3.4    2019-05-15 [1] CRAN (R 3.4.4)
 scales        1.0.0    2018-08-09 [1] CRAN (R 3.4.4)
 sessioninfo   1.1.1    2018-11-05 [1] CRAN (R 3.4.4)
 stringi       1.4.3    2019-03-12 [1] CRAN (R 3.4.4)
 stringr     * 1.4.0    2019-02-10 [1] CRAN (R 3.4.4)
 survival    * 2.44-1.1 2019-04-01 [1] CRAN (R 3.4.4)
 testthat      2.2.1    2019-07-25 [1] CRAN (R 3.4.4)
 tibble      * 2.1.3    2019-06-06 [1] CRAN (R 3.4.4)
 tidyr       * 0.8.3    2019-03-01 [1] CRAN (R 3.4.4)
 tidyselect    0.2.5    2018-10-11 [1] CRAN (R 3.4.4)
 tidyverse   * 1.2.1    2017-11-14 [1] CRAN (R 3.4.4)
 usethis     * 1.5.1    2019-07-04 [1] CRAN (R 3.4.4)
 utf8          1.1.4    2018-05-24 [1] CRAN (R 3.4.4)
 vctrs         0.2.0    2019-07-05 [1] CRAN (R 3.4.4)
 viridis     * 0.5.1    2018-03-29 [1] CRAN (R 3.4.4)
 viridisLite * 0.3.0    2018-02-01 [1] CRAN (R 3.4.4)
 withr         2.1.2    2018-03-15 [1] CRAN (R 3.4.4)
 xfun          0.9      2019-08-21 [1] CRAN (R 3.4.4)
 xml2          1.2.2    2019-08-09 [1] CRAN (R 3.4.4)
 yaml          2.2.0    2018-07-25 [1] CRAN (R 3.4.4)
 zeallot       0.1.0    2018-01-28 [1] CRAN (R 3.4.4)

[1] /home/olavur/R/x86_64-pc-linux-gnu-library/3.4
[2] /usr/local/lib/R/site-library
[3] /usr/lib/R/site-library
[4] /usr/lib/R/library
LS0tCnRpdGxlOiAiTWFwcyB3b3Jrc2hvcCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KClRoaXMgd29ya3Nob3Agd2lsbCBpbnRyb2R1Y2UgZHJhd2luZyBtYXBzIGFuZCBpbnRlZ3JhdGluZyBzdGF0aXN0aWNhbCBpbmZvcm1hdGlvbiBpbiBtYXBzLgoKTWFwcyBjYW4gYmUgcGxvdHRldCBpbiBhIHZhcmlvdXMgb2YgZGlmZmVyZW50IHdheXMsIGhvd2V2ZXIsIHRoZSBlYXNpZXN0IHdheSBpcyB1c2luZyBnZ3Bsb3QgYW5kIGEgZnVuY3Rpb24gY2FsbGVkIGdncGxvdC4KCldlIHdpbGwgdXNlIHRoZSBnZ21hcCB0byBjcmVhdGUgbWFwcy4KClRoZXJlIGFyZSAyIGJhc2ljIHN0ZXBzIHRvIG1ha2luZyBhIG1hcCB1c2luZyBnZ21hcDoKCi0gUGFydCAxOiBEb3dubG9hZCBtYXAgcmFzdGVyCi0gUGFydCAyOiBQbG90IHJhc3RlciBhbmQgb3ZlcmxheSBkYXRhCgojIyBQQVJUIDEgLSBEb3dubG9hZGluZyB0aGUgbWFwIHJhc3RlcgoKRmlyc3Qgd2Ugd2lsbCBpbnN0YWxsIGFuZCBsb2FkIHRoZSBuZWNlc3NhcnkgcGFja2FnZXM6CgpgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWJibGUiKQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCmluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2dtYXAiKQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJtYXBzIikKaW5zdGFsbC5wYWNrYWdlcygibWFwcHJvaiIpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGliYmxlKSAgIyBUaGUgInRpYmJsZSIgZGF0YXN0cnVjdHVyZS4KbGlicmFyeShkcGx5cikgICMgV29ya2luZyB3aXRoIHRoZSB0aWJibGUgZGF0YXN0cnVjdHVyZS4KbGlicmFyeShtYWdyaXR0cikgICMgTmVlZCB0aGlzIGZvciB0aGUgJT4lIG9wZXJhdG9yLgpsaWJyYXJ5KGdnbWFwKSAjIGdnbWFwIHBsb3RzIHRoZSByYXN0ZXIgb2JqZWN0cwpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkodGlkeXZlcnNlKSAjIGdncGxvdDIgKHZpc3VhbGlzYXRpb24pLCBkcGx5ciAobWFuaXB1bGF0aW9uKSwgdGlkeXIgKHRpZHlpbmcpLCByZWFkciAoaW1wb3J0KSBhbmQgbW9yZSBwYWNrYWdlcyBsb2FkZWQgaW4gb25lCmxpYnJhcnkobWFwcykKbGlicmFyeShtYXBwcm9qKQpsaWJyYXJ5KGRldnRvb2xzKSAgIyBGb3IgcHJpbnRpbmcgdGhlIHNlc3Npb24gaW5mbyBhdCB0aGUgZW5kIG9mIHRoZSBub3RlYm9vay4KbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShrbml0cikKYGBgCgojIyBNYXAgc291cmNlcwoKVGhlIGdldF9tYXAgZnVuY3Rpb24gcHJvdmlkZXMgYSBnZW5lcmFsIGFwcHJvYWNoIGZvciBxdWlja2x5IG9idGFpbmluZyBtYXBzIGZyb20gbXVsdGlwbGUgc291cmNlcy4KClRoZXJlIGFyZSBkaWZmZXJlbnQgc291cmNlcyBvZiBtYXAgZGF0YS4KCi0gc3RhbWVuOiBlYXNpbHkgYWNjZXNpYmxlIGFuZCB3aWRlbHkgdXNlZC4KbWFwdHlwZSA9IGMo4oCcdGVycmFpbuKAnSwg4oCcdG9uZXLigJ0sIOKAnHdhdGVyY29sb3LigJ0pCgotIGdvb2dsZTogYW4gYXBpIGtleSBhbmQgYSBhY2NvdW50IG11c3QgYmUgb2J0YWluZWQuIEhvd2V2ZXIgaXQgaXMgZnJlZSBpZiB5b3UgdXNlIGxlc3MgdGhhbiAyNS4wMDAgdmlld3MgLyBtb250aAptYXB0eXBlID0gYyjigJxyb2FkbWFw4oCdLCDigJx0ZXJyYWlu4oCdLCDigJxzYXRlbGxpdGXigJ0sIOKAnGh5YnJpZOKAnSkKCi0gb3NtOiBvcGVuIHN0cmVldCBtYXAKCi0gY2xvdWRtYWRlOiBhbiBhcGkga2V5IG11c3QgYmUgb2J0YWluZWQsIGFuZCBpdCBpcyBmcmVlIGZvciB0aGUgZmlyc3QgMTAwLjAwMCB2aWV3cywgYnV0IHRoZXJlIGFyZSBkZXRhaWxlZCBtYXAgaW5mb3JtYXRpb24uCgojIyBMb2NhdGlvbgoKVGhlcmUgYXJlIHRocmVlIGRpZmZlcmVudCB3YXlzIHRvIGRlZmluZSBsb2NhdGlvbjoKCi0gbG9jYXRpb24vYWRkcmVzcwpteUxvY2F0aW9uIDwtICJGYXJvZSBJc2xhbmRz4oCdCgotIGxhdC9sb25nCmBgYHtyfQpteUxvY2F0aW9uIDwtIGMobG9uID0gLTYuOTExODA2MSwgbGF0ID0gNjEuODkyNjM1MykKYGBgCgotIGJvdW5kaW5nIGJveCBsb3dlcmxlZnRsb24sIGxvd2VybGVmdGxhdCwgdXBwZXJyaWdodGxvbiwgdXBwZXJyaWdodGxhdApgYGB7cn0KbXlMb2NhdGlvbiA8LSBjKGxlZnQgPSAtNy44NjY5MjcsIGJvdHRvbSA9ICA2MS4yMzAyNDQsIHJpZ2h0ID0gLTUuOTExMzYsIHRvcCA9IDYyLjYwNTYzOSkKYGBgCgojIyBEZWZpbmUgYW5kIGxvYWQgYSBtYXAgdXNpbmcgc3RhbWVuIG1hcHMKCmBgYHtyIE1hcDEsIGV2YWw9RkFMU0V9Cm1hcEZPIDwtIGdldF9zdGFtZW5tYXAoYmJveCA9IGMobGVmdCA9IC03Ljg2NjkyNywgYm90dG9tID0gIDYxLjIzMDI0NCwgcmlnaHQgPSAtNS45MTEzNiwgdG9wID0gNjIuNjA1NjM5KSwgem9vbT04LCBtYXB0eXBlID0gInRlcnJhaW4iLCBjcm9wPUZBTFNFKSAjIGNyb3AgPSBGQUxTRSBiZWNhdXNlIG90aGVyd2lzZSB0aGUgbWFwIGlzIHNsaWdodGx5IHNoaWZ0ZWQgd2hlbiB3ZSBvdmVybGF5IGRhdGEuCmdnbWFwKG1hcEZPKQpgYGAKCiMjIERlZmluZSBtYXAgem9vbSwgdHlwZSwgYW5kIGNvbG9yCgpGaW5lIHR1bmUgdGhlIHNjYWxlIG9yIHJlc29sdXRpb24gb2YgdGhlIG1hcCB1c2luZyB6b29tLiBUaGUgZ2V0X21hcCBmdW5jdGlvbiB0YWtlcyBhIGd1ZXNzIGF0IHRoZSB6b29tIGxldmVsLCBidXQgeW91IGNhbiBhbHRlciBpdDoKCi0gem9vbSA9IGludGVnZXIgZnJvbSAzLTIxCi0gMyA9IGNvbnRpbmVudCwgMTA9Y2l0eSwgMjE9YnVpbGRpbmcgKG9wZW5zdHJlZXRtYXAgbGltaXQgb2YgMTgpCgpXZSBjaG9vc2Ugem9vbSA5IHRvIGRpc3BsYXkgRmFyb2UgSXNsYW5kcy4KCmBgYHtyIE1hcDIsIG1lc3NhZ2U9RkFMU0V9Cm1hcEZPIDwtIGdldF9zdGFtZW5tYXAoYmJveCA9IGMobGVmdCA9IC03Ljg2NjkyNywgYm90dG9tID0gIDYxLjIzMDI0NCwgcmlnaHQgPSAtNS45MTEzNiwgdG9wID0gNjIuNjA1NjM5KSwgem9vbSA9IDksIG1hcHR5cGUgPSAid2F0ZXJjb2xvciIsIGNyb3A9RkFMU0UpICMgY3JvcCA9IEZBTFNFIGJlY2F1c2Ugb3RoZXJ3aXNlIHRoZSBtYXAgaXMgc2xpZ2h0bHkgc2hpZnRlZCB3aGVuIHdlIG92ZXJsYXkgZGF0YS4KZ2dtYXAobWFwRk8pCmBgYAoKSGVyZSB3ZSBjaGFuZ2VkIHRoZSBsYXlvdXQgdG8gbG9vayBsaWtlIGEgd2F0ZXJjb2xvciBtYXAuIEFsbCBzdHlsZXMgY2FuIGJlIHNlZW4gYXQgaHR0cHM6Ly9zdGFtZW4uY29tLiBBbGwgbWFwcyBjYW4gYWxzbyBiZSBkaXNwbGF5ZWQgaW4gYmxhY2sgYW5kIHdoaXRlCmBgYHtyLCBldmFsPUZBTFNFfQpjb2xvciA9IOKAnGJ34oCdCmBgYAoKCiMjIERpc3BsYXkgYSBzaW1wbGUgbWFwCgpgYGB7ciBNYXAzfQpVSyA8LSBtYXBfZGF0YSgid29ybGQiKSAlPiUgZmlsdGVyKHJlZ2lvbj09IlVLIikKZ2dwbG90KCkgKwpnZW9tX3BvbHlnb24oZGF0YSA9IFVLLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMykgKwp0aGVtZV92b2lkKCkgKyB5bGltKDUwLDU5KSArIGNvb3JkX21hcCgpIApgYGAKCjIgLiBBZGQgcG9pbnRzIHdpdGggbGF0aXR1ZGUvbG9uZ2l0dWRlIGNvb3JkaW5hdGVzOgoKVXNpbmcgYW4gb3BlbiBkYXRhIHNoZWV0IHdpdGggbGF0aXR1ZGUvbG9uZ2l0dWRlIG9mIGFsbCB3b3JsZCBjaXRpZXMgYW5kIHBvcHVsYXRpb24gZGF0YSB3ZSBjYW4gcGxvdCB0aGVzZSBkYXRhIG9uIHRoZSBtYXAuCgpUaGUgZGF0YSBjb250YWlucyBpbmZvcm1hdGlvbiBvbiBuYW1lIG9mIGNpdHksIGNvdW50cnksIHBvcHVsYXRpb24sIGxhdGl0dWRlL2xvbmdpdHVkZSBhbmQgaWYgaXQgaXMgdGhlIGNhcGl0b2wgb3Igbm90OgoKYGBge3J9CmhlYWQod29ybGQuY2l0aWVzKQpgYGAKCldlIGZpbHRlciB0aGUgZGF0YSBzb24gb25seSBVSyBjaXRpZXMgYXJlIGluY2x1ZGVkOgoKYGBge3J9CmRhdGFVSz13b3JsZC5jaXRpZXMgJT4lIGZpbHRlcihjb3VudHJ5LmV0Yz09IlVLIikKYGBgCgpBbmQgdGhlbiB3ZSBwbG90IHRoZSBjaXRpZXMgb24gdGhlIG1hcDoKCmBgYHtyIE1hcDR9CmdncGxvdCgpICsKZ2VvbV9wb2x5Z29uKGRhdGEgPSBVSywgYWVzKHg9bG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGw9ImdyZXkiLCBhbHBoYT0wLjMpICsKZ2VvbV9wb2ludCggZGF0YT1kYXRhVUssIGFlcyh4PWxvbmcsIHk9bGF0KSkgKwp0aGVtZV92b2lkKCkgKyB5bGltKDUwLDU5KSArIGNvb3JkX21hcCgpIApgYGAKCklmIHdlIGdvIGJhY2sgdG8gRmFyb2UgSXNsYW5kcwoKYGBge3IgTWFwNX0KRk8gPC0gbWFwX2RhdGEoIndvcmxkIikgJT4lIGZpbHRlcihyZWdpb249PSJGYXJvZSBJc2xhbmRzIikgIyBUbyByYXN0ZXIgdGhlIG1hcApnZ3Bsb3QoKSArCmdlb21fcG9seWdvbihkYXRhID0gRk8sIGFlcyh4PWxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsPSJncmV5IiwgYWxwaGE9MC4zKSArCnRoZW1lX3ZvaWQoKSArIHlsaW0oNjEsNjMpICsgeGxpbSAoLTgsLTYpICsgY29vcmRfbWFwKCkgCmBgYAoKV2Ugc2VlIHRoYXQgdGhpcyB3YXkgb2YgZGlzcGxheWluZyB0aGUgbWFwIGlzIGluYWRlcXVhdGUgaW4gcmVzb2x1dGlvbi4gVGhpcyB3b3JsZCBkYXRhYmFzZSBjb21lcyBmcm9tIGEgdGhpbm5lZCBjbGVhbmVkLXVwIHZlcnNpb24gb2YgdGhlIENJQSBXb3JsZCBEYXRhIEJhbmsgSUkgZGF0YSBhbmQgY29udGFpbnMgYXBwcm94aW1hdGVseSAzMCwwMDAgcG9pbnRzIHJlcHJlc2VudGluZyB0aGUgd29ybGQgY29hc3RsaW5lcyBhbmQgbmF0aW9uYWwgYm91bmRhcmllcy4gQXBwZXJlbnRseSB0aGV5IGRpZCBub3Qgc2F2ZSBlbm91Z2ggcG9pbnRzIHRvIGluY2x1ZGUgYWxsIGZhcm9lc2UgaXNsYW5kcy4gSG93ZXZlciwgd2Ugd2lsbCB1c2UgaXQgZm9yIHBsb3R0aW5nIGNpdGllczogCgpgYGB7ciBNYXA1LjF9CkZPIDwtIG1hcF9kYXRhKCJ3b3JsZCIpICU+JSBmaWx0ZXIocmVnaW9uPT0iRmFyb2UgSXNsYW5kcyIpICMgVG8gcmFzdGVyIHRoZSBtYXAKZGF0YUZPPXdvcmxkLmNpdGllcyAlPiUgZmlsdGVyKGNvdW50cnkuZXRjPT0iRmFyb2UgSXNsYW5kcyIpICMgVG8gZmlsdGVyIGNpdGllcyBmcm9tIHRoZSB3b3JsZC5jaXRpZXMgZGF0YWJhc2UKZ2dwbG90KCkgKwpnZW9tX3BvbHlnb24oZGF0YSA9IEZPLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMykgKwpnZW9tX3BvaW50KCBkYXRhPWRhdGFGTywgYWVzKHg9bG9uZywgeT1sYXQpKSArCnRoZW1lX3ZvaWQoKSArIHlsaW0oNjEsNjMpICsgeGxpbSAoLTgsLTYpICsgY29vcmRfbWFwKCkgCmBgYAoKV2Ugd2lsbCBjaGFuZ2UgYmFjayB0byB0aGUgc3RhbWVuIG1hcHMgdG8gY29udGludWUgdG8gZXhwbG9yZSB0aGUgcG9zc2liaWxpdGllczoKClJhc3RlciBGYXJvZSBJc2xhbmRzIHdpdGggYSB6b29tIGxldmVsIG9mIDkgaW4gYSB3YXRlciBjb2xvciBtb2RlOgoKYGBge3IgTWFwNn0KbWFwIDwtIGdldF9zdGFtZW5tYXAoIGJib3ggPSBjKGxlZnQgPSAtNy44NjY5MjcsIGJvdHRvbSA9ICA2MS4yMzAyNDQsIHJpZ2h0ID0gLTUuOTExMzYsIHRvcCA9IDYyLjYwNTYzOSksIHpvb20gPSA5LCBtYXB0eXBlID0gIndhdGVyY29sb3IiKQpgYGAKClBsb3QgRmFyb2UgSXNsYW5kcwoKYGBge3IgTWFwN30KZ2dtYXAobWFwKSArIAogICAgdGhlbWVfdm9pZCgpICsgCiAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJvcmFuZ2UiKSwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0yKQogICAgKQpgYGAKCgpQbG90IGNpdGllczoKCmBgYHtyIE1hcDh9CmdnbWFwKG1hcCkgKyBnZW9tX3BvaW50KCBkYXRhPWRhdGFGTywgYWVzKHg9bG9uZywgeT1sYXQsIGFscGhhPXBvcCkpICsKICAgIGdlb21fcG9pbnQoIGRhdGE9ZGF0YUZPLCBhZXMoeD1sb25nLCB5PWxhdCksIGNvbG9yPSJibGFjayIsIHNpemU9MikgKwogICAgdGhlbWVfdm9pZCgpICsgY29vcmRfbWFwKCkgKwogICAgdGhlbWVfdm9pZCgpICsgCiAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJvcmFuZ2UiKSwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0yKSAKICAgICkKYGBgCgpQbG90IHRvcCAxMCBsYXJnZXN0IGNpdGllcyB3aXRoIGNpdHkgbmFtZXM6CgpgYGB7ciBNYXA5fQpkYXRhRk8xMCA8LSBkYXRhRk8gJT4lIGFycmFuZ2UocG9wKSAlPiUgdGFpbCgxMCkKCmdnbWFwKG1hcCkgKyBnZW9tX3BvaW50KCBkYXRhPWRhdGFGTywgYWVzKHg9bG9uZywgeT1sYXQsIGFscGhhPXBvcCkpICsKICAgIGdlb21fdGV4dF9yZXBlbCggZGF0YT1kYXRhRk8xMCwgYWVzKHg9bG9uZywgeT1sYXQsIGxhYmVsPW5hbWUpLCBzaXplPTQpICsKICAgIGdlb21fcG9pbnQoIGRhdGE9ZGF0YUZPLCBhZXMoeD1sb25nLCB5PWxhdCksIGNvbG9yPSJibGFjayIsIHNpemU9MSkgKwogICAgZ2VvbV9wb2ludCggZGF0YT1kYXRhRk8xMCwgYWVzKHg9bG9uZywgeT1sYXQpLCBjb2xvcj0icmVkIiwgc2l6ZT0zKSArCiAgICB0aGVtZV92b2lkKCkgKyBjb29yZF9tYXAoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJvcmFuZ2UiKSwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmV5IiwgZmlsbD1OQSwgc2l6ZT0yKSAKICAgICkKYGBgCgoKTWFwIGNpdGllcyBhcyB2YXJpYWJsZXMgdG8gc2l6ZToKCmBgYHtyIE1hcDEwfQpnZ21hcChtYXApICsgZ2VvbV9wb2ludCggZGF0YT1kYXRhRk8sIGFlcyh4PWxvbmcsIHk9bGF0LCBhbHBoYT1wb3ApKSArCiAgICBnZW9tX3BvaW50KCBkYXRhPWRhdGFGTywgYWVzKHg9bG9uZywgeT1sYXQsIHNpemU9cG9wKSkgKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMSwxMikpICsKICAgIHRoZW1lX3ZvaWQoKSArIGNvb3JkX21hcCgpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIm9yYW5nZSIpLCAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTIpIAopCmBgYAoKTWFwIGNpdGllcyBhcyB2YXJpYWJsZXMgdG8gY29sb3VyOgoKYGBge3IgTWFwMTF9CmdnbWFwKG1hcCkgKyBnZW9tX3BvaW50KCBkYXRhPWRhdGFGTywgYWVzKHg9bG9uZywgeT1sYXQsIGFscGhhPXBvcCkpICsKICAgIGdlb21fcG9pbnQoIGRhdGE9ZGF0YUZPLCBhZXMoeD1sb25nLCB5PWxhdCwgY29sb3I9cG9wLCBzaXplPTMpKSArCiAgICB0aGVtZV92b2lkKCkgKyBjb29yZF9tYXAoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJvcmFuZ2UiKSwgCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsPU5BLCBzaXplPTIpIAopCmBgYAoKCgpgYGB7cn0KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYAoK