Disclaimer

This tutorial is converted from my 2025 30 Day Map Challenge Day 3 Polygon. All code is written by myself, but converted to R Markdown with Gemini 3 assistance.

Here is the screenshot of my chat with Gemini 3 for this conversion: Gemini 3 Chat Screenshot

Introduction

In this tutorial, we will create a choropleth map—a thematic map where areas are shaded in proportion to a statistical variable. Specifically, we will map Population Density in Philadelphia using data from the US Census Bureau.

We will cover:

  1. Acquiring spatial data using tidycensus.
  2. Projecting coordinate systems for accurate area calculations.
  3. Calculating population density.
  4. Incorporating external geographic layers (water bodies).
  5. Designing an aesthetic map using ggplot2 and custom color scales.

Prequisites

You will need the following R packages. If you don’t have them, install them using install.packages().

library(tidyverse)  # For data manipulation and visualization
library(tidycensus) # For accessing US Census data
library(sf)         # For spatial data handling
library(scales)     # For formatting numbers (commas, percentages)
library(units)      # For handling measurement units (km^2)

API Key Setup

To use tidycensus, you need an API key from the US Census Bureau. You can sign up for one here.

Once you have your key, load it into your environment (uncomment the line below and replace with your key):

# census_api_key("YOUR_CENSUS_API_KEY_HERE", install = TRUE)

Step 1: Acquiring Census Data

We use the get_acs() function to fetch American Community Survey (ACS) data. We are requesting the Total Population (variable B01003_001) for Census Tracts in Philadelphia, PA.

census_data <- get_acs(
  geography = "tract",
  variables = c(population = "B01003_001"), # Renaming variable to 'population'
  state = "PA",
  county = "Philadelphia",
  geometry = TRUE, # Downloads the shapefiles along with data
  year = 2023,
  progress= FALSE
)

Step 2: Spatial Transformation & Density Calculation

Projecting the Data

Census data usually comes in the WGS84 coordinate system (lat/long). To calculate area accurately, we should project it to a coordinate system designed for the specific region. For Philadelphia, EPSG:2272 (NAD83 / Pennsylvania South) is the standard.

# Transform to projected CRS (EPSG:2272)
census_data <- st_transform(census_data, crs = 2272)

Calculating Population Density

Now that our map is projected, we can calculate the area of each tract.

  1. Calculate area using st_area().
  2. Convert that area to square kilometers.
  3. Divide population by area to get density.
census_data <- census_data %>%
  mutate(
    # Calculate area and convert to km^2
    area_sq_km = st_area(geometry) %>% set_units("km^2"),
    
    # Calculate density
    pop_density = estimate / area_sq_km
  )

# Remove the 'units' class to make it a regular numeric column for plotting
census_data$pop_density <- drop_units(census_data$pop_density)

Step 3: Data Binning (Quantiles)

To make the map easier to read, we often group continuous data into “bins.” Here, we use ntile() to split the tracts into 5 equal groups (quintiles) based on density.

census_data <- census_data %>%
  mutate(
    pop_density_bin = ntile(pop_density, 5)
  )

Step 4: Adding Context (Water Layer)

A map of Philadelphia looks incomplete without the Delaware and Schuylkill rivers. We can fetch water polygon data from an open data source (ArcGIS Hub) via a GeoJSON URL.

Crucial Step: We must transform the water layer to EPSG:2272 to match our census data, and then clip it (st_intersection) so we only show water that is actually inside Philadelphia.

# 1. Read GeoJSON data directly from URL
water_url <- "https://hub.arcgis.com/api/v3/datasets/2b10034796f34c81a0eb44c676d86729_1/downloads/data?format=geojson&spatialRefId=4326&where=1%3D1"
water <- st_read(water_url, quiet = TRUE)

# 2. Transform to match our census data projection
water <- st_transform(water, crs = 2272)

# 3. Clip water to the boundary of our census data
# st_union(census_data) creates one big outline of Philadelphia to clip against
water_phila <- st_intersection(water, st_union(census_data))

Step 5: Visualization

Creating Custom Labels

Instead of showing “1, 2, 3, 4, 5” in the legend, we want to show the actual population ranges (e.g., “10,000 – 20,000”).

# Calculate the break points for the 5 bins
qs <- quantile(census_data$pop_density, probs = seq(0, 1, by = 0.2), na.rm = TRUE)

# Create label strings with commas
labs <- paste0(comma(round(qs[-length(qs)])), " – ", comma(round(qs[-1])))

# Assign these labels to the numbers 1 through 5
labs_named <- setNames(labs, as.character(1:5))

The Final Plot

We use ggplot2 with geom_sf. Note the use of factor(pop_density_bin) to treat the bins as discrete categories rather than a continuous gradient.

ggplot(data = census_data) +
  # Layer 1: Census Tracts
  geom_sf(aes(fill = factor(pop_density_bin)), color = NA) +
  
  # Layer 2: Custom Colors and Legend
  scale_fill_manual(
    values = c(
      "1" = "#ffccd5", # Lightest
      "2" = "#ffb3c1",
      "3" = "#ff758f",
      "4" = "#c9184a",
      "5" = "#800f2f"  # Darkest
    ),
    breaks = c("1","2","3","4","5"),
    labels = labs_named,
    drop = FALSE,
    name = "People per km²"
  ) +
  
  # Layer 3: Water Overlay
  geom_sf(data = water_phila, fill = "#3f88c5", color = NA, alpha = 0.5) +
  
  # Layer 4: Text and Theme
  labs(
    title = "Population Density by Census Tract in Philadelphia",
    subtitle = "Data Source: 2023 ACS 5-Year Estimates",
    caption = "Visualization Tutorial based on tidycensus"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(size = 18, face = "bold", margin = margin(b=10)),
    plot.subtitle = element_text(size = 12, color = "grey40", margin = margin(b=20)),
    plot.caption = element_text(size = 8, hjust = 0, color = "grey60"),
    legend.position = "right",
    legend.title = element_text(face = "bold")
  )

LS0tCnRpdGxlOiAiQ3JlYXRpbmcgYSBQb3B1bGF0aW9uIERlbnNpdHkgQ2hvcm9wbGV0aCBNYXAgaW4gUiIKYXV0aG9yOiAiWmhhbmNoYW8gWWFuZyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBmbGF0bHkKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgbWF0aGpheDogZGVmYXVsdAotLS0KCiMjIERpc2NsYWltZXIKVGhpcyB0dXRvcmlhbCBpcyBjb252ZXJ0ZWQgZnJvbSBteSAyMDI1IDMwIERheSBNYXAgQ2hhbGxlbmdlIERheSAzIFBvbHlnb24uIEFsbCBjb2RlIGlzIHdyaXR0ZW4gYnkgbXlzZWxmLCBidXQgY29udmVydGVkIHRvIFIgTWFya2Rvd24gd2l0aCBHZW1pbmkgMyBhc3Npc3RhbmNlLgoKSGVyZSBpcyB0aGUgc2NyZWVuc2hvdCBvZiBteSBjaGF0IHdpdGggR2VtaW5pIDMgZm9yIHRoaXMgY29udmVyc2lvbjoKIVtHZW1pbmkgMyBDaGF0IFNjcmVlbnNob3RdKC9Vc2Vycy96aGFuY2hhb3lhbmcvRGVza3RvcC9jaGF0LnBuZykKCiMgSW50cm9kdWN0aW9uCgpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIGNyZWF0ZSBhIGNob3JvcGxldGggbWFw4oCUYSB0aGVtYXRpYyBtYXAgd2hlcmUgYXJlYXMgYXJlIHNoYWRlZCBpbiBwcm9wb3J0aW9uIHRvIGEgc3RhdGlzdGljYWwgdmFyaWFibGUuIFNwZWNpZmljYWxseSwgd2Ugd2lsbCBtYXAgUG9wdWxhdGlvbiBEZW5zaXR5IGluIFBoaWxhZGVscGhpYSB1c2luZyBkYXRhIGZyb20gdGhlIFVTIENlbnN1cyBCdXJlYXUuCgpXZSB3aWxsIGNvdmVyOgoKMS4gQWNxdWlyaW5nIHNwYXRpYWwgZGF0YSB1c2luZyBgdGlkeWNlbnN1c2AuCjIuIFByb2plY3RpbmcgY29vcmRpbmF0ZSBzeXN0ZW1zIGZvciBhY2N1cmF0ZSBhcmVhIGNhbGN1bGF0aW9ucy4KMy4gQ2FsY3VsYXRpbmcgcG9wdWxhdGlvbiBkZW5zaXR5Lgo0LiBJbmNvcnBvcmF0aW5nIGV4dGVybmFsIGdlb2dyYXBoaWMgbGF5ZXJzICh3YXRlciBib2RpZXMpLgo1LiBEZXNpZ25pbmcgYW4gYWVzdGhldGljIG1hcCB1c2luZyBgZ2dwbG90MmAgYW5kIGN1c3RvbSBjb2xvciBzY2FsZXMuCgojIFByZXF1aXNpdGVzCgpZb3Ugd2lsbCBuZWVkIHRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcy4gSWYgeW91IGRvbid0IGhhdmUgdGhlbSwgaW5zdGFsbCB0aGVtIHVzaW5nIGBpbnN0YWxsLnBhY2thZ2VzKClgLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKSAgIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGl6YXRpb24KbGlicmFyeSh0aWR5Y2Vuc3VzKSAjIEZvciBhY2Nlc3NpbmcgVVMgQ2Vuc3VzIGRhdGEKbGlicmFyeShzZikgICAgICAgICAjIEZvciBzcGF0aWFsIGRhdGEgaGFuZGxpbmcKbGlicmFyeShzY2FsZXMpICAgICAjIEZvciBmb3JtYXR0aW5nIG51bWJlcnMgKGNvbW1hcywgcGVyY2VudGFnZXMpCmxpYnJhcnkodW5pdHMpICAgICAgIyBGb3IgaGFuZGxpbmcgbWVhc3VyZW1lbnQgdW5pdHMgKGttXjIpCmBgYAoKIyMgQVBJIEtleSBTZXR1cAoKVG8gdXNlIHRpZHljZW5zdXMsIHlvdSBuZWVkIGFuIEFQSSBrZXkgZnJvbSB0aGUgVVMgQ2Vuc3VzIEJ1cmVhdS4gWW91IGNhbiBzaWduIHVwIGZvciBvbmUgW2hlcmVdKGh0dHBzOi8vYXBpLmNlbnN1cy5nb3YvZGF0YS9rZXlfc2lnbnVwLmh0bWwpLgoKT25jZSB5b3UgaGF2ZSB5b3VyIGtleSwgbG9hZCBpdCBpbnRvIHlvdXIgZW52aXJvbm1lbnQgKHVuY29tbWVudCB0aGUgbGluZSBiZWxvdyBhbmQgcmVwbGFjZSB3aXRoIHlvdXIga2V5KToKYGBge3J9CiMgY2Vuc3VzX2FwaV9rZXkoIllPVVJfQ0VOU1VTX0FQSV9LRVlfSEVSRSIsIGluc3RhbGwgPSBUUlVFKQpgYGAKCiMgU3RlcCAxOiBBY3F1aXJpbmcgQ2Vuc3VzIERhdGEKV2UgdXNlIHRoZSBgZ2V0X2FjcygpYCBmdW5jdGlvbiB0byBmZXRjaCBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGRhdGEuIFdlIGFyZSByZXF1ZXN0aW5nIHRoZSBUb3RhbCBQb3B1bGF0aW9uICh2YXJpYWJsZSBgQjAxMDAzXzAwMWApIGZvciBDZW5zdXMgVHJhY3RzIGluIFBoaWxhZGVscGhpYSwgUEEuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2Vuc3VzX2RhdGEgPC0gZ2V0X2FjcygKICBnZW9ncmFwaHkgPSAidHJhY3QiLAogIHZhcmlhYmxlcyA9IGMocG9wdWxhdGlvbiA9ICJCMDEwMDNfMDAxIiksICMgUmVuYW1pbmcgdmFyaWFibGUgdG8gJ3BvcHVsYXRpb24nCiAgc3RhdGUgPSAiUEEiLAogIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogIGdlb21ldHJ5ID0gVFJVRSwgIyBEb3dubG9hZHMgdGhlIHNoYXBlZmlsZXMgYWxvbmcgd2l0aCBkYXRhCiAgeWVhciA9IDIwMjMsCiAgcHJvZ3Jlc3M9IEZBTFNFCikKCmBgYAoKIyBTdGVwIDI6IFNwYXRpYWwgVHJhbnNmb3JtYXRpb24gJiBEZW5zaXR5IENhbGN1bGF0aW9uCgojIyBQcm9qZWN0aW5nIHRoZSBEYXRhCgpDZW5zdXMgZGF0YSB1c3VhbGx5IGNvbWVzIGluIHRoZSBXR1M4NCBjb29yZGluYXRlIHN5c3RlbSAobGF0L2xvbmcpLiBUbyBjYWxjdWxhdGUgYXJlYSBhY2N1cmF0ZWx5LCB3ZSBzaG91bGQgcHJvamVjdCBpdCB0byBhIGNvb3JkaW5hdGUgc3lzdGVtIGRlc2lnbmVkIGZvciB0aGUgc3BlY2lmaWMgcmVnaW9uLiBGb3IgUGhpbGFkZWxwaGlhLCAqKkVQU0c6MjI3MiAoTkFEODMgLyBQZW5uc3lsdmFuaWEgU291dGgpKiogaXMgdGhlIHN0YW5kYXJkLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBUcmFuc2Zvcm0gdG8gcHJvamVjdGVkIENSUyAoRVBTRzoyMjcyKQpjZW5zdXNfZGF0YSA8LSBzdF90cmFuc2Zvcm0oY2Vuc3VzX2RhdGEsIGNycyA9IDIyNzIpCmBgYAoKIyMgQ2FsY3VsYXRpbmcgUG9wdWxhdGlvbiBEZW5zaXR5CgpOb3cgdGhhdCBvdXIgbWFwIGlzIHByb2plY3RlZCwgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgYXJlYSBvZiBlYWNoIHRyYWN0LgoKMS4gQ2FsY3VsYXRlIGFyZWEgdXNpbmcgc3RfYXJlYSgpLgoyLiBDb252ZXJ0IHRoYXQgYXJlYSB0byBzcXVhcmUga2lsb21ldGVycy4KMy4gRGl2aWRlIHBvcHVsYXRpb24gYnkgYXJlYSB0byBnZXQgZGVuc2l0eS4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNlbnN1c19kYXRhIDwtIGNlbnN1c19kYXRhICU+JQogIG11dGF0ZSgKICAgICMgQ2FsY3VsYXRlIGFyZWEgYW5kIGNvbnZlcnQgdG8ga21eMgogICAgYXJlYV9zcV9rbSA9IHN0X2FyZWEoZ2VvbWV0cnkpICU+JSBzZXRfdW5pdHMoImttXjIiKSwKICAgIAogICAgIyBDYWxjdWxhdGUgZGVuc2l0eQogICAgcG9wX2RlbnNpdHkgPSBlc3RpbWF0ZSAvIGFyZWFfc3Ffa20KICApCgojIFJlbW92ZSB0aGUgJ3VuaXRzJyBjbGFzcyB0byBtYWtlIGl0IGEgcmVndWxhciBudW1lcmljIGNvbHVtbiBmb3IgcGxvdHRpbmcKY2Vuc3VzX2RhdGEkcG9wX2RlbnNpdHkgPC0gZHJvcF91bml0cyhjZW5zdXNfZGF0YSRwb3BfZGVuc2l0eSkKYGBgCgojIFN0ZXAgMzogRGF0YSBCaW5uaW5nIChRdWFudGlsZXMpClRvIG1ha2UgdGhlIG1hcCBlYXNpZXIgdG8gcmVhZCwgd2Ugb2Z0ZW4gZ3JvdXAgY29udGludW91cyBkYXRhIGludG8gImJpbnMuIiBIZXJlLCB3ZSB1c2UgYG50aWxlKClgIHRvIHNwbGl0IHRoZSB0cmFjdHMgaW50byA1IGVxdWFsIGdyb3VwcyAocXVpbnRpbGVzKSBiYXNlZCBvbiBkZW5zaXR5LgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2Vuc3VzX2RhdGEgPC0gY2Vuc3VzX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgcG9wX2RlbnNpdHlfYmluID0gbnRpbGUocG9wX2RlbnNpdHksIDUpCiAgKQoKYGBgCgojIFN0ZXAgNDogQWRkaW5nIENvbnRleHQgKFdhdGVyIExheWVyKQoKQSBtYXAgb2YgUGhpbGFkZWxwaGlhIGxvb2tzIGluY29tcGxldGUgd2l0aG91dCB0aGUgRGVsYXdhcmUgYW5kIFNjaHV5bGtpbGwgcml2ZXJzLiBXZSBjYW4gZmV0Y2ggd2F0ZXIgcG9seWdvbiBkYXRhIGZyb20gYW4gb3BlbiBkYXRhIHNvdXJjZSAoQXJjR0lTIEh1YikgdmlhIGEgR2VvSlNPTiBVUkwuCgoqKkNydWNpYWwgU3RlcCoqOiBXZSBtdXN0IHRyYW5zZm9ybSB0aGUgd2F0ZXIgbGF5ZXIgdG8gKipFUFNHOjIyNzIqKiB0byBtYXRjaCBvdXIgY2Vuc3VzIGRhdGEsIGFuZCB0aGVuIGNsaXAgaXQgKGBzdF9pbnRlcnNlY3Rpb25gKSBzbyB3ZSBvbmx5IHNob3cgd2F0ZXIgdGhhdCBpcyBhY3R1YWxseSAqaW5zaWRlKiBQaGlsYWRlbHBoaWEuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyAxLiBSZWFkIEdlb0pTT04gZGF0YSBkaXJlY3RseSBmcm9tIFVSTAp3YXRlcl91cmwgPC0gImh0dHBzOi8vaHViLmFyY2dpcy5jb20vYXBpL3YzL2RhdGFzZXRzLzJiMTAwMzQ3OTZmMzRjODFhMGViNDRjNjc2ZDg2NzI5XzEvZG93bmxvYWRzL2RhdGE/Zm9ybWF0PWdlb2pzb24mc3BhdGlhbFJlZklkPTQzMjYmd2hlcmU9MSUzRDEiCndhdGVyIDwtIHN0X3JlYWQod2F0ZXJfdXJsLCBxdWlldCA9IFRSVUUpCgojIDIuIFRyYW5zZm9ybSB0byBtYXRjaCBvdXIgY2Vuc3VzIGRhdGEgcHJvamVjdGlvbgp3YXRlciA8LSBzdF90cmFuc2Zvcm0od2F0ZXIsIGNycyA9IDIyNzIpCgojIDMuIENsaXAgd2F0ZXIgdG8gdGhlIGJvdW5kYXJ5IG9mIG91ciBjZW5zdXMgZGF0YQojIHN0X3VuaW9uKGNlbnN1c19kYXRhKSBjcmVhdGVzIG9uZSBiaWcgb3V0bGluZSBvZiBQaGlsYWRlbHBoaWEgdG8gY2xpcCBhZ2FpbnN0CndhdGVyX3BoaWxhIDwtIHN0X2ludGVyc2VjdGlvbih3YXRlciwgc3RfdW5pb24oY2Vuc3VzX2RhdGEpKQpgYGAKCiMgU3RlcCA1OiBWaXN1YWxpemF0aW9uCiMjIENyZWF0aW5nIEN1c3RvbSBMYWJlbHMKSW5zdGVhZCBvZiBzaG93aW5nICIxLCAyLCAzLCA0LCA1IiBpbiB0aGUgbGVnZW5kLCB3ZSB3YW50IHRvIHNob3cgdGhlIGFjdHVhbCBwb3B1bGF0aW9uIHJhbmdlcyAoZS5nLiwgIjEwLDAwMCDigJMgMjAsMDAwIikuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBDYWxjdWxhdGUgdGhlIGJyZWFrIHBvaW50cyBmb3IgdGhlIDUgYmlucwpxcyA8LSBxdWFudGlsZShjZW5zdXNfZGF0YSRwb3BfZGVuc2l0eSwgcHJvYnMgPSBzZXEoMCwgMSwgYnkgPSAwLjIpLCBuYS5ybSA9IFRSVUUpCgojIENyZWF0ZSBsYWJlbCBzdHJpbmdzIHdpdGggY29tbWFzCmxhYnMgPC0gcGFzdGUwKGNvbW1hKHJvdW5kKHFzWy1sZW5ndGgocXMpXSkpLCAiIOKAkyAiLCBjb21tYShyb3VuZChxc1stMV0pKSkKCiMgQXNzaWduIHRoZXNlIGxhYmVscyB0byB0aGUgbnVtYmVycyAxIHRocm91Z2ggNQpsYWJzX25hbWVkIDwtIHNldE5hbWVzKGxhYnMsIGFzLmNoYXJhY3RlcigxOjUpKQpgYGAKCiMjIFRoZSBGaW5hbCBQbG90CldlIHVzZSBgZ2dwbG90MmAgd2l0aCBgZ2VvbV9zZmAuIE5vdGUgdGhlIHVzZSBvZiBgZmFjdG9yKHBvcF9kZW5zaXR5X2JpbilgIHRvIHRyZWF0IHRoZSBiaW5zIGFzIGRpc2NyZXRlIGNhdGVnb3JpZXMgcmF0aGVyIHRoYW4gYSBjb250aW51b3VzIGdyYWRpZW50LgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0gY2Vuc3VzX2RhdGEpICsKICAjIExheWVyIDE6IENlbnN1cyBUcmFjdHMKICBnZW9tX3NmKGFlcyhmaWxsID0gZmFjdG9yKHBvcF9kZW5zaXR5X2JpbikpLCBjb2xvciA9IE5BKSArCiAgCiAgIyBMYXllciAyOiBDdXN0b20gQ29sb3JzIGFuZCBMZWdlbmQKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIHZhbHVlcyA9IGMoCiAgICAgICIxIiA9ICIjZmZjY2Q1IiwgIyBMaWdodGVzdAogICAgICAiMiIgPSAiI2ZmYjNjMSIsCiAgICAgICIzIiA9ICIjZmY3NThmIiwKICAgICAgIjQiID0gIiNjOTE4NGEiLAogICAgICAiNSIgPSAiIzgwMGYyZiIgICMgRGFya2VzdAogICAgKSwKICAgIGJyZWFrcyA9IGMoIjEiLCIyIiwiMyIsIjQiLCI1IiksCiAgICBsYWJlbHMgPSBsYWJzX25hbWVkLAogICAgZHJvcCA9IEZBTFNFLAogICAgbmFtZSA9ICJQZW9wbGUgcGVyIGttwrIiCiAgKSArCiAgCiAgIyBMYXllciAzOiBXYXRlciBPdmVybGF5CiAgZ2VvbV9zZihkYXRhID0gd2F0ZXJfcGhpbGEsIGZpbGwgPSAiIzNmODhjNSIsIGNvbG9yID0gTkEsIGFscGhhID0gMC41KSArCiAgCiAgIyBMYXllciA0OiBUZXh0IGFuZCBUaGVtZQogIGxhYnMoCiAgICB0aXRsZSA9ICJQb3B1bGF0aW9uIERlbnNpdHkgYnkgQ2Vuc3VzIFRyYWN0IGluIFBoaWxhZGVscGhpYSIsCiAgICBzdWJ0aXRsZSA9ICJEYXRhIFNvdXJjZTogMjAyMyBBQ1MgNS1ZZWFyIEVzdGltYXRlcyIsCiAgICBjYXB0aW9uID0gIlZpc3VhbGl6YXRpb24gVHV0b3JpYWwgYmFzZWQgb24gdGlkeWNlbnN1cyIKICApICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIsIG1hcmdpbiA9IG1hcmdpbihiPTEwKSksCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiZ3JleTQwIiwgbWFyZ2luID0gbWFyZ2luKGI9MjApKSwKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgaGp1c3QgPSAwLCBjb2xvciA9ICJncmV5NjAiKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikKICApCmBgYAo=