In this Notebook we will use functions from sf that measure geometric properties (area, length, distance).

  • compute the total area of YNP in meters2 and miles2
  • compute the area of watersheds
  • compute the total length of roads
  • compute the distance between campgrounds and cell towers

Setup

Load the packages we’ll need and set tmap mode to ‘plot’:

library(sf)
library(units)
library(tmap)
tmap_mode("plot")
tmap mode set to plotting

Load dplyr and set name conflict preferences:

library(dplyr)

## Load the conflicted package
library(conflicted)

# Set conflict preferences
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)
conflict_prefer("arrange", "dplyr", quiet = TRUE)

Import the Park Boundary

## Define a convenience variable for UTM Zone 11
epsg_utm11n_nad83 <- 26911

## Import the YNP border
yose_bnd_utm <- st_read(dsn="./data", layer="yose_boundary") |> 
  st_transform(epsg_utm11n_nad83)
Reading layer `yose_boundary' from data source 
  `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_bgs23\notebooks\data' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 11 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -119.8864 ymin: 37.4947 xmax: -119.1964 ymax: 38.18515
Geodetic CRS:  North_American_Datum_1983

Compute the area of the park

Find area with st_area():

yose_area <- yose_bnd_utm |> st_area()
yose_area
3019918537 [m^2]

Find the area in miles2.

## View in square miles
set_units(yose_area, mi^2)
1165.997 [mi^2]

Import the Watersheds

Next import the watershed boundaries.

yose_watersheds_utm <- st_read("./data/yose_watersheds.gpkg", layer="calw221") |> 
  st_transform(epsg_utm11n_nad83) |> 
  select(CALWNUM, HRNAME, RBNAME, HUNAME, CDFSPWNAME, CDFPWSNAME, HUC_8, HUC_8_NAME)
Reading layer `calw221' from data source 
  `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_bgs23\notebooks\data\yose_watersheds.gpkg' 
  using driver `GPKG'
Simple feature collection with 127 features and 12 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1383.82 ymin: -61442.93 xmax: 81596.71 ymax: 26405.66
Projected CRS: NAD83 / California Albers
yose_watersheds_utm |> slice(1:10) |> as_tibble()

tm_shape(yose_watersheds_utm) +
  tm_borders() +
tm_shape(yose_bnd_utm) +
  tm_borders(col="red", lwd=3)

CHALLENGE: Compute the watershed areas

Compute the watershed areas, and add them as a new column in the attribute table. Answer

# Your answer here

CHALLENGE: Find the largest watershed

Find the largest watershed, and report the area in acres. Answer

# Your answer here

Import the Roads

yose_roads_utm <- st_read("./data/yose_roads.gdb", "Yosemite_Roads") |> 
  st_transform(epsg_utm11n_nad83) |> 
  select(RDNAME, RTENUMBER, YOSE_Surface, YOSE_FIRE_ROAD, YOSE_INPARK, YOSE_Type)
Reading layer `Yosemite_Roads' from data source 
  `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_bgs23\notebooks\data\yose_roads.gdb' 
  using driver `OpenFileGDB'
Simple feature collection with 823 features and 40 fields
Geometry type: MULTILINESTRING
Dimension:     XY
Bounding box:  xmin: 234658.1 ymin: 4139484 xmax: 324852.6 ymax: 4250252
Projected CRS: NAD83 / UTM zone 11N

Compute the length of roads and save it in the attribute table:

yose_roads_utm <- yose_roads_utm |> 
  mutate(rd_length = st_length(yose_roads_utm))
yose_roads_utm |> slice(1:10) |> as_tibble()

CHALLENGE: Compute Road Lengths

Find the total length for each type of Road (see YOSE_Type) in miles. Answer

# Your answer here

Import the Campgrounds and Cell Towers

yose_campgrounds_utm <- st_read("./data", layer="yose_poi") |> 
  st_transform(epsg_utm11n_nad83) |> 
  filter(POITYPE == 'Campground') |> 
  select(POINAME)
Reading layer `yose_poi' from data source 
  `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_bgs23\notebooks\data' using driver `ESRI Shapefile'
Simple feature collection with 2720 features and 30 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 246416.2 ymin: 4153717 xmax: 301510.7 ymax: 4208419
Projected CRS: NAD83 / UTM zone 11N
  
yose_celltwrs_utm <- st_read("./data/yose_communications.gdb", "Cell_Towers") |> 
  st_transform(epsg_utm11n_nad83) 
Reading layer `Cell_Towers' from data source 
  `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_bgs23\notebooks\data\yose_communications.gdb' 
  using driver `OpenFileGDB'
Simple feature collection with 5 features and 6 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 251532.4 ymin: 4158756 xmax: 293307.2 ymax: 4194328
Projected CRS: NAD83 / UTM zone 11N

Plot them:

tm_shape(yose_bnd_utm) +
  tm_borders() +
tm_shape(yose_campgrounds_utm) +
  tm_symbols(size = 0.3, col = "blue") +
tm_shape(yose_celltwrs_utm) +
  tm_symbols(size = 0.3, col = "red") 

Compute Distances Between Features - 1 sf data frame

st_distance() can be used to compute distances between features. If you pass it one sf object, it will return a symmetric distance matrix between all pairs of features.

Compute the distance between all pairs of campgrounds:

campgrounds_dist_mat <- st_distance(yose_campgrounds_utm)
campgrounds_dist_mat
Units: [m]
           1         2         3         4         5         6        7         8        9        10         11
1      0.000  8275.796 12628.738 20897.961 24202.415 26366.307 45699.01 26318.172 32505.98 24022.360 27053.5366
2   8275.796     0.000  5634.176 19310.626 20243.499 21669.864 41526.22 18231.678 24574.95 17323.278 20522.7859
3  12628.738  5634.176     0.000 15448.311 15106.794 16243.769 36084.79 14050.939 23230.46 11754.726 14931.6215
4  20897.961 19310.626 15448.311     0.000  6776.104  9775.471 25681.09 23140.503 36074.82 14860.874 15706.6234
5  24202.415 20243.499 15106.794  6776.104     0.000  3014.800 21555.06 18297.281 31962.27  9501.164  9443.6883
6  26366.307 21669.864 16243.769  9775.471  3014.800     0.000 19858.20 16911.114 30774.47  8198.298  7236.6861
7  45699.014 41526.218 36084.793 25681.086 21555.058 19858.200     0.00 32839.145 45873.18 26134.546 23327.6854
8  26318.172 18231.678 14050.939 23140.503 18297.281 16911.114 32839.14     0.000 13892.94  8814.694 10183.5588
9  32505.983 24574.955 23230.456 36074.816 31962.268 30774.468 45873.18 13892.936     0.00 22606.591 24037.3432
10 24022.360 17323.278 11754.726 14860.874  9501.164  8198.298 26134.55  8814.694 22606.59     0.000  3222.2366
11 27053.537 20522.786 14931.621 15706.623  9443.688  7236.686 23327.69 10183.559 24037.34  3222.237     0.0000
12 27174.786 20589.170 15009.085 15972.557  9717.027  7503.431 23439.68  9979.152 23820.38  3268.848   273.4135
13 27088.787 20464.194 14892.172 16067.174  9849.639  7671.851 23645.40  9768.727 23613.03  3140.976   440.3027
14 27616.957 20889.582 15345.375 16750.368 10495.364  8244.331 23684.28  9487.701 23272.44  3604.361  1053.9344
15 22073.083 24539.640 22864.646 11107.445 17786.365 20717.442 32873.41 33489.566 45540.25 25705.918 26791.2639
           12         13         14       15
1  27174.7857 27088.7873 27616.9569 22073.08
2  20589.1697 20464.1936 20889.5823 24539.64
3  15009.0854 14892.1717 15345.3754 22864.65
4  15972.5573 16067.1742 16750.3676 11107.45
5   9717.0274  9849.6393 10495.3635 17786.37
6   7503.4311  7671.8512  8244.3308 20717.44
7  23439.6793 23645.4050 23684.2839 32873.41
8   9979.1521  9768.7266  9487.7011 33489.57
9  23820.3830 23613.0283 23272.4414 45540.25
10  3268.8477  3140.9757  3604.3606 25705.92
11   273.4135   440.3027  1053.9344 26791.26
12     0.0000   212.8079   782.1046 27054.79
13   212.8079     0.0000   692.4943 27142.02
14   782.1046   692.4943     0.0000 27829.09
15 27054.7878 27142.0250 27829.0938     0.00

Compute Distances Between Features - 2 sf data frames

If you pass two sf objects to st_distance(), it will return a n x m matrix containing the distance between all pairs of features.

Compute the distance between all pairs of campgrounds and cell towers:

cgct_dist_mat <- st_distance(yose_campgrounds_utm, yose_celltwrs_utm)
cgct_dist_mat
Units: [m]
            [,1]      [,2]      [,3]      [,4]      [,5]
 [1,] 46219.4109 25884.856 26225.834 33538.244  5903.577
 [2,] 42063.8195 19210.673 19163.004 25491.865  2384.789
 [3,] 36624.3349 13645.182 13737.613 23773.991  7499.216
 [4,] 26168.3039 15613.133 17308.435 36061.840 19627.095
 [5,] 22082.7907  9689.048 11543.363 31666.279 21313.320
 [6,] 20396.4206  7834.371  9723.708 30321.762 22987.367
 [7,]   541.1383 24682.675 25995.385 44830.662 42783.486
 [8,] 33352.8435  9178.442  7376.002 13416.944 20585.520
 [9,] 46361.8785 23070.914 21252.438  1824.674 26769.154
[10,] 26671.4314  1890.457  2646.959 22220.557 19249.627
[11,] 23859.2190  1411.166  2821.810 23427.597 22413.196
[12,] 23970.1116  1403.475  2639.230 23199.118 22497.225
[13,] 24175.5888  1257.224  2426.427 22995.829 22383.935
[14,] 24211.1160  1733.602  2314.224 22611.632 22843.581
[15,] 33272.4448 26622.758 28245.217 45778.512 23754.044

CHALLENGE: Find the closest cell tower

For the first campground (row), which is the closest cell tower? Answer

Hint: use which.min()

# Your answer here

End

Congratulations, you’ve completed the Notebook!

To view your Notebook at HTML, save it (again), then click the ‘Preview’ button in the RStudio toolbar.

LS0tDQp0aXRsZTogIkdlb3Byb2Nlc3NpbmcgMSAtIEdlb21ldHJpYyBNZWFzdXJlbWVudHMiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KDQpJbiB0aGlzIE5vdGVib29rIHdlIHdpbGwgdXNlIGZ1bmN0aW9ucyBmcm9tIGBzZmAgdGhhdCBtZWFzdXJlIGdlb21ldHJpYyBwcm9wZXJ0aWVzIChhcmVhLCBsZW5ndGgsIGRpc3RhbmNlKS4NCg0KLSBjb21wdXRlIHRoZSB0b3RhbCBhcmVhIG9mIFlOUCBpbiBtZXRlcnNeMl4gYW5kIG1pbGVzXjJeICANCi0gY29tcHV0ZSB0aGUgYXJlYSBvZiB3YXRlcnNoZWRzICAgDQotIGNvbXB1dGUgdGhlIHRvdGFsIGxlbmd0aCBvZiByb2FkcyAgDQotIGNvbXB1dGUgdGhlIGRpc3RhbmNlIGJldHdlZW4gY2FtcGdyb3VuZHMgYW5kIGNlbGwgdG93ZXJzICAgIA0KDQojIyBTZXR1cA0KDQpMb2FkIHRoZSBwYWNrYWdlcyB3ZSdsbCBuZWVkIGFuZCBzZXQgdG1hcCBtb2RlIHRvICdwbG90JzoNCg0KYGBge3IgY2h1bmswMX0NCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHVuaXRzKQ0KbGlicmFyeSh0bWFwKQ0KdG1hcF9tb2RlKCJwbG90IikNCmBgYA0KDQpMb2FkIGBkcGx5cmAgYW5kIHNldCBuYW1lIGNvbmZsaWN0IHByZWZlcmVuY2VzOg0KDQpgYGB7ciBjaHVuazAyfQ0KbGlicmFyeShkcGx5cikNCg0KIyMgTG9hZCB0aGUgY29uZmxpY3RlZCBwYWNrYWdlDQpsaWJyYXJ5KGNvbmZsaWN0ZWQpDQoNCiMgU2V0IGNvbmZsaWN0IHByZWZlcmVuY2VzDQpjb25mbGljdF9wcmVmZXIoImZpbHRlciIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigiY291bnQiLCAiZHBseXIiLCBxdWlldCA9IFRSVUUpDQpjb25mbGljdF9wcmVmZXIoInNlbGVjdCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigiYXJyYW5nZSIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmBgYA0KDQojIyBJbXBvcnQgdGhlIFBhcmsgQm91bmRhcnkNCg0KYGBge3IgY2h1bmswM30NCiMjIERlZmluZSBhIGNvbnZlbmllbmNlIHZhcmlhYmxlIGZvciBVVE0gWm9uZSAxMQ0KZXBzZ191dG0xMW5fbmFkODMgPC0gMjY5MTENCg0KIyMgSW1wb3J0IHRoZSBZTlAgYm9yZGVyDQp5b3NlX2JuZF91dG0gPC0gc3RfcmVhZChkc249Ii4vZGF0YSIsIGxheWVyPSJ5b3NlX2JvdW5kYXJ5IikgfD4gDQogIHN0X3RyYW5zZm9ybShlcHNnX3V0bTExbl9uYWQ4MykNCmBgYA0KDQojIyBDb21wdXRlIHRoZSBhcmVhIG9mIHRoZSBwYXJrDQoNCkZpbmQgYXJlYSB3aXRoIGBzdF9hcmVhKClgOg0KDQpgYGB7ciBjaHVuazA0fQ0KeW9zZV9hcmVhIDwtIHlvc2VfYm5kX3V0bSB8PiBzdF9hcmVhKCkNCnlvc2VfYXJlYQ0KYGBgDQoNCkZpbmQgdGhlIGFyZWEgaW4gbWlsZXNeMl4uDQoNCmBgYHtyIGNodW5rMDV9DQojIyBWaWV3IGluIHNxdWFyZSBtaWxlcw0Kc2V0X3VuaXRzKHlvc2VfYXJlYSwgbWleMikNCmBgYA0KDQojIyBJbXBvcnQgdGhlIFdhdGVyc2hlZHMNCg0KTmV4dCBpbXBvcnQgdGhlIHdhdGVyc2hlZCBib3VuZGFyaWVzLg0KDQpgYGB7ciBjaHVuazA2fQ0KeW9zZV93YXRlcnNoZWRzX3V0bSA8LSBzdF9yZWFkKCIuL2RhdGEveW9zZV93YXRlcnNoZWRzLmdwa2ciLCBsYXllcj0iY2FsdzIyMSIpIHw+IA0KICBzdF90cmFuc2Zvcm0oZXBzZ191dG0xMW5fbmFkODMpIHw+IA0KICBzZWxlY3QoQ0FMV05VTSwgSFJOQU1FLCBSQk5BTUUsIEhVTkFNRSwgQ0RGU1BXTkFNRSwgQ0RGUFdTTkFNRSwgSFVDXzgsIEhVQ184X05BTUUpDQoNCnlvc2Vfd2F0ZXJzaGVkc191dG0gfD4gc2xpY2UoMToxMCkgfD4gYXNfdGliYmxlKCkNCg0KdG1fc2hhcGUoeW9zZV93YXRlcnNoZWRzX3V0bSkgKw0KICB0bV9ib3JkZXJzKCkgKw0KdG1fc2hhcGUoeW9zZV9ibmRfdXRtKSArDQogIHRtX2JvcmRlcnMoY29sPSJyZWQiLCBsd2Q9MykNCmBgYA0KDQojIyBDSEFMTEVOR0U6IENvbXB1dGUgdGhlIHdhdGVyc2hlZCBhcmVhcw0KDQpDb21wdXRlIHRoZSB3YXRlcnNoZWQgYXJlYXMsIGFuZCBhZGQgdGhlbSBhcyBhIG5ldyBjb2x1bW4gaW4gdGhlIGF0dHJpYnV0ZSB0YWJsZS4gW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM2RvdjladykNCg0KYGBge3IgY2h1bmswN30NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgQ0hBTExFTkdFOiBGaW5kIHRoZSBsYXJnZXN0IHdhdGVyc2hlZA0KDQpGaW5kIHRoZSBsYXJnZXN0IHdhdGVyc2hlZCwgYW5kIHJlcG9ydCB0aGUgYXJlYSBpbiBhY3Jlcy4gW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM3VhWTRIRSkNCg0KYGBge3IgY2h1bmswOH0NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgSW1wb3J0IHRoZSBSb2Fkcw0KDQpgYGB7ciBjaHVuazA5fQ0KeW9zZV9yb2Fkc191dG0gPC0gc3RfcmVhZCgiLi9kYXRhL3lvc2Vfcm9hZHMuZ2RiIiwgIllvc2VtaXRlX1JvYWRzIikgfD4gDQogIHN0X3RyYW5zZm9ybShlcHNnX3V0bTExbl9uYWQ4MykgfD4gDQogIHNlbGVjdChSRE5BTUUsIFJURU5VTUJFUiwgWU9TRV9TdXJmYWNlLCBZT1NFX0ZJUkVfUk9BRCwgWU9TRV9JTlBBUkssIFlPU0VfVHlwZSkNCmBgYA0KDQpDb21wdXRlIHRoZSBsZW5ndGggb2Ygcm9hZHMgYW5kIHNhdmUgaXQgaW4gdGhlIGF0dHJpYnV0ZSB0YWJsZToNCg0KYGBge3IgY2h1bmsxMH0NCnlvc2Vfcm9hZHNfdXRtIDwtIHlvc2Vfcm9hZHNfdXRtIHw+IA0KICBtdXRhdGUocmRfbGVuZ3RoID0gc3RfbGVuZ3RoKHlvc2Vfcm9hZHNfdXRtKSkNCnlvc2Vfcm9hZHNfdXRtIHw+IHNsaWNlKDE6MTApIHw+IGFzX3RpYmJsZSgpDQpgYGANCg0KIyMgQ0hBTExFTkdFOiBDb21wdXRlIFJvYWQgTGVuZ3Rocw0KDQpGaW5kIHRoZSB0b3RhbCBsZW5ndGggZm9yIGVhY2ggdHlwZSBvZiBSb2FkIChzZWUgWU9TRV9UeXBlKSBpbiBtaWxlcy4gW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM3diYkxiYikNCg0KYGBge3IgY2h1bmsxMX0NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgSW1wb3J0IHRoZSBDYW1wZ3JvdW5kcyBhbmQgQ2VsbCBUb3dlcnMNCg0KYGBge3IgY2h1bmsxMn0NCnlvc2VfY2FtcGdyb3VuZHNfdXRtIDwtIHN0X3JlYWQoIi4vZGF0YSIsIGxheWVyPSJ5b3NlX3BvaSIpIHw+IA0KICBzdF90cmFuc2Zvcm0oZXBzZ191dG0xMW5fbmFkODMpIHw+IA0KICBmaWx0ZXIoUE9JVFlQRSA9PSAnQ2FtcGdyb3VuZCcpIHw+IA0KICBzZWxlY3QoUE9JTkFNRSkNCiAgDQp5b3NlX2NlbGx0d3JzX3V0bSA8LSBzdF9yZWFkKCIuL2RhdGEveW9zZV9jb21tdW5pY2F0aW9ucy5nZGIiLCAiQ2VsbF9Ub3dlcnMiKSB8PiANCiAgc3RfdHJhbnNmb3JtKGVwc2dfdXRtMTFuX25hZDgzKSANCmBgYA0KDQpQbG90IHRoZW06DQoNCmBgYHtyIGNodW5rMTN9DQp0bV9zaGFwZSh5b3NlX2JuZF91dG0pICsNCiAgdG1fYm9yZGVycygpICsNCnRtX3NoYXBlKHlvc2VfY2FtcGdyb3VuZHNfdXRtKSArDQogIHRtX3N5bWJvbHMoc2l6ZSA9IDAuMywgY29sID0gImJsdWUiKSArDQp0bV9zaGFwZSh5b3NlX2NlbGx0d3JzX3V0bSkgKw0KICB0bV9zeW1ib2xzKHNpemUgPSAwLjMsIGNvbCA9ICJyZWQiKSANCmBgYA0KDQojIyBDb21wdXRlIERpc3RhbmNlcyBCZXR3ZWVuIEZlYXR1cmVzIC0gMSBzZiBkYXRhIGZyYW1lDQoNCmBzdF9kaXN0YW5jZSgpYCBjYW4gYmUgdXNlZCB0byBjb21wdXRlIGRpc3RhbmNlcyBiZXR3ZWVuIGZlYXR1cmVzLiBJZiB5b3UgcGFzcyBpdCBvbmUgc2Ygb2JqZWN0LCBpdCB3aWxsIHJldHVybiBhIHN5bW1ldHJpYyBkaXN0YW5jZSBtYXRyaXggYmV0d2VlbiBhbGwgcGFpcnMgb2YgZmVhdHVyZXMuIA0KDQpDb21wdXRlIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGFsbCBwYWlycyBvZiBjYW1wZ3JvdW5kczoNCg0KYGBge3IgY2h1bmsxNH0NCmNhbXBncm91bmRzX2Rpc3RfbWF0IDwtIHN0X2Rpc3RhbmNlKHlvc2VfY2FtcGdyb3VuZHNfdXRtKQ0KY2FtcGdyb3VuZHNfZGlzdF9tYXQNCmBgYA0KDQoNCiMjIENvbXB1dGUgRGlzdGFuY2VzIEJldHdlZW4gRmVhdHVyZXMgLSAyIHNmIGRhdGEgZnJhbWVzDQoNCklmIHlvdSBwYXNzIHR3byBzZiBvYmplY3RzIHRvIGBzdF9kaXN0YW5jZSgpYCwgaXQgd2lsbCByZXR1cm4gYSAqbiB4IG0qIG1hdHJpeCBjb250YWluaW5nIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGFsbCBwYWlycyBvZiBmZWF0dXJlcy4gDQoNCkNvbXB1dGUgdGhlIGRpc3RhbmNlIGJldHdlZW4gYWxsIHBhaXJzIG9mIGNhbXBncm91bmRzIGFuZCBjZWxsIHRvd2VyczoNCg0KYGBge3IgY2h1bmsxNX0NCmNnY3RfZGlzdF9tYXQgPC0gc3RfZGlzdGFuY2UoeW9zZV9jYW1wZ3JvdW5kc191dG0sIHlvc2VfY2VsbHR3cnNfdXRtKQ0KY2djdF9kaXN0X21hdA0KYGBgDQoNCiMjIENIQUxMRU5HRTogRmluZCB0aGUgY2xvc2VzdCBjZWxsIHRvd2VyDQoNCkZvciB0aGUgZmlyc3QgY2FtcGdyb3VuZCAocm93KSwgd2hpY2ggaXMgdGhlIGNsb3Nlc3QgY2VsbCB0b3dlcj8gW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM3dlYlRYcCkNCg0KSGludDogdXNlIGB3aGljaC5taW4oKWANCg0KYGBge3IgY2h1bmsxNn0NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgRW5kDQoNCkNvbmdyYXR1bGF0aW9ucywgeW91J3ZlIGNvbXBsZXRlZCB0aGUgTm90ZWJvb2shIA0KDQpUbyB2aWV3IHlvdXIgTm90ZWJvb2sgYXQgSFRNTCwgc2F2ZSBpdCAoYWdhaW4pLCB0aGVuIGNsaWNrIHRoZSAnUHJldmlldycgYnV0dG9uIGluIHRoZSBSU3R1ZGlvIHRvb2xiYXIuDQoNCg0KDQo=