1 Overview

The following document describes the use of the R statistical package to generate map rendering of statistics. This document was developed as an interactive notebook using R Studio Desktop1 edition as the development and test environment. This allows the inclusion of the R2 source code blocks for each example provided. Both R and RStudio must be installed to experiment with the code. These software are free and fully supported by a vibrant user community. The urls for the installers for these software can be found in the Bibliography. It is important to choose the installer that corresponds to the operating system you are using. (The basic, free versions are all you need.)

2 Basic Mapping Functions

There are numerous ways to plot information on maps with R. The 3 packages described in this section represent the most common methods used. Each of them use latitude and longitude making it possible to combine the different techniques together as shown in the next chapter. It should be noted that each technology will require the installation and load of specific packages. The code for this is given in the following sections.

2.1 World Map

This mapping technology works from an outline of the world map and requires a specific package.

2.1.1 Installing the package

You can install the package using the INSTALL button in the Packages in RStudio or copy and run the following code to download the package from the Internet. This copy only needs to be run once.

install.packages("maps")
install.packages("mapdata")

2.1.2 Loading the package

This package is not part of the core of R and must be loaded into the working library once before using its functions. The code to do this is given below:

library(maps)
library(mapdata)

2.1.3 Key functions

Maps are drawn merely by listing the names of the countries (or region) of interest as shown by these examples:

  1. The entire world.
map('worldHires')

  1. A region (as an array of countries).
map('worldHires',
  region=c("myanmar","thailand",
    "laos","cambodia","vietnam",
    "malaysia","singapore",
    "indonesia","brunei",
    "philippines"))

  1. A single country
map('worldHires','myanmar')

It is also possible to fill each country with a different color.

countries = c("myanmar","thailand",
  "laos","cambodia","vietnam",
  "malaysia","singapore",
  "indonesia", "brunei",
  "philippines")
len = length(countries)
colors = rainbow(len + 1)
map('worldHires',
  region=countries,
  fill=TRUE,col=colors[1])
map.scale(120,26,
  relwidth=0.25,cex=0.6,
  metric=TRUE,ratio=TRUE)
for (i in c(2:6,8:10)) {
  map('worldHires',
    region=countries[i],
    fill=TRUE,add=TRUE,
    col=colors[i])
}

2.2 Google Maps

Google maps are updated approximately every 9 months. These functions make if possible to capture a road map, satellite photo, or topographic map. Access to these maps requires the corresponding package.

2.2.1 Installing the package

You can install the package using the INSTALL button in the Packages in RStudio or copy and run the following code to download the package from the Internet. This copy only needs to be run once.

install.packages("RgoogleMaps")

2.2.2 Loading the package

This package is not part of the core of R and must be loaded into the working library once before using its functions. The code to do this is given below:

library(RgoogleMaps)

2.2.3 Key functions

  1. Getting a map: In Google maps one can search for a map by the name of a landmark or by its latitude and longitude. In the following code block, the Myanmar Bible House (next to Sule) in Yangon is referenced.
map1 <- GetMap(
 "Bible House, Yangon, Myanmar",
 destfile = "MyTile1.png",
 size=c(640,640), zoom=18,
 maptype = "roadmap")

This creates a graphic file (named MyTile1.png) which is stored in the project directory.

map

map

  1. Displaying the map. The map can be displayed in R using the following code.
PlotOnStaticMap(map1)

  1. Specifying other map types. Of course, satellite images can also be generated using the same function. In fact the range of possibilities are roadmap, mobile, satellite, terrain, hybrid, and mapmaker-roadmap.
map2 <- GetMap("Bible House, Yangon, Myanmar",
 destfile = "MyTile2.png",size=c(640,640), zoom=18,
 maptype = "satellite")
PlotOnStaticMap(map2)

2.3 GADM Mapping

GADM3 is a geographic information system (GIS) collection of the shapes of 4 levels of political boundaries for most countries of the world (namely country, province/state, district, townships). The databases are free for non-commercial uses and use longitude/latitude coordinate system. Each country collection is available in many versions so it is important to download the RDS version which was designed for use with R. The current version (2.8) was released in Nov 2015 and Version 3 is expected in Aug 2017. These maps are ideally suited for epidemic, political and economic research.

2.3.1 Installing the package

You can install the package using the INSTALL button in the Packages in RStudio or copy and run the following code to download the package from the Internet. This copy only needs to be run once.

install.packages("sp")

2.3.2 Loading the package

This package is not part of the core of R and must be loaded into the working library once before using its functions. The code to do this is given below:

library(sp)

2.3.3 Key functions

  1. Reading the geopolitical boundaries file
gadm <- readRDS(
  "datasets/MMR_adm2.rds")
  1. Creating a map
colors = rainbow(100)
plot(gadm, col = colors, border = 'darkgrey')

  1. Selecting particular regions to color
gadm <- readRDS("datasets/MMR_adm1.rds")
colors = rep("#00FF0022",70)
colors[15] = "purple"
colors[gadm@data$NAME_1 ==
    "Naypyitaw"] = "red"
plot(gadm, col = colors, 
     border = FALSE)
legend(99,19,c("Yangon","Naypyitaw"),fill=c("purple","red"))

The ADM file contains a database and by matching the ordering or by selecting a particular region it is possible to highlight selected regions of the country.

3 Using the Maps

3.1 Coloring the Maps

In this example, the various states and regions of Myanmar are colored according to the local populations.4

gadm <- readRDS(
 "datasets/MMR_adm1.rds")
regions = c("Ayeyarwady","Bago",
  "Chin", "Kachin", "Kayah",
  "Kayin","Magway","Mandalay",
  "Mon", "Naypyitaw","Rakhine",
  "Sagaing", "Shan", "Tanintharyi",
  "Yangon")
pop = c(6184829, 4867373, 
  478801, 1689441, 286627,
  1574079, 3917055, 6165723, 
  2054393, 1160242, 3188807, 
  5325347, 5824432, 1408401,
  1160242)
colors = rainbow(10)
plot(gadm, border = 'darkgrey',
  col = colors[pop / 1000000],
  main="Population by Region")
legend(101,20, 
  c("0.5M","1M","2M","4M","8M"),
  fill = c(colors[1 + c(0.5,1,2,4,8)]))

3.1.1 Saving a graph.

Plotted graphs can be saved as graphic files that can be included in other documents and reports. The key is to activate a graphic output stream as shown in the following code block which re-plots the previous chart directly into a PNG file.

png("./myanmarpop.png",
    width=480,height=800)
plot(gadm, border = 'darkgrey',
  col = colors[pop / 1000000],
  main="Population by Region")
legend(101,20, 
  c("0.5M","1M","2M","4M","8M"),
  fill = c(colors[1 + c(0.5,1,2,4,8)]))
dev.off()
null device 
          1 

The resulting graphic file can be loaded in the R notebook by referencing the external image.

Myanmar Population

Myanmar Population

3.2 Plotting on Maps

The following example plots the location of Myanmar airports on a Google satellite image. The data was extracted from the master database of airport from Open Flight5

library(knitr)
airports = read.csv("datasets/myanmarairports.csv")
kable(airports)
name call lat long
Kalay Airport VYKL 23.1888 94.0511
Mandalay International Airport VYMD 21.7022 95.9779
Myeik Airport VYME 12.4398 98.6215
Myitkyina Airport VYMK 25.3836 97.3519
Momeik Airport VYMO 23.0925 96.6453
Mong Hsat Airport VYMS 20.5168 99.2568
Nampong Air Base VYNP 25.3544 97.2952
Namsang Airport VYNS 20.8905 97.7359
Hpa-N Airport VYPA 16.8937 97.6746
Putao Airport VYPT 27.3299 97.4263
Pyay Airport VYPY 18.8245 95.2660
Shante Air Base VYST 20.9417 95.9145
Sittwe Airport VYSW 20.1327 92.8726
Thandwe Airport VYTD 18.4607 94.3001
Tachileik Airport VYTL 20.4838 99.9354
Taungoo Airport VYTO 19.0313 96.4012
Yangon International Airport RGN 16.9073 96.1332

These data can be plotted on maps generated within R as per the following code.

airports = read.csv("datasets/myanmarairports.csv")
map3 <- GetMap("Naypyitaw, Myanmar",
 destfile = "MyTile2.png",size=c(640,640), zoom=5,
 maptype = "satellite")
PlotOnStaticMap(map3,
  airports$lat,airports$long,
  col="yellow",pch=19)

```

The following code plots the same airport information on a silhouette of Myanmar.

gadm <- readRDS(
  "datasets/MMR_adm0.rds")
plot(gadm, col = "#00FF0033", 
  border = 'darkgreen')
points(airports$long, airports$lat,
  col="red",pch=19)
title("Commerical Airports of Myanmar")

4 Summary

While this technical note has only covered on the basics of mapping in R, there are many more advanced functions to allow the user to generate a wide assortment of maps and projections. The online help for each package can be found by double clicking on the package name in the Packages tab of RStudio. If you get stuck, you are welcome to contact me.

Enjoy!

5 Bibliography


  1. RStudio Team (2015). RStudio: Integrated Development for R. RStudio, Inc., Boston, MA URL http://www.rstudio.com/products/RStudio/.

  2. R Consortium (2017). R language downloaded from https://cran.r-project.org/mirrors.html

  3. Global Administrative Areas (2015). GADM database of Global Administrative Areas, version 2.8. available online at http://www.gadm.org.

  4. Wikipedia contributors (2017) Administrative divisions of Myanmar. in Wikipedia, The Free Encyclopedia. Available online

  5. Open Flights (2012). Airport, airline and route data. Available online at https://openflights.org/data.html

LS0tDQp0aXRsZTogIjxiPk1hcHBpbmcgd2l0aCBSOjwvYj48YnIvPjxmb250IHNpemU9NT48Yj5SZWxldmFudCBub3RlcyBmcm9tIG15IHNob3J0IGNvdXJzZTwvYj48L2ZvbnQ+Ig0KYXV0aG9yOiBCb2IgQmF0emluZ2VyPGJyPkNvbXB1dGVyIFNjaWVuY2UgSW5zdHJ1Y3RvciBFbWVyaXR1czxicj5QYXlhcCBVbml2ZXJzaXR5LA0KICBDaGlhbmcgTWFpLCBUaGFpbGFuZA0KZGF0ZTogIjEzIEp1bmUgMjAxNyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgZmlnX2hlaWdodDogNg0KICAgIGZpZ193aWR0aDogNw0KICAgIGhpZ2hsaWdodDogaGFkZG9jaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IHNhbmRzdG9uZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMgT3ZlcnZpZXcNCg0KVGhlIGZvbGxvd2luZyBkb2N1bWVudCBkZXNjcmliZXMgdGhlIHVzZSBvZiB0aGUgUiBzdGF0aXN0aWNhbCBwYWNrYWdlIHRvIGdlbmVyYXRlIG1hcCByZW5kZXJpbmcgb2Ygc3RhdGlzdGljcy4gVGhpcyBkb2N1bWVudCB3YXMgZGV2ZWxvcGVkIGFzIGFuIGludGVyYWN0aXZlIG5vdGVib29rIHVzaW5nIFIgU3R1ZGlvIERlc2t0b3BbXjFdIGVkaXRpb24gYXMgdGhlIGRldmVsb3BtZW50IGFuZCB0ZXN0IGVudmlyb25tZW50LiBUaGlzIGFsbG93cyB0aGUgaW5jbHVzaW9uIG9mIHRoZSBSW14yXSBzb3VyY2UgY29kZSBibG9ja3MgZm9yIGVhY2ggZXhhbXBsZSBwcm92aWRlZC4gQm90aCBSIGFuZCBSU3R1ZGlvIG11c3QgYmUgaW5zdGFsbGVkIHRvIGV4cGVyaW1lbnQgd2l0aCB0aGUgY29kZS4gVGhlc2Ugc29mdHdhcmUgYXJlIGZyZWUgYW5kIGZ1bGx5IHN1cHBvcnRlZCBieSBhIHZpYnJhbnQgdXNlciBjb21tdW5pdHkuIFRoZSB1cmxzIGZvciB0aGUgaW5zdGFsbGVycyBmb3IgdGhlc2Ugc29mdHdhcmUgY2FuIGJlIGZvdW5kIGluIHRoZSBbQmlibGlvZ3JhcGh5XSgjYmlibGlvZ3JhcGh5KS4gSXQgaXMgaW1wb3J0YW50IHRvIGNob29zZSB0aGUgaW5zdGFsbGVyIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIG9wZXJhdGluZyBzeXN0ZW0geW91IGFyZSB1c2luZy4gKFRoZSBiYXNpYywgZnJlZSB2ZXJzaW9ucyBhcmUgYWxsIHlvdSBuZWVkLikNCg0KW14xXTogUlN0dWRpbyBUZWFtICgyMDE1KS4gUlN0dWRpbzogSW50ZWdyYXRlZCBEZXZlbG9wbWVudCBmb3IgUi4gUlN0dWRpbywgSW5jLiwgQm9zdG9uLCBNQSBVUkwgaHR0cDovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9SU3R1ZGlvLy4NCg0KW14yXTogIFIgQ29uc29ydGl1bSAoMjAxNykuIFIgbGFuZ3VhZ2UgZG93bmxvYWRlZCBmcm9tIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL21pcnJvcnMuaHRtbA0KDQojIEJhc2ljIE1hcHBpbmcgRnVuY3Rpb25zDQoNClRoZXJlIGFyZSBudW1lcm91cyB3YXlzIHRvIHBsb3QgaW5mb3JtYXRpb24gb24gbWFwcyB3aXRoIFIuIFRoZSAzIHBhY2thZ2VzIGRlc2NyaWJlZCBpbiB0aGlzIHNlY3Rpb24gcmVwcmVzZW50IHRoZSBtb3N0IGNvbW1vbiBtZXRob2RzIHVzZWQuIEVhY2ggb2YgdGhlbSB1c2UgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBtYWtpbmcgaXQgcG9zc2libGUgdG8gY29tYmluZSB0aGUgZGlmZmVyZW50IHRlY2huaXF1ZXMgdG9nZXRoZXIgYXMgc2hvd24gaW4gdGhlIFtuZXh0IGNoYXB0ZXJdKCNVc2luZyB0aGUgTWFwcykuIEl0IHNob3VsZCBiZSBub3RlZCB0aGF0IGVhY2ggdGVjaG5vbG9neSB3aWxsIHJlcXVpcmUgdGhlIGluc3RhbGxhdGlvbiBhbmQgbG9hZCBvZiBzcGVjaWZpYyBwYWNrYWdlcy4gVGhlIGNvZGUgZm9yIHRoaXMgaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZyBzZWN0aW9ucy4NCg0KDQojIyBXb3JsZCBNYXANCg0KVGhpcyBtYXBwaW5nIHRlY2hub2xvZ3kgd29ya3MgZnJvbSBhbiBvdXRsaW5lIG9mIHRoZSB3b3JsZCBtYXAgYW5kIHJlcXVpcmVzIGEgc3BlY2lmaWMgcGFja2FnZS4NCg0KIyMjIEluc3RhbGxpbmcgdGhlIHBhY2thZ2UNCg0KWW91IGNhbiBpbnN0YWxsIHRoZSBwYWNrYWdlIHVzaW5nIHRoZSBJTlNUQUxMIGJ1dHRvbiBpbiB0aGUgUGFja2FnZXMgaW4gUlN0dWRpbyBvciBjb3B5IGFuZCBydW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIGRvd25sb2FkIHRoZSBwYWNrYWdlIGZyb20gdGhlIEludGVybmV0LiBUaGlzIGNvcHkgb25seSBuZWVkcyB0byBiZSBydW4gb25jZS4NCg0KYGBgDQppbnN0YWxsLnBhY2thZ2VzKCJtYXBzIikNCmluc3RhbGwucGFja2FnZXMoIm1hcGRhdGEiKQ0KYGBgDQoNCiMjIyBMb2FkaW5nIHRoZSBwYWNrYWdlIA0KDQpUaGlzIHBhY2thZ2UgaXMgbm90IHBhcnQgb2YgdGhlIGNvcmUgb2YgUiBhbmQgbXVzdCBiZSBsb2FkZWQgaW50byB0aGUgd29ya2luZyBsaWJyYXJ5IG9uY2UgYmVmb3JlIHVzaW5nIGl0cyBmdW5jdGlvbnMuIFRoZSBjb2RlIHRvIGRvIHRoaXMgaXMgZ2l2ZW4gYmVsb3c6DQoNCmBgYHtyfQ0KbGlicmFyeShtYXBzKQ0KbGlicmFyeShtYXBkYXRhKQ0KYGBgDQoNCiMjIyBLZXkgZnVuY3Rpb25zDQoNCk1hcHMgYXJlIGRyYXduIG1lcmVseSBieSBsaXN0aW5nIHRoZSBuYW1lcyBvZiB0aGUgY291bnRyaWVzIChvciByZWdpb24pIG9mIGludGVyZXN0IGFzIHNob3duIGJ5IHRoZXNlIGV4YW1wbGVzOg0KDQoxLiBUaGUgZW50aXJlIHdvcmxkLg0KDQpgYGB7cn0NCm1hcCgnd29ybGRIaXJlcycpDQpgYGANCg0KMi4gQSByZWdpb24gKGFzIGFuIGFycmF5IG9mIGNvdW50cmllcykuDQoNCmBgYHtyfQ0KbWFwKCd3b3JsZEhpcmVzJywNCiAgcmVnaW9uPWMoIm15YW5tYXIiLCJ0aGFpbGFuZCIsDQogICAgImxhb3MiLCJjYW1ib2RpYSIsInZpZXRuYW0iLA0KICAgICJtYWxheXNpYSIsInNpbmdhcG9yZSIsDQogICAgImluZG9uZXNpYSIsImJydW5laSIsDQogICAgInBoaWxpcHBpbmVzIikpDQpgYGANCg0KMy4gQSBzaW5nbGUgY291bnRyeQ0KYGBge3IsZmlnLndpZHRoPTguNX0NCm1hcCgnd29ybGRIaXJlcycsJ215YW5tYXInKQ0KYGBgDQoNCkl0IGlzIGFsc28gcG9zc2libGUgdG8gZmlsbCBlYWNoIGNvdW50cnkgd2l0aCBhIGRpZmZlcmVudCBjb2xvci4NCg0KYGBge3J9DQpjb3VudHJpZXMgPSBjKCJteWFubWFyIiwidGhhaWxhbmQiLA0KICAibGFvcyIsImNhbWJvZGlhIiwidmlldG5hbSIsDQogICJtYWxheXNpYSIsInNpbmdhcG9yZSIsDQogICJpbmRvbmVzaWEiLCAiYnJ1bmVpIiwNCiAgInBoaWxpcHBpbmVzIikNCmxlbiA9IGxlbmd0aChjb3VudHJpZXMpDQpjb2xvcnMgPSByYWluYm93KGxlbiArIDEpDQoNCm1hcCgnd29ybGRIaXJlcycsDQogIHJlZ2lvbj1jb3VudHJpZXMsDQogIGZpbGw9VFJVRSxjb2w9Y29sb3JzWzFdKQ0KbWFwLnNjYWxlKDEyMCwyNiwNCiAgcmVsd2lkdGg9MC4yNSxjZXg9MC42LA0KICBtZXRyaWM9VFJVRSxyYXRpbz1UUlVFKQ0KZm9yIChpIGluIGMoMjo2LDg6MTApKSB7DQogIG1hcCgnd29ybGRIaXJlcycsDQogICAgcmVnaW9uPWNvdW50cmllc1tpXSwNCiAgICBmaWxsPVRSVUUsYWRkPVRSVUUsDQogICAgY29sPWNvbG9yc1tpXSkNCn0NCmBgYA0KDQoNCiMjIEdvb2dsZSBNYXBzDQoNCkdvb2dsZSBtYXBzIGFyZSB1cGRhdGVkIGFwcHJveGltYXRlbHkgZXZlcnkgOSBtb250aHMuIFRoZXNlIGZ1bmN0aW9ucyBtYWtlIGlmIHBvc3NpYmxlIHRvIGNhcHR1cmUgYSByb2FkIG1hcCwgc2F0ZWxsaXRlIHBob3RvLCBvciB0b3BvZ3JhcGhpYyBtYXAuIEFjY2VzcyB0byB0aGVzZSBtYXBzIHJlcXVpcmVzIHRoZSBjb3JyZXNwb25kaW5nIHBhY2thZ2UuDQoNCg0KIyMjIEluc3RhbGxpbmcgdGhlIHBhY2thZ2UNCg0KWW91IGNhbiBpbnN0YWxsIHRoZSBwYWNrYWdlIHVzaW5nIHRoZSBJTlNUQUxMIGJ1dHRvbiBpbiB0aGUgUGFja2FnZXMgaW4gUlN0dWRpbyBvciBjb3B5IGFuZCBydW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIGRvd25sb2FkIHRoZSBwYWNrYWdlIGZyb20gdGhlIEludGVybmV0LiBUaGlzIGNvcHkgb25seSBuZWVkcyB0byBiZSBydW4gb25jZS4NCg0KYGBgDQppbnN0YWxsLnBhY2thZ2VzKCJSZ29vZ2xlTWFwcyIpDQpgYGANCg0KIyMjIExvYWRpbmcgdGhlIHBhY2thZ2UgDQoNClRoaXMgcGFja2FnZSBpcyBub3QgcGFydCBvZiB0aGUgY29yZSBvZiBSIGFuZCBtdXN0IGJlIGxvYWRlZCBpbnRvIHRoZSB3b3JraW5nIGxpYnJhcnkgb25jZSBiZWZvcmUgdXNpbmcgaXRzIGZ1bmN0aW9ucy4gVGhlIGNvZGUgdG8gZG8gdGhpcyBpcyBnaXZlbiBiZWxvdzoNCg0KYGBge3J9DQpsaWJyYXJ5KFJnb29nbGVNYXBzKQ0KYGBgDQoNCiMjIyBLZXkgZnVuY3Rpb25zDQoNCjEuIEdldHRpbmcgYSBtYXA6IEluIEdvb2dsZSBtYXBzIG9uZSBjYW4gc2VhcmNoIGZvciBhIG1hcCBieSB0aGUgbmFtZSBvZiBhIGxhbmRtYXJrIG9yIGJ5IGl0cyBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlLiBJbiB0aGUgZm9sbG93aW5nIGNvZGUgYmxvY2ssIHRoZSBNeWFubWFyIEJpYmxlIEhvdXNlIChuZXh0IHRvIFN1bGUpIGluIFlhbmdvbiBpcyByZWZlcmVuY2VkLg0KDQpgYGB7cn0NCm1hcDEgPC0gR2V0TWFwKA0KICJCaWJsZSBIb3VzZSwgWWFuZ29uLCBNeWFubWFyIiwNCiBkZXN0ZmlsZSA9ICJNeVRpbGUxLnBuZyIsDQogc2l6ZT1jKDY0MCw2NDApLCB6b29tPTE4LA0KIG1hcHR5cGUgPSAicm9hZG1hcCIpDQpgYGANClRoaXMgY3JlYXRlcyBhIGdyYXBoaWMgZmlsZSAobmFtZWQgTXlUaWxlMS5wbmcpIHdoaWNoIGlzIHN0b3JlZCBpbiB0aGUgcHJvamVjdCBkaXJlY3RvcnkuDQoNCiFbbWFwXShNeVRpbGUxLnBuZykNCg0KMi4gRGlzcGxheWluZyB0aGUgbWFwLiBUaGUgbWFwIGNhbiBiZSBkaXNwbGF5ZWQgaW4gUiB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGUuDQoNCmBgYHtyfQ0KUGxvdE9uU3RhdGljTWFwKG1hcDEpDQpgYGANCjMuIFNwZWNpZnlpbmcgb3RoZXIgbWFwIHR5cGVzLg0KT2YgY291cnNlLCBzYXRlbGxpdGUgaW1hZ2VzIGNhbiBhbHNvIGJlIGdlbmVyYXRlZCB1c2luZyB0aGUgc2FtZSBmdW5jdGlvbi4gSW4gZmFjdCB0aGUgcmFuZ2Ugb2YgcG9zc2liaWxpdGllcyBhcmUgcm9hZG1hcCwgbW9iaWxlLCBzYXRlbGxpdGUsIHRlcnJhaW4sIGh5YnJpZCwgYW5kICBtYXBtYWtlci1yb2FkbWFwLg0KDQpgYGB7cn0NCm1hcDIgPC0gR2V0TWFwKCJCaWJsZSBIb3VzZSwgWWFuZ29uLCBNeWFubWFyIiwNCiBkZXN0ZmlsZSA9ICJNeVRpbGUyLnBuZyIsc2l6ZT1jKDY0MCw2NDApLCB6b29tPTE4LA0KIG1hcHR5cGUgPSAic2F0ZWxsaXRlIikNClBsb3RPblN0YXRpY01hcChtYXAyKQ0KYGBgDQojIyBHQURNIE1hcHBpbmcNCg0KR0FETVteM10gaXMgYSBnZW9ncmFwaGljIGluZm9ybWF0aW9uIHN5c3RlbSAoR0lTKSBjb2xsZWN0aW9uIG9mIHRoZSBzaGFwZXMgb2YgNCBsZXZlbHMgb2YgcG9saXRpY2FsIGJvdW5kYXJpZXMgZm9yIG1vc3QgY291bnRyaWVzIG9mIHRoZSB3b3JsZCAobmFtZWx5IGNvdW50cnksIHByb3ZpbmNlL3N0YXRlLCBkaXN0cmljdCwgdG93bnNoaXBzKS4gVGhlIGRhdGFiYXNlcyBhcmUgZnJlZSBmb3Igbm9uLWNvbW1lcmNpYWwgdXNlcyBhbmQgdXNlIGxvbmdpdHVkZS9sYXRpdHVkZSBjb29yZGluYXRlIHN5c3RlbS4gRWFjaCBjb3VudHJ5IGNvbGxlY3Rpb24gaXMgYXZhaWxhYmxlIGluIG1hbnkgdmVyc2lvbnMgc28gaXQgaXMgaW1wb3J0YW50IHRvIGRvd25sb2FkIHRoZSBSRFMgdmVyc2lvbiB3aGljaCB3YXMgZGVzaWduZWQgZm9yIHVzZSB3aXRoIFIuIFRoZSBjdXJyZW50IHZlcnNpb24gKDIuOCkgd2FzIHJlbGVhc2VkIGluIE5vdiAyMDE1IGFuZCBWZXJzaW9uIDMgaXMgZXhwZWN0ZWQgaW4gQXVnIDIwMTcuIFRoZXNlIG1hcHMgYXJlIGlkZWFsbHkgc3VpdGVkIGZvciBlcGlkZW1pYywgcG9saXRpY2FsIGFuZCBlY29ub21pYyByZXNlYXJjaC4NCg0KW14zXTogR2xvYmFsIEFkbWluaXN0cmF0aXZlIEFyZWFzICgyMDE1KS4gR0FETSBkYXRhYmFzZSBvZiBHbG9iYWwgQWRtaW5pc3RyYXRpdmUgQXJlYXMsIHZlcnNpb24gMi44LiBhdmFpbGFibGUgb25saW5lIGF0IGh0dHA6Ly93d3cuZ2FkbS5vcmcuDQoNCiMjIyBJbnN0YWxsaW5nIHRoZSBwYWNrYWdlDQoNCllvdSBjYW4gaW5zdGFsbCB0aGUgcGFja2FnZSB1c2luZyB0aGUgSU5TVEFMTCBidXR0b24gaW4gdGhlIFBhY2thZ2VzIGluIFJTdHVkaW8gb3IgY29weSBhbmQgcnVuIHRoZSBmb2xsb3dpbmcgY29kZSB0byBkb3dubG9hZCB0aGUgcGFja2FnZSBmcm9tIHRoZSBJbnRlcm5ldC4gVGhpcyBjb3B5IG9ubHkgbmVlZHMgdG8gYmUgcnVuIG9uY2UuDQoNCmBgYA0KaW5zdGFsbC5wYWNrYWdlcygic3AiKQ0KYGBgDQoNCiMjIyBMb2FkaW5nIHRoZSBwYWNrYWdlIA0KDQpUaGlzIHBhY2thZ2UgaXMgbm90IHBhcnQgb2YgdGhlIGNvcmUgb2YgUiBhbmQgbXVzdCBiZSBsb2FkZWQgaW50byB0aGUgd29ya2luZyBsaWJyYXJ5IG9uY2UgYmVmb3JlIHVzaW5nIGl0cyBmdW5jdGlvbnMuIFRoZSBjb2RlIHRvIGRvIHRoaXMgaXMgZ2l2ZW4gYmVsb3c6DQoNCmBgYHtyfQ0KbGlicmFyeShzcCkNCmBgYA0KDQojIyMgS2V5IGZ1bmN0aW9ucw0KDQoxLiBSZWFkaW5nIHRoZSBnZW9wb2xpdGljYWwgYm91bmRhcmllcyBmaWxlIA0KYGBge3J9DQpnYWRtIDwtIHJlYWRSRFMoDQogICJkYXRhc2V0cy9NTVJfYWRtMi5yZHMiKQ0KYGBgDQoNCjIuIENyZWF0aW5nIGEgbWFwDQpgYGB7cixmaWcud2lkdGg9OH0NCmNvbG9ycyA9IHJhaW5ib3coMTAwKQ0KcGxvdChnYWRtLCBjb2wgPSBjb2xvcnMsIGJvcmRlciA9ICdkYXJrZ3JleScpDQpgYGANCjMuIFNlbGVjdGluZyBwYXJ0aWN1bGFyIHJlZ2lvbnMgdG8gY29sb3INCg0KYGBge3IsZmlnLndpZHRoPTh9DQpnYWRtIDwtIHJlYWRSRFMoImRhdGFzZXRzL01NUl9hZG0xLnJkcyIpDQpjb2xvcnMgPSByZXAoIiMwMEZGMDAyMiIsNzApDQpjb2xvcnNbMTVdID0gInB1cnBsZSINCmNvbG9yc1tnYWRtQGRhdGEkTkFNRV8xID09DQogICAgIk5heXB5aXRhdyJdID0gInJlZCINCnBsb3QoZ2FkbSwgY29sID0gY29sb3JzLCANCiAgICAgYm9yZGVyID0gRkFMU0UpDQpsZWdlbmQoOTksMTksDQogIGMoIllhbmdvbiIsIk5heXB5aXRhdyIpLA0KICBmaWxsPWMoInB1cnBsZSIsInJlZCIpKQ0KYGBgDQoNCg0KVGhlIEFETSBmaWxlIGNvbnRhaW5zIGEgZGF0YWJhc2UgYW5kIGJ5IG1hdGNoaW5nIHRoZSBvcmRlcmluZyBvciBieSBzZWxlY3RpbmcgYSBwYXJ0aWN1bGFyIHJlZ2lvbiBpdCBpcyBwb3NzaWJsZSB0byBoaWdobGlnaHQgc2VsZWN0ZWQgcmVnaW9ucyBvZiB0aGUgY291bnRyeS4NCg0KIyBVc2luZyB0aGUgTWFwcw0KDQojIyBDb2xvcmluZyB0aGUgTWFwcw0KDQpJbiB0aGlzIGV4YW1wbGUsIHRoZSB2YXJpb3VzIHN0YXRlcyBhbmQgcmVnaW9ucyBvZiBNeWFubWFyIGFyZSBjb2xvcmVkIGFjY29yZGluZyB0byB0aGUgbG9jYWwgcG9wdWxhdGlvbnMuW140XQ0KDQpbXjRdOiAgV2lraXBlZGlhIGNvbnRyaWJ1dG9ycyAgKDIwMTcpIEFkbWluaXN0cmF0aXZlIGRpdmlzaW9ucyBvZiBNeWFubWFyLiBpbiBXaWtpcGVkaWEsIFRoZSBGcmVlIEVuY3ljbG9wZWRpYS4gIEF2YWlsYWJsZSBbb25saW5lXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BZG1pbmlzdHJhdGl2ZV9kaXZpc2lvbnNfb2ZfTXlhbm1hcikgDQoNCmBgYHtyLGZpZy53aWR0aD04fQ0KZ2FkbSA8LSByZWFkUkRTKA0KICJkYXRhc2V0cy9NTVJfYWRtMS5yZHMiKQ0KDQpyZWdpb25zID0gYygiQXlleWFyd2FkeSIsIkJhZ28iLA0KICAiQ2hpbiIsICJLYWNoaW4iLCAiS2F5YWgiLA0KICAiS2F5aW4iLCJNYWd3YXkiLCJNYW5kYWxheSIsDQogICJNb24iLCAiTmF5cHlpdGF3IiwiUmFraGluZSIsDQogICJTYWdhaW5nIiwgIlNoYW4iLCAiVGFuaW50aGFyeWkiLA0KICAiWWFuZ29uIikNCg0KcG9wID0gYyg2MTg0ODI5LCA0ODY3MzczLCANCiAgNDc4ODAxLCAxNjg5NDQxLCAyODY2MjcsDQogIDE1NzQwNzksIDM5MTcwNTUsIDYxNjU3MjMsIA0KICAyMDU0MzkzLCAxMTYwMjQyLCAzMTg4ODA3LCANCiAgNTMyNTM0NywgNTgyNDQzMiwgMTQwODQwMSwNCiAgMTE2MDI0MikNCg0KY29sb3JzID0gcmFpbmJvdygxMCkNCg0KcGxvdChnYWRtLCBib3JkZXIgPSAnZGFya2dyZXknLA0KICBjb2wgPSBjb2xvcnNbcG9wIC8gMTAwMDAwMF0sDQogIG1haW49IlBvcHVsYXRpb24gYnkgUmVnaW9uIikNCmxlZ2VuZCgxMDEsMjAsIA0KICBjKCIwLjVNIiwiMU0iLCIyTSIsIjRNIiwiOE0iKSwNCiAgZmlsbCA9IGMoY29sb3JzWzEgKyBjKDAuNSwxLDIsNCw4KV0pKQ0KYGBgDQoNCiMjIyBTYXZpbmcgYSBncmFwaC4NCg0KUGxvdHRlZCBncmFwaHMgY2FuIGJlIHNhdmVkIGFzIGdyYXBoaWMgZmlsZXMgdGhhdCBjYW4gYmUgaW5jbHVkZWQgaW4gb3RoZXIgZG9jdW1lbnRzIGFuZCByZXBvcnRzLiBUaGUga2V5IGlzIHRvIGFjdGl2YXRlIGEgZ3JhcGhpYyBvdXRwdXQgc3RyZWFtIGFzIHNob3duIGluIHRoZSBmb2xsb3dpbmcgY29kZSBibG9jayB3aGljaCByZS1wbG90cyB0aGUgcHJldmlvdXMgY2hhcnQgZGlyZWN0bHkgaW50byBhIFBORyBmaWxlLg0KDQpgYGB7cn0NCnBuZygiLi9teWFubWFycG9wLnBuZyIsDQogICAgd2lkdGg9NDgwLGhlaWdodD04MDApDQpwbG90KGdhZG0sIGJvcmRlciA9ICdkYXJrZ3JleScsDQogIGNvbCA9IGNvbG9yc1twb3AgLyAxMDAwMDAwXSwNCiAgbWFpbj0iUG9wdWxhdGlvbiBieSBSZWdpb24iKQ0KbGVnZW5kKDEwMSwyMCwgDQogIGMoIjAuNU0iLCIxTSIsIjJNIiwiNE0iLCI4TSIpLA0KICBmaWxsID0gYyhjb2xvcnNbMSArIGMoMC41LDEsMiw0LDgpXSkpDQpkZXYub2ZmKCkNCmBgYA0KDQpUaGUgcmVzdWx0aW5nIGdyYXBoaWMgZmlsZSBjYW4gYmUgbG9hZGVkIGluIHRoZSBSIG5vdGVib29rIGJ5IHJlZmVyZW5jaW5nIHRoZSBleHRlcm5hbCBpbWFnZS4NCg0KIVtNeWFubWFyIFBvcHVsYXRpb25dKG15YW5tYXJwb3AucG5nKQ0KDQoNCg0KDQojIyBQbG90dGluZyBvbiBNYXBzDQoNClRoZSBmb2xsb3dpbmcgZXhhbXBsZSBwbG90cyB0aGUgbG9jYXRpb24gb2YgTXlhbm1hciBhaXJwb3J0cyBvbiBhIEdvb2dsZSBzYXRlbGxpdGUgaW1hZ2UuDQpUaGUgZGF0YSB3YXMgZXh0cmFjdGVkIGZyb20gdGhlIG1hc3RlciBkYXRhYmFzZSBvZiBhaXJwb3J0IGZyb20gT3BlbiBGbGlnaHRbXjVdDQoNClteNV06IE9wZW4gRmxpZ2h0cyAoMjAxMikuIEFpcnBvcnQsIGFpcmxpbmUgYW5kIHJvdXRlIGRhdGEuIEF2YWlsYWJsZSBvbmxpbmUgYXQgaHR0cHM6Ly9vcGVuZmxpZ2h0cy5vcmcvZGF0YS5odG1sDQoNCmBgYHtyfQ0KbGlicmFyeShrbml0cikNCmFpcnBvcnRzID0gcmVhZC5jc3YoImRhdGFzZXRzL215YW5tYXJhaXJwb3J0cy5jc3YiKQ0Ka2FibGUoYWlycG9ydHMpDQpgYGANCg0KVGhlc2UgZGF0YSBjYW4gYmUgcGxvdHRlZCBvbiBtYXBzIGdlbmVyYXRlZCB3aXRoaW4gUiBhcyBwZXIgdGhlIGZvbGxvd2luZyBjb2RlLg0KDQpgYGB7cn0NCm1hcDMgPC0gR2V0TWFwKCJOYXlweWl0YXcsIE15YW5tYXIiLA0KIGRlc3RmaWxlID0gIk15VGlsZTIucG5nIixzaXplPWMoNjQwLDY0MCksIHpvb209NSwNCiBtYXB0eXBlID0gInNhdGVsbGl0ZSIpDQpQbG90T25TdGF0aWNNYXAobWFwMywNCiAgYWlycG9ydHMkbGF0LGFpcnBvcnRzJGxvbmcsDQogIGNvbD0ieWVsbG93IixwY2g9MTkpDQpgYGANCg0KDQpgYGANCg0KVGhlIGZvbGxvd2luZyBjb2RlIHBsb3RzIHRoZSBzYW1lIGFpcnBvcnQgaW5mb3JtYXRpb24gb24gYSBzaWxob3VldHRlIG9mIE15YW5tYXIuDQoNCmBgYHtyLGZpZy53aWR0aD04fQ0KZ2FkbSA8LSByZWFkUkRTKA0KICAiZGF0YXNldHMvTU1SX2FkbTAucmRzIikNCnBsb3QoZ2FkbSwgY29sID0gIiMwMEZGMDAzMyIsIA0KICBib3JkZXIgPSAnZGFya2dyZWVuJykNCnBvaW50cyhhaXJwb3J0cyRsb25nLCBhaXJwb3J0cyRsYXQsDQogIGNvbD0icmVkIixwY2g9MTkpDQp0aXRsZSgiQ29tbWVyaWNhbCBBaXJwb3J0cyBvZiBNeWFubWFyIikNCmBgYA0KDQoNCg0KDQojIFN1bW1hcnkNCg0KV2hpbGUgdGhpcyB0ZWNobmljYWwgbm90ZSBoYXMgb25seSBjb3ZlcmVkIG9uIHRoZSBiYXNpY3Mgb2YgbWFwcGluZyBpbiBSLCB0aGVyZSBhcmUgbWFueSBtb3JlIGFkdmFuY2VkIGZ1bmN0aW9ucyB0byBhbGxvdyB0aGUgdXNlciB0byBnZW5lcmF0ZSBhIHdpZGUgYXNzb3J0bWVudCBvZiBtYXBzIGFuZCBwcm9qZWN0aW9ucy4gVGhlIG9ubGluZSBoZWxwIGZvciBlYWNoIHBhY2thZ2UgY2FuIGJlIGZvdW5kIGJ5IGRvdWJsZSBjbGlja2luZyBvbiB0aGUgcGFja2FnZSBuYW1lIGluIHRoZSBQYWNrYWdlcyB0YWIgb2YgUlN0dWRpby4gSWYgeW91IGdldCBzdHVjaywgeW91IGFyZSB3ZWxjb21lIHRvIFtjb250YWN0IG1lXShtYWlsdG86cm9iZXJ0X2JAcGF5YXAuYWMudGgpLiANCg0KRW5qb3khDQoNCiMgQmlibGlvZ3JhcGh5DQo=