Introduction

This notebook provides examples of:

Dependencies

This notebook requires a number of packages:

# tidyverse packages
library(dplyr)       # data cleaning
library(ggplot2)     # static mapping
library(readr)       # read/write tabular data

# spatial packages
library(mapview)     # preview spatial data
library(sf)          # spatial data tools
library(tigris)      # access TIGER/line data

# other packages
library(here)        # file path management
library(measurements) # unit conversion
library(naniar)     # missing data

Load Data

This notebook requires one set of data:

sluPlaces <- read_csv(here("data", "example-data", "sluPlaces.csv"))
Rows: 6 Columns: 4
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): name
dbl (3): id, lng, lat

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Data Cleaning Notes

Make sure your x and y coordinate variables are numeric or double:

str(sluPlaces)
spec_tbl_df [6 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ id  : num [1:6] 1 2 3 4 5 6
 $ name: chr [1:6] "Morrissey Hall" "Starbucks" "Simon Rec" "Pius Library" ...
 $ lng : num [1:6] -90.2 -90.2 -90.2 -90.2 -90.2 ...
 $ lat : num [1:6] 38.6 38.6 38.6 38.6 38.6 ...
 - attr(*, "spec")=
  .. cols(
  ..   id = col_double(),
  ..   name = col_character(),
  ..   lng = col_double(),
  ..   lat = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 

If they are not, use mutate() with as.numeric() to convert them.

Next, we need to make sure there are no missing data in our coordinate columns. This will cause errors when we go to project these data.

miss_var_summary(sluPlaces)

Great! We have no missing data in lng and lat. If we did, we would need to use filter() from dplyr to remove those missing values. Also be aware of coordinates that are set to 0,0. These can sometimes be used to identify missing data as well.

sluPlaces %>%
  filter(lng == 0 | lat == 0)

Again, excellent, no missing data!

Identifying Coordinates

Identifying the coordinate system that lng and lat represent can be challenging:

Once we have candidates for projecting our points, we need to identify the CRS/EPSG values (or, alternatively, the Proj4 strings) that correspond to our coordinate system candidate(s). For this, we’ll use websites like EPSG.io and Spatial Reference.

Project Data

First, we want to convert these data to from a tibble to an sf object with st_as_sf(). We use the lng variable as our x variable and lat as our y variable, and use 4269 for our crs argument since these data are in decimal degrees and this corresponds to the likely coordinate system we identified above:

sluPlaces_sf <- st_as_sf(sluPlaces, coords = c("lng", "lat"), crs = 4269)

Next, we want to confirm that this worked:

mapview(sluPlaces_sf)

Excellent!

Transform Our Projection

We’ve already used st_transform(), but now can do so with purpose. For example, to convert our data to State Plane (meters). We’ll use the data based on the 2007 update to NAD 1983:

sluPlaces_statePlane <- st_transform(sluPlaces_sf, crs = 3601)

If we need our data in feet, there are also options. However, these are ESRI products that are not included in the sf package. How do we know? We can use the EPSG value 102696.

st_transform(sluPlaces_sf, crs = 102696)

Note that error includes this language - GDAL Error 1: PROJ: proj_create_from_database: crs not found. Even so, we can still use the coordinate system by specifying the Proj4 string value instead of the CRS number:

sluPlaces_statePlane_ft <- st_transform(sluPlaces_sf, crs = "ESRI:102696")

Sometimes this doesn’t work, and so it is also useful to know how to apply Proj4 strings as well.

sluPlaces_statePlane_ft <- st_transform(sluPlaces_sf, crs = "+proj=tmerc +lat_0=35.83333333333334 +lon_0=-90.5 +k=0.9999333333333333 +x_0=250000 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs ")

This should give us correctly projected data. Using this trick with the Proj4 strings also works, by the way, with st_as_sf() as well.

Write Data

Finally, we’ll write our data:

st_write(sluPlaces_statePlane, here("data", "example-data", "clean-data", "sluPlaces.shp"), delete_dsn = TRUE)
Deleting source `/Users/prenercg/GitHub/slu-soc5650/module-3-projections/data/example-data/clean-data/sluPlaces.shp' using driver `ESRI Shapefile'
Writing layer `sluPlaces' to data source 
  `/Users/prenercg/GitHub/slu-soc5650/module-3-projections/data/example-data/clean-data/sluPlaces.shp' using driver `ESRI Shapefile'
Writing 6 features with 2 fields and geometry type Point.

The st_write() function identifies the file type from what you include at the end of the here() statement. If I am working solely in R, I tend to use .geojson because:

  1. It does not impose limits on column names or data types.
  2. It is an open standard for storing data that is plain text.
  3. It can be previewed live on GitHub.com (where I share most my data).

However, if you are going to be working in the ESRI ecosystem, saving data as shapefiles is suggested. Note that #1 above needs to be addressed - keep variable names short (8 characters or less) and convert big numbers to strings or remove them completely before saving.

Using Projections to Calculate Area

One final task we have when working with projections is to use them as the basis for making calculations. Often, this entails calculating area so that we can use it for normalizing our data. Sometimes our data come with measurements, but we don’t know what those units are. Other times, we don’t have an area measure at all. Consider these data from tigris:

moCounties <- counties(state = 29) %>%
  select(GEOID, NAMELSAD, ALAND, AWATER)

  |                                                                                                                                                        
  |                                                                                                                                                  |   0%
  |                                                                                                                                                        
  |=                                                                                                                                                 |   0%
  |                                                                                                                                                        
  |=                                                                                                                                                 |   1%
  |                                                                                                                                                        
  |==                                                                                                                                                |   1%
  |                                                                                                                                                        
  |==                                                                                                                                                |   2%
  |                                                                                                                                                        
  |===                                                                                                                                               |   2%
  |                                                                                                                                                        
  |====                                                                                                                                              |   2%
  |                                                                                                                                                        
  |====                                                                                                                                              |   3%
  |                                                                                                                                                        
  |=====                                                                                                                                             |   3%
  |                                                                                                                                                        
  |=====                                                                                                                                             |   4%
  |                                                                                                                                                        
  |======                                                                                                                                            |   4%
  |                                                                                                                                                        
  |=======                                                                                                                                           |   4%
  |                                                                                                                                                        
  |=======                                                                                                                                           |   5%
  |                                                                                                                                                        
  |========                                                                                                                                          |   5%
  |                                                                                                                                                        
  |========                                                                                                                                          |   6%
  |                                                                                                                                                        
  |=========                                                                                                                                         |   6%
  |                                                                                                                                                        
  |=========                                                                                                                                         |   7%
  |                                                                                                                                                        
  |==========                                                                                                                                        |   7%
  |                                                                                                                                                        
  |===========                                                                                                                                       |   7%
  |                                                                                                                                                        
  |===========                                                                                                                                       |   8%
  |                                                                                                                                                        
  |============                                                                                                                                      |   8%
  |                                                                                                                                                        
  |===============                                                                                                                                   |  10%
  |                                                                                                                                                        
  |=================                                                                                                                                 |  11%
  |                                                                                                                                                        
  |===================                                                                                                                               |  13%
  |                                                                                                                                                        
  |=====================                                                                                                                             |  14%
  |                                                                                                                                                        
  |=====================                                                                                                                             |  15%
  |                                                                                                                                                        
  |======================                                                                                                                            |  15%
  |                                                                                                                                                        
  |=======================                                                                                                                           |  15%
  |                                                                                                                                                        
  |=======================                                                                                                                           |  16%
  |                                                                                                                                                        
  |========================                                                                                                                          |  16%
  |                                                                                                                                                        
  |========================                                                                                                                          |  17%
  |                                                                                                                                                        
  |=========================                                                                                                                         |  17%
  |                                                                                                                                                        
  |==========================                                                                                                                        |  17%
  |                                                                                                                                                        
  |==========================                                                                                                                        |  18%
  |                                                                                                                                                        
  |=============================                                                                                                                     |  20%
  |                                                                                                                                                        
  |================================                                                                                                                  |  22%
  |                                                                                                                                                        
  |===================================                                                                                                               |  24%
  |                                                                                                                                                        
  |======================================                                                                                                            |  26%
  |                                                                                                                                                        
  |=========================================                                                                                                         |  28%
  |                                                                                                                                                        
  |============================================                                                                                                      |  30%
  |                                                                                                                                                        
  |===============================================                                                                                                   |  32%
  |                                                                                                                                                        
  |==================================================                                                                                                |  34%
  |                                                                                                                                                        
  |=====================================================                                                                                             |  36%
  |                                                                                                                                                        
  |======================================================                                                                                            |  37%
  |                                                                                                                                                        
  |=========================================================                                                                                         |  39%
  |                                                                                                                                                        
  |============================================================                                                                                      |  41%
  |                                                                                                                                                        
  |=============================================================                                                                                     |  41%
  |                                                                                                                                                        
  |==============================================================                                                                                    |  42%
  |                                                                                                                                                        
  |===============================================================                                                                                   |  43%
  |                                                                                                                                                        
  |================================================================                                                                                  |  44%
  |                                                                                                                                                        
  |===================================================================                                                                               |  46%
  |                                                                                                                                                        
  |======================================================================                                                                            |  48%
  |                                                                                                                                                        
  |=========================================================================                                                                         |  50%
  |                                                                                                                                                        
  |============================================================================                                                                      |  52%
  |                                                                                                                                                        
  |===============================================================================                                                                   |  54%
  |                                                                                                                                                        
  |================================================================================                                                                  |  55%
  |                                                                                                                                                        
  |===================================================================================                                                               |  57%
  |                                                                                                                                                        
  |======================================================================================                                                            |  59%
  |                                                                                                                                                        
  |=========================================================================================                                                         |  61%
  |                                                                                                                                                        
  |==========================================================================================                                                        |  61%
  |                                                                                                                                                        
  |==========================================================================================                                                        |  62%
  |                                                                                                                                                        
  |===========================================================================================                                                       |  62%
  |                                                                                                                                                        
  |===========================================================================================                                                       |  63%
  |                                                                                                                                                        
  |============================================================================================                                                      |  63%
  |                                                                                                                                                        
  |===============================================================================================                                                   |  65%
  |                                                                                                                                                        
  |==================================================================================================                                                |  67%
  |                                                                                                                                                        
  |====================================================================================================                                              |  68%
  |                                                                                                                                                        
  |====================================================================================================                                              |  69%
  |                                                                                                                                                        
  |=====================================================================================================                                             |  69%
  |                                                                                                                                                        
  |======================================================================================================                                            |  70%
  |                                                                                                                                                        
  |=======================================================================================================                                           |  70%
  |                                                                                                                                                        
  |=======================================================================================================                                           |  71%
  |                                                                                                                                                        
  |========================================================================================================                                          |  71%
  |                                                                                                                                                        
  |=========================================================================================================                                         |  72%
  |                                                                                                                                                        
  |==========================================================================================================                                        |  72%
  |                                                                                                                                                        
  |==========================================================================================================                                        |  73%
  |                                                                                                                                                        
  |===========================================================================================================                                       |  73%
  |                                                                                                                                                        
  |===========================================================================================================                                       |  74%
  |                                                                                                                                                        
  |============================================================================================================                                      |  74%
  |                                                                                                                                                        
  |===============================================================================================================                                   |  76%
  |                                                                                                                                                        
  |==================================================================================================================                                |  78%
  |                                                                                                                                                        
  |====================================================================================================================                              |  80%
  |                                                                                                                                                        
  |=====================================================================================================================                             |  80%
  |                                                                                                                                                        
  |======================================================================================================================                            |  80%
  |                                                                                                                                                        
  |======================================================================================================================                            |  81%
  |                                                                                                                                                        
  |=======================================================================================================================                           |  81%
  |                                                                                                                                                        
  |=======================================================================================================================                           |  82%
  |                                                                                                                                                        
  |========================================================================================================================                          |  82%
  |                                                                                                                                                        
  |===========================================================================================================================                       |  84%
  |                                                                                                                                                        
  |==============================================================================================================================                    |  86%
  |                                                                                                                                                        
  |=================================================================================================================================                 |  88%
  |                                                                                                                                                        
  |==================================================================================================================================                |  89%
  |                                                                                                                                                        
  |===================================================================================================================================               |  90%
  |                                                                                                                                                        
  |====================================================================================================================================              |  90%
  |                                                                                                                                                        
  |====================================================================================================================================              |  91%
  |                                                                                                                                                        
  |=====================================================================================================================================             |  91%
  |                                                                                                                                                        
  |========================================================================================================================================          |  93%
  |                                                                                                                                                        
  |===========================================================================================================================================       |  95%
  |                                                                                                                                                        
  |=============================================================================================================================================     |  97%
  |                                                                                                                                                        
  |===============================================================================================================================================   |  98%
  |                                                                                                                                                        
  |================================================================================================================================================  |  98%
  |                                                                                                                                                        
  |================================================================================================================================================  |  99%
  |                                                                                                                                                        
  |==================================================================================================================================================| 100%

There are both ALAND and AWATER columns, but it isn’t immediately clear what their units are (they are in meters, FYI). If we want the total area of these units, we can combine a few functions from sf, dplyr, and measurements to achieve a few different outcomes. First, we’ll re-calculate the area based on a projected coordinate system. We need to move from the current coordinate system to a projected coordinate system. To get a sense of our starting place, we’ll use st_crs():

st_crs(moCounties)
Coordinate Reference System:
  User input: NAD83 
  wkt:
GEOGCRS["NAD83",
    DATUM["North American Datum 1983",
        ELLIPSOID["GRS 1980",6378137,298.257222101,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["latitude",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["longitude",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    ID["EPSG",4269]]

This confirms that we are using a geographic coordinate system (this is typical for TIGER/Line data). We’ll switch to Albers Equal Area Conic for the contiguous United States:

moCounties <- st_transform(moCounties, crs = "ESRI:102003")
# moCounties <- st_transform(moCounties, crs = "+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs")

I’ve also included the Proj4 string, but it is commented out. Notice how +units=2 appears in the Proj4 string. This means are data are in meters. If we wanted to recalculate our area units meters, we can use the st_area() function and the geometry column to do so:

moCounties %>%
  select(-c("ALAND", "AWATER")) %>%
  mutate(sq_m = as.numeric(st_area(geometry)), .after = NAMELSAD) %>%
  mutate(sq_km = conv_unit(sq_m, from = "m2", to = "km2"), .after = sq_m) -> moCounties

We can do the same thing if we want to convert to square miles:

moCounties <- mutate(moCounties, sq_mi = conv_unit(sq_m, from = "m2", to = "mi2"), .after = sq_km)

Writing the sq_m column to shapefile might be tricky, so its best to do these conversions and then remove the source column before saving!

Symbolizing Points

One thing I wanted to share quickly this week is how we can approach displaying points with ggplot2, since we happen to have some point data to work with. We’ll use the shape and size arguments to make simple adjustments:

ggplot() +
  geom_sf(data = sluPlaces_sf, shape = 18, size = 4)

Some point symbols - those with values 21 through 25 - can be customized with fills and colors as well:

ggplot() +
  geom_sf(data = sluPlaces_sf, shape = 22, size = 6, fill = "#9d4a9d", color = "#5d92e5")

LS0tCnRpdGxlOiAiTWVldGluZyBFeGFtcGxlcywgQ29tcGxldGVkIgphdXRob3I6ICJDaHJpc3RvcGhlciBQcmVuZXIsIFBoLkQuIgpkYXRlOiAnKGByIGZvcm1hdChTeXMudGltZSgpLCAiJUIgJWQsICVZIilgKScKb3V0cHV0OiAKICBnaXRodWJfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0IAphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQotLS0KCiMjIEludHJvZHVjdGlvbgpUaGlzIG5vdGVib29rIHByb3ZpZGVzIGV4YW1wbGVzIG9mOgoKICAqIHdvcmtpbmcgd2l0aCBwcm9qZWN0aW9ucyB3aXRoIGBzdF90cmFuc2Zvcm0oKWAsIAogICogcHJvamVjdGluZyBwb2ludHMgd2l0aCBgc3RfYXNfc2YoKWAsCiAgKiBzYXZpbmcgZ2VvbWV0cmljIGRhdGEgd2l0aCBgc3RfcmVhZCgpYCwKICAqIGNhbGN1bGF0aW5nIGFyZWEgd2l0aCBgc3RfYXJlYSgpYCBhbmQgdGhlIGBtZWFzdXJlbWVudHNgIHBhY2thZ2UsCiAgKiBhbmQgdXNlIHRoZSBgc2hhcGVgIGFuZCBgc2l6ZWAgYXJndW1lbnRzIHdpdGggYGdncGxvdDJgLgoKIyMgRGVwZW5kZW5jaWVzClRoaXMgbm90ZWJvb2sgcmVxdWlyZXMgYSBudW1iZXIgb2YgcGFja2FnZXM6CgpgYGB7ciBsb2FkLXBhY2thZ2VzfQojIHRpZHl2ZXJzZSBwYWNrYWdlcwpsaWJyYXJ5KGRwbHlyKSAgICAgICAjIGRhdGEgY2xlYW5pbmcKbGlicmFyeShnZ3Bsb3QyKSAgICAgIyBzdGF0aWMgbWFwcGluZwpsaWJyYXJ5KHJlYWRyKSAgICAgICAjIHJlYWQvd3JpdGUgdGFidWxhciBkYXRhCgojIHNwYXRpYWwgcGFja2FnZXMKbGlicmFyeShtYXB2aWV3KSAgICAgIyBwcmV2aWV3IHNwYXRpYWwgZGF0YQpsaWJyYXJ5KHNmKSAgICAgICAgICAjIHNwYXRpYWwgZGF0YSB0b29scwpsaWJyYXJ5KHRpZ3JpcykgICAgICAjIGFjY2VzcyBUSUdFUi9saW5lIGRhdGEKCiMgb3RoZXIgcGFja2FnZXMKbGlicmFyeShoZXJlKSAgICAgICAgIyBmaWxlIHBhdGggbWFuYWdlbWVudApsaWJyYXJ5KG1lYXN1cmVtZW50cykgIyB1bml0IGNvbnZlcnNpb24KbGlicmFyeShuYW5pYXIpICAgICAjIG1pc3NpbmcgZGF0YQpgYGAKCiMjIExvYWQgRGF0YQpUaGlzIG5vdGVib29rIHJlcXVpcmVzIG9uZSBzZXQgb2YgZGF0YToKCmBgYHtyIGxvYWQtZGF0YX0Kc2x1UGxhY2VzIDwtIHJlYWRfY3N2KGhlcmUoImRhdGEiLCAiZXhhbXBsZS1kYXRhIiwgInNsdVBsYWNlcy5jc3YiKSkKYGBgCgojIyBEYXRhIENsZWFuaW5nIE5vdGVzCk1ha2Ugc3VyZSB5b3VyIGB4YCBhbmQgYHlgIGNvb3JkaW5hdGUgdmFyaWFibGVzIGFyZSBudW1lcmljIG9yIGRvdWJsZToKCmBgYHtyIGNsYXNzfQpzdHIoc2x1UGxhY2VzKQpgYGAKCklmIHRoZXkgYXJlIG5vdCwgdXNlIGBtdXRhdGUoKWAgd2l0aCBgYXMubnVtZXJpYygpYCB0byBjb252ZXJ0IHRoZW0uCgpOZXh0LCB3ZSBuZWVkIHRvIG1ha2Ugc3VyZSB0aGVyZSBhcmUgbm8gbWlzc2luZyBkYXRhIGluIG91ciBjb29yZGluYXRlIGNvbHVtbnMuIFRoaXMgd2lsbCBjYXVzZSBlcnJvcnMgd2hlbiB3ZSBnbyB0byBwcm9qZWN0IHRoZXNlIGRhdGEuCgpgYGB7ciBtaXNzaW5nfQptaXNzX3Zhcl9zdW1tYXJ5KHNsdVBsYWNlcykKYGBgCgpHcmVhdCEgV2UgaGF2ZSBubyBtaXNzaW5nIGRhdGEgaW4gYGxuZ2AgYW5kIGBsYXRgLiBJZiB3ZSBkaWQsIHdlIHdvdWxkIG5lZWQgdG8gdXNlIGBmaWx0ZXIoKWAgZnJvbSBgZHBseXJgIHRvIHJlbW92ZSB0aG9zZSBtaXNzaW5nIHZhbHVlcy4gQWxzbyBiZSBhd2FyZSBvZiBjb29yZGluYXRlcyB0aGF0IGFyZSBzZXQgdG8gMCwwLiBUaGVzZSBjYW4gc29tZXRpbWVzIGJlIHVzZWQgdG8gaWRlbnRpZnkgbWlzc2luZyBkYXRhIGFzIHdlbGwuCgpgYGB7ciB6ZXJvLWNvb3Jkc30Kc2x1UGxhY2VzICU+JQogIGZpbHRlcihsbmcgPT0gMCB8IGxhdCA9PSAwKQpgYGAKCkFnYWluLCBleGNlbGxlbnQsIG5vIG1pc3NpbmcgZGF0YSEKCiMjIElkZW50aWZ5aW5nIENvb3JkaW5hdGVzCklkZW50aWZ5aW5nIHRoZSBjb29yZGluYXRlIHN5c3RlbSB0aGF0IGBsbmdgIGFuZCBgbGF0YCByZXByZXNlbnQgY2FuIGJlIGNoYWxsZW5naW5nOgoKICAqIFNvbWV0aW1lcywgdGhlICJtZXRhZGF0YSIgdGhhdCB5b3VyIHRhYnVsYXIgZGF0YSBjb21lIHdpdGggKGlmIHRoZXkgY29tZSB3aXRoIGFueSBhdCBhbGwpIHdpbGwgc3RhdGUgd2hhdCBjb29yZGluYXRlIHN5c3RlbSB3YXMgdXNlZC4gVGhpcyBpcyBub3QgdHlwaWNhbCwgaG93ZXZlci4KICAqIERlY2ltYWwgZGVncmVlcyBhcmUgb25lIGNvbW1vbiB3YXkgdGhhdCB3ZSByZXByZXNlbnQgcG9pbnRzLiBJdCdzIGhlbHBmdWwgdG8ga25vdyByb3VnaGx5IHRoZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIG9mIHRoZSBhcmVhIHlvdSBhcmUgd29ya2luZyBpbi4gRm9yIGV4YW1wbGUsIFN0LiBMb3VpcyBpcyByb3VnaGx5IGxvY2F0ZWQgYXQgMzggZGVncmVlcyBub3J0aCBhbmQgOTAuMiBkZWdyZWVzIHdlc3QuIFdoZW4gSSBzZWUgYC05MC4yYCBpbiB0aGUgbG9uZ2l0dWRlIGNvbHVtbiwgdGhpcyBpbW1lZGlhdGVseSBzdWdnZXN0cyB0byBtZSB0aGF0IHdlIGhhdmUgZGVjaW1hbCBkZWdyZWVzIGRhdGEgaGVyZS4gSWYgdGhlIGRhdGEgb3JpZ2luYXRlIGluIHRoZSBVbml0ZWQgU3RhdGVzLCB0aGV5J3JlIHR5cGljYWxseSBlbmNvZGVkIHVzaW5nIE5BRCAxOTgzLiBJZiB0aGV5IGFyZSBpbnRlcm5hdGlvbmFsIGRhdGEsIHRoZXknbGwgdXNlIFdHUyAxOTg0LgogICogQW5vdGhlciBjb21tb24gd2F5IHRvIHJlcHJlc2VudCBkYXRhIGFyZSB3aXRoIFN0YXRlIFBsYW5lIGNvb3JkaW5hdGUgc3lzdGVtcy4gVGhlcmUgaXNuJ3QgYW4gaW50dWl0aXZlIHdheSB0byBpZGVudGlmeSB0aGVzZSBjb29yZGluYXRlcy4gSXQncyBhbHNvIGltcG9ydGFudCB0byBrbm93IHRoYXQgdGhlIFN0YXRlIFBsYW5lIHN5c3RlbSBzaGlwcyB3aXRoIGJvdGggZmVldCBhbmQgbWV0ZXJzIG1lYXN1cmVtZW50IG9wdGlvbnMuIFBvaW50cyBlbmNvZGVkIGluIE1pc3NvdXJpIFN0YXRlIFBsYW5lIEVhc3QsIGZvciBleGFtcGxlLCB3aWxsIGVpdGhlciBiZSBlbmNvZGVkIHVzaW5nIGZlZXQgb3IgbWV0ZXJzLiBVbmZvcnR1bmF0ZWx5LCB0aGUgYHgseWAgcGFpcnMgZm9yIFN0YXRlIFBsYW5lIG1ldGVycyBkbyBub3QgY29ycmVzcG9uZCB0byBTdGF0ZSBQbGFuZSBmZWV0LiBTbywgd2UgbmVlZCB0byBleHBlcmltZW50IGhlcmUuIE1hbnkgbG9jYWwgbXVuaWNpcGFsaXRpZXMgcmVseSBvbiBTdGF0ZSBQbGFuZSBmb3IgbWFwcGluZyBvdXQgb2YgdHJhZGl0aW9uLCBhbmQgaXQncyB0aGVyZWZvcmUgY29tbW9uIHRvIHJ1biBpbnRvIGJvdGggdGhlIGZlZXQgYW5kIG1ldGVycyB2ZXJzaW9ucyB3aGVuIHdvcmtpbmcgd2l0aCBsb2NhbCBkYXRhLgogICogSXQncyBhbHNvIHdvcnRoIHJlbWVtYmVyIHRoYXQgc29tZSB1c2VycyBtYXkgZW5jb2RlIHRoZWlyIHBvaW50cyB1c2luZyBVVE0gem9uZXMuIFRoaXMgaXMgbm90IG5lYXJseSBhcyBjb21tb24gYXMgU3RhdGUgUGxhbmUuIAogIApPbmNlIHdlIGhhdmUgY2FuZGlkYXRlcyBmb3IgcHJvamVjdGluZyBvdXIgcG9pbnRzLCB3ZSBuZWVkIHRvIGlkZW50aWZ5IHRoZSBDUlMvRVBTRyB2YWx1ZXMgKG9yLCBhbHRlcm5hdGl2ZWx5LCB0aGUgUHJvajQgc3RyaW5ncykgdGhhdCBjb3JyZXNwb25kIHRvIG91ciBjb29yZGluYXRlIHN5c3RlbSBjYW5kaWRhdGUocykuIEZvciB0aGlzLCB3ZSdsbCB1c2Ugd2Vic2l0ZXMgbGlrZSBbRVBTRy5pb10oaHR0cHM6Ly9lcHNnLmlvLykgYW5kIFtTcGF0aWFsIFJlZmVyZW5jZV0oaHR0cHM6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy8pLgoKIyMgUHJvamVjdCBEYXRhCkZpcnN0LCB3ZSB3YW50IHRvIGNvbnZlcnQgdGhlc2UgZGF0YSB0byBmcm9tIGEgYHRpYmJsZWAgdG8gYW4gYHNmYCBvYmplY3Qgd2l0aCBgc3RfYXNfc2YoKWAuIFdlIHVzZSB0aGUgYGxuZ2AgdmFyaWFibGUgYXMgb3VyIGB4YCB2YXJpYWJsZSBhbmQgYGxhdGAgYXMgb3VyIGB5YCB2YXJpYWJsZSwgYW5kIHVzZSBgNDI2OWAgZm9yIG91ciBgY3JzYCBhcmd1bWVudCBzaW5jZSB0aGVzZSBkYXRhIGFyZSBpbiBkZWNpbWFsIGRlZ3JlZXMgYW5kIHRoaXMgY29ycmVzcG9uZHMgdG8gdGhlIGxpa2VseSBjb29yZGluYXRlIHN5c3RlbSB3ZSBpZGVudGlmaWVkIGFib3ZlOgoKYGBge3IgcHJvamVjdH0Kc2x1UGxhY2VzX3NmIDwtIHN0X2FzX3NmKHNsdVBsYWNlcywgY29vcmRzID0gYygibG5nIiwgImxhdCIpLCBjcnMgPSA0MjY5KQpgYGAKCk5leHQsIHdlIHdhbnQgdG8gY29uZmlybSB0aGF0IHRoaXMgd29ya2VkOgoKYGBge3IgcHJldmlld30KbWFwdmlldyhzbHVQbGFjZXNfc2YpCmBgYAoKRXhjZWxsZW50IQoKIyMgVHJhbnNmb3JtIE91ciBQcm9qZWN0aW9uCldlJ3ZlIGFscmVhZHkgdXNlZCBgc3RfdHJhbnNmb3JtKClgLCBidXQgbm93IGNhbiBkbyBzbyB3aXRoIHB1cnBvc2UuIEZvciBleGFtcGxlLCB0byBjb252ZXJ0IG91ciBkYXRhIHRvIFN0YXRlIFBsYW5lIChtZXRlcnMpLiBXZSdsbCB1c2UgdGhlIGRhdGEgYmFzZWQgb24gdGhlIDIwMDcgdXBkYXRlIHRvIE5BRCAxOTgzOgoKYGBge3IgY29udmVydC10by1zdGF0ZS1wbGFuZS1tfQpzbHVQbGFjZXNfc3RhdGVQbGFuZSA8LSBzdF90cmFuc2Zvcm0oc2x1UGxhY2VzX3NmLCBjcnMgPSAzNjAxKQpgYGAKCklmIHdlIG5lZWQgb3VyIGRhdGEgaW4gZmVldCwgdGhlcmUgYXJlIGFsc28gb3B0aW9ucy4gSG93ZXZlciwgdGhlc2UgYXJlIEVTUkkgcHJvZHVjdHMgdGhhdCBhcmUgbm90IGluY2x1ZGVkIGluIHRoZSBgc2ZgIHBhY2thZ2UuIEhvdyBkbyB3ZSBrbm93PyBXZSBjYW4gdXNlIHRoZSBFUFNHIHZhbHVlIGAxMDI2OTZgLgoKYGBgcgpzdF90cmFuc2Zvcm0oc2x1UGxhY2VzX3NmLCBjcnMgPSAxMDI2OTYpCmBgYAoKTm90ZSB0aGF0IGVycm9yIGluY2x1ZGVzIHRoaXMgbGFuZ3VhZ2UgLSBgR0RBTCBFcnJvciAxOiBQUk9KOiBwcm9qX2NyZWF0ZV9mcm9tX2RhdGFiYXNlOiBjcnMgbm90IGZvdW5kYC4gRXZlbiBzbywgd2UgY2FuIHN0aWxsIHVzZSB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gYnkgc3BlY2lmeWluZyB0aGUgYFByb2o0YCBzdHJpbmcgdmFsdWUgaW5zdGVhZCBvZiB0aGUgQ1JTIG51bWJlcjoKCmBgYHtyIGNvbnZlcnQtdG8tc3RhdGUtcGxhbmUtZnR9CnNsdVBsYWNlc19zdGF0ZVBsYW5lX2Z0IDwtIHN0X3RyYW5zZm9ybShzbHVQbGFjZXNfc2YsIGNycyA9ICJFU1JJOjEwMjY5NiIpCmBgYAoKU29tZXRpbWVzIHRoaXMgZG9lc24ndCB3b3JrLCBhbmQgc28gaXQgaXMgYWxzbyB1c2VmdWwgdG8ga25vdyBob3cgdG8gYXBwbHkgYFByb2o0YCBzdHJpbmdzIGFzIHdlbGwuCgpgYGB7ciBjb252ZXJ0LXRvLXN0YXRlLXBsYW5lLWZ0LXN0cmluZ30Kc2x1UGxhY2VzX3N0YXRlUGxhbmVfZnQgPC0gc3RfdHJhbnNmb3JtKHNsdVBsYWNlc19zZiwgY3JzID0gIitwcm9qPXRtZXJjICtsYXRfMD0zNS44MzMzMzMzMzMzMzMzNCArbG9uXzA9LTkwLjUgK2s9MC45OTk5MzMzMzMzMzMzMzMzICt4XzA9MjUwMDAwICt5XzA9MCArZWxscHM9R1JTODAgK2RhdHVtPU5BRDgzICt0b19tZXRlcj0wLjMwNDgwMDYwOTYwMTIxOTIgK25vX2RlZnMgIikKYGBgCgpUaGlzIHNob3VsZCBnaXZlIHVzIGNvcnJlY3RseSBwcm9qZWN0ZWQgZGF0YS4gVXNpbmcgdGhpcyB0cmljayB3aXRoIHRoZSBgUHJvajRgIHN0cmluZ3MgYWxzbyB3b3JrcywgYnkgdGhlIHdheSwgd2l0aCBgc3RfYXNfc2YoKWAgYXMgd2VsbC4KCiMjIFdyaXRlIERhdGEKRmluYWxseSwgd2UnbGwgd3JpdGUgb3VyIGRhdGE6CgpgYGB7ciB3cml0ZS1kYXRhfQpzdF93cml0ZShzbHVQbGFjZXNfc3RhdGVQbGFuZSwgaGVyZSgiZGF0YSIsICJleGFtcGxlLWRhdGEiLCAiY2xlYW4tZGF0YSIsICJzbHVQbGFjZXMuc2hwIiksIGRlbGV0ZV9kc24gPSBUUlVFKQpgYGAKClRoZSBgc3Rfd3JpdGUoKWAgZnVuY3Rpb24gaWRlbnRpZmllcyB0aGUgZmlsZSB0eXBlIGZyb20gd2hhdCB5b3UgaW5jbHVkZSBhdCB0aGUgZW5kIG9mIHRoZSBgaGVyZSgpYCBzdGF0ZW1lbnQuIElmIEkgYW0gd29ya2luZyBzb2xlbHkgaW4gYFJgLCBJIHRlbmQgdG8gdXNlIGAuZ2VvanNvbmAgYmVjYXVzZToKCiAgMS4gSXQgZG9lcyBub3QgaW1wb3NlIGxpbWl0cyBvbiBjb2x1bW4gbmFtZXMgb3IgZGF0YSB0eXBlcy4KICAyLiBJdCBpcyBhbiBvcGVuIHN0YW5kYXJkIGZvciBzdG9yaW5nIGRhdGEgdGhhdCBpcyBwbGFpbiB0ZXh0LgogIDMuIEl0IGNhbiBiZSBwcmV2aWV3ZWQgbGl2ZSBvbiBHaXRIdWIuY29tICh3aGVyZSBJIHNoYXJlIG1vc3QgbXkgZGF0YSkuCgpIb3dldmVyLCBpZiB5b3UgYXJlIGdvaW5nIHRvIGJlIHdvcmtpbmcgaW4gdGhlIEVTUkkgZWNvc3lzdGVtLCBzYXZpbmcgZGF0YSBhcyBzaGFwZWZpbGVzIGlzIHN1Z2dlc3RlZC4gTm90ZSB0aGF0ICMxIGFib3ZlIG5lZWRzIHRvIGJlIGFkZHJlc3NlZCAtIGtlZXAgdmFyaWFibGUgbmFtZXMgc2hvcnQgKDggY2hhcmFjdGVycyBvciBsZXNzKSBhbmQgY29udmVydCBiaWcgbnVtYmVycyB0byBzdHJpbmdzIG9yIHJlbW92ZSB0aGVtIGNvbXBsZXRlbHkgYmVmb3JlIHNhdmluZy4KCiMjIFVzaW5nIFByb2plY3Rpb25zIHRvIENhbGN1bGF0ZSBBcmVhCk9uZSBmaW5hbCB0YXNrIHdlIGhhdmUgd2hlbiB3b3JraW5nIHdpdGggcHJvamVjdGlvbnMgaXMgdG8gdXNlIHRoZW0gYXMgdGhlIGJhc2lzIGZvciBtYWtpbmcgY2FsY3VsYXRpb25zLiBPZnRlbiwgdGhpcyBlbnRhaWxzIGNhbGN1bGF0aW5nIGFyZWEgc28gdGhhdCB3ZSBjYW4gdXNlIGl0IGZvciBub3JtYWxpemluZyBvdXIgZGF0YS4gU29tZXRpbWVzIG91ciBkYXRhIGNvbWUgd2l0aCBtZWFzdXJlbWVudHMsIGJ1dCB3ZSBkb24ndCBrbm93IHdoYXQgdGhvc2UgdW5pdHMgYXJlLiBPdGhlciB0aW1lcywgd2UgZG9uJ3QgaGF2ZSBhbiBhcmVhIG1lYXN1cmUgYXQgYWxsLiBDb25zaWRlciB0aGVzZSBkYXRhIGZyb20gYHRpZ3Jpc2A6CgpgYGB7cn0KbW9Db3VudGllcyA8LSBjb3VudGllcyhzdGF0ZSA9IDI5KSAlPiUKICBzZWxlY3QoR0VPSUQsIE5BTUVMU0FELCBBTEFORCwgQVdBVEVSKQpgYGAKClRoZXJlIGFyZSBib3RoIGBBTEFORGAgYW5kIGBBV0FURVJgIGNvbHVtbnMsIGJ1dCBpdCBpc24ndCBpbW1lZGlhdGVseSBjbGVhciB3aGF0IHRoZWlyIHVuaXRzIGFyZSAodGhleSBhcmUgaW4gbWV0ZXJzLCBGWUkpLiBJZiB3ZSB3YW50IHRoZSB0b3RhbCBhcmVhIG9mIHRoZXNlIHVuaXRzLCB3ZSBjYW4gY29tYmluZSBhIGZldyBmdW5jdGlvbnMgZnJvbSBgc2ZgLCBgZHBseXJgLCBhbmQgYG1lYXN1cmVtZW50c2AgdG8gYWNoaWV2ZSBhIGZldyBkaWZmZXJlbnQgb3V0Y29tZXMuIEZpcnN0LCB3ZSdsbCByZS1jYWxjdWxhdGUgdGhlIGFyZWEgYmFzZWQgb24gYSAqKnByb2plY3RlZCoqIGNvb3JkaW5hdGUgc3lzdGVtLiBXZSBuZWVkIHRvIG1vdmUgZnJvbSB0aGUgY3VycmVudCBjb29yZGluYXRlIHN5c3RlbSB0byBhIHByb2plY3RlZCBjb29yZGluYXRlIHN5c3RlbS4gVG8gZ2V0IGEgc2Vuc2Ugb2Ygb3VyIHN0YXJ0aW5nIHBsYWNlLCB3ZSdsbCB1c2UgYHN0X2NycygpYDoKCmBgYHtyfQpzdF9jcnMobW9Db3VudGllcykKYGBgCgpUaGlzIGNvbmZpcm1zIHRoYXQgd2UgYXJlIHVzaW5nIGEgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIHN5c3RlbSAodGhpcyBpcyB0eXBpY2FsIGZvciBUSUdFUi9MaW5lIGRhdGEpLiBXZSdsbCBzd2l0Y2ggdG8gQWxiZXJzIEVxdWFsIEFyZWEgQ29uaWMgZm9yIHRoZSBjb250aWd1b3VzIFVuaXRlZCBTdGF0ZXM6CgpgYGB7cn0KbW9Db3VudGllcyA8LSBzdF90cmFuc2Zvcm0obW9Db3VudGllcywgY3JzID0gIkVTUkk6MTAyMDAzIikKIyBtb0NvdW50aWVzIDwtIHN0X3RyYW5zZm9ybShtb0NvdW50aWVzLCBjcnMgPSAiK3Byb2o9YWVhICtsYXRfMT0yOS41ICtsYXRfMj00NS41ICtsYXRfMD0zNy41ICtsb25fMD0tOTYgK3hfMD0wICt5XzA9MCArZWxscHM9R1JTODAgK2RhdHVtPU5BRDgzICt1bml0cz1tICtub19kZWZzIikKYGBgCgpJJ3ZlIGFsc28gaW5jbHVkZWQgdGhlIGBQcm9qNGAgc3RyaW5nLCBidXQgaXQgaXMgY29tbWVudGVkIG91dC4gTm90aWNlIGhvdyBgK3VuaXRzPTJgIGFwcGVhcnMgaW4gdGhlIGBQcm9qNGAgc3RyaW5nLiBUaGlzIG1lYW5zIGFyZSBkYXRhIGFyZSBpbiBtZXRlcnMuIElmIHdlIHdhbnRlZCB0byByZWNhbGN1bGF0ZSBvdXIgYXJlYSB1bml0cyBtZXRlcnMsIHdlIGNhbiB1c2UgdGhlIGBzdF9hcmVhKClgIGZ1bmN0aW9uIGFuZCB0aGUgYGdlb21ldHJ5YCBjb2x1bW4gdG8gZG8gc286CgpgYGB7cn0KbW9Db3VudGllcyAlPiUKICBzZWxlY3QoLWMoIkFMQU5EIiwgIkFXQVRFUiIpKSAlPiUKICBtdXRhdGUoc3FfbSA9IGFzLm51bWVyaWMoc3RfYXJlYShnZW9tZXRyeSkpLCAuYWZ0ZXIgPSBOQU1FTFNBRCkgJT4lCiAgbXV0YXRlKHNxX2ttID0gY29udl91bml0KHNxX20sIGZyb20gPSAibTIiLCB0byA9ICJrbTIiKSwgLmFmdGVyID0gc3FfbSkgLT4gbW9Db3VudGllcwpgYGAKCldlIGNhbiBkbyB0aGUgc2FtZSB0aGluZyBpZiB3ZSB3YW50IHRvIGNvbnZlcnQgdG8gc3F1YXJlIG1pbGVzOgoKYGBge3J9Cm1vQ291bnRpZXMgPC0gbXV0YXRlKG1vQ291bnRpZXMsIHNxX21pID0gY29udl91bml0KHNxX20sIGZyb20gPSAibTIiLCB0byA9ICJtaTIiKSwgLmFmdGVyID0gc3Ffa20pCmBgYAoKV3JpdGluZyB0aGUgYHNxX21gIGNvbHVtbiB0byBzaGFwZWZpbGUgbWlnaHQgYmUgdHJpY2t5LCBzbyBpdHMgYmVzdCB0byBkbyB0aGVzZSBjb252ZXJzaW9ucyBhbmQgdGhlbiByZW1vdmUgdGhlIHNvdXJjZSBjb2x1bW4gYmVmb3JlIHNhdmluZyEKCiMjIFN5bWJvbGl6aW5nIFBvaW50cwpPbmUgdGhpbmcgSSB3YW50ZWQgdG8gc2hhcmUgcXVpY2tseSB0aGlzIHdlZWsgaXMgaG93IHdlIGNhbiBhcHByb2FjaCBkaXNwbGF5aW5nIHBvaW50cyB3aXRoIGBnZ3Bsb3QyYCwgc2luY2Ugd2UgaGFwcGVuIHRvIGhhdmUgc29tZSBwb2ludCBkYXRhIHRvIHdvcmsgd2l0aC4gV2UnbGwgdXNlIHRoZSBgc2hhcGVgIGFuZCBgc2l6ZWAgYXJndW1lbnRzIHRvIG1ha2Ugc2ltcGxlIGFkanVzdG1lbnRzOgoKYGBge3Igc3ltYm9saXplLXBvaW50c30KZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IHNsdVBsYWNlc19zZiwgc2hhcGUgPSAxOCwgc2l6ZSA9IDQpCmBgYAoKU29tZSBwb2ludCBzeW1ib2xzIC0gdGhvc2Ugd2l0aCB2YWx1ZXMgMjEgdGhyb3VnaCAyNSAtIGNhbiBiZSBjdXN0b21pemVkIHdpdGggZmlsbHMgYW5kIGNvbG9ycyBhcyB3ZWxsOgoKYGBge3Igc3ltYm9saXplLXBvaW50cy1jdXN0b219CmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBzbHVQbGFjZXNfc2YsIHNoYXBlID0gMjIsIHNpemUgPSA2LCBmaWxsID0gIiM5ZDRhOWQiLCBjb2xvciA9ICIjNWQ5MmU1IikKYGBgCgoKCmBgYHtyIG1vdmUtdG8tZG9jcywgaW5jbHVkZT1GQUxTRX0KIyB5b3UgZG8gbmVlZCB0byBpbmNsdWRlIHRoaXMgaW4gYW55IG5vdGVib29rIHlvdSBjcmVhdGUgZm9yIHRoaXMgY2xhc3MKZnM6OmZpbGVfY29weShoZXJlOjpoZXJlKCJleGFtcGxlcyIsICJtZWV0aW5nLTMtMS1leGFtcGxlcy1jb21wbGV0ZS5uYi5odG1sIiksIAogICAgICAgICAgICAgIGhlcmU6OmhlcmUoImRvY3MiLCAiaW5kZXgubmIuaHRtbCIpLCAKICAgICAgICAgICAgICBvdmVyd3JpdGUgPSBUUlVFKQpgYGAKCg==