Some personal and computational notes loosely based on the analytical work I have done in response to COVID-19 pandemic in Scotland. In my GIS Analyst role I use wide range of proprietary and open source GIS software as well as tend to blend R and Python languages. Here is a flavor of my work in open and reproducible notebook, following a Donald Knuth’s “literate programming” paradigm.
Packages
First,all packages necessary for the analysis.
library(dplyr)
library(tidyr)
library(lubridate)
library(readr)
library(ggplot2)
library(sf)
library(stringr)
library(tmap)
library(hrbrthemes)
library(zoo)
Data Wrangling
Now, lets read the data from the Data Science Scotland repository
cases_scot = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scotland%20-%20Testing.csv") %>%
select(Date,'Testing - Cumulative people tested for COVID-19 - Positive') %>%
rename(total = 'Testing - Cumulative people tested for COVID-19 - Positive') %>%
mutate(date = as.Date(Date, "%d-%b-%Y"))
# deaths
deaths_scot = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scotland%20-%20Deaths.csv") %>%
rename(total = 'Number of COVID-19 confirmed deaths registered to date') %>%
mutate(date = as.Date(Date, "%d-%b-%Y"))
Since we only have on variable with total cases and deaths, we are going to calculate additional attributes: daily new cases, percentage change and seven days rolling average.
#cases
cases_scot = cases_scot %>%
mutate(new_cases = (total - lag(total)), # new daily cases
pct_change = new_cases / lag(total) * 100, # percentage change
cases_07da = rollmean(new_cases, k = 7, fill = NA)) # 7 days rolling average
# deaths
deaths_scot = deaths_scot %>%
mutate(new_deaths = (total - lag(total)), # new daily cases
pct_change = new_deaths / lag(total) * 100, # percentage change
deaths_07da = rollmean(new_deaths, k = 7, fill = NA)) # 7 days rolling average
Plotting
We are ready to plot our variables using ggplot package
ggplot()+
geom_line(data = cases_scot, aes(x=Date, y=new_cases), color='gray') +
geom_line(data = cases_scot, aes(x=Date, y=cases_07da), color='#ba0000') +
labs( title = "Scotland's 7 days rolling average COVID-19 cases",
subtitle = "Between March 2020 and March 2021",
caption="Source:https://github.com/DataScienceScotland/COVID-19-Management-Information ",
y = "Cases",
x = "Month") +
scale_x_date(date_breaks = '1 month', date_labels = '%m %y') +
scale_y_log10() +
hrbrthemes::theme_ipsum()
Mapping
We can also create a map showing latest cases at NHS Health Board level. But first we need to get the data for each geograph unit.
# load data
cases_by_hb = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scottish%20Health%20Boards%20-%20Cumulative%20cases.csv")
# pivot data to long format nd select latest date
last_cases_by_hb_long = cases_by_hb %>%
pivot_longer(!Date,names_to = "hb_name", values_to = "cases") %>%
mutate(cases = as.numeric(cases)) %>%
filter(Date == max(Date))
# load mid 2019 population estimatie
pop_hb = read_csv("data/hb_pop_2019.csv")
# first join: cases with population, also calculate rate per 100k
cases_by_hb_pop = left_join(last_cases_by_hb_long, pop_hb) %>%
mutate(
rate_100k = cases / hb_pop * 100000
)
# load Health Board geographies
health_boards = st_read("data/SG_NHS_HealthBoards_2019.shp", quiet = TRUE)
# second join: health board boundaries
cases_hb_rate100k = left_join(health_boards,cases_by_hb_pop, by = c("HBCode" = "hb_code"))
Finally, we are ready to produce the map using tmap package. Note that we are saving the output to png file.
map = tm_shape(cases_hb_rate100k) +
tm_polygons(col = "rate_100k",
title = "Data from 17th March 2021 \nNumber of confirmed cases per 100,000 people",
lwd = 1,
border.col = "white",
style = "equal",
n = 5,
palette="cividis" # choose between reds or cividis
# legend.format=list(fun=function(x) paste0(formatC(x, digits=0, format="f")))
) +
tm_layout(
# fontfamily = "arial",
title = "Total COVID - 19 cases \nper 100,000 people, by NHS Health Board",
title.size = 0.9,
legend.title.size = 1,
attr.outside= TRUE,
attr.outside.position = "bottom",
attr.outside.size = .1,
attr.just = c("left", "top"),
inner.margins = 0.02,
outer.margins = c(0,0.01,0.01,0.01),
asp = 4/6,
frame = F,
design.mode = F
) +
tm_credits("Cases: https://www.gov.scot/coronavirus-covid-19\nPopulation data: NRS Population Estimates, mid-2018\nBoundaries: SpatialData.gov.scot",
col = "grey50",
position=c("left","top"),
align = "left",
size = 0.325)
# save graphic to png file
tmap_save(
tm = map,
filename = "hb_map_cases_rate.png",
height = 6,
width = 4,
units = "in",
asp = 4/6
)
Ok, map is ready !
I can’t recommend tmap package enough.
Session info
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Mojave 10.14.6
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
##
## locale:
## [1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] zoo_1.8-9 hrbrthemes_0.8.0 tmap_3.0 stringr_1.4.0.9000
## [5] sf_0.9-6 ggplot2_3.3.2 readr_1.4.0 lubridate_1.7.9.2
## [9] tidyr_1.1.2 dplyr_1.0.2
##
## loaded via a namespace (and not attached):
## [1] Rcpp_1.0.6 lattice_0.20-41 png_0.1-7 class_7.3-17
## [5] assertthat_0.2.1 digest_0.6.27 R6_2.5.0 evaluate_0.14
## [9] e1071_1.7-4 highr_0.8 pillar_1.4.7 gdtools_0.2.2
## [13] rlang_0.4.10 curl_4.3 rstudioapi_0.13 extrafontdb_1.0
## [17] raster_3.1-5 rmarkdown_2.6.4 extrafont_0.17 htmlwidgets_1.5.1
## [21] munsell_0.5.0 compiler_4.0.2 xfun_0.22 systemfonts_0.2.1
## [25] pkgconfig_2.0.3 tmaptools_3.0 base64enc_0.1-3 htmltools_0.5.1.1
## [29] tidyselect_1.1.0 tibble_3.0.4 codetools_0.2-16 XML_3.99-0.3
## [33] viridisLite_0.3.0 crayon_1.4.1 withr_2.3.0 grid_4.0.2
## [37] lwgeom_0.2-3 Rttf2pt1_1.3.8 gtable_0.3.0 lifecycle_0.2.0
## [41] DBI_1.1.0 magrittr_2.0.1 units_0.6-7 scales_1.1.1
## [45] KernSmooth_2.23-17 cli_2.3.1 stringi_1.5.3 farver_2.0.3
## [49] leafsync_0.1.0 leaflet_2.0.3 sp_1.4-5 ellipsis_0.3.1
## [53] generics_0.1.0 vctrs_0.3.6 RColorBrewer_1.1-2 tools_4.0.2
## [57] dichromat_2.0-0 leafem_0.1.1 glue_1.4.2 purrr_0.3.4
## [61] hms_0.5.3 crosstalk_1.1.0.1 abind_1.4-5 parallel_4.0.2
## [65] yaml_2.2.1 colorspace_1.4-1 stars_0.4-1 classInt_0.4-3
## [69] knitr_1.31
A work by Michal Michalski
LS0tCnRpdGxlOiAiQ09WSUQtMTkgYW5hbHlzaXMgYW5kIG1hcHBpbmciCmF1dGhvcjogIk1pY2hhbCBNaWNoYWxza2kiCmRhdGU6ICIwMS8wMy8yMDIxIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogIAotLS0KPHN0eWxlPgpkaXYuYmx1ZSB7IGJhY2tncm91bmQtY29sb3I6I0QzRDNEMzsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30KPC9zdHlsZT4KPGRpdiBjbGFzcyA9ICJibHVlIj4KU29tZSBwZXJzb25hbCBhbmQgY29tcHV0YXRpb25hbCBub3RlcyBsb29zZWx5IGJhc2VkIG9uIHRoZSBhbmFseXRpY2FsIHdvcmsgSSBoYXZlIGRvbmUgaW4gcmVzcG9uc2UgdG8gQ09WSUQtMTkgcGFuZGVtaWMgaW4gU2NvdGxhbmQuCkluIG15IEdJUyBBbmFseXN0IHJvbGUgSSB1c2Ugd2lkZSByYW5nZSBvZiBwcm9wcmlldGFyeSBhbmQgb3BlbiBzb3VyY2UgR0lTIHNvZnR3YXJlIGFzIHdlbGwgYXMgdGVuZCB0byBibGVuZCBSIGFuZCBQeXRob24gbGFuZ3VhZ2VzLiAKSGVyZSBpcyBhIGZsYXZvciBvZiBteSB3b3JrIGluIG9wZW4gYW5kIHJlcHJvZHVjaWJsZSBub3RlYm9vaywgZm9sbG93aW5nIGEgRG9uYWxkIEtudXRoJ3MgImxpdGVyYXRlIHByb2dyYW1taW5nIiBwYXJhZGlnbS4KPC9kaXY+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIyBQYWNrYWdlcwoKRmlyc3QsYWxsIHBhY2thZ2VzIG5lY2Vzc2FyeSBmb3IgdGhlIGFuYWx5c2lzLgpgYGB7ciBwYWNrYWdlcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2YpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KGhyYnJ0aGVtZXMpCmxpYnJhcnkoem9vKQpgYGAKCiMjIERhdGEgV3JhbmdsaW5nCgpOb3csIGxldHMgcmVhZCB0aGUgZGF0YSBmcm9tIHRoZSAqRGF0YSBTY2llbmNlIFNjb3RsYW5kKiByZXBvc2l0b3J5CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KY2FzZXNfc2NvdCA9IHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uL21hc3Rlci9DT1ZJRDE5JTIwLSUyMERhaWx5JTIwTWFuYWdlbWVudCUyMEluZm9ybWF0aW9uJTIwLSUyMFNjb3RsYW5kJTIwLSUyMFRlc3RpbmcuY3N2IikgJT4lIAogIHNlbGVjdChEYXRlLCdUZXN0aW5nIC0gQ3VtdWxhdGl2ZSBwZW9wbGUgdGVzdGVkIGZvciBDT1ZJRC0xOSAtIFBvc2l0aXZlJykgJT4lIAogIHJlbmFtZSh0b3RhbCA9ICdUZXN0aW5nIC0gQ3VtdWxhdGl2ZSBwZW9wbGUgdGVzdGVkIGZvciBDT1ZJRC0xOSAtIFBvc2l0aXZlJykgJT4lIAogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShEYXRlLCAiJWQtJWItJVkiKSkKICAKCiMgZGVhdGhzIApkZWF0aHNfc2NvdCA9IHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uL21hc3Rlci9DT1ZJRDE5JTIwLSUyMERhaWx5JTIwTWFuYWdlbWVudCUyMEluZm9ybWF0aW9uJTIwLSUyMFNjb3RsYW5kJTIwLSUyMERlYXRocy5jc3YiKSAlPiUgCiAgICByZW5hbWUodG90YWwgPSAnTnVtYmVyIG9mIENPVklELTE5IGNvbmZpcm1lZCBkZWF0aHMgcmVnaXN0ZXJlZCB0byBkYXRlJykgJT4lIAogICAgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKERhdGUsICIlZC0lYi0lWSIpKQoKYGBgCgpTaW5jZSB3ZSBvbmx5IGhhdmUgb24gdmFyaWFibGUgd2l0aCB0b3RhbCBjYXNlcyBhbmQgZGVhdGhzLCB3ZSBhcmUgZ29pbmcgdG8gY2FsY3VsYXRlIGFkZGl0aW9uYWwgYXR0cmlidXRlczogCmRhaWx5IG5ldyBjYXNlcywgcGVyY2VudGFnZSBjaGFuZ2UgYW5kIHNldmVuIGRheXMgcm9sbGluZyBhdmVyYWdlLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CgojY2FzZXMgCmNhc2VzX3Njb3QgID0gY2FzZXNfc2NvdCAgJT4lIAptdXRhdGUobmV3X2Nhc2VzID0gKHRvdGFsIC0gbGFnKHRvdGFsKSksICMgbmV3IGRhaWx5IGNhc2VzIAogICAgICAgcGN0X2NoYW5nZSA9IG5ld19jYXNlcyAvIGxhZyh0b3RhbCkgKiAxMDAsICMgcGVyY2VudGFnZSBjaGFuZ2UKICAgICAgIGNhc2VzXzA3ZGEgPSByb2xsbWVhbihuZXdfY2FzZXMsIGsgPSA3LCBmaWxsID0gTkEpKSAjIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UKCgoKIyBkZWF0aHMKZGVhdGhzX3Njb3QgID0gZGVhdGhzX3Njb3QgICU+JSAKbXV0YXRlKG5ld19kZWF0aHMgPSAodG90YWwgLSBsYWcodG90YWwpKSwgIyBuZXcgZGFpbHkgY2FzZXMgCiAgICAgIHBjdF9jaGFuZ2UgPSBuZXdfZGVhdGhzIC8gbGFnKHRvdGFsKSAqIDEwMCwgIyBwZXJjZW50YWdlIGNoYW5nZQogICAgICBkZWF0aHNfMDdkYSA9IHJvbGxtZWFuKG5ld19kZWF0aHMsIGsgPSA3LCBmaWxsID0gTkEpKSAjIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UKCgpgYGAKCiMjIFBsb3R0aW5nCgpXZSBhcmUgcmVhZHkgdG8gcGxvdCBvdXIgdmFyaWFibGVzIHVzaW5nICoqZ2dwbG90KiogcGFja2FnZQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUoZGF0YSA9IGNhc2VzX3Njb3QsIGFlcyh4PURhdGUsIHk9bmV3X2Nhc2VzKSwgY29sb3I9J2dyYXknKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBjYXNlc19zY290LCBhZXMoeD1EYXRlLCB5PWNhc2VzXzA3ZGEpLCBjb2xvcj0nI2JhMDAwMCcpICsKICBsYWJzKCB0aXRsZSA9ICJTY290bGFuZCdzIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UgQ09WSUQtMTkgY2FzZXMiLAogICAgICAgIHN1YnRpdGxlID0gIkJldHdlZW4gTWFyY2ggMjAyMCBhbmQgTWFyY2ggMjAyMSIsIAogICAgICAgIGNhcHRpb249IlNvdXJjZTpodHRwczovL2dpdGh1Yi5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uICIsCiAgICAgICAgeSA9ICJDYXNlcyIsIAogICAgICAgIHggPSAiTW9udGgiKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gJzEgbW9udGgnLCBkYXRlX2xhYmVscyA9ICclbSAleScpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGhyYnJ0aGVtZXM6OnRoZW1lX2lwc3VtKCkKYGBgCgojIyBNYXBwaW5nCgpXZSBjYW4gYWxzbyBjcmVhdGUgYSBtYXAgc2hvd2luZyBsYXRlc3QgY2FzZXMgYXQgTkhTIEhlYWx0aCBCb2FyZCBsZXZlbC4KQnV0IGZpcnN0IHdlIG5lZWQgdG8gZ2V0IHRoZSBkYXRhIGZvciBlYWNoIGdlb2dyYXBoIHVuaXQuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KIyBsb2FkIGRhdGEKY2FzZXNfYnlfaGIgPSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0RhdGFTY2llbmNlU2NvdGxhbmQvQ09WSUQtMTktTWFuYWdlbWVudC1JbmZvcm1hdGlvbi9tYXN0ZXIvQ09WSUQxOSUyMC0lMjBEYWlseSUyME1hbmFnZW1lbnQlMjBJbmZvcm1hdGlvbiUyMC0lMjBTY290dGlzaCUyMEhlYWx0aCUyMEJvYXJkcyUyMC0lMjBDdW11bGF0aXZlJTIwY2FzZXMuY3N2IikKCiMgcGl2b3QgZGF0YSB0byBsb25nIGZvcm1hdCBuZCBzZWxlY3QgbGF0ZXN0IGRhdGUKbGFzdF9jYXNlc19ieV9oYl9sb25nID0gY2FzZXNfYnlfaGIgJT4lCnBpdm90X2xvbmdlcighRGF0ZSxuYW1lc190byA9ICJoYl9uYW1lIiwgdmFsdWVzX3RvID0gImNhc2VzIikgICU+JQptdXRhdGUoY2FzZXMgPSBhcy5udW1lcmljKGNhc2VzKSkgJT4lIApmaWx0ZXIoRGF0ZSA9PSBtYXgoRGF0ZSkpCgojIGxvYWQgbWlkIDIwMTkgcG9wdWxhdGlvbiBlc3RpbWF0aWUKcG9wX2hiID0gcmVhZF9jc3YoImRhdGEvaGJfcG9wXzIwMTkuY3N2IikKCiMgZmlyc3Qgam9pbjogY2FzZXMgd2l0aCBwb3B1bGF0aW9uLCBhbHNvIGNhbGN1bGF0ZSByYXRlIHBlciAxMDBrCmNhc2VzX2J5X2hiX3BvcCA9IGxlZnRfam9pbihsYXN0X2Nhc2VzX2J5X2hiX2xvbmcsIHBvcF9oYikgJT4lIAogIG11dGF0ZSgKICAgIHJhdGVfMTAwayA9IGNhc2VzIC8gaGJfcG9wICogMTAwMDAwCiAgKQoKIyBsb2FkIEhlYWx0aCBCb2FyZCBnZW9ncmFwaGllcwpoZWFsdGhfYm9hcmRzID0gc3RfcmVhZCgiZGF0YS9TR19OSFNfSGVhbHRoQm9hcmRzXzIwMTkuc2hwIiwgcXVpZXQgPSBUUlVFKQoKIyBzZWNvbmQgam9pbjogaGVhbHRoIGJvYXJkIGJvdW5kYXJpZXMgCmNhc2VzX2hiX3JhdGUxMDBrID0gbGVmdF9qb2luKGhlYWx0aF9ib2FyZHMsY2FzZXNfYnlfaGJfcG9wLCBieSA9IGMoIkhCQ29kZSIgPSAiaGJfY29kZSIpKQoKCmBgYAoKRmluYWxseSwgd2UgYXJlIHJlYWR5IHRvIHByb2R1Y2UgdGhlIG1hcCB1c2luZyAqKnRtYXAqKiBwYWNrYWdlLiAKTm90ZSB0aGF0IHdlIGFyZSBzYXZpbmcgdGhlIG91dHB1dCB0byBwbmcgZmlsZS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQoKbWFwID0gdG1fc2hhcGUoY2FzZXNfaGJfcmF0ZTEwMGspICsKICB0bV9wb2x5Z29ucyhjb2wgPSAicmF0ZV8xMDBrIiwKICAgICAgICAgICAgICB0aXRsZSA9ICJEYXRhIGZyb20gMTd0aCBNYXJjaCAyMDIxIFxuTnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcyBwZXIgMTAwLDAwMCBwZW9wbGUiLAogICAgICAgICAgICAgIGx3ZCA9IDEsCiAgICAgICAgICAgICAgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgc3R5bGUgPSAiZXF1YWwiLAogICAgICAgICAgICAgIG4gPSA1LAogICAgICAgICAgICAgIHBhbGV0dGU9ImNpdmlkaXMiICMgY2hvb3NlIGJldHdlZW4gcmVkcyBvciBjaXZpZGlzCiAgICAgICAgICAgICAjIGxlZ2VuZC5mb3JtYXQ9bGlzdChmdW49ZnVuY3Rpb24oeCkgcGFzdGUwKGZvcm1hdEMoeCwgZGlnaXRzPTAsIGZvcm1hdD0iZiIpKSkKICAgICAgICAgICAgICApICsKICB0bV9sYXlvdXQoCiAgICAgICAgICAgIyBmb250ZmFtaWx5ID0gImFyaWFsIiwKICAgICAgICAgICAgdGl0bGUgPSAiVG90YWwgQ09WSUQgLSAxOSBjYXNlcyBcbnBlciAxMDAsMDAwIHBlb3BsZSwgYnkgTkhTIEhlYWx0aCBCb2FyZCIsCiAgICAgICAgICAgIHRpdGxlLnNpemUgPSAwLjksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZS5zaXplID0gMSwKICAgICAgICAgICAgYXR0ci5vdXRzaWRlPSBUUlVFLAogICAgICAgICAgICBhdHRyLm91dHNpZGUucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgYXR0ci5vdXRzaWRlLnNpemUgPSAuMSwKICAgICAgICAgICAgYXR0ci5qdXN0ID0gYygibGVmdCIsICJ0b3AiKSwKICAgICAgICAgICAgaW5uZXIubWFyZ2lucyA9IDAuMDIsCiAgICAgICAgICAgIG91dGVyLm1hcmdpbnMgPSBjKDAsMC4wMSwwLjAxLDAuMDEpLAogICAgICAgICAgICBhc3AgPSA0LzYsCiAgICAgICAgICAgIGZyYW1lID0gRiwKICAgICAgICAgICAgZGVzaWduLm1vZGUgPSBGCiAgICAgICAgICAgKSArCiAgdG1fY3JlZGl0cygiQ2FzZXM6IGh0dHBzOi8vd3d3Lmdvdi5zY290L2Nvcm9uYXZpcnVzLWNvdmlkLTE5XG5Qb3B1bGF0aW9uIGRhdGE6IE5SUyBQb3B1bGF0aW9uIEVzdGltYXRlcywgbWlkLTIwMThcbkJvdW5kYXJpZXM6ICBTcGF0aWFsRGF0YS5nb3Yuc2NvdCIsCiAgICAgICAgICAgICBjb2wgPSAiZ3JleTUwIiwKICAgICAgICAgICAgIHBvc2l0aW9uPWMoImxlZnQiLCJ0b3AiKSwgCiAgICAgICAgICAgICBhbGlnbiA9ICJsZWZ0IiwKICAgICAgICAgICAgIHNpemUgPSAwLjMyNSkKIyBzYXZlIGdyYXBoaWMgdG8gcG5nIGZpbGUKdG1hcF9zYXZlKAogIHRtID0gbWFwLAogIGZpbGVuYW1lID0gImhiX21hcF9jYXNlc19yYXRlLnBuZyIsCiAgaGVpZ2h0ID0gNiwKICB3aWR0aCA9IDQsCiAgdW5pdHMgPSAiaW4iLAogIGFzcCA9IDQvNgopCmBgYAoKT2ssIG1hcCBpcyByZWFkeSAhCgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSA2MDAsIGZpZy5jYXA9IiIsIGZpZy5hbGlnbj0nbGVmdCd9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJvdXRwdXQvaGJfbWFwX2Nhc2VzX3JhdGUucG5nIikKYGBgCgpJIGNhbid0IHJlY29tbWVuZCAqKnRtYXAqKiBwYWNrYWdlIGVub3VnaC4KCmBgYHtyLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IDYwMCwgZmlnLmNhcD0iIiwgZmlnLmFsaWduPSdsZWZ0J30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIm91dHB1dC90bWFwX2FuaW0uZ2lmIikKYGBgCgojIyBTZXNzaW9uIGluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKCiZuYnNwOwo8aHIgLz4KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPkEgd29yayBieSA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vdG9wb2dyYXBob3MvIj5NaWNoYWwgTWljaGFsc2tpPC9hPjwvcD4KJm5ic3A7Cg==