Overview

This notebook will demonstrate how to download Cal-Adapt data as rasters.

Setup

Load caladaptR and the other package we’re going to need. (If you haven’t installed these yet, see this setup script).

library(caladaptr)
library(units)
library(dplyr)
library(lubridate)
library(sf)
library(stars)
library(conflicted)
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)


Downloading Rasters

The same API Request object can be used to get raster data if you feed it into ca_getrst_stars().

For additional info on downloading and analyzing rasters, see the 3 articles on Downloading Rasters.

Below we get a raster of observed historic temperature data for the Sierra climate region:

sierra_cap <- ca_loc_aoipreset(type = "climregions", idfld = "name", idval = "Sierra") %>% 
  ca_livneh(TRUE) %>% 
  ca_period("year") %>% 
  ca_cvar("pr") %>% 
  ca_years(start = 1970, end = 2010)

sierra_cap

sierra_cap %>% ca_preflight(check_for = "getrst")

plot(sierra_cap, locagrid = TRUE)


To fetch the data as TIFs, use :

tiff_dir <- "./data"

sierra_tiff_fn <- sierra_cap %>% 
  ca_getrst_stars(out_dir = tiff_dir, mask = TRUE, quiet = TRUE, overwrite = FALSE)


Pro Tip:


ca_getrst_stars() returns a vector of TIF files that were downloaded. To work with them, you next have to load them back into R as stars objects (space-time arrays) using ca_stars_read():

sierra_stars_lst <- ca_stars_read(sierra_tiff_fn)
length(sierra_stars_lst)
sierra_stars_lst[[1]]


Plot

To plot a stars objects, you have to decide which layer(s) to plot. In this case, each layer represents a year from 1970 to 2010. Below we plot 4 of the 40 years:

plot(sierra_stars_lst[[1]] %>% slice(index = seq(1,40,length.out =4), along = "year"), 
     axes = TRUE,
     main = attributes(sierra_stars_lst[[1]])$ca_metadata$slug)

Not sure what the units are? You can double-check by viewing the metadata for the slug from the catalog:

ca_catalog_search("pr_year_livneh")


Subset by Dimension

Subsetting stars objects by a dimension is the counterpart to doing an attribute query in GIS. You can use dplyr filter function as you would a tibble. For example, to get the precipitation for only the 1990s:

sierra_stars_lst[[1]] %>% filter(year >= 1990, year < 2000)

To get the values of a dimension, you can use

(x_range <- sierra_stars_lst[[1]] %>% st_get_dimension_values("x"))
(yr_range <- sierra_stars_lst[[1]] %>% st_get_dimension_values("year"))


You can also use square bracket notation as you would an array. Just put an extra comma at the beginning of the bracket.

sierra_stars_lst[[1]][ , , , which(yr_range >= 1990 & yr_range < 2000)]


Spatial Selection

To spatially select, you can put a SF object in the first slot of the square bracket notation. Let’s get the precipitation history for Yosemite NP:

(ynp_bnd_sf <- st_read("https://raw.githubusercontent.com/ucanr-igis/caladaptr-res/main/geoms/ynp_bnd.geojson"))
plot(ynp_bnd_sf$geometry, axes = TRUE)


To do a spatial selection, put the sf object in the first slot in the square brackets:

ynp_pr_stars <- sierra_stars_lst[[1]][ynp_bnd_sf , , , ]
ynp_pr_stars


Plot to verify:

{plot(ynp_pr_stars %>% slice(index = 1, along = "year"), 
     axes = TRUE, main = "Average Daily Precipitation in YNP (mm), 1970", reset = FALSE)
plot(ynp_bnd_sf$geometry, border = "red", lwd = 2, add = TRUE)}


Convert Values / Raster Algebra

Math operators (like * / - +) do ‘pixelwise’ operations on stars objects, just like regular arrays.

To compute the yearly summary in inches, rather than daily average in mm, we just multiply the stars object by a conversion constant. A mm is 0.0393701 inches, so to go from daily averages in mm to annual total in inches:

(ynp_prtotal_stars <- ynp_pr_stars * 0.0393701 * 365)


Plot the first year to verify:

{plot(ynp_prtotal_stars %>% slice(index = 1, along = "year"), 
     axes = TRUE, main = "Total Precipitation in YNP (in), 1970", reset = FALSE)
plot(ynp_bnd_sf$geometry, border = "red", lwd = 2, add = TRUE)}


Aggregate Across Time and Space

To take the average precip per pixel for the entire time range, use st_apply(). The MARGIN argument should be the indices of the dimensions you want to keep:

ynp_prtotal_stars %>% 
  st_apply(MARGIN = 1:2, FUN = mean) %>% 
  plot(axes = TRUE, main = "Average Total Annual Precipitation in YNP (in), 1970-2010")


To take the average total precip of all pixels in the park per year, we simply change the value of MARGIN to 3 (because year is the third dimension). We also have to add na.rm = TRUE to tell the mean function to ignore all the NA values outside the park boundary:

(ynp_pr_annual_avg_stars <- ynp_prtotal_stars %>% 
  st_apply(MARGIN = 3, FUN = mean, na.rm = TRUE))


stars objects can be converted to data frames:

ynp_pr_annual_avg_df <- ynp_pr_annual_avg_stars %>% as.data.frame
plot(ynp_pr_annual_avg_df, type="b", main = "YNP Total Precip Averaged Across Park (in)")


To plot the distribution of values, we can grab the individual values with [] (just like an array) and make a histogram:

hist(ynp_prtotal_stars, main = "Distribution of Total Annual Precip in YNP")

There is a lot more you can do with rasters, including pixel summaries, combining them into higher dimensional data cubes, spatially mosaicing them, etc. For more info, see the Rasters articles on the website.


Your Turn

Download historic precipitation data for the county where you live or work. [Answer]

## Your answer here
LS0tDQp0aXRsZTogIlJhc3RlcnMiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGNzczogaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHItcmVzL2Fzc2V0cy9uYl9jc3MwMS5jc3MNCiAgICBpbmNsdWRlczoNCiAgICAgIGFmdGVyX2JvZHk6IGh0dHBzOi8vdWNhbnItaWdpcy5naXRodWIuaW8vY2FsYWRhcHRyLXJlcy9hc3NldHMvbmJfZm9vdGVyMDEuaHRtbA0KLS0tDQoNCiMgT3ZlcnZpZXcNCg0KVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byBkb3dubG9hZCBDYWwtQWRhcHQgZGF0YSBhcyByYXN0ZXJzLg0KDQojIFNldHVwDQoNCkxvYWQgY2FsYWRhcHRSIGFuZCB0aGUgb3RoZXIgcGFja2FnZSB3ZSdyZSBnb2luZyB0byBuZWVkLiAoSWYgeW91IGhhdmVuJ3QgaW5zdGFsbGVkIHRoZXNlIHlldCwgc2VlIHRoaXMgW3NldHVwIHNjcmlwdF0oaHR0cHM6Ly9naXRodWIuY29tL3VjYW5yLWlnaXMvY2FsYWRhcHRyLXJlcy9ibG9iL21haW4vZG9jcy93b3Jrc2hvcHMvY2FfaW50cm9fYXByMjIvc2NyaXB0cy9jYWxhZGFwdHJfc2V0dXAuUikpLiANCg0KYGBge3IgY2h1bmswMSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naG9sZCd9DQpsaWJyYXJ5KGNhbGFkYXB0cikNCmxpYnJhcnkodW5pdHMpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShzdGFycykNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNClwNCg0KIyBEb3dubG9hZGluZyBSYXN0ZXJzDQoNClRoZSBzYW1lIEFQSSBSZXF1ZXN0IG9iamVjdCBjYW4gYmUgdXNlZCB0byBnZXQgcmFzdGVyIGRhdGEgaWYgeW91IGZlZWQgaXQgaW50byBgY2FfZ2V0cnN0X3N0YXJzKClgLg0KDQpGb3IgYWRkaXRpb25hbCBpbmZvIG9uIGRvd25sb2FkaW5nIGFuZCBhbmFseXppbmcgcmFzdGVycywgc2VlIHRoZSAzIGFydGljbGVzIG9uIFtEb3dubG9hZGluZyBSYXN0ZXJzXShodHRwczovL3VjYW5yLWlnaXMuZ2l0aHViLmlvL2NhbGFkYXB0ci9hcnRpY2xlcy9yYXN0ZXJzLXB0MS5odG1sKS4NCg0KQmVsb3cgd2UgZ2V0IGEgcmFzdGVyIG9mIG9ic2VydmVkIGhpc3RvcmljIHRlbXBlcmF0dXJlIGRhdGEgZm9yIHRoZSBTaWVycmEgY2xpbWF0ZSByZWdpb246DQoNCmBgYHtyIGNodW5rMDIsIGNhY2hlID0gRkFMU0V9DQpzaWVycmFfY2FwIDwtIGNhX2xvY19hb2lwcmVzZXQodHlwZSA9ICJjbGltcmVnaW9ucyIsIGlkZmxkID0gIm5hbWUiLCBpZHZhbCA9ICJTaWVycmEiKSAlPiUgDQogIGNhX2xpdm5laChUUlVFKSAlPiUgDQogIGNhX3BlcmlvZCgieWVhciIpICU+JSANCiAgY2FfY3ZhcigicHIiKSAlPiUgDQogIGNhX3llYXJzKHN0YXJ0ID0gMTk3MCwgZW5kID0gMjAxMCkNCg0Kc2llcnJhX2NhcA0KDQpzaWVycmFfY2FwICU+JSBjYV9wcmVmbGlnaHQoY2hlY2tfZm9yID0gImdldHJzdCIpDQoNCnBsb3Qoc2llcnJhX2NhcCwgbG9jYWdyaWQgPSBUUlVFKQ0KYGBgDQoNClwNCg0KVG8gZmV0Y2ggdGhlIGRhdGEgYXMgVElGcywgdXNlIDoNCg0KYGBge3IgY2h1bmswM30NCnRpZmZfZGlyIDwtICIuL2RhdGEiDQoNCnNpZXJyYV90aWZmX2ZuIDwtIHNpZXJyYV9jYXAgJT4lIA0KICBjYV9nZXRyc3Rfc3RhcnMob3V0X2RpciA9IHRpZmZfZGlyLCBtYXNrID0gVFJVRSwgcXVpZXQgPSBUUlVFLCBvdmVyd3JpdGUgPSBGQUxTRSkNCmBgYA0KDQpcDQoNCioqUHJvIFRpcDoqKg0KDQogLSB0byBhdm9pZCBkb3dubG9hZGluZyB0aGUgc2FtZSBUSUYgbXVsdGlwbGUgdGltZXMsIHVzZSB0aGUgc2FtZSBvdXRwdXQgZGlyZWN0b3J5IGFuZCBzZXQgYG92ZXJ3cml0ZSA9IEZBTFNFYA0KIA0KXA0KIA0KYGNhX2dldHJzdF9zdGFycygpYCByZXR1cm5zIGEgdmVjdG9yIG9mIFRJRiBmaWxlcyB0aGF0IHdlcmUgZG93bmxvYWRlZC4gVG8gd29yayB3aXRoIHRoZW0sIHlvdSBuZXh0IGhhdmUgdG8gbG9hZCB0aGVtIGJhY2sgaW50byBSIGFzIHN0YXJzIG9iamVjdHMgKHNwYWNlLXRpbWUgYXJyYXlzKSB1c2luZyBgY2Ffc3RhcnNfcmVhZCgpYDoNCg0KYGBge3IgY2h1bmswNCwgcGFnZWQucHJpbnQgPSBGQUxTRX0NCnNpZXJyYV9zdGFyc19sc3QgPC0gY2Ffc3RhcnNfcmVhZChzaWVycmFfdGlmZl9mbikNCmxlbmd0aChzaWVycmFfc3RhcnNfbHN0KQ0Kc2llcnJhX3N0YXJzX2xzdFtbMV1dDQpgYGANCg0KXA0KDQojIFBsb3QNCg0KVG8gcGxvdCBhIHN0YXJzIG9iamVjdHMsIHlvdSBoYXZlIHRvIGRlY2lkZSB3aGljaCBsYXllcihzKSB0byBwbG90LiBJbiB0aGlzIGNhc2UsIGVhY2ggbGF5ZXIgcmVwcmVzZW50cyBhIHllYXIgZnJvbSAxOTcwIHRvIDIwMTAuIEJlbG93IHdlIHBsb3QgNCBvZiB0aGUgNDAgeWVhcnM6DQoNCmBgYHtyIGNodW5rMDUsIGNhY2hlID0gRkFMU0V9DQpwbG90KHNpZXJyYV9zdGFyc19sc3RbWzFdXSAlPiUgc2xpY2UoaW5kZXggPSBzZXEoMSw0MCxsZW5ndGgub3V0ID00KSwgYWxvbmcgPSAieWVhciIpLCANCiAgICAgYXhlcyA9IFRSVUUsDQogICAgIG1haW4gPSBhdHRyaWJ1dGVzKHNpZXJyYV9zdGFyc19sc3RbWzFdXSkkY2FfbWV0YWRhdGEkc2x1ZykNCmBgYA0KDQpOb3Qgc3VyZSB3aGF0IHRoZSB1bml0cyBhcmU/IFlvdSBjYW4gZG91YmxlLWNoZWNrIGJ5IHZpZXdpbmcgdGhlIG1ldGFkYXRhIGZvciB0aGUgc2x1ZyBmcm9tIHRoZSBjYXRhbG9nOg0KDQpgYGB7ciBjaHVuazA2fQ0KY2FfY2F0YWxvZ19zZWFyY2goInByX3llYXJfbGl2bmVoIikNCmBgYA0KDQpcDQoNCiMgU3Vic2V0IGJ5IERpbWVuc2lvbg0KDQpTdWJzZXR0aW5nIHN0YXJzIG9iamVjdHMgYnkgYSBkaW1lbnNpb24gaXMgdGhlIGNvdW50ZXJwYXJ0IHRvIGRvaW5nIGFuIGF0dHJpYnV0ZSBxdWVyeSBpbiBHSVMuIFlvdSBjYW4gdXNlIGRwbHlyIGZpbHRlciBmdW5jdGlvbiBhcyB5b3Ugd291bGQgYSB0aWJibGUuIEZvciBleGFtcGxlLCB0byBnZXQgdGhlIHByZWNpcGl0YXRpb24gZm9yIG9ubHkgdGhlIDE5OTBzOg0KDQpgYGB7ciBjaHVuazA3fQ0Kc2llcnJhX3N0YXJzX2xzdFtbMV1dICU+JSBmaWx0ZXIoeWVhciA+PSAxOTkwLCB5ZWFyIDwgMjAwMCkNCmBgYA0KDQpUbyBnZXQgdGhlIHZhbHVlcyBvZiBhIGRpbWVuc2lvbiwgeW91IGNhbiB1c2UNCg0KYGBge3IgY2h1bmswOH0NCih4X3JhbmdlIDwtIHNpZXJyYV9zdGFyc19sc3RbWzFdXSAlPiUgc3RfZ2V0X2RpbWVuc2lvbl92YWx1ZXMoIngiKSkNCih5cl9yYW5nZSA8LSBzaWVycmFfc3RhcnNfbHN0W1sxXV0gJT4lIHN0X2dldF9kaW1lbnNpb25fdmFsdWVzKCJ5ZWFyIikpDQpgYGANCg0KXA0KDQpZb3UgY2FuIGFsc28gdXNlIHNxdWFyZSBicmFja2V0IG5vdGF0aW9uIGFzIHlvdSB3b3VsZCBhbiBhcnJheS4gSnVzdCBwdXQgYW4gZXh0cmEgY29tbWEgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgYnJhY2tldC4NCg0KYGBge3IgY2h1bmswOX0NCnNpZXJyYV9zdGFyc19sc3RbWzFdXVsgLCAsICwgd2hpY2goeXJfcmFuZ2UgPj0gMTk5MCAmIHlyX3JhbmdlIDwgMjAwMCldDQpgYGANCg0KXA0KDQojIFNwYXRpYWwgU2VsZWN0aW9uDQoNClRvIHNwYXRpYWxseSBzZWxlY3QsIHlvdSBjYW4gcHV0IGEgU0Ygb2JqZWN0IGluIHRoZSBmaXJzdCBzbG90IG9mIHRoZSBzcXVhcmUgYnJhY2tldCBub3RhdGlvbi4gTGV0J3MgZ2V0IHRoZSBwcmVjaXBpdGF0aW9uIGhpc3RvcnkgZm9yIFlvc2VtaXRlIE5QOg0KDQpgYGB7ciBjaHVuazEwfQ0KKHlucF9ibmRfc2YgPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VjYW5yLWlnaXMvY2FsYWRhcHRyLXJlcy9tYWluL2dlb21zL3lucF9ibmQuZ2VvanNvbiIpKQ0KcGxvdCh5bnBfYm5kX3NmJGdlb21ldHJ5LCBheGVzID0gVFJVRSkNCmBgYA0KDQpcDQoNClRvIGRvIGEgc3BhdGlhbCBzZWxlY3Rpb24sIHB1dCB0aGUgc2Ygb2JqZWN0IGluIHRoZSBmaXJzdCBzbG90IGluIHRoZSBzcXVhcmUgYnJhY2tldHM6DQoNCmBgYHtyIGNodW5rMTF9DQp5bnBfcHJfc3RhcnMgPC0gc2llcnJhX3N0YXJzX2xzdFtbMV1dW3lucF9ibmRfc2YgLCAsICwgXQ0KeW5wX3ByX3N0YXJzDQpgYGANCg0KXA0KDQpQbG90IHRvIHZlcmlmeToNCg0KYGBge3IgY2h1bmsxMn0NCntwbG90KHlucF9wcl9zdGFycyAlPiUgc2xpY2UoaW5kZXggPSAxLCBhbG9uZyA9ICJ5ZWFyIiksIA0KICAgICBheGVzID0gVFJVRSwgbWFpbiA9ICJBdmVyYWdlIERhaWx5IFByZWNpcGl0YXRpb24gaW4gWU5QIChtbSksIDE5NzAiLCByZXNldCA9IEZBTFNFKQ0KcGxvdCh5bnBfYm5kX3NmJGdlb21ldHJ5LCBib3JkZXIgPSAicmVkIiwgbHdkID0gMiwgYWRkID0gVFJVRSl9DQpgYGANCg0KXA0KDQojIENvbnZlcnQgVmFsdWVzIC8gUmFzdGVyIEFsZ2VicmENCg0KTWF0aCBvcGVyYXRvcnMgKGxpa2UgYCogLyAtICtgKSBkbyAncGl4ZWx3aXNlJyBvcGVyYXRpb25zIG9uIHN0YXJzIG9iamVjdHMsIGp1c3QgbGlrZSByZWd1bGFyIGFycmF5cy4NCg0KVG8gY29tcHV0ZSB0aGUgeWVhcmx5IHN1bW1hcnkgaW4gaW5jaGVzLCByYXRoZXIgdGhhbiBkYWlseSBhdmVyYWdlIGluIG1tLCB3ZSBqdXN0IG11bHRpcGx5IHRoZSBzdGFycyBvYmplY3QgYnkgYSBjb252ZXJzaW9uIGNvbnN0YW50LiBBIG1tIGlzIDAuMDM5MzcwMSBpbmNoZXMsIHNvIHRvIGdvIGZyb20gZGFpbHkgYXZlcmFnZXMgaW4gbW0gdG8gYW5udWFsIHRvdGFsIGluIGluY2hlczoNCg0KYGBge3IgY2h1bmsxM30NCih5bnBfcHJ0b3RhbF9zdGFycyA8LSB5bnBfcHJfc3RhcnMgKiAwLjAzOTM3MDEgKiAzNjUpDQpgYGANCg0KXA0KDQpQbG90IHRoZSBmaXJzdCB5ZWFyIHRvIHZlcmlmeToNCg0KYGBge3IgY2h1bmsxNH0NCntwbG90KHlucF9wcnRvdGFsX3N0YXJzICU+JSBzbGljZShpbmRleCA9IDEsIGFsb25nID0gInllYXIiKSwgDQogICAgIGF4ZXMgPSBUUlVFLCBtYWluID0gIlRvdGFsIFByZWNpcGl0YXRpb24gaW4gWU5QIChpbiksIDE5NzAiLCByZXNldCA9IEZBTFNFKQ0KcGxvdCh5bnBfYm5kX3NmJGdlb21ldHJ5LCBib3JkZXIgPSAicmVkIiwgbHdkID0gMiwgYWRkID0gVFJVRSl9DQoNCmBgYA0KDQpcDQoNCiMgQWdncmVnYXRlIEFjcm9zcyBUaW1lIGFuZCBTcGFjZQ0KDQpUbyB0YWtlIHRoZSBhdmVyYWdlIHByZWNpcCBwZXIgcGl4ZWwgZm9yIHRoZSBlbnRpcmUgdGltZSByYW5nZSwgdXNlIGBzdF9hcHBseSgpYC4gVGhlIGBNQVJHSU5gIGFyZ3VtZW50IHNob3VsZCBiZSB0aGUgaW5kaWNlcyBvZiB0aGUgZGltZW5zaW9ucyB5b3Ugd2FudCB0byAqa2VlcCo6DQoNCmBgYHtyIGNodW5rMTV9DQp5bnBfcHJ0b3RhbF9zdGFycyAlPiUgDQogIHN0X2FwcGx5KE1BUkdJTiA9IDE6MiwgRlVOID0gbWVhbikgJT4lIA0KICBwbG90KGF4ZXMgPSBUUlVFLCBtYWluID0gIkF2ZXJhZ2UgVG90YWwgQW5udWFsIFByZWNpcGl0YXRpb24gaW4gWU5QIChpbiksIDE5NzAtMjAxMCIpDQpgYGANCg0KXA0KDQpUbyB0YWtlIHRoZSBhdmVyYWdlIHRvdGFsIHByZWNpcCBvZiBhbGwgcGl4ZWxzIGluIHRoZSBwYXJrIHBlciB5ZWFyLCB3ZSBzaW1wbHkgY2hhbmdlIHRoZSB2YWx1ZSBvZiBgTUFSR0lOYCB0byAzIChiZWNhdXNlIHllYXIgaXMgdGhlIHRoaXJkIGRpbWVuc2lvbikuIFdlIGFsc28gaGF2ZSB0byBhZGQgYG5hLnJtID0gVFJVRWAgdG8gdGVsbCB0aGUgYG1lYW5gIGZ1bmN0aW9uIHRvIGlnbm9yZSBhbGwgdGhlIE5BIHZhbHVlcyBvdXRzaWRlIHRoZSBwYXJrIGJvdW5kYXJ5Og0KDQpgYGB7ciBjaHVuazE2fQ0KKHlucF9wcl9hbm51YWxfYXZnX3N0YXJzIDwtIHlucF9wcnRvdGFsX3N0YXJzICU+JSANCiAgc3RfYXBwbHkoTUFSR0lOID0gMywgRlVOID0gbWVhbiwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQpcDQoNCnN0YXJzIG9iamVjdHMgY2FuIGJlIGNvbnZlcnRlZCB0byBkYXRhIGZyYW1lczoNCg0KYGBge3IgY2h1bmsxN30NCnlucF9wcl9hbm51YWxfYXZnX2RmIDwtIHlucF9wcl9hbm51YWxfYXZnX3N0YXJzICU+JSBhcy5kYXRhLmZyYW1lDQpwbG90KHlucF9wcl9hbm51YWxfYXZnX2RmLCB0eXBlPSJiIiwgbWFpbiA9ICJZTlAgVG90YWwgUHJlY2lwIEF2ZXJhZ2VkIEFjcm9zcyBQYXJrIChpbikiKQ0KYGBgDQoNClwNCg0KVG8gcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcywgd2UgY2FuIGdyYWIgdGhlIGluZGl2aWR1YWwgdmFsdWVzIHdpdGggYFtdYCAoanVzdCBsaWtlIGFuIGFycmF5KSBhbmQgbWFrZSBhIGhpc3RvZ3JhbToNCg0KYGBge3IgY2h1bmsxOH0NCmhpc3QoeW5wX3BydG90YWxfc3RhcnMsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIFRvdGFsIEFubnVhbCBQcmVjaXAgaW4gWU5QIikNCmBgYA0KDQoNClRoZXJlIGlzIGEgKipsb3QqKiBtb3JlIHlvdSBjYW4gZG8gd2l0aCByYXN0ZXJzLCBpbmNsdWRpbmcgcGl4ZWwgc3VtbWFyaWVzLCBjb21iaW5pbmcgdGhlbSBpbnRvIGhpZ2hlciBkaW1lbnNpb25hbCBkYXRhIGN1YmVzLCBzcGF0aWFsbHkgbW9zYWljaW5nIHRoZW0sIGV0Yy4gRm9yIG1vcmUgaW5mbywgc2VlIHRoZSBSYXN0ZXJzIGFydGljbGVzIG9uIHRoZSBbd2Vic2l0ZV0oaHR0cHM6Ly91Y2Fuci1pZ2lzLmdpdGh1Yi5pby9jYWxhZGFwdHIvKS4NCiANClwNCg0KIyMjIyBZb3VyIFR1cm4NCg0KRG93bmxvYWQgaGlzdG9yaWMgcHJlY2lwaXRhdGlvbiBkYXRhIGZvciB0aGUgY291bnR5IHdoZXJlIHlvdSBsaXZlIG9yIHdvcmsuIFtbQW5zd2VyXShodHRwczovL2JpdC5seS8zbTdCY1dNKV0NCiANCmBgYHtyIGNodW5rMTksIHBhZ2VkLnByaW50ID0gRkFMU0UsIGNhY2hlPUZBTFNFfQ0KIyMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCiANCg0K