<< Workshop Home

Visualizing Quantitative Data: Points

Now that we have geocoded and visualized where our ANC sites are located, let’s map the HIV prevalence data!

# Set working directory

# Load packages
library(rgdal) # rgdal automatically loads SP package with it
library(prettymapr)

# Options
options(stringsAsFactors = F)
# import our previously geocoded HIV prevalence data
ANC2012_geocoded <- read.csv("ANC2012_geocoded.csv")
ANC_shp <- ANC2012_geocoded
coordinates(ANC_shp) <- ~lon + lat
class(ANC_shp)

# import our provinces shapefile to map in background
provinces_shp <- readOGR("zwe_polbnda_adm1_250k_cso.shp")

Labeling Points

# Label HIV prevalence
plot(provinces_shp, col = "azure")
plot(ANC_shp, add = T, pch = 15)
text(ANC_shp$lon, ANC_shp$lat - 0.1, labels = ANC_shp$prevalence, cex = 0.8)

Labeling is nice, but it doesn’t really help us visualize the data. Let’s try again.

Varying Colors

We’re going to want a few more packages to help us create data categories and assign nice colors to each category.

# Load packages
library(classInt)
library(RColorBrewer)
#check out the ColorBrewer palette options
display.brewer.all() 
### Represent HIV prevalence with colored symbols

# first set some parameters
plotvar <- ANC_shp$prevalence #variable to display
ncolors <- 5 #number of colors
pal <- brewer.pal(ncolors,"Reds") #color palette

Equal Intervals

# 1. Equal-interval class intervals
breaks <- classIntervals(plotvar, ncolors, style="equal") #category breaks
colcode <- findColours(breaks, pal) #assign colors based on category

plot(provinces_shp, col = "grey93", border = "grey")
points(ANC_shp, pch=16, col=colcode, cex=2)
title("HIV Prevalence at ANC Sites", sub = "Equal Intervals")
legend("bottomleft", legend = names(attr(colcode, "table")),
       fill = attr(colcode, "palette"), cex = 1, bty = "n")

NOTE: In the legend, square brackets indicate that a number is included in the category, whereas parentheses indicate that a number is not included. For example, in the above map, the first category reads “From 7.1 up to but not including 10.88” and the second category is “From 10.88 up to but not including 14.66”. If a clinic’s HIV prevalence is exactly 10.88, it is included in the second category.

Quantile Intervals

# 2. Quantile class intervals
breaks <- classIntervals(plotvar, ncolors, style="quantile")
colcode <- findColours(breaks, pal)

plot(provinces_shp, col = "grey93", border = "grey")
points(ANC_shp, pch=16, col=colcode, cex=2)
title("HIV Prevalence at ANC Sites", sub = "Quantile Intervals")
legend("bottomleft", legend = names(attr(colcode, "table")),
       fill = attr(colcode, "palette"), cex = 1, bty = "n")

These two maps show the same data, but first map uses equal intervals (each category is 3.78 pct points) while the second map uses quantiles (each category contains 20% of the data).

Natural Breaks (Jenks)

A third type of interval classification method is called “Natural Breaks” or “Jenks”. This method seeks to reduce the variance within classes and maximize the variance between classes. Essentially, it tries to break the data into natural groupings.

# 3. Jenks class intervals
breaks <- classIntervals(plotvar, ncolors, style="jenks")
colcode <- findColours(breaks, pal)

plot(provinces_shp, col = "grey93", border = "grey")
points(ANC_shp, pch=16, col=colcode, cex=2)
title("HIV Prevalence at ANC Sites", sub = "Natural Breaks (Jenks)")
legend("bottomleft", legend = names(attr(colcode, "table")),
       fill = attr(colcode, "palette"), cex = 1, bty = "n")

Varying Symbol Sizes

Now, we can go one step further and add varying circle sizes to enhance the visualization.

# vary the size of the site symbol
breaks <- classIntervals(plotvar, ncolors, style="jenks")
colcode <- findColours(breaks, pal)
plot(provinces_shp, col = "grey93", border = "grey")
symbols(ANC_shp$lon, ANC_shp$lat, add = TRUE,
        circles = ANC_shp$prevalence, bg = colcode, inches = 0.1)
title("HIV Prevalence at ANC Sites", sub = "Natural Breaks (Jenks)")
legend("bottomleft", legend = names(attr(colcode, "table")),
       fill = attr(colcode, "palette"), cex = 1, bty = "n")

Visualizing Quantitative Data: Polygons

Joining Data to a Shapefile

# Import the provinces shapefile
provinces_shp <- readOGR("zwe_polbnda_adm1_250k_cso.shp")
class(provinces_shp) # check the class of data
class(provinces_shp@data) # @data is the data frame embedded in the shapefile
provinces_shp@data #view the shapefile's data frame
plot(provinces_shp) #map the shapefile
# Import the Census Data CSV spreadsheet
Census2012 <- read.csv("Census2012.csv")
class(Census2012)
head(Census2012)

We’ll use the inner_join() function from the dplyr package to join the data.

# Load dplyr package to get the inner_join function
library(dplyr)

### Merge the Census data with the province shapefile 

# First, make sure the column we'll use to merge is the same
# rename the 1st column, currently province, to PROVINCE as in the shp
colnames(Census2012)[1] <- "PROVINCE"
head(Census2012)

# check that province names match
cbind(sort(provinces_shp@data$PROVINCE), sort(Census2012$PROVINCE))
cbind(table(provinces_shp@data$PROVINCE), table(Census2012$PROVINCE))

# Now join using the PROVINCE variable
provinces_shp@data <- inner_join(provinces_shp@data, Census2012, by="PROVINCE")
provinces_shp@data #check the result

Labeling Polygons

# Simply label the provinces
plot(provinces_shp, col = "azure", border = "grey")
text(getSpPPolygonsLabptSlots(provinces_shp),
labels=as.character(provinces_shp$MMR), cex=0.8)
title("Maternal Mortality Ratio 2012 (per 100,000 live births)")

Choropleth Maps

# Set parameters
plotvar <- provinces_shp$MMR
ncolors <- 4
pal <- brewer.pal(ncolors,"PuRd")

#Set plot window to combine 4 maps (2x2)
par(mfrow=c(2,2))

# 1. Fixed intervals
breaks <- classIntervals(plotvar, ncolors, style="fixed",
                         fixedBreaks=c(300,400,500,600,700))
colcode <- findColours(breaks, pal)

plot(provinces_shp, col=colcode, border = "grey",
     main="Fixed Intervals")
legend("bottomleft", legend=names(attr(colcode, "table")),
       fill=attr(colcode, "palette"), cex=1, bty="n")
text(getSpPPolygonsLabptSlots(provinces_shp),
labels=plotvar, cex=0.8)

# 2. Equal intervals
breaks <- classIntervals(plotvar, ncolors, style="equal")
colcode <- findColours(breaks, pal)

plot(provinces_shp, col = colcode, border = "grey", 
     main="Equal Intervals")
legend("bottomleft", legend=names(attr(colcode, "table")),
       fill=attr(colcode, "palette"), cex=1, bty="n")
text(getSpPPolygonsLabptSlots(provinces_shp),
labels=plotvar, cex=0.8)

# 3. Quantile intervals
breaks <- classIntervals(plotvar, ncolors, style="quantile")
colcode <- findColours(breaks, pal)

plot(provinces_shp, col=colcode, border = "grey",
     main="Quantile Intervals")
legend("bottomleft", legend=names(attr(colcode, "table")),
       fill=attr(colcode, "palette"), cex=1, bty="n")
text(getSpPPolygonsLabptSlots(provinces_shp),
labels=plotvar, cex=0.8)

# 4. Jenks intervals
breaks <- classIntervals(plotvar, ncolors, style="jenks")
colcode <- findColours(breaks, pal)

plot(provinces_shp, col=colcode, border = "grey",
     main="Natural Intervals (Jenks)")
legend("bottomleft", legend=names(attr(colcode, "table")),
       fill=attr(colcode, "palette"), cex=1, bty="n")
text(getSpPPolygonsLabptSlots(provinces_shp),
labels=plotvar, cex=0.8)

#Title for combined plot
title("Maternal Mortality Ratio 2012 (per 100,000 live births)",
       outer=TRUE, line = -1, cex = 1)
#Return to a single plot format
par(mfrow=c(1,1))

Layering Points and Polygons


# Prettier quantiles: round quantiles to nearest 5
breaks <- classIntervals(plotvar, ncolors, style="fixed",
fixedBreaks=c(370,515,560,585,680))
colcode <- findColours(breaks, pal)

# Parameters for legend
sym <- c(15,15,15,15,19)
labels <- c(paste0("MMR: ", names(attr(colcode, "table"))),
                   "ANC HIV Prevalence")
labels
colors <- c(attr(colcode, "palette"), "black")
colors

#Plot
plot(provinces_shp, col=colcode, border = "grey",
     main="Zimbabwe Maternal Health 2012")
symbols(ANC_shp$lon, ANC_shp$lat, add = TRUE,
        circles = ANC_shp$prevalence, bg = "black", inches = 0.1)
legend("topleft", legend=labels, pch=sym, col=colors,
       cex=1, pt.cex=2, bty="y")
addscalebar(style = "ticks")
addnortharrow(cols = c("white", "grey50"), scale = 0.6)

<< Workshop Home

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBNYXBwaW5nIFNwYXRpYWwgRGF0YSBpbiBSIg0Kc3VidGl0bGU6ICJQYXJ0IDQ6IFZpc3VhbGl6aW5nIFF1YW50aXRhdGl2ZSBEYXRhIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICB0b2NfZGVwdGg6IDINCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlIA0KICAgIHRoZW1lOiBsdW1lbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICB0b2NfZGVwdGg6IDINCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlIA0KICAgIHRoZW1lOiBsdW1lbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUgIA0KDQotLS0NCiAgDQpgYGB7Y3NzfQ0KY29kZSB7DQogIHdoaXRlLXNwYWNlOiBwcmUgIWltcG9ydGFudDsNCiAgb3ZlcmZsb3cteDogc2Nyb2xsICFpbXBvcnRhbnQ7DQogIHdvcmQtYnJlYWs6IGtlZXAtYWxsICFpbXBvcnRhbnQ7DQogIHdvcmQtd3JhcDogaW5pdGlhbCAhaW1wb3J0YW50Ow0KfQ0KYm9keXsgLyogTm9ybWFsICAqLw0KICAgICAgZm9udC1zaXplOiAxNnB4Ow0KICB9DQoNCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkNCm9wdGlvbnMod2lkdGg9NTAwKQ0KDQpgYGANCg0KWyoqPDwgV29ya3Nob3AgSG9tZSoqXShodHRwczovL2NhZmFoZXkuZ2l0aHViLmlvL3NwYXRpYWwpDQoNCg0KDQoNCiMgVmlzdWFsaXppbmcgUXVhbnRpdGF0aXZlIERhdGE6IFBvaW50cw0KDQpOb3cgdGhhdCB3ZSBoYXZlIGdlb2NvZGVkIGFuZCB2aXN1YWxpemVkIHdoZXJlIG91ciBBTkMgc2l0ZXMgYXJlIGxvY2F0ZWQsIGxldCdzIG1hcCB0aGUgSElWIHByZXZhbGVuY2UgZGF0YSENCg0KYGBge3J9DQojIFNldCB3b3JraW5nIGRpcmVjdG9yeQ0KDQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkocmdkYWwpICMgcmdkYWwgYXV0b21hdGljYWxseSBsb2FkcyBTUCBwYWNrYWdlIHdpdGggaXQNCmxpYnJhcnkocHJldHR5bWFwcikNCg0KIyBPcHRpb25zDQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQ0KYGBgDQoNCg0KYGBge3IgfQ0KIyBpbXBvcnQgb3VyIHByZXZpb3VzbHkgZ2VvY29kZWQgSElWIHByZXZhbGVuY2UgZGF0YQ0KQU5DMjAxMl9nZW9jb2RlZCA8LSByZWFkLmNzdigiQU5DMjAxMl9nZW9jb2RlZC5jc3YiKQ0KQU5DX3NocCA8LSBBTkMyMDEyX2dlb2NvZGVkDQpjb29yZGluYXRlcyhBTkNfc2hwKSA8LSB+bG9uICsgbGF0DQpjbGFzcyhBTkNfc2hwKQ0KDQojIGltcG9ydCBvdXIgcHJvdmluY2VzIHNoYXBlZmlsZSB0byBtYXAgaW4gYmFja2dyb3VuZA0KcHJvdmluY2VzX3NocCA8LSByZWFkT0dSKCJ6d2VfcG9sYm5kYV9hZG0xXzI1MGtfY3NvLnNocCIpDQpgYGANCg0KIyMgTGFiZWxpbmcgUG9pbnRzDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KIyBMYWJlbCBISVYgcHJldmFsZW5jZQ0KcGxvdChwcm92aW5jZXNfc2hwLCBjb2wgPSAiYXp1cmUiKQ0KcGxvdChBTkNfc2hwLCBhZGQgPSBULCBwY2ggPSAxNSkNCnRleHQoQU5DX3NocCRsb24sIEFOQ19zaHAkbGF0IC0gMC4xLCBsYWJlbHMgPSBBTkNfc2hwJHByZXZhbGVuY2UsIGNleCA9IDAuOCkNCmBgYA0KTGFiZWxpbmcgaXMgbmljZSwgYnV0IGl0IGRvZXNuJ3QgcmVhbGx5IGhlbHAgdXMgdmlzdWFsaXplIHRoZSBkYXRhLiBMZXQncyB0cnkgYWdhaW4uIA0KDQojIyBWYXJ5aW5nIENvbG9ycw0KV2UncmUgZ29pbmcgdG8gd2FudCBhIGZldyBtb3JlIHBhY2thZ2VzIHRvIGhlbHAgdXMgY3JlYXRlIGRhdGEgY2F0ZWdvcmllcyBhbmQgYXNzaWduIG5pY2UgY29sb3JzIHRvIGVhY2ggY2F0ZWdvcnkuDQpgYGB7cn0NCiMgTG9hZCBwYWNrYWdlcw0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0NCiNjaGVjayBvdXQgdGhlIENvbG9yQnJld2VyIHBhbGV0dGUgb3B0aW9ucw0KZGlzcGxheS5icmV3ZXIuYWxsKCkgDQpgYGANCg0KYGBge3J9DQojIyMgUmVwcmVzZW50IEhJViBwcmV2YWxlbmNlIHdpdGggY29sb3JlZCBzeW1ib2xzDQoNCiMgZmlyc3Qgc2V0IHNvbWUgcGFyYW1ldGVycw0KcGxvdHZhciA8LSBBTkNfc2hwJHByZXZhbGVuY2UgI3ZhcmlhYmxlIHRvIGRpc3BsYXkNCm5jb2xvcnMgPC0gNSAjbnVtYmVyIG9mIGNvbG9ycw0KcGFsIDwtIGJyZXdlci5wYWwobmNvbG9ycywiUmVkcyIpICNjb2xvciBwYWxldHRlDQpgYGANCg0KIyMjRXF1YWwgSW50ZXJ2YWxzDQpgYGB7cn0NCiMgMS4gRXF1YWwtaW50ZXJ2YWwgY2xhc3MgaW50ZXJ2YWxzDQpicmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocGxvdHZhciwgbmNvbG9ycywgc3R5bGU9ImVxdWFsIikgI2NhdGVnb3J5IGJyZWFrcw0KY29sY29kZSA8LSBmaW5kQ29sb3VycyhicmVha3MsIHBhbCkgI2Fzc2lnbiBjb2xvcnMgYmFzZWQgb24gY2F0ZWdvcnkNCg0KcGxvdChwcm92aW5jZXNfc2hwLCBjb2wgPSAiZ3JleTkzIiwgYm9yZGVyID0gImdyZXkiKQ0KcG9pbnRzKEFOQ19zaHAsIHBjaD0xNiwgY29sPWNvbGNvZGUsIGNleD0yKQ0KdGl0bGUoIkhJViBQcmV2YWxlbmNlIGF0IEFOQyBTaXRlcyIsIHN1YiA9ICJFcXVhbCBJbnRlcnZhbHMiKQ0KbGVnZW5kKCJib3R0b21sZWZ0IiwgbGVnZW5kID0gbmFtZXMoYXR0cihjb2xjb2RlLCAidGFibGUiKSksDQogICAgICAgZmlsbCA9IGF0dHIoY29sY29kZSwgInBhbGV0dGUiKSwgY2V4ID0gMSwgYnR5ID0gIm4iKQ0KYGBgDQoNCioqTk9URToqKiBJbiB0aGUgbGVnZW5kLCBzcXVhcmUgYnJhY2tldHMgaW5kaWNhdGUgdGhhdCBhIG51bWJlciBpcyBpbmNsdWRlZCBpbiB0aGUgY2F0ZWdvcnksIHdoZXJlYXMgcGFyZW50aGVzZXMgaW5kaWNhdGUgdGhhdCBhIG51bWJlciBpcyBub3QgaW5jbHVkZWQuIEZvciBleGFtcGxlLCBpbiB0aGUgYWJvdmUgbWFwLCB0aGUgZmlyc3QgY2F0ZWdvcnkgcmVhZHMgIkZyb20gNy4xIHVwIHRvICpidXQgbm90IGluY2x1ZGluZyogMTAuODgiIGFuZCB0aGUgc2Vjb25kIGNhdGVnb3J5IGlzICJGcm9tIDEwLjg4IHVwIHRvICpidXQgbm90IGluY2x1ZGluZyogMTQuNjYiLiBJZiBhIGNsaW5pYydzIEhJViBwcmV2YWxlbmNlIGlzIGV4YWN0bHkgMTAuODgsIGl0IGlzIGluY2x1ZGVkIGluIHRoZSBzZWNvbmQgY2F0ZWdvcnkuDQoNCiMjI1F1YW50aWxlIEludGVydmFscw0KYGBge3J9DQojIDIuIFF1YW50aWxlIGNsYXNzIGludGVydmFscw0KYnJlYWtzIDwtIGNsYXNzSW50ZXJ2YWxzKHBsb3R2YXIsIG5jb2xvcnMsIHN0eWxlPSJxdWFudGlsZSIpDQpjb2xjb2RlIDwtIGZpbmRDb2xvdXJzKGJyZWFrcywgcGFsKQ0KDQpwbG90KHByb3ZpbmNlc19zaHAsIGNvbCA9ICJncmV5OTMiLCBib3JkZXIgPSAiZ3JleSIpDQpwb2ludHMoQU5DX3NocCwgcGNoPTE2LCBjb2w9Y29sY29kZSwgY2V4PTIpDQp0aXRsZSgiSElWIFByZXZhbGVuY2UgYXQgQU5DIFNpdGVzIiwgc3ViID0gIlF1YW50aWxlIEludGVydmFscyIpDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQgPSBuYW1lcyhhdHRyKGNvbGNvZGUsICJ0YWJsZSIpKSwNCiAgICAgICBmaWxsID0gYXR0cihjb2xjb2RlLCAicGFsZXR0ZSIpLCBjZXggPSAxLCBidHkgPSAibiIpDQpgYGANCg0KVGhlc2UgdHdvIG1hcHMgc2hvdyB0aGUgc2FtZSBkYXRhLCBidXQgZmlyc3QgbWFwIHVzZXMgZXF1YWwgaW50ZXJ2YWxzIChlYWNoIGNhdGVnb3J5IGlzIDMuNzggcGN0IHBvaW50cykgd2hpbGUgdGhlIHNlY29uZCBtYXAgdXNlcyBxdWFudGlsZXMgKGVhY2ggY2F0ZWdvcnkgY29udGFpbnMgMjAlIG9mIHRoZSBkYXRhKS4NCg0KIyMjTmF0dXJhbCBCcmVha3MgKEplbmtzKQ0KDQpBIHRoaXJkIHR5cGUgb2YgaW50ZXJ2YWwgY2xhc3NpZmljYXRpb24gbWV0aG9kIGlzIGNhbGxlZCAiTmF0dXJhbCBCcmVha3MiIG9yICJKZW5rcyIuIFRoaXMgbWV0aG9kIHNlZWtzIHRvIHJlZHVjZSB0aGUgdmFyaWFuY2Ugd2l0aGluIGNsYXNzZXMgYW5kIG1heGltaXplIHRoZSB2YXJpYW5jZSBiZXR3ZWVuIGNsYXNzZXMuIEVzc2VudGlhbGx5LCBpdCB0cmllcyB0byBicmVhayB0aGUgZGF0YSBpbnRvIG5hdHVyYWwgZ3JvdXBpbmdzLg0KDQpgYGB7cn0NCiMgMy4gSmVua3MgY2xhc3MgaW50ZXJ2YWxzDQpicmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocGxvdHZhciwgbmNvbG9ycywgc3R5bGU9ImplbmtzIikNCmNvbGNvZGUgPC0gZmluZENvbG91cnMoYnJlYWtzLCBwYWwpDQoNCnBsb3QocHJvdmluY2VzX3NocCwgY29sID0gImdyZXk5MyIsIGJvcmRlciA9ICJncmV5IikNCnBvaW50cyhBTkNfc2hwLCBwY2g9MTYsIGNvbD1jb2xjb2RlLCBjZXg9MikNCnRpdGxlKCJISVYgUHJldmFsZW5jZSBhdCBBTkMgU2l0ZXMiLCBzdWIgPSAiTmF0dXJhbCBCcmVha3MgKEplbmtzKSIpDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQgPSBuYW1lcyhhdHRyKGNvbGNvZGUsICJ0YWJsZSIpKSwNCiAgICAgICBmaWxsID0gYXR0cihjb2xjb2RlLCAicGFsZXR0ZSIpLCBjZXggPSAxLCBidHkgPSAibiIpDQpgYGANCg0KIyMgVmFyeWluZyBTeW1ib2wgU2l6ZXMNCk5vdywgd2UgY2FuIGdvIG9uZSBzdGVwIGZ1cnRoZXIgYW5kIGFkZCB2YXJ5aW5nIGNpcmNsZSBzaXplcyB0byBlbmhhbmNlIHRoZSB2aXN1YWxpemF0aW9uLg0KDQpgYGB7cn0NCiMgdmFyeSB0aGUgc2l6ZSBvZiB0aGUgc2l0ZSBzeW1ib2wNCmJyZWFrcyA8LSBjbGFzc0ludGVydmFscyhwbG90dmFyLCBuY29sb3JzLCBzdHlsZT0iamVua3MiKQ0KY29sY29kZSA8LSBmaW5kQ29sb3VycyhicmVha3MsIHBhbCkNCnBsb3QocHJvdmluY2VzX3NocCwgY29sID0gImdyZXk5MyIsIGJvcmRlciA9ICJncmV5IikNCnN5bWJvbHMoQU5DX3NocCRsb24sIEFOQ19zaHAkbGF0LCBhZGQgPSBUUlVFLA0KICAgICAgICBjaXJjbGVzID0gQU5DX3NocCRwcmV2YWxlbmNlLCBiZyA9IGNvbGNvZGUsIGluY2hlcyA9IDAuMSkNCnRpdGxlKCJISVYgUHJldmFsZW5jZSBhdCBBTkMgU2l0ZXMiLCBzdWIgPSAiTmF0dXJhbCBCcmVha3MgKEplbmtzKSIpDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQgPSBuYW1lcyhhdHRyKGNvbGNvZGUsICJ0YWJsZSIpKSwNCiAgICAgICBmaWxsID0gYXR0cihjb2xjb2RlLCAicGFsZXR0ZSIpLCBjZXggPSAxLCBidHkgPSAibiIpDQpgYGANCg0KIyBWaXN1YWxpemluZyBRdWFudGl0YXRpdmUgRGF0YTogUG9seWdvbnMNCg0KIyMgSm9pbmluZyBEYXRhIHRvIGEgU2hhcGVmaWxlDQoNCmBgYHtyfQ0KIyBJbXBvcnQgdGhlIHByb3ZpbmNlcyBzaGFwZWZpbGUNCnByb3ZpbmNlc19zaHAgPC0gcmVhZE9HUigiendlX3BvbGJuZGFfYWRtMV8yNTBrX2Nzby5zaHAiKQ0KY2xhc3MocHJvdmluY2VzX3NocCkgIyBjaGVjayB0aGUgY2xhc3Mgb2YgZGF0YQ0KY2xhc3MocHJvdmluY2VzX3NocEBkYXRhKSAjIEBkYXRhIGlzIHRoZSBkYXRhIGZyYW1lIGVtYmVkZGVkIGluIHRoZSBzaGFwZWZpbGUNCnByb3ZpbmNlc19zaHBAZGF0YSAjdmlldyB0aGUgc2hhcGVmaWxlJ3MgZGF0YSBmcmFtZQ0KcGxvdChwcm92aW5jZXNfc2hwKSAjbWFwIHRoZSBzaGFwZWZpbGUNCg0KYGBgDQoNCmBgYHtyfQ0KIyBJbXBvcnQgdGhlIENlbnN1cyBEYXRhIENTViBzcHJlYWRzaGVldA0KQ2Vuc3VzMjAxMiA8LSByZWFkLmNzdigiQ2Vuc3VzMjAxMi5jc3YiKQ0KY2xhc3MoQ2Vuc3VzMjAxMikNCmhlYWQoQ2Vuc3VzMjAxMikNCmBgYA0KDQpXZSdsbCB1c2UgdGhlIGBpbm5lcl9qb2luKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSB0byBqb2luIHRoZSBkYXRhLg0KYGBge3J9DQojIExvYWQgZHBseXIgcGFja2FnZSB0byBnZXQgdGhlIGlubmVyX2pvaW4gZnVuY3Rpb24NCmxpYnJhcnkoZHBseXIpDQoNCiMjIyBNZXJnZSB0aGUgQ2Vuc3VzIGRhdGEgd2l0aCB0aGUgcHJvdmluY2Ugc2hhcGVmaWxlIA0KDQojIEZpcnN0LCBtYWtlIHN1cmUgdGhlIGNvbHVtbiB3ZSdsbCB1c2UgdG8gbWVyZ2UgaXMgdGhlIHNhbWUNCiMgcmVuYW1lIHRoZSAxc3QgY29sdW1uLCBjdXJyZW50bHkgcHJvdmluY2UsIHRvIFBST1ZJTkNFIGFzIGluIHRoZSBzaHANCmNvbG5hbWVzKENlbnN1czIwMTIpWzFdIDwtICJQUk9WSU5DRSINCmhlYWQoQ2Vuc3VzMjAxMikNCg0KIyBjaGVjayB0aGF0IHByb3ZpbmNlIG5hbWVzIG1hdGNoDQpjYmluZChzb3J0KHByb3ZpbmNlc19zaHBAZGF0YSRQUk9WSU5DRSksIHNvcnQoQ2Vuc3VzMjAxMiRQUk9WSU5DRSkpDQpjYmluZCh0YWJsZShwcm92aW5jZXNfc2hwQGRhdGEkUFJPVklOQ0UpLCB0YWJsZShDZW5zdXMyMDEyJFBST1ZJTkNFKSkNCg0KIyBOb3cgam9pbiB1c2luZyB0aGUgUFJPVklOQ0UgdmFyaWFibGUNCnByb3ZpbmNlc19zaHBAZGF0YSA8LSBpbm5lcl9qb2luKHByb3ZpbmNlc19zaHBAZGF0YSwgQ2Vuc3VzMjAxMiwgYnk9IlBST1ZJTkNFIikNCnByb3ZpbmNlc19zaHBAZGF0YSAjY2hlY2sgdGhlIHJlc3VsdA0KYGBgDQoNCg0KIyMgTGFiZWxpbmcgUG9seWdvbnMNCg0KYGBge3J9DQojIFNpbXBseSBsYWJlbCB0aGUgcHJvdmluY2VzDQpwbG90KHByb3ZpbmNlc19zaHAsIGNvbCA9ICJhenVyZSIsIGJvcmRlciA9ICJncmV5IikNCnRleHQoZ2V0U3BQUG9seWdvbnNMYWJwdFNsb3RzKHByb3ZpbmNlc19zaHApLA0KbGFiZWxzPWFzLmNoYXJhY3Rlcihwcm92aW5jZXNfc2hwJE1NUiksIGNleD0wLjgpDQp0aXRsZSgiTWF0ZXJuYWwgTW9ydGFsaXR5IFJhdGlvIDIwMTIgKHBlciAxMDAsMDAwIGxpdmUgYmlydGhzKSIpDQpgYGANCg0KIyMgQ2hvcm9wbGV0aCBNYXBzDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9DQojIFNldCBwYXJhbWV0ZXJzDQpwbG90dmFyIDwtIHByb3ZpbmNlc19zaHAkTU1SDQpuY29sb3JzIDwtIDQNCnBhbCA8LSBicmV3ZXIucGFsKG5jb2xvcnMsIlB1UmQiKQ0KDQojU2V0IHBsb3Qgd2luZG93IHRvIGNvbWJpbmUgNCBtYXBzICgyeDIpDQpwYXIobWZyb3c9YygyLDIpKQ0KDQojIDEuIEZpeGVkIGludGVydmFscw0KYnJlYWtzIDwtIGNsYXNzSW50ZXJ2YWxzKHBsb3R2YXIsIG5jb2xvcnMsIHN0eWxlPSJmaXhlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgZml4ZWRCcmVha3M9YygzMDAsNDAwLDUwMCw2MDAsNzAwKSkNCmNvbGNvZGUgPC0gZmluZENvbG91cnMoYnJlYWtzLCBwYWwpDQoNCnBsb3QocHJvdmluY2VzX3NocCwgY29sPWNvbGNvZGUsIGJvcmRlciA9ICJncmV5IiwNCiAgICAgbWFpbj0iRml4ZWQgSW50ZXJ2YWxzIikNCmxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZD1uYW1lcyhhdHRyKGNvbGNvZGUsICJ0YWJsZSIpKSwNCiAgICAgICBmaWxsPWF0dHIoY29sY29kZSwgInBhbGV0dGUiKSwgY2V4PTEsIGJ0eT0ibiIpDQp0ZXh0KGdldFNwUFBvbHlnb25zTGFicHRTbG90cyhwcm92aW5jZXNfc2hwKSwNCmxhYmVscz1wbG90dmFyLCBjZXg9MC44KQ0KDQojIDIuIEVxdWFsIGludGVydmFscw0KYnJlYWtzIDwtIGNsYXNzSW50ZXJ2YWxzKHBsb3R2YXIsIG5jb2xvcnMsIHN0eWxlPSJlcXVhbCIpDQpjb2xjb2RlIDwtIGZpbmRDb2xvdXJzKGJyZWFrcywgcGFsKQ0KDQpwbG90KHByb3ZpbmNlc19zaHAsIGNvbCA9IGNvbGNvZGUsIGJvcmRlciA9ICJncmV5IiwgDQogICAgIG1haW49IkVxdWFsIEludGVydmFscyIpDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQ9bmFtZXMoYXR0cihjb2xjb2RlLCAidGFibGUiKSksDQogICAgICAgZmlsbD1hdHRyKGNvbGNvZGUsICJwYWxldHRlIiksIGNleD0xLCBidHk9Im4iKQ0KdGV4dChnZXRTcFBQb2x5Z29uc0xhYnB0U2xvdHMocHJvdmluY2VzX3NocCksDQpsYWJlbHM9cGxvdHZhciwgY2V4PTAuOCkNCg0KIyAzLiBRdWFudGlsZSBpbnRlcnZhbHMNCmJyZWFrcyA8LSBjbGFzc0ludGVydmFscyhwbG90dmFyLCBuY29sb3JzLCBzdHlsZT0icXVhbnRpbGUiKQ0KY29sY29kZSA8LSBmaW5kQ29sb3VycyhicmVha3MsIHBhbCkNCg0KcGxvdChwcm92aW5jZXNfc2hwLCBjb2w9Y29sY29kZSwgYm9yZGVyID0gImdyZXkiLA0KICAgICBtYWluPSJRdWFudGlsZSBJbnRlcnZhbHMiKQ0KbGVnZW5kKCJib3R0b21sZWZ0IiwgbGVnZW5kPW5hbWVzKGF0dHIoY29sY29kZSwgInRhYmxlIikpLA0KICAgICAgIGZpbGw9YXR0cihjb2xjb2RlLCAicGFsZXR0ZSIpLCBjZXg9MSwgYnR5PSJuIikNCnRleHQoZ2V0U3BQUG9seWdvbnNMYWJwdFNsb3RzKHByb3ZpbmNlc19zaHApLA0KbGFiZWxzPXBsb3R2YXIsIGNleD0wLjgpDQoNCiMgNC4gSmVua3MgaW50ZXJ2YWxzDQpicmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocGxvdHZhciwgbmNvbG9ycywgc3R5bGU9ImplbmtzIikNCmNvbGNvZGUgPC0gZmluZENvbG91cnMoYnJlYWtzLCBwYWwpDQoNCnBsb3QocHJvdmluY2VzX3NocCwgY29sPWNvbGNvZGUsIGJvcmRlciA9ICJncmV5IiwNCiAgICAgbWFpbj0iTmF0dXJhbCBJbnRlcnZhbHMgKEplbmtzKSIpDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQ9bmFtZXMoYXR0cihjb2xjb2RlLCAidGFibGUiKSksDQogICAgICAgZmlsbD1hdHRyKGNvbGNvZGUsICJwYWxldHRlIiksIGNleD0xLCBidHk9Im4iKQ0KdGV4dChnZXRTcFBQb2x5Z29uc0xhYnB0U2xvdHMocHJvdmluY2VzX3NocCksDQpsYWJlbHM9cGxvdHZhciwgY2V4PTAuOCkNCg0KI1RpdGxlIGZvciBjb21iaW5lZCBwbG90DQp0aXRsZSgiTWF0ZXJuYWwgTW9ydGFsaXR5IFJhdGlvIDIwMTIgKHBlciAxMDAsMDAwIGxpdmUgYmlydGhzKSIsDQogICAgICAgb3V0ZXI9VFJVRSwgbGluZSA9IC0xLCBjZXggPSAxKQ0KYGBgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9DQojUmV0dXJuIHRvIGEgc2luZ2xlIHBsb3QgZm9ybWF0DQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCiMjIExheWVyaW5nIFBvaW50cyBhbmQgUG9seWdvbnMNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9DQoNCiMgUHJldHRpZXIgcXVhbnRpbGVzOiByb3VuZCBxdWFudGlsZXMgdG8gbmVhcmVzdCA1DQpicmVha3MgPC0gY2xhc3NJbnRlcnZhbHMocGxvdHZhciwgbmNvbG9ycywgc3R5bGU9ImZpeGVkIiwNCmZpeGVkQnJlYWtzPWMoMzcwLDUxNSw1NjAsNTg1LDY4MCkpDQpjb2xjb2RlIDwtIGZpbmRDb2xvdXJzKGJyZWFrcywgcGFsKQ0KDQojIFBhcmFtZXRlcnMgZm9yIGxlZ2VuZA0Kc3ltIDwtIGMoMTUsMTUsMTUsMTUsMTkpDQpsYWJlbHMgPC0gYyhwYXN0ZTAoIk1NUjogIiwgbmFtZXMoYXR0cihjb2xjb2RlLCAidGFibGUiKSkpLA0KICAgICAgICAgICAgICAgICAgICJBTkMgSElWIFByZXZhbGVuY2UiKQ0KbGFiZWxzDQpjb2xvcnMgPC0gYyhhdHRyKGNvbGNvZGUsICJwYWxldHRlIiksICJibGFjayIpDQpjb2xvcnMNCg0KI1Bsb3QNCnBsb3QocHJvdmluY2VzX3NocCwgY29sPWNvbGNvZGUsIGJvcmRlciA9ICJncmV5IiwNCiAgICAgbWFpbj0iWmltYmFid2UgTWF0ZXJuYWwgSGVhbHRoIDIwMTIiKQ0Kc3ltYm9scyhBTkNfc2hwJGxvbiwgQU5DX3NocCRsYXQsIGFkZCA9IFRSVUUsDQogICAgICAgIGNpcmNsZXMgPSBBTkNfc2hwJHByZXZhbGVuY2UsIGJnID0gImJsYWNrIiwgaW5jaGVzID0gMC4xKQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgbGVnZW5kPWxhYmVscywgcGNoPXN5bSwgY29sPWNvbG9ycywNCiAgICAgICBjZXg9MSwgcHQuY2V4PTIsIGJ0eT0ieSIpDQphZGRzY2FsZWJhcihzdHlsZSA9ICJ0aWNrcyIpDQphZGRub3J0aGFycm93KGNvbHMgPSBjKCJ3aGl0ZSIsICJncmV5NTAiKSwgc2NhbGUgPSAwLjYpDQpgYGANCg0KDQoNCg0KDQpbKio8PCBXb3Jrc2hvcCBIb21lKipdKGh0dHBzOi8vY2FmYWhleS5naXRodWIuaW8vc3BhdGlhbCkNCg==