By Valentina Valle Velasco

March 29th, 2020

This R Notebook is in order to teach how to read, filter and plot vector geospatial data.

1st. Introduction.

You should install the simple features (sf) and the tidyverse libraries. You can use the next code:

install.packages(c(“tidyverse”,“sf”))

Next step: We need to load both libraries.

library(tidyverse)
library(sf)

Maybe you will find this when you load the tidyverse library.

## -- Attaching packages ------------------------------------------------------tidyverse 1.3.0  
## v ggplot2 3.3.0     v purrr   0.3.3  
## v tibble  2.1.3     v dplyr   0.8.5  
## v tidyr   1.0.2     v stringr 1.4.0  
## v readr   1.3.1     v forcats 0.5.0
## ---Conflicts--------------------------------------------tidyverse_conflicts()  
## x dplyr::filter() masks stats::filter()                        
## x dplyr::lag()    masks stats::lag()

And this when you load the sf library.

## Linking to GEOS 3.5.1, GDAL 2.2.2, PROJ 4.9.2 

2nd. How to read a vector data.

So we are going to read a shape file that we have downloaded before from DIVA-GIS, it is about Colombian departments. You need to use the next code:

deptos <- read_sf("C:/Users/vale_/Desktop/UNAL/6to semestre/COL_adm/COL_adm1.shp")

The code depends on where you have saved the shape file. If you want to know what is the contents of deptos, you should put this code

head(deptos)

It will show you the top lines of a table.
No, we can review basic functions provided by the sf library. For example, you can use the st_crs function to know what is the coordinate reference system of the vector data stored in deptos object.

st_crs(deptos)

3rd. Using ggplot in order to visualize geospatial data:

We are going to use ggplot for plotting the data. This is the code and it will show you a map of Colombia.

ggplot() + geom_sf(data = deptos)

You can choose any coordinate reference system, but this must be defined for your country. The next code allows change the coordinate reference sytem, for example, to CRS 3978 that is used in Canada.

ggplot() + geom_sf(data = deptos) + coord_sf(crs=st_crs(3978))

Now, we are going to look for the properties of the CRS with EPSG code 32618. If we need that CRS, we have to convert the spatial object from EPSG4326 into EPSG:32618. For that we will use the next code

deptos_utm <- st_transform(deptos, crs = st_crs(32618))

It will appear a table like the follow, after de code:

deptos_utm
Simple feature collection with 32 features and 9 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -245935.3 ymin: -469204.3 xmax: 1407491 ymax: 1763314
CRS:            EPSG:32618
ggplot() + geom_sf(data = deptos_utm)

4th. How to filter geospatial data based on attributes

We are going to filter the data because we are just interested in one departament. Note the following code and change it to match your departament:

magdalena <- deptos %>% filter(NAME_1 == "Magdalena")

Now, we must plot the new object:

ggplot() + geom_sf(data = magdalena)

You can repeat the previous steps to load the Colombian municipalities and filter the Magdalena ones:

munic <- read_sf("C:/Users/vale_/Desktop/UNAL/6to semestre/COL_adm/COL_adm2.shp")

Again, the code depend on where you saved the shape file.

mun_magdalena <- munic %>% filter(NAME_1 == "Magdalena")
ggplot() + geom_sf(data = mun_magdalena)

 mun_magdalena
Simple feature collection with 20 features and 11 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -74.9569 ymin: 8.956799 xmax: -73.6021 ymax: 11.34958
CRS:            4326
magdalena_points <- st_centroid(mun_magdalena)
st_centroid assumes attributes are constant over geometries of xst_centroid does not give correct centroids for longitude/latitude data
magdalena_points <- cbind(mun_magdalena, st_coordinates(st_centroid(mun_magdalena$geometry)))
st_centroid does not give correct centroids for longitude/latitude data

We can improve our plot with the next chunk:

ggplot(magdalena) + geom_sf() + geom_sf(data = magdalena_points, fill = "antiquewhite") + geom_text(data = magdalena_points, aes(x=X, y=Y,label = ID_2), size = 2) + coord_sf(xlim = c(-75, -73.55), ylim = c(8.9, 11.4), expand = FALSE)

library(scales)

Attaching package: 㤼㸱scales㤼㸲

The following object is masked from 㤼㸱package:purrr㤼㸲:

    discard

The following object is masked from 㤼㸱package:readr㤼㸲:

    col_factor
ggplot(magdalena) + geom_sf(data = magdalena_points, aes(x=X, y=Y, fill = ID_2), color = "white", size = 0.25) + geom_text(data = magdalena_points, aes(x=X, y=Y,label = ID_2), size = 2) + theme(aspect.ratio = 1.5)+ scale_fill_distiller(name="ID_2", palette = "YlGn", breaks = pretty_breaks(n = 5))+ labs(title = "Another Map of Magdalena")
Ignoring unknown aesthetics: x, y

You can save it either as a pdf or as a png.

ggsave("magdalena_municipios.pdf")
Saving 7 x 7 in image
ggsave("map_magdalena.png", width = 6, height = 6, dpi = "screen")

5th. How to visualiza data using leaflet

We need to install the required library:

install.packages(“leaflet”)

Then load the library

library(leaflet)

Now, we need to convert from simple features (sf) to spatial points. Use this code:

ant_points <- as(magdalena_points, 'Spatial')

Uncomment the following command to see what is inside the object:

#head(ant_points)

Get area of municipalities:

install.packages(“lwgeom”)

Load the library:

library(lwgeom)
Linking to liblwgeom 3.0.0beta1 r16016, GEOS 3.6.1, PROJ 4.9.3

Attaching package: 㤼㸱lwgeom㤼㸲

The following object is masked from 㤼㸱package:sf㤼㸲:

    st_make_valid

Let’s calculate the area of each municipality (in square meters):

mun_magdalena$area <- st_area(mun_magdalena) #Take care of units

Now, let’s create a new field to store area in square kilometers:

mun_magdalena$km2 <- mun_magdalena$area/(1000000)

Check the output:

mun_magdalena$km2
Units: [m^2]
 [1] 1929.0519 1663.8062
 [3]  279.0380  596.2701
 [5] 1757.0900  798.8020
 [7]  593.2350 1084.1263
 [9]  555.4957  527.5458
[11] 2170.1403 2584.7184
[13]  238.2250  547.6491
[15]  184.8835  417.2063
[17]  271.4884 2211.6646
[19] 2390.0368  746.1562

Again, we need a coversion from simple features to spatial polygons:

ant_mun <- as(mun_magdalena, 'Spatial')

Uncomment the following code to see what is inside the spatial object:

#head(ant_mun)

Now, we are going to prepare the plot:

bins <- c(0, 50, 100, 200, 300, 500, 1000, 2000, Inf)
pal <- colorBin("YlOrRd", domain = ant_mun$km2, bins = bins)
labels <- mun_magdalena$NAME_2
labels
 [1] "Aracataca"                  
 [2] "Ariguaní"                   
 [3] "Cerro de San Antonio"       
 [4] "Chivolo"                    
 [5] "Ciénaga"                    
 [6] "El Banco"                   
 [7] "El Piñón"                   
 [8] "Fundación"                  
 [9] "Guamal"                     
[10] "Pedraza"                    
[11] "Pivijay"                    
[12] "Plato"                      
[13] "Pueblo Viejo"               
[14] "Remolino"                   
[15] "Salamina"                   
[16] "San Sebastián de Buenavista"
[17] "San Zenón"                  
[18] "Santa Ana"                  
[19] "Santa Marta (Dist. Esp.)"   
[20] "Tenerife"                   

Then, we are goning to create the plot:

m <- leaflet(ant_mun) %>% setView(lng = -74.47, lat = 10.5, zoom = 6.5) %>% addPolygons(fillColor = ~pal(km2), weight = 2, opacity = 1, color = "white", dashArray = "3", fillOpacity = 0.7, highlight = highlightOptions(weight = 5, color = "#666", dashArray = "", bringToFront = TRUE), label = labels) %>% addLegend(pal = pal, values = ~km2, opacity = 0.7, title = NULL, position = "bottomleft")

View the plot:

m

There is an simpler way of plotting, yous should use this code:

leaflet() %>% addProviderTiles(providers$Esri.WorldImagery, options = providerTileOptions(opacity = 0.99)) %>% addPolygons(data = ant_mun, popup = ant_mun$NAME_2, stroke = TRUE, fillOpacity = 0.25, smoothFactor = 0.25)

You can try different providers to improve your map. Take advantage of the tab-completion feature to select the preferred base map just scrolling through the ist of 110 providers!
Uncomment the following chunk, complete the code and CREATE a beautiful visualization for the capital of your department. In case of any trouble, use the help of R.

santa_marta <- mun_magdalena %>% filter(NAME_2 == "Santa Marta (Dist. Esp.)")
leaflet() %>% addProviderTiles(providers$Esri.WorldImagery, options = providerTileOptions(opacity = 3)) %>% addPolygons(data = santa_marta, popup = santa_marta$NAME_2, stroke = TRUE, fillOpacity = 0.1, smoothFactor = 0.15) %>% setView(lng = -74.201, lat = 11.247, zoom = 13.5)

Make sure all your codes are correctly written, with the spaces, commas and other signs; you have downloaded all the packages and you have the data and the files you need.

LS0tDQp0aXRsZTogIlJlYWRpbmcsIGZpbHRlcmluZyBhbmQgdmlzdWFsaXppbmcgZ2Vvc3BhdGlhbCB2ZWN0b3IgZGF0YSBpbiBSIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCiMjIyMgQnkgVmFsZW50aW5hIFZhbGxlIFZlbGFzY28gICAgICAgIA0KTWFyY2ggMjl0aCwgMjAyMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNClRoaXMgUiBOb3RlYm9vayBpcyBpbiBvcmRlciB0byB0ZWFjaCBob3cgdG8gcmVhZCwgZmlsdGVyIGFuZCBwbG90IHZlY3RvciBnZW9zcGF0aWFsIGRhdGEuDQoNCiMjIDFzdC4gSW50cm9kdWN0aW9uLg0KWW91IHNob3VsZCBpbnN0YWxsIHRoZSBfX19zaW1wbGUgZmVhdHVyZXNfX18gKHNmKSBhbmQgdGhlIF9fX3RpZHl2ZXJzZV9fXyBsaWJyYXJpZXMuIFlvdSBjYW4gdXNlIHRoZSBuZXh0IGNvZGU6DQoNCg0KIyMjIyBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsInNmIikpDQoNCk5leHQgc3RlcDogV2UgbmVlZCB0byBsb2FkIGJvdGggbGlicmFyaWVzLg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KYGBge3J9DQpsaWJyYXJ5KHNmKQ0KYGBgDQpNYXliZSB5b3Ugd2lsbCBmaW5kIHRoaXMgd2hlbiB5b3UgbG9hZCB0aGUgX3RpZHl2ZXJzZV8gbGlicmFyeS4gICAgICAgICAgICAgICAgICAgDQoNCmBgYA0KIyMgLS0gQXR0YWNoaW5nIHBhY2thZ2VzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXRpZHl2ZXJzZSAxLjMuMCAgDQpgYGANCmBgYA0KIyMgdiBnZ3Bsb3QyIDMuMy4wICAgICB2IHB1cnJyICAgMC4zLjMgIA0KIyMgdiB0aWJibGUgIDIuMS4zICAgICB2IGRwbHlyICAgMC44LjUgIA0KIyMgdiB0aWR5ciAgIDEuMC4yICAgICB2IHN0cmluZ3IgMS40LjAgIA0KIyMgdiByZWFkciAgIDEuMy4xICAgICB2IGZvcmNhdHMgMC41LjANCmBgYA0KYGBgDQojIyAtLS1Db25mbGljdHMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXRpZHl2ZXJzZV9jb25mbGljdHMoKSAgDQojIyB4IGRwbHlyOjpmaWx0ZXIoKSBtYXNrcyBzdGF0czo6ZmlsdGVyKCkgICAgICAgICAgICAgICAgICAgICAgICANCiMjIHggZHBseXI6OmxhZygpICAgIG1hc2tzIHN0YXRzOjpsYWcoKQ0KYGBgDQoNCkFuZCB0aGlzIHdoZW4geW91IGxvYWQgdGhlIHNmIGxpYnJhcnkuICANCmBgYA0KIyMgTGlua2luZyB0byBHRU9TIDMuNS4xLCBHREFMIDIuMi4yLCBQUk9KIDQuOS4yIA0KYGBgDQoNCiMjIDJuZC4gSG93IHRvIHJlYWQgYSB2ZWN0b3IgZGF0YS4NCg0KU28gd2UgYXJlIGdvaW5nIHRvIHJlYWQgYSBzaGFwZSBmaWxlIHRoYXQgd2UgaGF2ZSBkb3dubG9hZGVkIGJlZm9yZSBmcm9tIERJVkEtR0lTLCBpdCBpcyBhYm91dCBDb2xvbWJpYW4gZGVwYXJ0bWVudHMuIFlvdSBuZWVkIHRvIHVzZSB0aGUgbmV4dCBjb2RlOg0KYGBge3J9DQpkZXB0b3MgPC0gcmVhZF9zZigiQzovVXNlcnMvdmFsZV8vRGVza3RvcC9VTkFMLzZ0byBzZW1lc3RyZS9DT0xfYWRtL0NPTF9hZG0xLnNocCIpDQpgYGANClRoZSBjb2RlIGRlcGVuZHMgb24gd2hlcmUgeW91IGhhdmUgc2F2ZWQgdGhlIHNoYXBlIGZpbGUuIElmIHlvdSB3YW50IHRvIGtub3cgd2hhdCBpcyB0aGUgY29udGVudHMgb2YgZGVwdG9zLCB5b3Ugc2hvdWxkIHB1dCB0aGlzIGNvZGUNCmBgYHtyfQ0KaGVhZChkZXB0b3MpDQpgYGANCkl0IHdpbGwgc2hvdyB5b3UgdGhlIHRvcCBsaW5lcyBvZiBhIHRhYmxlLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCk5vLCB3ZSBjYW4gcmV2aWV3IGJhc2ljIGZ1bmN0aW9ucyBwcm92aWRlZCBieSB0aGUgW19fX3NmIGxpYnJhcnlfX19dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gdXNlIHRoZSBfX19zdF9jcnNfX18gZnVuY3Rpb24gdG8ga25vdyB3aGF0IGlzIHRoZSBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW0gb2YgdGhlIHZlY3RvciBkYXRhIHN0b3JlZCBpbiBfZGVwdG9zXyBvYmplY3QuDQpgYGB7cn0NCnN0X2NycyhkZXB0b3MpDQpgYGANCg0KIyMgM3JkLiBVc2luZyBfX2dncGxvdF9fIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSBnZW9zcGF0aWFsIGRhdGE6DQoNCldlIGFyZSBnb2luZyB0byB1c2UgZ2dwbG90IGZvciBwbG90dGluZyB0aGUgZGF0YS4gVGhpcyBpcyB0aGUgY29kZSBhbmQgaXQgd2lsbCBzaG93IHlvdSBhIG1hcCBvZiBDb2xvbWJpYS4NCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBkZXB0b3MpDQpgYGANCg0KWW91IGNhbiBjaG9vc2UgYW55IGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSwgYnV0IHRoaXMgbXVzdCBiZSBkZWZpbmVkIGZvciB5b3VyIGNvdW50cnkuDQpUaGUgbmV4dCBjb2RlIGFsbG93cyBjaGFuZ2UgdGhlIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5dGVtLCBmb3IgZXhhbXBsZSwgdG8gQ1JTIDM5NzggdGhhdCBpcyB1c2VkIGluIENhbmFkYS4NCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBkZXB0b3MpICsgY29vcmRfc2YoY3JzPXN0X2NycygzOTc4KSkNCmBgYA0KTm93LCB3ZSBhcmUgZ29pbmcgdG8gbG9vayBmb3IgdGhlIHByb3BlcnRpZXMgb2YgdGhlIENSUyB3aXRoIEVQU0cgY29kZSAzMjYxOC4gSWYgd2UgbmVlZCB0aGF0IENSUywgd2UgaGF2ZSB0byBjb252ZXJ0IHRoZSBzcGF0aWFsIG9iamVjdCBmcm9tIEVQU0c0MzI2IGludG8gRVBTRzozMjYxOC4gRm9yIHRoYXQgd2Ugd2lsbCB1c2UgdGhlIG5leHQgY29kZQ0KYGBge3J9DQpkZXB0b3NfdXRtIDwtIHN0X3RyYW5zZm9ybShkZXB0b3MsIGNycyA9IHN0X2NycygzMjYxOCkpDQpgYGANCkl0IHdpbGwgYXBwZWFyIGEgdGFibGUgbGlrZSB0aGUgZm9sbG93LCBhZnRlciBkZSBjb2RlOg0KYGBge3J9DQpkZXB0b3NfdXRtDQpgYGANCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBkZXB0b3NfdXRtKQ0KYGBgDQoNCiMjIDR0aC4gSG93IHRvIGZpbHRlciBnZW9zcGF0aWFsIGRhdGEgYmFzZWQgb24gYXR0cmlidXRlcw0KDQpXZSBhcmUgZ29pbmcgdG8gZmlsdGVyIHRoZSBkYXRhIGJlY2F1c2Ugd2UgYXJlIGp1c3QgaW50ZXJlc3RlZCBpbiBvbmUgZGVwYXJ0YW1lbnQuIE5vdGUgdGhlIGZvbGxvd2luZyBjb2RlIGFuZCBjaGFuZ2UgaXQgdG8gbWF0Y2ggeW91ciBkZXBhcnRhbWVudDoNCmBgYHtyfQ0KbWFnZGFsZW5hIDwtIGRlcHRvcyAlPiUgZmlsdGVyKE5BTUVfMSA9PSAiTWFnZGFsZW5hIikNCmBgYA0KTm93LCB3ZSBtdXN0IHBsb3QgdGhlIG5ldyBvYmplY3Q6DQpgYGB7cn0NCmdncGxvdCgpICsgZ2VvbV9zZihkYXRhID0gbWFnZGFsZW5hKQ0KYGBgDQpZb3UgY2FuIHJlcGVhdCB0aGUgcHJldmlvdXMgc3RlcHMgdG8gbG9hZCB0aGUgQ29sb21iaWFuIG11bmljaXBhbGl0aWVzIGFuZCBmaWx0ZXIgdGhlIE1hZ2RhbGVuYSBvbmVzOg0KYGBge3J9DQptdW5pYyA8LSByZWFkX3NmKCJDOi9Vc2Vycy92YWxlXy9EZXNrdG9wL1VOQUwvNnRvIHNlbWVzdHJlL0NPTF9hZG0vQ09MX2FkbTIuc2hwIikNCmBgYA0KQWdhaW4sIHRoZSBjb2RlIGRlcGVuZCBvbiB3aGVyZSB5b3Ugc2F2ZWQgdGhlIHNoYXBlIGZpbGUuDQpgYGB7cn0NCm11bl9tYWdkYWxlbmEgPC0gbXVuaWMgJT4lIGZpbHRlcihOQU1FXzEgPT0gIk1hZ2RhbGVuYSIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSBtdW5fbWFnZGFsZW5hKQ0KYGBgDQpgYGB7cn0NCiBtdW5fbWFnZGFsZW5hDQpgYGANCmBgYHtyfQ0KbWFnZGFsZW5hX3BvaW50cyA8LSBzdF9jZW50cm9pZChtdW5fbWFnZGFsZW5hKQ0KYGBgDQpgYGB7cn0NCm1hZ2RhbGVuYV9wb2ludHMgPC0gY2JpbmQobXVuX21hZ2RhbGVuYSwgc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQobXVuX21hZ2RhbGVuYSRnZW9tZXRyeSkpKQ0KYGBgDQpXZSBjYW4gaW1wcm92ZSBvdXIgcGxvdCB3aXRoIHRoZSBuZXh0IGNodW5rOg0KYGBge3J9DQpnZ3Bsb3QobWFnZGFsZW5hKSArIGdlb21fc2YoKSArIGdlb21fc2YoZGF0YSA9IG1hZ2RhbGVuYV9wb2ludHMsIGZpbGwgPSAiYW50aXF1ZXdoaXRlIikgKyBnZW9tX3RleHQoZGF0YSA9IG1hZ2RhbGVuYV9wb2ludHMsIGFlcyh4PVgsIHk9WSxsYWJlbCA9IElEXzIpLCBzaXplID0gMikgKyBjb29yZF9zZih4bGltID0gYygtNzUsIC03My41NSksIHlsaW0gPSBjKDguOSwgMTEuNCksIGV4cGFuZCA9IEZBTFNFKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtYWdkYWxlbmEpICsgZ2VvbV9zZihkYXRhID0gbWFnZGFsZW5hX3BvaW50cywgYWVzKHg9WCwgeT1ZLCBmaWxsID0gSURfMiksIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuMjUpICsgZ2VvbV90ZXh0KGRhdGEgPSBtYWdkYWxlbmFfcG9pbnRzLCBhZXMoeD1YLCB5PVksbGFiZWwgPSBJRF8yKSwgc2l6ZSA9IDIpICsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMS41KSsgc2NhbGVfZmlsbF9kaXN0aWxsZXIobmFtZT0iSURfMiIsIHBhbGV0dGUgPSAiWWxHbiIsIGJyZWFrcyA9IHByZXR0eV9icmVha3MobiA9IDUpKSsgbGFicyh0aXRsZSA9ICJBbm90aGVyIE1hcCBvZiBNYWdkYWxlbmEiKQ0KYGBgDQpZb3UgY2FuIHNhdmUgaXQgZWl0aGVyIGFzIGEgcGRmIG9yIGFzIGEgcG5nLg0KYGBge3J9DQpnZ3NhdmUoIm1hZ2RhbGVuYV9tdW5pY2lwaW9zLnBkZiIpDQpgYGANCmBgYHtyfQ0KZ2dzYXZlKCJtYXBfbWFnZGFsZW5hLnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgZHBpID0gInNjcmVlbiIpDQpgYGANCiMjIDV0aC4gSG93IHRvIHZpc3VhbGl6YSBkYXRhIHVzaW5nIF9fX2xlYWZsZXRfX18NCldlIG5lZWQgdG8gaW5zdGFsbCB0aGUgcmVxdWlyZWQgbGlicmFyeTogICAgICAgICAgDQoNCiMjIyMgaW5zdGFsbC5wYWNrYWdlcygibGVhZmxldCIpDQoNClRoZW4gbG9hZCB0aGUgbGlicmFyeQ0KYGBge3J9DQpsaWJyYXJ5KGxlYWZsZXQpDQpgYGANCk5vdywgd2UgbmVlZCB0byBjb252ZXJ0IGZyb20gc2ltcGxlIGZlYXR1cmVzIF9fKHNmKV9fIHRvIHNwYXRpYWwgcG9pbnRzLiBVc2UgdGhpcyBjb2RlOg0KYGBge3J9DQphbnRfcG9pbnRzIDwtIGFzKG1hZ2RhbGVuYV9wb2ludHMsICdTcGF0aWFsJykNCmBgYA0KVW5jb21tZW50IHRoZSBmb2xsb3dpbmcgY29tbWFuZCB0byBzZWUgd2hhdCBpcyBpbnNpZGUgdGhlIG9iamVjdDoNCmBgYHtyfQ0KI2hlYWQoYW50X3BvaW50cykNCmBgYA0KR2V0IGFyZWEgb2YgbXVuaWNpcGFsaXRpZXM6DQogICAgDQojIyMjIGluc3RhbGwucGFja2FnZXMoImx3Z2VvbSIpDQoNCkxvYWQgdGhlIGxpYnJhcnk6DQpgYGB7cn0NCmxpYnJhcnkobHdnZW9tKQ0KYGBgDQpMZXQncyBjYWxjdWxhdGUgdGhlIGFyZWEgb2YgZWFjaCBtdW5pY2lwYWxpdHkgKGluIHNxdWFyZSBtZXRlcnMpOg0KYGBge3J9DQptdW5fbWFnZGFsZW5hJGFyZWEgPC0gc3RfYXJlYShtdW5fbWFnZGFsZW5hKSAjVGFrZSBjYXJlIG9mIHVuaXRzDQpgYGANCk5vdywgbGV0J3MgY3JlYXRlIGEgbmV3IGZpZWxkIHRvIHN0b3JlIGFyZWEgaW4gc3F1YXJlIGtpbG9tZXRlcnM6DQpgYGB7cn0NCm11bl9tYWdkYWxlbmEka20yIDwtIG11bl9tYWdkYWxlbmEkYXJlYS8oMTAwMDAwMCkNCmBgYA0KQ2hlY2sgdGhlIG91dHB1dDoNCmBgYHtyfQ0KbXVuX21hZ2RhbGVuYSRrbTINCmBgYA0KQWdhaW4sIHdlIG5lZWQgYSBjb3ZlcnNpb24gZnJvbSBfc2ltcGxlIGZlYXR1cmVzXyB0byBfc3BhdGlhbCBwb2x5Z29uc186DQpgYGB7cn0NCmFudF9tdW4gPC0gYXMobXVuX21hZ2RhbGVuYSwgJ1NwYXRpYWwnKQ0KYGBgDQpVbmNvbW1lbnQgdGhlIGZvbGxvd2luZyBjb2RlIHRvIHNlZSB3aGF0IGlzIGluc2lkZSB0aGUgc3BhdGlhbCBvYmplY3Q6DQpgYGB7cn0NCiNoZWFkKGFudF9tdW4pDQpgYGANCk5vdywgd2UgYXJlIGdvaW5nIHRvIHByZXBhcmUgdGhlIHBsb3Q6DQpgYGB7cn0NCmJpbnMgPC0gYygwLCA1MCwgMTAwLCAyMDAsIDMwMCwgNTAwLCAxMDAwLCAyMDAwLCBJbmYpDQpgYGANCmBgYHtyfQ0KcGFsIDwtIGNvbG9yQmluKCJZbE9yUmQiLCBkb21haW4gPSBhbnRfbXVuJGttMiwgYmlucyA9IGJpbnMpDQpgYGANCmBgYHtyfQ0KbGFiZWxzIDwtIG11bl9tYWdkYWxlbmEkTkFNRV8yDQpgYGANCmBgYHtyfQ0KbGFiZWxzDQpgYGANClRoZW4sIHdlIGFyZSBnb25pbmcgdG8gY3JlYXRlIHRoZSBwbG90Og0KYGBge3J9DQptIDwtIGxlYWZsZXQoYW50X211bikgJT4lIHNldFZpZXcobG5nID0gLTc0LjQ3LCBsYXQgPSAxMC41LCB6b29tID0gNi41KSAlPiUgYWRkUG9seWdvbnMoZmlsbENvbG9yID0gfnBhbChrbTIpLCB3ZWlnaHQgPSAyLCBvcGFjaXR5ID0gMSwgY29sb3IgPSAid2hpdGUiLCBkYXNoQXJyYXkgPSAiMyIsIGZpbGxPcGFjaXR5ID0gMC43LCBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDUsIGNvbG9yID0gIiM2NjYiLCBkYXNoQXJyYXkgPSAiIiwgYnJpbmdUb0Zyb250ID0gVFJVRSksIGxhYmVsID0gbGFiZWxzKSAlPiUgYWRkTGVnZW5kKHBhbCA9IHBhbCwgdmFsdWVzID0gfmttMiwgb3BhY2l0eSA9IDAuNywgdGl0bGUgPSBOVUxMLCBwb3NpdGlvbiA9ICJib3R0b21sZWZ0IikNCmBgYA0KVmlldyB0aGUgcGxvdDoNCmBgYHtyfQ0KbQ0KYGBgDQpUaGVyZSBpcyBhbiBzaW1wbGVyIHdheSBvZiBwbG90dGluZywgeW91cyBzaG91bGQgdXNlIHRoaXMgY29kZToNCmBgYHtyfQ0KbGVhZmxldCgpICU+JSBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkSW1hZ2VyeSwgb3B0aW9ucyA9IHByb3ZpZGVyVGlsZU9wdGlvbnMob3BhY2l0eSA9IDAuOTkpKSAlPiUgYWRkUG9seWdvbnMoZGF0YSA9IGFudF9tdW4sIHBvcHVwID0gYW50X211biROQU1FXzIsIHN0cm9rZSA9IFRSVUUsIGZpbGxPcGFjaXR5ID0gMC4yNSwgc21vb3RoRmFjdG9yID0gMC4yNSkNCmBgYA0KWW91IGNhbiB0cnkgZGlmZmVyZW50IHByb3ZpZGVycyB0byBpbXByb3ZlIHlvdXIgbWFwLiBUYWtlIGFkdmFudGFnZSBvZiB0aGUgdGFiLWNvbXBsZXRpb24gZmVhdHVyZSB0byBzZWxlY3QgdGhlIHByZWZlcnJlZCBiYXNlIG1hcCBqdXN0IHNjcm9sbGluZyB0aHJvdWdoIHRoZSBpc3Qgb2YgMTEwIHByb3ZpZGVycyEgICAgICAgICAgICAgICAgICAgICAgICAgDQpVbmNvbW1lbnQgdGhlIGZvbGxvd2luZyBjaHVuaywgX19fY29tcGxldGUgdGhlIGNvZGVfX18gYW5kIF9fQ1JFQVRFX18gYSBiZWF1dGlmdWwgdmlzdWFsaXphdGlvbiBmb3IgdGhlIGNhcGl0YWwgb2YgeW91ciBkZXBhcnRtZW50LiBJbiBjYXNlIG9mIGFueSB0cm91YmxlLCB1c2UgdGhlIGhlbHAgb2YgUi4NCmBgYHtyfQ0Kc2FudGFfbWFydGEgPC0gbXVuX21hZ2RhbGVuYSAlPiUgZmlsdGVyKE5BTUVfMiA9PSAiU2FudGEgTWFydGEgKERpc3QuIEVzcC4pIikNCmBgYA0KYGBge3J9DQpsZWFmbGV0KCkgJT4lIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuV29ybGRJbWFnZXJ5LCBvcHRpb25zID0gcHJvdmlkZXJUaWxlT3B0aW9ucyhvcGFjaXR5ID0gMykpICU+JSBhZGRQb2x5Z29ucyhkYXRhID0gc2FudGFfbWFydGEsIHBvcHVwID0gc2FudGFfbWFydGEkTkFNRV8yLCBzdHJva2UgPSBUUlVFLCBmaWxsT3BhY2l0eSA9IDAuMSwgc21vb3RoRmFjdG9yID0gMC4xNSkgJT4lIHNldFZpZXcobG5nID0gLTc0LjIwMSwgbGF0ID0gMTEuMjQ3LCB6b29tID0gMTMuNSkNCmBgYA0KTWFrZSBzdXJlIGFsbCB5b3VyIGNvZGVzIGFyZSBjb3JyZWN0bHkgd3JpdHRlbiwgd2l0aCB0aGUgc3BhY2VzLCBjb21tYXMgYW5kIG90aGVyIHNpZ25zOyB5b3UgaGF2ZSBkb3dubG9hZGVkIGFsbCB0aGUgcGFja2FnZXMgYW5kIHlvdSBoYXZlIHRoZSBkYXRhIGFuZCB0aGUgZmlsZXMgeW91IG5lZWQuDQo=