Why use R as a GIS?
There are many other GIS software, including ArcGIS and the free, cross-platform and open-source QGIS. Why complicate matters by using R, which introduces the need to code and hasn’t the sorts of drop-down menus and other graphical user interfaces found in those other packages?
The answer, for me, concerns integration – the integration of a coding environment with a statistical environment, a graphical environment and so forth. It allows me to write scripts that process the data through all the stages of ‘data wrangling’ from inputting and collating the data, through analysing and fitting models, to communicating and mapping the results. That is why, for me, R provides an ideal environment for geographic data science and analysis.
In fact, it has been years since I last used a ‘proper’ GIS package because everything I need is in one of the many spatial packages for R, many of which are gathered here:
browseURL("https://cran.r-project.org/view=Spatial")
There are lots of them and their number is increasing! It is worth noting that there is a short cut way to installing many of these packages all at once. I have included the code to do so below but with the commands ‘commented out’. There is no need for you run the code here but it can be useful if you wish to install the packages on your own computer in the future.
## Do Not Run
# install.packages("ctv")
# ctv::update.views("Spatial")
Other packages are also grouped by Task View:
browseURL("https://cran.r-project.org/web/views/")
What are vector data?
Vector data are those that are reducible to points. They are usually encoded with a \((x, y)\) coordinate but it could also include height \((x, y, z)\) and even a fourth dimension such as measurement accuracy.
if(!"sf" %in% installed.packages()[,1]) install.packages("sf")
if(!"tmap" %in% installed.packages()[,1]) install.packages("tmap")
require(sf)
require(tmap)
pt <- st_point(c(-2.5879, 51.4545))
pt <- st_sfc(pt, crs = 4326)
tmap_mode("view")
tm_basemap("Stamen.Watercolor") +
tm_shape(pt) +
tm_tiles("Stamen.TonerLabels") +
tm_bubbles(col = "red")
Note that a preview of the various tm_basemaps is available at:
browseURL("http://leaflet-extras.github.io/leaflet-providers/preview/")
In any case, and regardless of the choice of basemap, the object, pt, is of class simple features column (sfc) and contains the geometry of the object (here just a single point feature) and also information about the coordinate reference system (crs):
pt
The coordinate reference was set and can be identified by its EPSG code – you can look it up using the website below:
browseURL("https://www.epsg-registry.org/")
Use the website to find the EPSG code for the British National Grid system.
A line is simply a collection of points, as in this .gpx file of a run route in Lyde Green (to the North East of Bristol):
if(!"tmaptools" %in% installed.packages()[,1]) install.packages("tmaptools")
require(tmaptools)
run_route <- read_GPX("Figure_Of_Height.gpx", layers = "tracks")
run_route
tmap_mode("view")
tm_basemap("OpenTopoMap") +
tm_shape(run_route) +
tm_lines(col = "red", lwd = 4)
A polygon is a collection of points where the start point is the same as the end point. As with points and lines, you can create them yourself…
pts <- rbind(c(1, 5), c(2, 2), c(4, 1), c(4, 4), c(1, 5))
multipoints <- st_sfc(st_multipoint(pts))
multipoints
a_line <- st_sfc(st_multilinestring(list(pts)))
a_line
a_polygon <- st_sfc(st_polygon(list(pts)))
a_polygon
… but it is more likely that you will read them in from other sources:
postcode_sectors <- st_read("england_pcs_2012.shp")
postcode_sectors
tmap_mode("plot")
tm_shape(postcode_sectors) +
tm_polygons(col = "red")+
tm_compass(position = c("right", "top")) +
tm_scale_bar(position = c("left", "bottom")) +
tm_layout(title = "BS Postcode sectors", title.size = 0.85) +
tm_logo("logo.png", position = c("right", "bottom"), height = 2,
margin = 0.01)
Although tmap is often convenient, we don’t have to use it to plot an sf object. The plot command can be used although it doesn’t necessarily behave as we might want it to:
plot(postcode_sectors)
We can infer what is happening by looking at the data
if(!"tidyverse" %in% installed.packages()[,1]) install.packages("tidyverse")
require(tidyverse)
postcode_sectors %>%
st_drop_geometry(.) %>%
glimpse(.)
To just map the boundaries – the geometry – of the map…
postcode_sectors %>%
st_geometry(.) %>%
plot(.)
… which can then be modified in various ways. For example,
postcode_sectors %>%
st_geometry(.) %>%
plot(., col = sf.colors(12, categorical = TRUE), border = "white")
plot(st_geometry(st_centroid(postcode_sectors)), add = TRUE, pch = 21,
bg = "white", cex = 0.5)
Try modifying the code above so that the centroids are represented by dark red squares. You may find the help menu ?points useful to achieve this – scroll down until you can see the ‘pch’ values.
We could also use ggplot2 for mapping. For example,
if(!"ggplot2" %in% installed.packages()[,1]) install.packages("ggplot2")
require(ggplot2)
centroids <- st_centroid(postcode_sectors)
ggplot() +
geom_sf(data = postcode_sectors, fill = "red", col = "white") +
geom_sf(data = centroids)
Another package to be aware of is ggmap as this can be useful for mapping sf objects over a Google basemap. In principle, the following code chunk will produce a .png file showing the run route from earlier layered on top of a Google road map.
if(!"ggmap" %in% installed.packages()[,1]) install.packages("ggmap")
require(ggmap)
png("road_map.png")
basemap <- get_map("bristol", maptype = "roadmap", source = "google",
zoom = 11)
plot(st_transform(st_geometry(run_route), crs = 3857)[1], bgMap = basemap,
expandBB = rep(1.5, 4), col = "red", lwd = 2)
dev.off()
The output should look like:
browseURL("road_map.png")
However, to obtain the Google basemap does require that you have a registered API key. You can obtain this by signing-up at,
browseURL("https://cloud.google.com/maps-platform/")
– click on Get Started. Once you have the key, you can register it,
# register_google(key = "[Your key goes here]", write = TRUE)
## NB I have 'commented out' the code above to prevent it from being run
## You would run it without the initial # and with you key replacing
## [Your key goes here]
If you have registered your API then modify the code above to save the map as a jpeg file instead of a png, and have the run route later on top of a terrain mean. Try the help menus, ?png and ?get_map if you are not sure how to do it.
There is a very useful introduction to plotting simple features using the various approaches at https://r-spatial.github.io/sf/articles/sf5.html.
LS0tCnRpdGxlOiAiTWFwcGluZyBBbmQgTW9kZWxsaW5nIEdlb2dyYXBoaWMgRGF0YSBJbiBSIgpzdWJ0aXRsZTogIlNlc3Npb24gMzogVXNpbmcgUiBhcyBhIEdJUyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uCgpUaGUgYWltIG9mIHRoaXMgc2Vzc2lvbiBpcyB0byBnaXZlIGEgZmxhdm91ciBvZiBob3cgUiBjYW4gYmUgdXNlZCB0byBzdG9yZSwgbWFuaXB1bGF0ZSBhbmQgdG8gYW5hbHlzZSBnZW9ncmFwaGljYWwgZGF0YSAoZGF0YSBmb3Igd2hpY2ggbG9jYXRpb25zIGFyZSByZWNvcmRlZCkuIEZvciBpdCwgd2Ugc2hhbGwgZm9jdXMgb24gdmVjdG9yIGRhdGEsIGRyYXdpbmcgZXh0ZW5zaXZlbHkgb24gdGhlIHBhY2thZ2UgZG9jdW1lbnRhdGlvbiBmb3IgW1NpbXBsZSBGZWF0dXJlcyBmb3IgUl0oaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmL2luZGV4Lmh0bWwpLgoKSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIHJhc3RlciBkYXRhLCBwbGVhc2UgdGFrZSBhIGxvb2sgYXQgdGhlIHJlbGV2YW50IHBhcnRzIG9mIFtHZW9jb21wdXRhdGlvbiB3aXRoIFJdKGh0dHBzOi8vZ2VvY29tcHIucm9iaW5sb3ZlbGFjZS5uZXQvKSBhbmQgW3RoZSBpbnRyb2R1Y3Rpb24gdG8gdGhlIHJhc3RlciBwYWNrYWdlIGF0IHJzcGF0aWFsLm9yZ10oaHR0cHM6Ly9yc3BhdGlhbC5vcmcvcmFzdGVyL3BrZy9pbmRleC5odG1sKS4KCiMjIFdoeSB1c2UgUiBhcyBhIEdJUz8KClRoZXJlIGFyZSBtYW55IG90aGVyIEdJUyBzb2Z0d2FyZSwgaW5jbHVkaW5nIEFyY0dJUyBhbmQgdGhlIGZyZWUsIGNyb3NzLXBsYXRmb3JtIGFuZCBvcGVuLXNvdXJjZSBbUUdJU10oaHR0cHM6Ly9xZ2lzLm9yZy9lbi9zaXRlLykuIFdoeSBjb21wbGljYXRlIG1hdHRlcnMgYnkgdXNpbmcgUiwgd2hpY2ggaW50cm9kdWNlcyB0aGUgbmVlZCB0byBjb2RlIGFuZCBoYXNuJ3QgdGhlIHNvcnRzIG9mIGRyb3AtZG93biBtZW51cyBhbmQgb3RoZXIgZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlcyBmb3VuZCBpbiB0aG9zZSBvdGhlciBwYWNrYWdlcz8KClRoZSBhbnN3ZXIsIGZvciBtZSwgY29uY2VybnMgaW50ZWdyYXRpb24gLS0gdGhlIGludGVncmF0aW9uIG9mIGEgY29kaW5nIGVudmlyb25tZW50IHdpdGggYSBzdGF0aXN0aWNhbCBlbnZpcm9ubWVudCwgYSBncmFwaGljYWwgZW52aXJvbm1lbnQgYW5kIHNvIGZvcnRoLiBJdCBhbGxvd3MgbWUgdG8gd3JpdGUgc2NyaXB0cyB0aGF0IHByb2Nlc3MgdGhlIGRhdGEgdGhyb3VnaCBhbGwgdGhlIHN0YWdlcyBvZiAnZGF0YSB3cmFuZ2xpbmcnIGZyb20gaW5wdXR0aW5nIGFuZCBjb2xsYXRpbmcgdGhlIGRhdGEsIHRocm91Z2ggYW5hbHlzaW5nIGFuZCBmaXR0aW5nIG1vZGVscywgdG8gY29tbXVuaWNhdGluZyBhbmQgbWFwcGluZyB0aGUgcmVzdWx0cy4gVGhhdCBpcyB3aHksIGZvciBtZSwgUiBwcm92aWRlcyBhbiBpZGVhbCBlbnZpcm9ubWVudCBmb3IgZ2VvZ3JhcGhpYyBkYXRhIHNjaWVuY2UgYW5kIGFuYWx5c2lzLgoKSW4gZmFjdCwgaXQgaGFzIGJlZW4geWVhcnMgc2luY2UgSSBsYXN0IHVzZWQgYSAncHJvcGVyJyBHSVMgcGFja2FnZSBiZWNhdXNlIGV2ZXJ5dGhpbmcgSSBuZWVkIGlzIGluIG9uZSBvZiB0aGUgbWFueSBzcGF0aWFsIHBhY2thZ2VzIGZvciBSLCBtYW55IG9mIHdoaWNoIGFyZSBnYXRoZXJlZCBoZXJlOgoKYGBge3J9CmJyb3dzZVVSTCgiaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvdmlldz1TcGF0aWFsIikKYGBgCgpUaGVyZSBhcmUgbG90cyBvZiB0aGVtIGFuZCB0aGVpciBudW1iZXIgaXMgaW5jcmVhc2luZyEgSXQgaXMgd29ydGggbm90aW5nIHRoYXQgdGhlcmUgaXMgYSBzaG9ydCBjdXQgd2F5IHRvIGluc3RhbGxpbmcgbWFueSBvZiB0aGVzZSBwYWNrYWdlcyBhbGwgYXQgb25jZS4gSSBoYXZlIGluY2x1ZGVkIHRoZSBjb2RlIHRvIGRvIHNvIGJlbG93IGJ1dCB3aXRoIHRoZSBjb21tYW5kcyAnY29tbWVudGVkIG91dCcuICpUaGVyZSBpcyBubyBuZWVkIGZvciB5b3UgcnVuIHRoZSBjb2RlIGhlcmUqIGJ1dCBpdCBjYW4gYmUgdXNlZnVsIGlmIHlvdSB3aXNoIHRvIGluc3RhbGwgdGhlIHBhY2thZ2VzIG9uIHlvdXIgb3duIGNvbXB1dGVyIGluIHRoZSBmdXR1cmUuCgpgYGB7cn0KIyMgRG8gTm90IFJ1bgojIGluc3RhbGwucGFja2FnZXMoImN0diIpCiMgY3R2Ojp1cGRhdGUudmlld3MoIlNwYXRpYWwiKQpgYGAKCk90aGVyIHBhY2thZ2VzIGFyZSBhbHNvIGdyb3VwZWQgYnkgVGFzayBWaWV3OgoKYGBge3J9CmJyb3dzZVVSTCgiaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3ZpZXdzLyIpCmBgYAoKCiMjIEZyb20gc3AgdG8gc2YKClVwIHVudGlsIHJlY2VudGx5LCB0aGUgc3AgcGFja2FnZSAoc2hvcnQgZm9yIHNwYXRpYWwpIHdhcyB0aGUgZm91bmRhdGlvbiBmb3IgaGFuZGxpbmcgc3BhdGlhbCBkYXRhIGluIFIsIHByb3ZpZGluZyBjbGFzc2VzIGFuZCBtZXRob2RzIHN1cHBvcnRpbmcgZ2VvZ3JhcGhpYyBkYXRhLCBwcmltYXJpbHkgdmVjdG9yIGRhdGEuIFRoaXMgaXMgYmVpbmcgc3VwZXJzZWRlZCBieSBzZiAoc2hvcnQgZm9yIHNpbXBsZSBmZWF0dXJlcykgdGhhdCBiZXR0ZXIgbWVldHMgdGhlIGZvcm1hbCBzdGFuZGFyZCAoSVNPIDE5MTI1LTE6MjAwNCkgZm9yICJob3cgb2JqZWN0cyBpbiB0aGUgcmVhbCB3b3JsZCBjYW4gYmUgcmVwcmVzZW50ZWQgaW4gY29tcHV0ZXJzLCB3aXRoIGVtcGhhc2lzIG9uIHRoZSBzcGF0aWFsIGdlb21ldHJ5IG9mIHRoZXNlIG9iamVjdHMiIChodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc2YvdmlnbmV0dGVzL3NmMS5odG1sKS4gQXQgdGhlIHRpbWUgb2Ygd3JpdGluZywgYm90aCBhcmUgaW4gb3BlcmF0aW9uIGFuZCBsaWtlbHkgdG8gYmUgc28gZm9yIHNvbWUgdGltZSBiZWNhdXNlIG9mIGhvdyBtYW55IG90aGVyIHBhY2thZ2VzIGJ1aWxkIHVwb24gc3AuIFRoZXJlIHdpbGwgYmUgZWxlbWVudHMgb2YgYm90aCBpbiB0aGlzIHVuaXQgYXMgdGhlcmUgYXJlIGFsc28gaW4gYm9va3Mgc3VjaCBhcyBCcnVuc2RvbidzIGFuZCBDb21iZXIncyAqQW4gSW50cm9kdWN0aW9uIHRvIFIgZm9yIFNwYXRpYWwgQW5hbHlzaXMgJiBNYXBwaW5nKiAoMm5kIGVkaXRpb24pLgoKIyMgV2hhdCBhcmUgdmVjdG9yIGRhdGE/CgpWZWN0b3IgZGF0YSBhcmUgdGhvc2UgdGhhdCBhcmUgcmVkdWNpYmxlIHRvIHBvaW50cy4gVGhleSBhcmUgdXN1YWxseSBlbmNvZGVkIHdpdGggYSAkKHgsIHkpJCBjb29yZGluYXRlIGJ1dCBpdCBjb3VsZCBhbHNvIGluY2x1ZGUgaGVpZ2h0ICQoeCwgeSwgeikkIGFuZCBldmVuIGEgZm91cnRoIGRpbWVuc2lvbiBzdWNoIGFzIG1lYXN1cmVtZW50IGFjY3VyYWN5LgoKYGBge3J9CmlmKCEic2YiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBpbnN0YWxsLnBhY2thZ2VzKCJzZiIpCmlmKCEidG1hcCIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pIGluc3RhbGwucGFja2FnZXMoInRtYXAiKQpyZXF1aXJlKHNmKQpyZXF1aXJlKHRtYXApCnB0IDwtIHN0X3BvaW50KGMoLTIuNTg3OSwgNTEuNDU0NSkpCnB0IDwtIHN0X3NmYyhwdCwgY3JzID0gNDMyNikKdG1hcF9tb2RlKCJ2aWV3IikKdG1fYmFzZW1hcCgiU3RhbWVuLldhdGVyY29sb3IiKSArCiAgdG1fc2hhcGUocHQpICsKICB0bV90aWxlcygiU3RhbWVuLlRvbmVyTGFiZWxzIikgKwogIHRtX2J1YmJsZXMoY29sID0gInJlZCIpCmBgYAoKTm90ZSB0aGF0IGEgcHJldmlldyBvZiB0aGUgdmFyaW91cyB0bV9iYXNlbWFwcyBpcyBhdmFpbGFibGUgYXQ6CgpgYGB7cn0KYnJvd3NlVVJMKCJodHRwOi8vbGVhZmxldC1leHRyYXMuZ2l0aHViLmlvL2xlYWZsZXQtcHJvdmlkZXJzL3ByZXZpZXcvIikKYGBgCgpJbiBhbnkgY2FzZSwgYW5kIHJlZ2FyZGxlc3Mgb2YgdGhlIGNob2ljZSBvZiBiYXNlbWFwLCB0aGUgb2JqZWN0LCBwdCwgaXMgb2YgY2xhc3Mgc2ltcGxlIGZlYXR1cmVzIGNvbHVtbiAoc2ZjKSBhbmQgY29udGFpbnMgdGhlIGdlb21ldHJ5IG9mIHRoZSBvYmplY3QgKGhlcmUganVzdCBhIHNpbmdsZSBwb2ludCBmZWF0dXJlKSBhbmQgYWxzbyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtIChjcnMpOgoKYGBge3J9CnB0CmBgYAoKVGhlIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHdhcyBzZXQgYW5kIGNhbiBiZSBpZGVudGlmaWVkIGJ5IGl0cyBFUFNHIGNvZGUgLS0geW91IGNhbiBsb29rIGl0IHVwIHVzaW5nIHRoZSB3ZWJzaXRlIGJlbG93OgoKYGBge3J9CmJyb3dzZVVSTCgiaHR0cHM6Ly93d3cuZXBzZy1yZWdpc3RyeS5vcmcvIikKYGBgCgpVc2UgdGhlIHdlYnNpdGUgdG8gZmluZCB0aGUgRVBTRyBjb2RlIGZvciB0aGUgQnJpdGlzaCBOYXRpb25hbCBHcmlkIHN5c3RlbS4KCkEgbGluZSBpcyBzaW1wbHkgYSBjb2xsZWN0aW9uIG9mIHBvaW50cywgYXMgaW4gdGhpcyAuZ3B4IGZpbGUgb2YgYSBydW4gcm91dGUgaW4gTHlkZSBHcmVlbiAodG8gdGhlIE5vcnRoIEVhc3Qgb2YgQnJpc3RvbCk6CgpgYGB7cn0KaWYoISJ0bWFwdG9vbHMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwdG9vbHMiKQpyZXF1aXJlKHRtYXB0b29scykKcnVuX3JvdXRlIDwtIHJlYWRfR1BYKCJGaWd1cmVfT2ZfSGVpZ2h0LmdweCIsIGxheWVycyA9ICJ0cmFja3MiKQpydW5fcm91dGUKdG1hcF9tb2RlKCJ2aWV3IikKdG1fYmFzZW1hcCgiT3BlblRvcG9NYXAiKSArCiAgdG1fc2hhcGUocnVuX3JvdXRlKSArCiAgdG1fbGluZXMoY29sID0gInJlZCIsIGx3ZCA9IDQpCmBgYAoKQSBwb2x5Z29uIGlzIGEgY29sbGVjdGlvbiBvZiBwb2ludHMgd2hlcmUgdGhlIHN0YXJ0IHBvaW50IGlzIHRoZSBzYW1lIGFzIHRoZSBlbmQgcG9pbnQuIEFzIHdpdGggcG9pbnRzIGFuZCBsaW5lcywgeW91IGNhbiBjcmVhdGUgdGhlbSB5b3Vyc2VsZi4uLgoKYGBge3J9CnB0cyA8LSByYmluZChjKDEsIDUpLCBjKDIsIDIpLCBjKDQsIDEpLCBjKDQsIDQpLCBjKDEsIDUpKQptdWx0aXBvaW50cyA8LSBzdF9zZmMoc3RfbXVsdGlwb2ludChwdHMpKQptdWx0aXBvaW50cwphX2xpbmUgPC0gc3Rfc2ZjKHN0X211bHRpbGluZXN0cmluZyhsaXN0KHB0cykpKQphX2xpbmUKYV9wb2x5Z29uIDwtIHN0X3NmYyhzdF9wb2x5Z29uKGxpc3QocHRzKSkpCmFfcG9seWdvbgpgYGAKCi4uLiBidXQgaXQgaXMgbW9yZSBsaWtlbHkgdGhhdCB5b3Ugd2lsbCByZWFkIHRoZW0gaW4gZnJvbSBvdGhlciBzb3VyY2VzOgoKYGBge3J9CnBvc3Rjb2RlX3NlY3RvcnMgPC0gc3RfcmVhZCgiZW5nbGFuZF9wY3NfMjAxMi5zaHAiKQpwb3N0Y29kZV9zZWN0b3JzCnRtYXBfbW9kZSgicGxvdCIpCiAgdG1fc2hhcGUocG9zdGNvZGVfc2VjdG9ycykgKwogIHRtX3BvbHlnb25zKGNvbCA9ICJyZWQiKSsKICB0bV9jb21wYXNzKHBvc2l0aW9uID0gYygicmlnaHQiLCAidG9wIikpICsKICB0bV9zY2FsZV9iYXIocG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIkJTIFBvc3Rjb2RlIHNlY3RvcnMiLCB0aXRsZS5zaXplID0gMC44NSkgKwogIHRtX2xvZ28oImxvZ28ucG5nIiwgcG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgaGVpZ2h0ID0gMiwKICAgICAgICAgIG1hcmdpbiA9IDAuMDEpCmBgYAoKQWx0aG91Z2ggdG1hcCBpcyBvZnRlbiBjb252ZW5pZW50LCB3ZSBkb24ndCBoYXZlIHRvIHVzZSBpdCB0byBwbG90IGFuIHNmIG9iamVjdC4gVGhlIHBsb3QgY29tbWFuZCBjYW4gYmUgdXNlZCBhbHRob3VnaCBpdCBkb2Vzbid0IG5lY2Vzc2FyaWx5IGJlaGF2ZSBhcyB3ZSBtaWdodCB3YW50IGl0IHRvOgoKYGBge3J9CnBsb3QocG9zdGNvZGVfc2VjdG9ycykKYGBgCgpXZSBjYW4gaW5mZXIgd2hhdCBpcyBoYXBwZW5pbmcgYnkgbG9va2luZyBhdCB0aGUgZGF0YQoKYGBge3J9CmlmKCEidGlkeXZlcnNlIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSkgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKcmVxdWlyZSh0aWR5dmVyc2UpCnBvc3Rjb2RlX3NlY3RvcnMgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSguKSAlPiUKICBnbGltcHNlKC4pCmBgYAoKVG8ganVzdCBtYXAgdGhlIGJvdW5kYXJpZXMgLS0gdGhlIGdlb21ldHJ5IC0tIG9mIHRoZSBtYXAuLi4KCmBgYHtyfQpwb3N0Y29kZV9zZWN0b3JzICU+JQogIHN0X2dlb21ldHJ5KC4pICU+JQogIHBsb3QoLikKYGBgCgouLi4gd2hpY2ggY2FuIHRoZW4gYmUgbW9kaWZpZWQgaW4gdmFyaW91cyB3YXlzLiBGb3IgZXhhbXBsZSwKCmBgYHtyfQpwb3N0Y29kZV9zZWN0b3JzICU+JQogIHN0X2dlb21ldHJ5KC4pICU+JQogIHBsb3QoLiwgY29sID0gc2YuY29sb3JzKDEyLCBjYXRlZ29yaWNhbCA9IFRSVUUpLCBib3JkZXIgPSAid2hpdGUiKQpwbG90KHN0X2dlb21ldHJ5KHN0X2NlbnRyb2lkKHBvc3Rjb2RlX3NlY3RvcnMpKSwgYWRkID0gVFJVRSwgcGNoID0gMjEsCiAgICAgYmcgPSAid2hpdGUiLCBjZXggPSAwLjUpCmBgYAoKVHJ5IG1vZGlmeWluZyB0aGUgY29kZSBhYm92ZSBzbyB0aGF0IHRoZSBjZW50cm9pZHMgYXJlIHJlcHJlc2VudGVkIGJ5IGRhcmsgcmVkIHNxdWFyZXMuIFlvdSBtYXkgZmluZCB0aGUgaGVscCBtZW51ID9wb2ludHMgdXNlZnVsIHRvIGFjaGlldmUgdGhpcyAtLSBzY3JvbGwgZG93biB1bnRpbCB5b3UgY2FuIHNlZSB0aGUgJ3BjaCcgdmFsdWVzLgoKV2UgY291bGQgYWxzbyB1c2UgZ2dwbG90MiBmb3IgbWFwcGluZy4gRm9yIGV4YW1wbGUsCgpgYGB7cn0KaWYoISJnZ3Bsb3QyIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSkgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCnJlcXVpcmUoZ2dwbG90MikKY2VudHJvaWRzIDwtIHN0X2NlbnRyb2lkKHBvc3Rjb2RlX3NlY3RvcnMpCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBwb3N0Y29kZV9zZWN0b3JzLCBmaWxsID0gInJlZCIsIGNvbCA9ICJ3aGl0ZSIpICsKICBnZW9tX3NmKGRhdGEgPSBjZW50cm9pZHMpCmBgYAoKQW5vdGhlciBwYWNrYWdlIHRvIGJlIGF3YXJlIG9mIGlzIGdnbWFwIGFzIHRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgbWFwcGluZyBzZiBvYmplY3RzIG92ZXIgYSBHb29nbGUgYmFzZW1hcC4gKkluIHByaW5jaXBsZSosIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayB3aWxsIHByb2R1Y2UgYSAucG5nIGZpbGUgc2hvd2luZyB0aGUgcnVuIHJvdXRlIGZyb20gZWFybGllciBsYXllcmVkIG9uIHRvcCBvZiBhIEdvb2dsZSByb2FkIG1hcC4KCmBgYHtyfQppZighImdnbWFwIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSkgaW5zdGFsbC5wYWNrYWdlcygiZ2dtYXAiKQpyZXF1aXJlKGdnbWFwKQpwbmcoInJvYWRfbWFwLnBuZyIpCmJhc2VtYXAgPC0gZ2V0X21hcCgiYnJpc3RvbCIsIG1hcHR5cGUgPSAicm9hZG1hcCIsIHNvdXJjZSA9ICJnb29nbGUiLAogICAgICAgICAgICAgICAgICAgem9vbSA9IDExKQpwbG90KHN0X3RyYW5zZm9ybShzdF9nZW9tZXRyeShydW5fcm91dGUpLCBjcnMgPSAzODU3KVsxXSwgYmdNYXAgPSBiYXNlbWFwLAogICAgIGV4cGFuZEJCID0gcmVwKDEuNSwgNCksIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpkZXYub2ZmKCkKYGBgCgpUaGUgb3V0cHV0IHNob3VsZCBsb29rIGxpa2U6CgpgYGB7cn0KYnJvd3NlVVJMKCJyb2FkX21hcC5wbmciKQpgYGAKCkhvd2V2ZXIsIHRvIG9idGFpbiB0aGUgR29vZ2xlIGJhc2VtYXAgZG9lcyByZXF1aXJlIHRoYXQgeW91IGhhdmUgYSByZWdpc3RlcmVkIEFQSSBrZXkuIFlvdSBjYW4gb2J0YWluIHRoaXMgYnkgc2lnbmluZy11cCBhdCwKCmBgYHtyfQpicm93c2VVUkwoImh0dHBzOi8vY2xvdWQuZ29vZ2xlLmNvbS9tYXBzLXBsYXRmb3JtLyIpCmBgYAoKLS0gY2xpY2sgb24gR2V0IFN0YXJ0ZWQuIE9uY2UgeW91IGhhdmUgdGhlIGtleSwgeW91IGNhbiByZWdpc3RlciBpdCwKCmBgYHtyfQojIHJlZ2lzdGVyX2dvb2dsZShrZXkgPSAiW1lvdXIga2V5IGdvZXMgaGVyZV0iLCB3cml0ZSA9IFRSVUUpCiMjIE5CIEkgaGF2ZSAnY29tbWVudGVkIG91dCcgdGhlIGNvZGUgYWJvdmUgdG8gcHJldmVudCBpdCBmcm9tIGJlaW5nIHJ1bgojIyBZb3Ugd291bGQgcnVuIGl0IHdpdGhvdXQgdGhlIGluaXRpYWwgIyBhbmQgd2l0aCB5b3Uga2V5IHJlcGxhY2luZwojIyBbWW91ciBrZXkgZ29lcyBoZXJlXQpgYGAKCklmIHlvdSBoYXZlIHJlZ2lzdGVyZWQgeW91ciBBUEkgdGhlbiBtb2RpZnkgdGhlIGNvZGUgYWJvdmUgdG8gc2F2ZSB0aGUgbWFwIGFzIGEganBlZyBmaWxlIGluc3RlYWQgb2YgYSBwbmcsIGFuZCBoYXZlIHRoZSBydW4gcm91dGUgbGF0ZXIgb24gdG9wIG9mIGEgdGVycmFpbiBtZWFuLiBUcnkgdGhlIGhlbHAgbWVudXMsID9wbmcgYW5kID9nZXRfbWFwIGlmIHlvdSBhcmUgbm90IHN1cmUgaG93IHRvIGRvIGl0LgoKVGhlcmUgaXMgYSB2ZXJ5IHVzZWZ1bCBpbnRyb2R1Y3Rpb24gdG8gcGxvdHRpbmcgc2ltcGxlIGZlYXR1cmVzIHVzaW5nIHRoZSB2YXJpb3VzIGFwcHJvYWNoZXMgYXQgaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmL2FydGljbGVzL3NmNS5odG1sLiAKCiMjIFNpbXBsZSBGZWF0dXJlcyBmb3IgUgoKRm9yIHRoZSByZXN0IG9mIHRoaXMgc2Vzc2lvbiBwcmltYXJpbHkgd2Ugd2lsbCB3b3JrIHRocm91Z2ggbWF0ZXJpYWwgZGV2ZWxvcGVkIGJ5IEVkemVyIFBlYmVzbWEgYXMgcGFydCBvZiB0aGUgc2ltcGxlIGZlYXR1cmVzIGRvY3VtZW50YXRpb24uIFRvIGJlZ2luLCBob3dldmVyLCByZWFkIHRocm91Z2ggQ2hhcHRlciAyIG9mIExvdmVsYWNlIGV0IGFsLiB1cCB0byAqYnV0IG5vdCBpbmNsdWRpbmcqIFNlY3Rpb24gMi4zOgoKYGBge3J9CmJyb3dzZVVSTCgiaHR0cHM6Ly9nZW9jb21wci5yb2JpbmxvdmVsYWNlLm5ldC9zcGF0aWFsLWNsYXNzLmh0bWwiKQpgYGAKCiMjIE1hbmlwdWxhdGluZyBTaW1wbGUgRmVhdHVyZSBHZW9tZXRyaWVzCgpUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgb3BlbnMgdGhlIHJhdyBtYXJrZG93biBmaWxlIHdyaXR0ZW4gYnkgRWR6ZXIgLS0gaXQgaW5jbHVkZXMgYWxsIHRoZSB2YXJpb3VzIGJsb2NrcyBvZiBjb2RlIGNvbnRhaW5lZCBpbiB0aGUgZG9jdW1lbnRhdGlvbi4gSWYgeW91IHByZWZlciB0byB1c2UgdGhlIHdlYiBkb2N1bWVudCwgaXQgaXMgYXQgaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmL2FydGljbGVzL3NmMy5odG1sIGJ1dCBpdCB3aWxsIHJlcXVpcmUgY3V0dGluZyBhbmQgcGFzdGluZyBiZXR3ZWVuIGl0IGFuZCB0aGUgUiBDb25zb2xlLgoKYGBge3J9CmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yLXNwYXRpYWwvc2YvbWFzdGVyL3ZpZ25ldHRlcy9zZjMuUm1kIiwgInNmMy5SbWQiKQpmaWxlLmVkaXQoInNmMy5SbWQiKQpgYGAKCiMjIE1hbmlwdWxhdGluZyBTaW1wbGUgRmVhdHVyZXMKCmh0dHBzOi8vci1zcGF0aWFsLmdpdGh1Yi5pby9zZi9hcnRpY2xlcy9zZjQuaHRtbAoKYGBge3J9CmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yLXNwYXRpYWwvc2YvbWFzdGVyL3ZpZ25ldHRlcy9zZjQuUm1kIiwgInNmNC5SbWQiKQpmaWxlLmVkaXQoInNmNC5SbWQiKQpgYGAKCklmIHlvdSBzdGlsbCBoYXZlIHRpbWUsIHRoZW4gd29yayB0aHJvdWdoLi4uCgojIyBSZWFkaW5nLCBXcml0aW5nIGFuZCBDb252ZXJ0aW5nIFNpbXBsZSBGZWF0dXJlcwoKaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmL2FydGljbGVzL3NmMi5odG1sCgpgYGB7cn0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Itc3BhdGlhbC9zZi9tYXN0ZXIvdmlnbmV0dGVzL3NmMi5SbWQiLCAic2YyLlJtZCIpCmZpbGUuZWRpdCgic2YyLlJtZCIpCmBgYAoKIyMgQ29uY2x1c2lvbgoKVGhpcyBzZXNzaW9uIGhhcyBzaG93biBob3cgUiBpcyBhYmxlIHRvIGFkb3B0IHRoZSBzYW1lIHNvcnQgb2YgZnVuY3Rpb25hbGl0eSBmb3VuZCBpbiB0eXBpY2FsIEdJUyBzb2Z0d2FyZSwgd2l0aCB0aGVzZSBtb3N0IHJlY2VudGx5IGltcGxlbWVudGVkIHRocm91Z2ggU2ltcGxlIEZlYXR1cmVzIGZvciBSLiBCZWZvcmUgZmluaXNoaW5nLCBpdCBpcyB3b3J0aCBjYXN0aW5nIGFuIGV5ZSBvdmVyIHRoZSBsaXN0IG9mIGZ1bmN0aW9ucyBjb250YWluZWQgaW4gdGhlIFtvbmxpbmUgcmVmZXJlbmNlIGZvciBzZl0oaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmL3JlZmVyZW5jZS9pbmRleC5odG1sKS4gVGhlcmUgYWxzbyBpcyBhIHVzZWZ1bCBbY2hlYXRzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvYmxvYi9tYXN0ZXIvc2YucGRmKSBhdmFpbGFibGUuCgojIyBGdXJ0aGVyIFJlYWRpbmcKCkxvdmVsYWNlIFIsIE5vd29zYWQgSiAmIE11ZW5jaG93IEogKDIwMTkpIFsqR2VvY29tcHV0YXRpb24gd2l0aCBSKl0oaHR0cHM6Ly9nZW9jb21wci5yb2JpbmxvdmVsYWNlLm5ldC8pLCBDaGFwdGVycyAyIHRvIDUKClBlYmVzbWEgUCAoMjAxOCkgU2ltcGxlIEZlYXR1cmVzIGZvciBSOiBTdGFuZGFyZGl6ZWQgU3VwcG9ydCBmb3IgU3BhdGlhbCBWZWN0b3IgRGF0YSBQREYgZG93bmxvYWQuICpUaGUgUiBKb3VybmFsKiAxMCAoMSksIHBwLiA0MzktNDYuIGh0dHBzOi8vam91cm5hbC5yLXByb2plY3Qub3JnL2FyY2hpdmUvMjAxOC9SSi0yMDE4LTAwOS9SSi0yMDE4LTAwOS5wZGYK