Getting Started

This sheet builds off of “Handling Census Data in R - Part 1”. Please complete that first

Again, everytime you start R you will need to load any libraries that you need for analysis. You will need to run this bit of code every time you open R to get this to work.

library(tidycensus)
library(sf)
library(ggplot2)
library(ggthemes)
library(dplyr)

To get started working with tidycensus, users should load the package along with the tidyverse package, and set their Census API key. A key can be obtained from Census API Key. It will provide you with a 40 digit text string. Please keep track of this number. Store it in a safe place.

API_Key = 'yourapikey'  # non working example - please paste your own in
census_api_key(API_Key, install = F, overwrite=TRUE)  

Creating New Variables

Let’s say we were interested in gentrification and wanted to map the percentage of the population that is white in DC at the census tract level for 2018. Notice that geography = 'tract' which download census tract data now. We are going to download two variables one for the white population and the other for total population. The following c(wht_pop ="B02001_002",total_pop="B01001_001") helps us set the name of the columns right away.

wht_pop =  get_acs(state = "DC", 
                   geography = "tract", 
                   year=2018,
                  variables =  c(wht_pop ="B02001_002",total_pop="B01001_001"), 
                  geometry = TRUE,
                  output = 'wide')   # just do this
Getting data from the 2014-2018 5-year ACS
Downloading feature geometry from the Census website.  To cache shapefiles for use in future sessions, set `options(tigris_use_cache = TRUE)`.
Using FIPS code '11' for state 'DC'

  |                                                                                                                                                         
  |                                                                                                                                                   |   0%
  |                                                                                                                                                         
  |======                                                                                                                                             |   4%
  |                                                                                                                                                         
  |=================================================                                                                                                  |  33%
  |                                                                                                                                                         
  |===========================================================                                                                                        |  40%
  |                                                                                                                                                         
  |======================================================================                                                                             |  48%
  |                                                                                                                                                         
  |=================================================================================                                                                  |  55%
  |                                                                                                                                                         
  |===========================================================================================                                                        |  62%
  |                                                                                                                                                         
  |======================================================================================================                                             |  69%
  |                                                                                                                                                         
  |=================================================================================================================                                  |  77%
  |                                                                                                                                                         
  |===========================================================================================================================                        |  84%
  |                                                                                                                                                         
  |======================================================================================================================================             |  91%
  |                                                                                                                                                         
  |===================================================================================================================================================| 100%
head(wht_pop )
Simple feature collection with 6 features and 6 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -77.07889 ymin: 38.88983 xmax: -76.95102 ymax: 38.9843
geographic CRS: NAD83
        GEOID                                                           NAME wht_popE wht_popM total_popE total_popM                       geometry
1 11001009501 Census Tract 95.01, District of Columbia, District of Columbia     2082      334       7277        644 POLYGON ((-77.01093 38.9446...
2 11001009604 Census Tract 96.04, District of Columbia, District of Columbia      105       57       2343        257 POLYGON ((-76.9618 38.89612...
3 11001010300   Census Tract 103, District of Columbia, District of Columbia      812      228       4008        617 POLYGON ((-77.03636 38.9748...
4 11001000600     Census Tract 6, District of Columbia, District of Columbia     4042      427       4969        338 POLYGON ((-77.07651 38.9422...
5 11001001200    Census Tract 12, District of Columbia, District of Columbia     4244      355       5247        487 POLYGON ((-77.07854 38.9465...
6 11001002102 Census Tract 21.02, District of Columbia, District of Columbia      824      197       5223        518 POLYGON ((-77.0199 38.95873...

Again we are interested in the variables ending in ‘E’ so white_popE and total_popE.

Now let’s calculate the percentage of the population that is white for each census tract. We are going to use the mutate function which is part of the Dplyr library see help.

We are going to pass our wht_pop object to mutate using %>%, which is called a ‘pipe’. The pipe indicates that wht_pop holds the data we want to use.

wht_pop = wht_pop %>% mutate(white_per = wht_popE/total_popE)
head(wht_pop)
Simple feature collection with 6 features and 7 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -77.07889 ymin: 38.88983 xmax: -76.95102 ymax: 38.9843
geographic CRS: NAD83
        GEOID                                                           NAME wht_popE wht_popM total_popE total_popM                       geometry
1 11001009501 Census Tract 95.01, District of Columbia, District of Columbia     2082      334       7277        644 POLYGON ((-77.01093 38.9446...
2 11001009604 Census Tract 96.04, District of Columbia, District of Columbia      105       57       2343        257 POLYGON ((-76.9618 38.89612...
3 11001010300   Census Tract 103, District of Columbia, District of Columbia      812      228       4008        617 POLYGON ((-77.03636 38.9748...
4 11001000600     Census Tract 6, District of Columbia, District of Columbia     4042      427       4969        338 POLYGON ((-77.07651 38.9422...
5 11001001200    Census Tract 12, District of Columbia, District of Columbia     4244      355       5247        487 POLYGON ((-77.07854 38.9465...
6 11001002102 Census Tract 21.02, District of Columbia, District of Columbia      824      197       5223        518 POLYGON ((-77.0199 38.95873...
   white_per
1 0.28610691
2 0.04481434
3 0.20259481
4 0.81344335
5 0.80884315
6 0.15776374

Visualize Populations

Now wht_pop has a new variable called ‘white_per’. Let’s look at the histogram of this data again using ggplot2’s geom_histogram, see help here. Here aes(white_per) is telling R to base the aestetics (e.g. ‘aes’) on the variable white_per. You can also set the number of bins to use in your histogram.

ggplot()+geom_histogram(data=wht_pop, aes(white_per), bins = 15)

This bimodal distribution already speaks to the fact that places tend to either be morely white or mostly black. Clear signs of segregation in the city.

But let’s look at the map using the geom_sf (spatial features geometry).

ggplot()+geom_sf(data=wht_pop, aes(fill = white_per))

We can also control the color scheme and legend name using scale_fill_continuous. Notice that aes(fill = white_per) and scale_fill_continuous both refer to ‘fill’, which is the shading of each polygon. You can also set the ‘name’ of the fill color scheme with name="% White".

You can also, if useful, change the color of the lines based on the data using scale_colour_continuous add aes(colour = white_per) instead if you wanted.

ggplot()+geom_sf(data=wht_pop, aes(fill = white_per),color='white') + 
                ggtitle('Segregation in DC 2018')+ 
                scale_fill_continuous(type = "viridis", name="% White")

Reading Data from the Internet

One problem with this map is that waterbodies aren’t included. We can directly access data online easily. Let’s take the example of the waterbodies dataset on DC open data here. To access the data online we just need the URL of the GeoJson file as seen below:

For this we will use read_sf from the sf package. SF will allow us to do pretty much anything that arcmap can do. In this case read_sf just reads the object from the internet for you and stores it in the same format as our census data.

Notice this is just the attribute data with a column called geometry that stores the outline of each river.

water = read_sf('https://opendata.arcgis.com/datasets/db65ff0038ed4270acb1435d931201cf_24.geojson')
head(water)
Simple feature collection with 6 features and 8 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -77.08418 ymin: 38.88663 xmax: -77.01135 ymax: 38.94176
geographic CRS: WGS 84

We can simply plot it using geom_sf(data=water). This time I am setting the fill and color outside of aes() because aes is only used if you want to use data from the attribute table to control an aesthetic property, here we just want all rivers, regardless of their attributes, to be lightblue and the outline to be white.

ggplot()+geom_sf(data=water, fill='lightblue', color='white')

Now let see if we can overlay it on our census data. We create a new plot using ggplot(). Now we can just overlay each geom_sf plot. White pop ends up on the bottom because it is put onto the map first. Notice that addition attributes or plots are added to the map by using +.

ggplot() + 
          # percent white plot
          geom_sf(data=wht_pop, aes(fill = white_per),color='white') + 
                ggtitle('Segregation in DC 2018')+ 
                scale_fill_continuous(type = "viridis", name="% White")+
          # water plot
          geom_sf(data=water, fill='lightblue', color='white')

Saving Map

Ggplot2 also makes it easy to save you maps or graphs out as images. You can save a variety of differnt graphics types including tif, png, or even svg (for use in illustrator). See the help here.

ggsave(filename='path_to_folder/segregationmap.png', 
       plot = last_plot(),  # save the last thing you ploted 
       device='png')
LS0tCnRpdGxlOiAiSGFuZGxpbmcgQ2Vuc3VzIERhdGEgaW4gUiAtIFBhcnQgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogTWljaGFlbCBNYW5uIEdXVSBtbWFubjExMjNAZ3d1LmVkdQoKLS0tCgojIEdldHRpbmcgU3RhcnRlZAoKVGhpcyBzaGVldCBidWlsZHMgb2ZmIG9mICJIYW5kbGluZyBDZW5zdXMgRGF0YSBpbiBSIC0gUGFydCAxIi4gUGxlYXNlIGNvbXBsZXRlIHRoYXQgZmlyc3QKCgpBZ2FpbiwgZXZlcnl0aW1lIHlvdSBzdGFydCBSIHlvdSB3aWxsIG5lZWQgdG8gbG9hZCBhbnkgbGlicmFyaWVzIHRoYXQgeW91IG5lZWQgZm9yIGFuYWx5c2lzLiAqWW91IHdpbGwgbmVlZCB0byBydW4gdGhpcyBiaXQgb2YgY29kZSBldmVyeSB0aW1lIHlvdSBvcGVuIFIqIHRvIGdldCB0aGlzIHRvIHdvcmsuIAoKYGBge3IgaW5jbHVkZT1UfQpsaWJyYXJ5KHRpZHljZW5zdXMpCmxpYnJhcnkoc2YpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShkcGx5cikKYGBgCgoKVG8gZ2V0IHN0YXJ0ZWQgd29ya2luZyB3aXRoIHRpZHljZW5zdXMsIHVzZXJzIHNob3VsZCBsb2FkIHRoZSBwYWNrYWdlIGFsb25nIHdpdGggdGhlIHRpZHl2ZXJzZSBwYWNrYWdlLCBhbmQgc2V0IHRoZWlyIENlbnN1cyBBUEkga2V5LiBBIGtleSBjYW4gYmUgb2J0YWluZWQgZnJvbSBbQ2Vuc3VzIEFQSSBLZXldKGh0dHA6Ly9hcGkuY2Vuc3VzLmdvdi9kYXRhL2tleV9zaWdudXAuaHRtbCkuICAqKkl0IHdpbGwgcHJvdmlkZSB5b3Ugd2l0aCBhIDQwIGRpZ2l0IHRleHQgc3RyaW5nLiBQbGVhc2Uga2VlcCB0cmFjayBvZiB0aGlzIG51bWJlci4gU3RvcmUgaXQgaW4gYSBzYWZlIHBsYWNlLioqCiAKYGBge3IgaW5jbHVkZT1ULCBldmFsPUZBTFNFfQpBUElfS2V5ID0gJ3lvdXJhcGlrZXknICAjIG5vbiB3b3JraW5nIGV4YW1wbGUgLSBwbGVhc2UgcGFzdGUgeW91ciBvd24gaW4KY2Vuc3VzX2FwaV9rZXkoQVBJX0tleSwgaW5zdGFsbCA9IEYsIG92ZXJ3cml0ZT1UUlVFKSAgCmBgYAoKIyBDcmVhdGluZyBOZXcgVmFyaWFibGVzCgpMZXQncyBzYXkgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGdlbnRyaWZpY2F0aW9uIGFuZCB3YW50ZWQgdG8gbWFwIHRoZSBwZXJjZW50YWdlIG9mIHRoZSBwb3B1bGF0aW9uIHRoYXQgaXMgd2hpdGUgaW4gREMgYXQgdGhlIGNlbnN1cyB0cmFjdCBsZXZlbCBmb3IgMjAxOC4gTm90aWNlIHRoYXQgYGdlb2dyYXBoeSA9ICd0cmFjdCdgIHdoaWNoIGRvd25sb2FkIGNlbnN1cyB0cmFjdCBkYXRhIG5vdy4gV2UgYXJlIGdvaW5nIHRvIGRvd25sb2FkIHR3byB2YXJpYWJsZXMgb25lIGZvciB0aGUgd2hpdGUgcG9wdWxhdGlvbiBhbmQgdGhlIG90aGVyIGZvciB0b3RhbCBwb3B1bGF0aW9uLiBUaGUgZm9sbG93aW5nIGBjKHdodF9wb3AgPSJCMDIwMDFfMDAyIix0b3RhbF9wb3A9IkIwMTAwMV8wMDEiKWAgaGVscHMgdXMgc2V0IHRoZSBuYW1lIG9mIHRoZSBjb2x1bW5zIHJpZ2h0IGF3YXkuIAoKYGBge3J9CndodF9wb3AgPSAgZ2V0X2FjcyhzdGF0ZSA9ICJEQyIsIAogICAgICAgICAgICAgICAgICAgZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICAgICAgICAgICB5ZWFyPTIwMTgsCiAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICBjKHdodF9wb3AgPSJCMDIwMDFfMDAyIix0b3RhbF9wb3A9IkIwMTAwMV8wMDEiKSwgCiAgICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gJ3dpZGUnKSAgICMganVzdCBkbyB0aGlzCgpoZWFkKHdodF9wb3AgKQpgYGAKCkFnYWluIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSB2YXJpYWJsZXMgZW5kaW5nIGluICdFJyBzbyB3aGl0ZV9wb3BFIGFuZCB0b3RhbF9wb3BFLgoKTm93IGxldCdzIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiB0aGUgcG9wdWxhdGlvbiB0aGF0IGlzIHdoaXRlIGZvciBlYWNoIGNlbnN1cyB0cmFjdC4gV2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgYG11dGF0ZWAgZnVuY3Rpb24gd2hpY2ggaXMgcGFydCBvZiB0aGUgRHBseXIgbGlicmFyeSBbc2VlIGhlbHBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pLiAKCldlIGFyZSBnb2luZyB0byBwYXNzIG91ciB3aHRfcG9wIG9iamVjdCB0byBtdXRhdGUgdXNpbmcgYCU+JWAsIHdoaWNoIGlzIGNhbGxlZCBhICdwaXBlJy4gVGhlIHBpcGUgaW5kaWNhdGVzIHRoYXQgd2h0X3BvcCBob2xkcyB0aGUgZGF0YSB3ZSB3YW50IHRvIHVzZS4gIAoKYGBge3J9CndodF9wb3AgPSB3aHRfcG9wICU+JSBtdXRhdGUod2hpdGVfcGVyID0gd2h0X3BvcEUvdG90YWxfcG9wRSkKaGVhZCh3aHRfcG9wKQpgYGAKCiMgVmlzdWFsaXplIFBvcHVsYXRpb25zCgpOb3cgd2h0X3BvcCBoYXMgYSBuZXcgdmFyaWFibGUgY2FsbGVkICd3aGl0ZV9wZXInLiBMZXQncyBsb29rIGF0IHRoZSBoaXN0b2dyYW0gb2YgdGhpcyBkYXRhIGFnYWluIHVzaW5nIGdncGxvdDIncyBgZ2VvbV9oaXN0b2dyYW1gLCBbc2VlIGhlbHAgaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21faGlzdG9ncmFtLmh0bWwpLiBIZXJlIGBhZXMod2hpdGVfcGVyKWAgaXMgdGVsbGluZyBSIHRvIGJhc2UgdGhlIGFlc3RldGljcyAoZS5nLiAnYWVzJykgb24gdGhlIHZhcmlhYmxlIHdoaXRlX3Blci4gWW91IGNhbiBhbHNvIHNldCB0aGUgbnVtYmVyIG9mIGJpbnMgdG8gdXNlIGluIHlvdXIgaGlzdG9ncmFtLiAKCmBgYHtyfQpnZ3Bsb3QoKStnZW9tX2hpc3RvZ3JhbShkYXRhPXdodF9wb3AsIGFlcyh3aGl0ZV9wZXIpLCBiaW5zID0gMTUpCmBgYAoKVGhpcyBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBhbHJlYWR5IHNwZWFrcyB0byB0aGUgZmFjdCB0aGF0IHBsYWNlcyB0ZW5kIHRvIGVpdGhlciBiZSBtb3JlbHkgd2hpdGUgb3IgbW9zdGx5IGJsYWNrLiBDbGVhciBzaWducyBvZiBzZWdyZWdhdGlvbiBpbiB0aGUgY2l0eS4gCgpCdXQgbGV0J3MgbG9vayBhdCB0aGUgbWFwIHVzaW5nIHRoZSBgZ2VvbV9zZmAgKHNwYXRpYWwgZmVhdHVyZXMgZ2VvbWV0cnkpLiAKCmBgYHtyfQpnZ3Bsb3QoKStnZW9tX3NmKGRhdGE9d2h0X3BvcCwgYWVzKGZpbGwgPSB3aGl0ZV9wZXIpKQpgYGAKCldlIGNhbiBhbHNvIGNvbnRyb2wgdGhlIGNvbG9yIHNjaGVtZSBhbmQgbGVnZW5kIG5hbWUgdXNpbmcgYHNjYWxlX2ZpbGxfY29udGludW91c2AuIE5vdGljZSB0aGF0IGBhZXMoZmlsbCA9IHdoaXRlX3BlcilgIGFuZCBgc2NhbGVfZmlsbF9jb250aW51b3VzYCBib3RoIHJlZmVyIHRvICdmaWxsJywgd2hpY2ggaXMgdGhlIHNoYWRpbmcgb2YgZWFjaCBwb2x5Z29uLiBZb3UgY2FuIGFsc28gc2V0IHRoZSAnbmFtZScgb2YgdGhlIGZpbGwgY29sb3Igc2NoZW1lIHdpdGggYG5hbWU9IiUgV2hpdGUiYC4gCgpZb3UgY2FuIGFsc28sIGlmIHVzZWZ1bCwgY2hhbmdlIHRoZSBjb2xvciBvZiB0aGUgbGluZXMgYmFzZWQgb24gdGhlIGRhdGEgdXNpbmcgYHNjYWxlX2NvbG91cl9jb250aW51b3VzYCBhZGQgYGFlcyhjb2xvdXIgPSB3aGl0ZV9wZXIpYCBpbnN0ZWFkIGlmIHlvdSB3YW50ZWQuIAoKCmBgYHtyfQpnZ3Bsb3QoKStnZW9tX3NmKGRhdGE9d2h0X3BvcCwgYWVzKGZpbGwgPSB3aGl0ZV9wZXIpLGNvbG9yPSd3aGl0ZScpICsgCiAgICAgICAgICAgICAgICBnZ3RpdGxlKCdTZWdyZWdhdGlvbiBpbiBEQyAyMDE4JykrIAogICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9jb250aW51b3VzKHR5cGUgPSAidmlyaWRpcyIsIG5hbWU9IiUgV2hpdGUiKQpgYGAKCgojIFJlYWRpbmcgRGF0YSBmcm9tIHRoZSBJbnRlcm5ldAoKT25lIHByb2JsZW0gd2l0aCB0aGlzIG1hcCBpcyB0aGF0IHdhdGVyYm9kaWVzIGFyZW4ndCBpbmNsdWRlZC4gV2UgY2FuIGRpcmVjdGx5IGFjY2VzcyBkYXRhIG9ubGluZSBlYXNpbHkuIExldCdzIHRha2UgdGhlIGV4YW1wbGUgb2YgdGhlIHdhdGVyYm9kaWVzIGRhdGFzZXQgb24gREMgb3BlbiBkYXRhIFtoZXJlXShodHRwczovL29wZW5kYXRhLmRjLmdvdi9kYXRhc2V0cy93YXRlcmJvZGllcykuIFRvIGFjY2VzcyB0aGUgZGF0YSBvbmxpbmUgd2UganVzdCBuZWVkIHRoZSBVUkwgb2YgdGhlIEdlb0pzb24gZmlsZSBhcyBzZWVuIGJlbG93OgoKIVsgXSguL09wZW5EYXRhRENBUEkucG5nKQoKRm9yIHRoaXMgd2Ugd2lsbCB1c2UgYHJlYWRfc2ZgIGZyb20gdGhlIFtzZiBwYWNrYWdlXShodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vc2YvKS4gU0Ygd2lsbCBhbGxvdyB1cyB0byBkbyBwcmV0dHkgbXVjaCBhbnl0aGluZyB0aGF0IGFyY21hcCBjYW4gZG8uIEluIHRoaXMgY2FzZSBgcmVhZF9zZmAganVzdCByZWFkcyB0aGUgb2JqZWN0IGZyb20gdGhlIGludGVybmV0IGZvciB5b3UgYW5kIHN0b3JlcyBpdCBpbiB0aGUgc2FtZSBmb3JtYXQgYXMgb3VyIGNlbnN1cyBkYXRhLiAgCgpOb3RpY2UgdGhpcyBpcyBqdXN0IHRoZSBhdHRyaWJ1dGUgZGF0YSB3aXRoIGEgY29sdW1uIGNhbGxlZCBgZ2VvbWV0cnlgIHRoYXQgc3RvcmVzIHRoZSBvdXRsaW5lIG9mIGVhY2ggcml2ZXIuCgpgYGB7cn0Kd2F0ZXIgPSByZWFkX3NmKCdodHRwczovL29wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvZGI2NWZmMDAzOGVkNDI3MGFjYjE0MzVkOTMxMjAxY2ZfMjQuZ2VvanNvbicpCmhlYWQod2F0ZXIpCmBgYAoKV2UgY2FuIHNpbXBseSBwbG90IGl0IHVzaW5nIGBnZW9tX3NmKGRhdGE9d2F0ZXIpYC4gVGhpcyB0aW1lIEkgYW0gc2V0dGluZyB0aGUgZmlsbCBhbmQgY29sb3Igb3V0c2lkZSBvZiBgYWVzKClgIGJlY2F1c2UgYGFlc2AgaXMgb25seSB1c2VkIGlmIHlvdSB3YW50IHRvIHVzZSBkYXRhIGZyb20gdGhlIGF0dHJpYnV0ZSB0YWJsZSB0byBjb250cm9sIGFuIGFlc3RoZXRpYyBwcm9wZXJ0eSwgaGVyZSB3ZSBqdXN0IHdhbnQgYWxsIHJpdmVycywgcmVnYXJkbGVzcyBvZiB0aGVpciBhdHRyaWJ1dGVzLCB0byBiZSBsaWdodGJsdWUgYW5kIHRoZSBvdXRsaW5lIHRvIGJlIHdoaXRlLiAKCmBgYHtyfQpnZ3Bsb3QoKStnZW9tX3NmKGRhdGE9d2F0ZXIsIGZpbGw9J2xpZ2h0Ymx1ZScsIGNvbG9yPSd3aGl0ZScpCgpgYGAKCk5vdyBsZXQgc2VlIGlmIHdlIGNhbiBvdmVybGF5IGl0IG9uIG91ciBjZW5zdXMgZGF0YS4gV2UgY3JlYXRlIGEgbmV3IHBsb3QgdXNpbmcgYGdncGxvdCgpYC4gTm93IHdlIGNhbiBqdXN0IG92ZXJsYXkgZWFjaCBgZ2VvbV9zZmAgcGxvdC4gV2hpdGUgcG9wIGVuZHMgdXAgb24gdGhlIGJvdHRvbSBiZWNhdXNlIGl0IGlzIHB1dCBvbnRvIHRoZSBtYXAgZmlyc3QuIE5vdGljZSB0aGF0IGFkZGl0aW9uIGF0dHJpYnV0ZXMgb3IgcGxvdHMgYXJlIGFkZGVkIHRvIHRoZSBtYXAgYnkgdXNpbmcgYCtgLiAKCgpgYGB7cn0KZ2dwbG90KCkgKyAKICAgICAgICAgICMgcGVyY2VudCB3aGl0ZSBwbG90CiAgICAgICAgICBnZW9tX3NmKGRhdGE9d2h0X3BvcCwgYWVzKGZpbGwgPSB3aGl0ZV9wZXIpLGNvbG9yPSd3aGl0ZScpICsgCiAgICAgICAgICAgICAgICBnZ3RpdGxlKCdTZWdyZWdhdGlvbiBpbiBEQyAyMDE4JykrIAogICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9jb250aW51b3VzKHR5cGUgPSAidmlyaWRpcyIsIG5hbWU9IiUgV2hpdGUiKSsKICAgICAgICAgICMgd2F0ZXIgcGxvdAogICAgICAgICAgZ2VvbV9zZihkYXRhPXdhdGVyLCBmaWxsPSdsaWdodGJsdWUnLCBjb2xvcj0nd2hpdGUnKQpgYGAKCiMgU2F2aW5nIE1hcAoKR2dwbG90MiBhbHNvIG1ha2VzIGl0IGVhc3kgdG8gc2F2ZSB5b3UgbWFwcyBvciBncmFwaHMgb3V0IGFzIGltYWdlcy4gWW91IGNhbiBzYXZlIGEgdmFyaWV0eSBvZiBkaWZmZXJudCBncmFwaGljcyB0eXBlcyBpbmNsdWRpbmcgdGlmLCBwbmcsIG9yIGV2ZW4gc3ZnIChmb3IgdXNlIGluIGlsbHVzdHJhdG9yKS4gU2VlIHRoZSBbaGVscCBoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2dzYXZlLmh0bWwpLgoKCmBgYHtyfQpnZ3NhdmUoZmlsZW5hbWU9J3BhdGhfdG9fZm9sZGVyL3NlZ3JlZ2F0aW9ubWFwLnBuZycsIAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLCAgIyBzYXZlIHRoZSBsYXN0IHRoaW5nIHlvdSBwbG90ZWQgCiAgICAgICBkZXZpY2U9J3BuZycpCmBgYAoKCgoKCg==