This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

This document is to summarise some key features and geo-processing in Geocomputation with R

Packages used

library(sf)
package 㤼㸱sf㤼㸲 was built under R version 3.6.3Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(spData)
package 㤼㸱spData㤼㸲 was built under R version 3.6.3To access larger datasets in this package, install the spDataLarge package with:
`install.packages('spDataLarge', repos='https://nowosad.github.io/drat/', type='source')`
library(raster)
package 㤼㸱raster㤼㸲 was built under R version 3.6.3
library(ggplot2)
package 㤼㸱ggplot2㤼㸲 was built under R version 3.6.3RStudio Community is a great place to get help: https://community.rstudio.com/c/tidyverse

This section is dedicated to the use of sf package which covers a wide range of geo-processing tools for vector data. it provides a consistent and easy-to-use procedure to analyse and process vector data as well as working well with dataframe. If you like, please have a look here

vignette(package = “sf”) # see which vignettes are available vignette(“sf1”) # an introduction to the package

Loading and checking data

world data contain different features of the world country such as population, area and country names. This dataset is stored as sf object. If we want to convert sp or other shapefiles to sf, we can use function st_as_sf()

names(world) # Names of different countries 
 [1] "iso_a2"    "name_long" "continent" "region_un" "subregion" "type"      "area_km2"  "pop"       "lifeExp"   "gdpPercap"
[11] "geom"     
head(world) # Check first few rows of dataf 
Simple feature collection with 6 features and 10 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -180 ymin: -18.28799 xmax: 180 ymax: 83.23324
CRS:            EPSG:4326
plot(world)
plotting the first 9 out of 10 attributes; use max.plot = 10 to plot all

*Choosing to plot only population variable and Asia

sf_asia<-world[world$continent=="Asia",]

plot(sf_asia["pop"])

sf_asia_union<-st_union(sf_asia)

# Plot world map

plot(world["pop"], reset=F)

plot(sf_asia_union,add=T,col="2")

plot(world["continent"], reset = FALSE)
cex = sqrt(world$pop) / 10000
world_cents = st_centroid(world, of_largest = TRUE)
st_centroid assumes attributes are constant over geometries of xst_centroid does not give correct centroids for longitude/latitude data
plot(st_geometry(world_cents), add = TRUE, cex = cex)

Create points and dataframe using sf

lnd_point<-rbind(c(0.1,51.5),c(0.2,52.3))

lnd_point = st_multipoint(lnd_point)                 # sfg object

lnd_geom = st_sfc(lnd_point, crs = 4326)           # sfc object

lnd_attrib = data.frame(                           # data.frame object
  name = c("London","HN"), temperature = c(25,27),
  date = as.Date(c("2017-06-21","2018-02-01"))
  )
lnd_sf = st_sf(lnd_attrib, geometry = lnd_geom)    # sf object
row names were found from a short variable and have been discarded
lnd_sf
Simple feature collection with 2 features and 3 fields
geometry type:  MULTIPOINT
dimension:      XY
bbox:           xmin: 0.1 ymin: 51.5 xmax: 0.2 ymax: 52.3
CRS:            EPSG:4326
    name temperature       date                       geometry
1 London          25 2017-06-21 MULTIPOINT ((0.1 51.5), (0....
2     HN          27 2018-02-01 MULTIPOINT ((0.1 51.5), (0....

# Coordinate Reference System CRS -Get all coordinate reference system

library(rgdal)
package 㤼㸱rgdal㤼㸲 was built under R version 3.6.3rgdal: version: 1.4-8, (SVN revision 845)
 Geospatial Data Abstraction Library extensions to R successfully loaded
 Loaded GDAL runtime: GDAL 2.2.3, released 2017/11/20
 Path to GDAL shared files: C:/Users/DELL/Documents/R/win-library/3.6/rgdal/gdal
 GDAL binary built with GEOS: TRUE 
 Loaded PROJ.4 runtime: Rel. 4.9.3, 15 August 2016, [PJ_VERSION: 493]
 Path to PROJ.4 shared files: C:/Users/DELL/Documents/R/win-library/3.6/rgdal/proj
 Linking to sp version: 1.4-1 
crs_data = rgdal::make_EPSG()

View(crs_data)

Get crs of the object

st_crs(world)
Coordinate Reference System:
  User input: EPSG:4326 
  wkt:
GEOGCS["WGS 84",
    DATUM["WGS_1984",
        SPHEROID["WGS 84",6378137,298.257223563,
            AUTHORITY["EPSG","7030"]],
        AUTHORITY["EPSG","6326"]],
    PRIMEM["Greenwich",0,
        AUTHORITY["EPSG","8901"]],
    UNIT["degree",0.0174532925199433,
        AUTHORITY["EPSG","9122"]],
    AUTHORITY["EPSG","4326"]]

If our object is missing crs or wrong, we can set crs with function st_set_crs(object,crs)

sf_newcrs<-st_set_crs(sf_asia_union,4326)

st_crs(sf_newcrs)
Coordinate Reference System:
  User input: EPSG:4326 
  wkt:
GEOGCS["WGS 84",
    DATUM["WGS_1984",
        SPHEROID["WGS 84",6378137,298.257223563,
            AUTHORITY["EPSG","7030"]],
        AUTHORITY["EPSG","6326"]],
    PRIMEM["Greenwich",0,
        AUTHORITY["EPSG","8901"]],
    UNIT["degree",0.0174532925199433,
        AUTHORITY["EPSG","9122"]],
    AUTHORITY["EPSG","4326"]]
library(ggplot2)
ggplot(data = world) + geom_sf(aes(fill = pop)) +scale_fill_viridis_c(option = "plasma", trans = "sqrt")

If we want to calculate the area of country or region, we can use st_area)(). This will result in square meters.

library(lwgeom)
package 㤼㸱lwgeom㤼㸲 was built under R version 3.6.3Linking to liblwgeom 3.0.0beta1 r16016, GEOS 3.6.1, PROJ 4.9.3
library(units)
package 㤼㸱units㤼㸲 was built under R version 3.6.3udunits system database from C:/Users/DELL/Documents/R/win-library/3.6/units/share/udunits
st_area(sf_asia_union)/1000000 # Square kilometer 
31252459 [m^2]
units::set_units(st_area(sf_asia_union), km^2)
31252459 [km^2]

If we want to label all country names, we can use text to name it

sf_points<-st_centroid(world, of_largest=T)
st_centroid assumes attributes are constant over geometries of xst_centroid does not give correct centroids for longitude/latitude data
sf_coord<-data.frame(st_coordinates(sf_points))

df<-data.frame(Names=world$name_long,x=sf_coord$X,y=sf_coord$Y)
               
plot(world["name_long"],main="World Map")

text(x=df[,2],y=df[,3],labels=df$Names)

Attribute data associated with sptial geometry

sf package supported following methods.

methods(class = "sf") # methods for sf objects, first 12 shown
 [1] $<-                        [                          [[<-                       aggregate                 
 [5] as.data.frame              cbind                      coerce                     dbDataType                
 [9] dbWriteTable               extent                     extract                    filter                    
[13] identify                   initialize                 mask                       merge                     
[17] plot                       print                      raster                     rasterize                 
[21] rbind                      select                     show                       slotsFromS3               
[25] st_agr                     st_agr<-                   st_area                    st_as_sf                  
[29] st_bbox                    st_boundary                st_buffer                  st_cast                   
[33] st_centroid                st_collection_extract      st_convex_hull             st_coordinates            
[37] st_crop                    st_crs                     st_crs<-                   st_difference             
[41] st_filter                  st_force_polygon_cw        st_geometry                st_geometry<-             
[45] st_interpolate_aw          st_intersection            st_intersects              st_is                     
[49] st_is_polygon_cw           st_join                    st_line_merge              st_linesubstring          
[53] st_m_range                 st_make_valid              st_minimum_bounding_circle st_nearest_points         
[57] st_node                    st_normalize               st_point_on_surface        st_polygonize             
[61] st_precision               st_reverse                 st_sample                  st_segmentize             
[65] st_set_precision           st_shift_longitude         st_simplify                st_snap                   
[69] st_snap_to_grid            st_split                   st_subdivide               st_sym_difference         
[73] st_transform               st_transform_proj          st_triangulate             st_union                  
[77] st_voronoi                 st_wrap_dateline           st_write                   st_z_range                
[81] st_zm                      transform                 
see '?methods' for accessing help and source code

Aggregation

-Nonsptial aggregation. Report the total of population in each continent

continent_pop<-aggregate(pop~continent, FUN=sum,data=world,na.rm=T)
continent_pop

-Spatial Aggregation. Report the total of population in each continent with spatial objects

continent_pop<-aggregate(world["pop"],by=list(world$continent),FUN=sum,na.rm=T)
continent_pop
Simple feature collection with 8 features and 2 fields
Attribute-geometry relationship: 0 constant, 1 aggregate, 1 identity
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513
CRS:            EPSG:4326
                  Group.1        pop                       geometry
1                  Africa 1154946633 MULTIPOLYGON (((49.54352 -1...
2              Antarctica          0 MULTIPOLYGON (((-163.7129 -...
3                    Asia 4311408059 MULTIPOLYGON (((120.295 -10...
4                  Europe  669036256 MULTIPOLYGON (((-51.6578 4....
5           North America  565028684 MULTIPOLYGON (((-61.68 10.7...
6                 Oceania   37757833 MULTIPOLYGON (((169.6678 -4...
7 Seven seas (open ocean)          0 POLYGON ((68.935 -48.625, 6...
8           South America  412060811 MULTIPOLYGON (((-66.95992 -...
continent_pop<- world %>% group_by(continent) %>% summarise(n=n(), total=sum(pop,na.rm=T))
Error in group_by(., continent) : could not find function "group_by"
populated_continent<-world %>% dplyr::select(pop, continent) %>% group_by(continent) %>% summarize(pop = sum(pop, na.rm = TRUE), 
                                                                              n_countries = n()) %>%  top_n(n = 4, wt = pop) %>%
  arrange(desc(pop)) %>%  st_drop_geometry()
populated_continent

Vector attribute Joining

we can see the full list of joining functions vignette("two-table")

# Left join
world_coffee<-left_join(world,coffee_data, by="name_long")

plot(world_coffee["coffee_production_2016"])

coffee_world<-left_join(coffee_data,world,by="name_long")

world_coffee_map<-st_as_sf(coffee_world) # Coarse the geometry columns to the sptial object. The coffee_data has only 47 observations
plot(world_coffee_map)
plotting the first 10 out of 12 attributes; use max.plot = 12 to plot all

Remove spatial informaiton or attributes

-This process is similar to dataframe, but it is noted that if we want to remove geometry column, we need to use st_drop_geometry()

-Creating a new spatial and give it a crs

st_crs(st_English)
Coordinate Reference System:
  User input: EPSG:27700 
  wkt:
PROJCS["OSGB 1936 / British National Grid",
    GEOGCS["OSGB 1936",
        DATUM["OSGB_1936",
            SPHEROID["Airy 1830",6377563.396,299.3249646,
                AUTHORITY["EPSG","7001"]],
            TOWGS84[446.448,-125.157,542.06,0.15,0.247,0.842,-20.489],
            AUTHORITY["EPSG","6277"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4277"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",49],
    PARAMETER["central_meridian",-2],
    PARAMETER["scale_factor",0.9996012717],
    PARAMETER["false_easting",400000],
    PARAMETER["false_northing",-100000],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["Easting",EAST],
    AXIS["Northing",NORTH],
    AUTHORITY["EPSG","27700"]]

Identify the UTM code any points in the Planet


UTM_code = function(lonlat) {
  utm = (floor((lonlat[1] + 180) / 6) %% 60) + 1
  if(lonlat[2] > 0) {
    utm + 32600
  } else{
    utm + 32700
  }
}
Warning message:
In png_available() : internal error -3 in R_decompress1
# Type your long and lat and get your UTM EPSG
UTM_code(c(105.83,21.02)) # Hanoi Vietnam
[1] 32648

Writing shapefile or saving shapefiles

It can be saved in various formats ESRI Shapefile, KML, gpkg Geopackage

library(rgdal)
st_write(world,dsn = "WorldMap.gpkg", append = F)
Deleting layer `WorldMap' using driver `GPKG'
Writing layer `WorldMap' to data source `WorldMap.gpkg' using driver `GPKG'
Writing 177 features with 10 fields and geometry type Multi Polygon.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVGhpcyBkb2N1bWVudCBpcyB0byBzdW1tYXJpc2Ugc29tZSBrZXkgZmVhdHVyZXMgYW5kIGdlby1wcm9jZXNzaW5nIGluIEdlb2NvbXB1dGF0aW9uIHdpdGggUiANCg0KIyBQYWNrYWdlcyB1c2VkIA0KYGBge3J9DQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShzcERhdGEpDQpsaWJyYXJ5KHJhc3RlcikNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KVGhpcyBzZWN0aW9uIGlzIGRlZGljYXRlZCB0byB0aGUgdXNlIG9mIGBzZmAgcGFja2FnZSB3aGljaCBjb3ZlcnMgYSB3aWRlIHJhbmdlIG9mIGdlby1wcm9jZXNzaW5nIHRvb2xzIGZvciB2ZWN0b3IgZGF0YS4gaXQgcHJvdmlkZXMgYSBjb25zaXN0ZW50IGFuZCBlYXN5LXRvLXVzZSBwcm9jZWR1cmUgdG8gYW5hbHlzZSBhbmQgcHJvY2VzcyB2ZWN0b3IgZGF0YSBhcyB3ZWxsIGFzIHdvcmtpbmcgd2VsbCB3aXRoIGRhdGFmcmFtZS4gSWYgeW91IGxpa2UsIHBsZWFzZSBoYXZlIGEgbG9vayBoZXJlDQoNCnZpZ25ldHRlKHBhY2thZ2UgPSAic2YiKSAjIHNlZSB3aGljaCB2aWduZXR0ZXMgYXJlIGF2YWlsYWJsZQ0KdmlnbmV0dGUoInNmMSIpICAgICAgICAgICMgYW4gaW50cm9kdWN0aW9uIHRvIHRoZSBwYWNrYWdlDQoNCiMgTG9hZGluZyBhbmQgY2hlY2tpbmcgZGF0YSANCmB3b3JsZGAgZGF0YSBjb250YWluIGRpZmZlcmVudCBmZWF0dXJlcyBvZiB0aGUgd29ybGQgY291bnRyeSBzdWNoIGFzIHBvcHVsYXRpb24sIGFyZWEgYW5kIGNvdW50cnkgbmFtZXMuIFRoaXMgZGF0YXNldCBpcyBzdG9yZWQgYXMgYHNmYCBvYmplY3QuIElmIHdlIHdhbnQgdG8gY29udmVydCBgc3BgIG9yIG90aGVyIHNoYXBlZmlsZXMgdG8gYHNmYCwgd2UgY2FuIHVzZSBmdW5jdGlvbiBgc3RfYXNfc2YoKWAgDQoNCmBgYHtyfQ0KbmFtZXMod29ybGQpICMgTmFtZXMgb2YgZGlmZmVyZW50IGNvdW50cmllcyANCg0KaGVhZCh3b3JsZCkgIyBDaGVjayBmaXJzdCBmZXcgcm93cyBvZiBkYXRhZiANCmBgYA0KDQpgYGB7cn0NCnBsb3Qod29ybGQpDQpgYGANCg0KKkNob29zaW5nIHRvIHBsb3Qgb25seSBwb3B1bGF0aW9uIHZhcmlhYmxlIGFuZCBBc2lhIA0KDQpgYGB7cn0NCnNmX2FzaWE8LXdvcmxkW3dvcmxkJGNvbnRpbmVudD09IkFzaWEiLF0NCg0KcGxvdChzZl9hc2lhWyJwb3AiXSkNCg0KYGBgDQoNCiogYHN0X3VuaW9uYCBmdW5jdGlvbiBpcyB1c2VkIHRvIGZvcm0gYSBzaW5nbGUgYm91bmRhcnkgb2YgYW4gYXJlYS4gRm9yIGV4YW1wbGUsIHdlIGNyZWF0ZSBhc2lhDQoNCmBgYHtyfQ0Kc2ZfYXNpYV91bmlvbjwtc3RfdW5pb24oc2ZfYXNpYSkNCg0KIyBQbG90IHdvcmxkIG1hcA0KDQpwbG90KHdvcmxkWyJwb3AiXSwgcmVzZXQ9RikNCg0KcGxvdChzZl9hc2lhX3VuaW9uLGFkZD1ULGNvbD0iMiIpDQoNCmBgYA0KDQoqIEV4dHJhY3QgY2VudHJvaWRzIGZyb20gcG9seWdvbnMgDQoNCmBgYHtyfQ0KcGxvdCh3b3JsZFsiY29udGluZW50Il0sIHJlc2V0ID0gRkFMU0UpDQpjZXggPSBzcXJ0KHdvcmxkJHBvcCkgLyAxMDAwMA0Kd29ybGRfY2VudHMgPSBzdF9jZW50cm9pZCh3b3JsZCwgb2ZfbGFyZ2VzdCA9IFRSVUUpDQpwbG90KHN0X2dlb21ldHJ5KHdvcmxkX2NlbnRzKSwgYWRkID0gVFJVRSwgY2V4ID0gY2V4KQ0KYGBgDQojIENyZWF0ZSBwb2ludHMgYW5kIGRhdGFmcmFtZSB1c2luZyBzZg0KDQpgYGB7cn0NCmxuZF9wb2ludDwtcmJpbmQoYygwLjEsNTEuNSksYygwLjIsNTIuMykpDQoNCmxuZF9wb2ludCA9IHN0X211bHRpcG9pbnQobG5kX3BvaW50KSAgICAgICAgICAgICAgICAgIyBzZmcgb2JqZWN0DQoNCmxuZF9nZW9tID0gc3Rfc2ZjKGxuZF9wb2ludCwgY3JzID0gNDMyNikgICAgICAgICAgICMgc2ZjIG9iamVjdA0KDQpsbmRfYXR0cmliID0gZGF0YS5mcmFtZSggICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRhdGEuZnJhbWUgb2JqZWN0DQogIG5hbWUgPSBjKCJMb25kb24iLCJITiIpLCB0ZW1wZXJhdHVyZSA9IGMoMjUsMjcpLA0KICBkYXRlID0gYXMuRGF0ZShjKCIyMDE3LTA2LTIxIiwiMjAxOC0wMi0wMSIpKQ0KICApDQpsbmRfc2YgPSBzdF9zZihsbmRfYXR0cmliLCBnZW9tZXRyeSA9IGxuZF9nZW9tKSAgICAjIHNmIG9iamVjdA0KDQpsbmRfc2YNCmBgYA0KICMgQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtIENSUw0KIC1HZXQgYWxsIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbQ0KIA0KYGBge3J9DQpsaWJyYXJ5KHJnZGFsKQ0KY3JzX2RhdGEgPSByZ2RhbDo6bWFrZV9FUFNHKCkNCg0KVmlldyhjcnNfZGF0YSkNCmBgYA0KIA0KIyBHZXQgY3JzIG9mIHRoZSBvYmplY3QNCg0KYGBge3J9DQpzdF9jcnMod29ybGQpDQpgYGANCg0KSWYgb3VyIG9iamVjdCBpcyBtaXNzaW5nIGBjcnNgIG9yIHdyb25nLCB3ZSBjYW4gc2V0IGNycyB3aXRoIGZ1bmN0aW9uIGBzdF9zZXRfY3JzKG9iamVjdCxjcnMpYA0KDQpgYGB7cn0NCnNmX25ld2Nyczwtc3Rfc2V0X2NycyhzZl9hc2lhX3VuaW9uLDQzMjYpDQoNCnN0X2NycyhzZl9uZXdjcnMpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGF0YSA9IHdvcmxkKSArIGdlb21fc2YoYWVzKGZpbGwgPSBwb3ApKSArc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIHRyYW5zID0gInNxcnQiKQ0KYGBgDQoNCklmIHdlIHdhbnQgdG8gY2FsY3VsYXRlIHRoZSBhcmVhIG9mIGNvdW50cnkgb3IgcmVnaW9uLCB3ZSBjYW4gdXNlIGBzdF9hcmVhKSgpYC4gVGhpcyB3aWxsIHJlc3VsdCBpbiBzcXVhcmUgbWV0ZXJzLg0KDQpgYGB7cn0NCmxpYnJhcnkobHdnZW9tKQ0KbGlicmFyeSh1bml0cykNCnN0X2FyZWEoc2ZfYXNpYV91bmlvbikvMTAwMDAwMCAjIFNxdWFyZSBraWxvbWV0ZXIgDQoNCnVuaXRzOjpzZXRfdW5pdHMoc3RfYXJlYShzZl9hc2lhX3VuaW9uKSwga21eMikNCmBgYA0KDQpJZiB3ZSB3YW50IHRvIGxhYmVsIGFsbCBjb3VudHJ5IG5hbWVzLCB3ZSBjYW4gdXNlIHRleHQgdG8gbmFtZSBpdA0KDQpgYGB7cn0NCnNmX3BvaW50czwtc3RfY2VudHJvaWQod29ybGQsIG9mX2xhcmdlc3Q9VCkNCg0Kc2ZfY29vcmQ8LWRhdGEuZnJhbWUoc3RfY29vcmRpbmF0ZXMoc2ZfcG9pbnRzKSkNCg0KZGY8LWRhdGEuZnJhbWUoTmFtZXM9d29ybGQkbmFtZV9sb25nLHg9c2ZfY29vcmQkWCx5PXNmX2Nvb3JkJFkpDQogICAgICAgICAgICAgICANCnBsb3Qod29ybGRbIm5hbWVfbG9uZyJdLG1haW49IldvcmxkIE1hcCIpDQoNCnRleHQoeD1kZlssMl0seT1kZlssM10sbGFiZWxzPWRmJE5hbWVzKQ0KYGBgDQoNCiMgQXR0cmlidXRlIGRhdGEgYXNzb2NpYXRlZCB3aXRoIHNwdGlhbCBnZW9tZXRyeQ0KDQpgc2ZgIHBhY2thZ2Ugc3VwcG9ydGVkIGZvbGxvd2luZyBtZXRob2RzLg0KYGBge3J9DQptZXRob2RzKGNsYXNzID0gInNmIikgIyBtZXRob2RzIGZvciBzZiBvYmplY3RzLCBmaXJzdCAxMiBzaG93bg0KYGBgDQoNCiMgQWdncmVnYXRpb24gDQoNCi1Ob25zcHRpYWwgYWdncmVnYXRpb24uIFJlcG9ydCB0aGUgdG90YWwgb2YgcG9wdWxhdGlvbiBpbiBlYWNoIGNvbnRpbmVudCANCg0KYGBge3J9DQpjb250aW5lbnRfcG9wPC1hZ2dyZWdhdGUocG9wfmNvbnRpbmVudCwgRlVOPXN1bSxkYXRhPXdvcmxkLG5hLnJtPVQpDQpjb250aW5lbnRfcG9wDQpgYGANCg0KLVNwYXRpYWwgQWdncmVnYXRpb24uIFJlcG9ydCB0aGUgdG90YWwgb2YgcG9wdWxhdGlvbiBpbiBlYWNoIGNvbnRpbmVudCB3aXRoIHNwYXRpYWwgb2JqZWN0cw0KYGBge3J9DQpjb250aW5lbnRfcG9wPC1hZ2dyZWdhdGUod29ybGRbInBvcCJdLGJ5PWxpc3Qod29ybGQkY29udGluZW50KSxGVU49c3VtLG5hLnJtPVQpDQpjb250aW5lbnRfcG9wDQpgYGANCg0KLSBBIGJldHRlciB3YXkgb2YgYWdncmVnYXRpbmcgdGhlIHRvdGFsIG9mIGVhY2ggY29udGluZW50IHBvcHVsYXRpb24NCmBgYHtyfQ0KY29udGluZW50X3BvcDwtIHdvcmxkICU+JSBncm91cF9ieShjb250aW5lbnQpICU+JSBzdW1tYXJpc2Uobj1uKCksIHRvdGFsPXN1bShwb3AsbmEucm09VCkpDQpjb250aW5lbnRfcG9wDQpgYGANCg0KLSBXZSBjYW4gZmluZCB0aGUgbW9zdCBwb3B1bGF0ZWQgY29udGluZW50cyANCg0KYGBge3J9DQpwb3B1bGF0ZWRfY29udGluZW50PC13b3JsZCAlPiUgZHBseXI6OnNlbGVjdChwb3AsIGNvbnRpbmVudCkgJT4lIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIHN1bW1hcml6ZShwb3AgPSBzdW0ocG9wLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fY291bnRyaWVzID0gbigpKSAlPiUgIHRvcF9uKG4gPSA0LCB3dCA9IHBvcCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwb3ApKSAlPiUgIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0KcG9wdWxhdGVkX2NvbnRpbmVudA0KYGBgDQoNCiMgVmVjdG9yIGF0dHJpYnV0ZSBKb2luaW5nDQoNCndlIGNhbiBzZWUgdGhlIGZ1bGwgbGlzdCBvZiBqb2luaW5nIGZ1bmN0aW9ucyBgdmlnbmV0dGUoInR3by10YWJsZSIpYA0KDQpgYGB7cn0NCiMgTGVmdCBqb2luDQp3b3JsZF9jb2ZmZWU8LWxlZnRfam9pbih3b3JsZCxjb2ZmZWVfZGF0YSwgYnk9Im5hbWVfbG9uZyIpDQoNCnBsb3Qod29ybGRfY29mZmVlWyJjb2ZmZWVfcHJvZHVjdGlvbl8yMDE2Il0pDQoNCmBgYA0KDQotIENyZWF0ZSBub24tc3BhdGlhbCBvYmplY3QgYW5kIGZvcmNlIGl0IHRvIGJlIHNwYXRpYWwgb2JqZWN0IA0KDQpgYGB7cn0NCmNvZmZlZV93b3JsZDwtbGVmdF9qb2luKGNvZmZlZV9kYXRhLHdvcmxkLGJ5PSJuYW1lX2xvbmciKQ0KDQp3b3JsZF9jb2ZmZWVfbWFwPC1zdF9hc19zZihjb2ZmZWVfd29ybGQpICMgQ29hcnNlIHRoZSBnZW9tZXRyeSBjb2x1bW5zIHRvIHRoZSBzcHRpYWwgb2JqZWN0LiBUaGUgY29mZmVlX2RhdGEgaGFzIG9ubHkgNDcgb2JzZXJ2YXRpb25zDQpwbG90KHdvcmxkX2NvZmZlZV9tYXApDQpgYGANCg0KIyBSZW1vdmUgc3BhdGlhbCBpbmZvcm1haXRvbiBvciBhdHRyaWJ1dGVzDQoNCi1UaGlzIHByb2Nlc3MgaXMgc2ltaWxhciB0byBkYXRhZnJhbWUsIGJ1dCBpdCBpcyBub3RlZCB0aGF0IGlmIHdlIHdhbnQgdG8gcmVtb3ZlIGdlb21ldHJ5IGNvbHVtbiwgd2UgbmVlZCB0byB1c2UgYHN0X2Ryb3BfZ2VvbWV0cnkoKWAgDQoNCi1DcmVhdGluZyBhIG5ldyBzcGF0aWFsIGFuZCBnaXZlIGl0IGEgYGNyc2AgDQoNCmBgYHtyfQ0KbG9uZG9uID0gZGF0YS5mcmFtZShsb24gPSAtMC4xLCBsYXQgPSA1MS41KSAlPiUgc3RfYXNfc2YoY29vcmRzID0gYygibG9uIiwgImxhdCIpKQ0Kc3RfaXNfbG9uZ2xhdChsb25kb24pICMgTm8gY29vcmRpbmF0ZSBzeXN0ZW0gDQoNCiMgR2l2ZSBnZW9ncmFwaGljIGNvb3JkaW5hdGUgc3lzdGVtDQoNCnN0X2xvbmRvbjwtc3Rfc2V0X2Nycyhsb25kb24sNDMyNikNCg0Kc3RfaXNfbG9uZ2xhdChzdF9sb25kb24pOyBzdF9jcnMoc3RfbG9uZG9uKSANCiMgVHJhbnNmb3JtIHRvIG90aGVyIGNvb3JkaW5hdGUgc3lzdGVtDQoNCnN0X0VuZ2xpc2g8LXN0X3RyYW5zZm9ybShzdF9sb25kb24sY3JzPTI3NzAwKQ0Kc3RfY3JzKHN0X0VuZ2xpc2gpDQoNCmBgYA0KDQojIElkZW50aWZ5IHRoZSBVVE0gY29kZSBhbnkgcG9pbnRzIGluIHRoZSBQbGFuZXQNCg0KYGBge3J9DQoNClVUTV9jb2RlID0gZnVuY3Rpb24obG9ubGF0KSB7DQogIHV0bSA9IChmbG9vcigobG9ubGF0WzFdICsgMTgwKSAvIDYpICUlIDYwKSArIDENCiAgaWYobG9ubGF0WzJdID4gMCkgew0KICAgIHV0bSArIDMyNjAwDQogIH0gZWxzZXsNCiAgICB1dG0gKyAzMjcwMA0KICB9DQp9DQojIFR5cGUgeW91ciBsb25nIGFuZCBsYXQgYW5kIGdldCB5b3VyIFVUTSBFUFNHDQpVVE1fY29kZShjKDEwNS44MywyMS4wMikpICMgSGFub2kgVmlldG5hbQ0KDQpgYGANCg0KIyBXcml0aW5nIHNoYXBlZmlsZSBvciBzYXZpbmcgc2hhcGVmaWxlcw0KSXQgY2FuIGJlIHNhdmVkIGluIHZhcmlvdXMgZm9ybWF0cyBgRVNSSSBTaGFwZWZpbGVgLCBgS01MYCwgYGdwa2dgIEdlb3BhY2thZ2UgDQoNCmBgYHtyfQ0KbGlicmFyeShyZ2RhbCkNCnN0X3dyaXRlKHdvcmxkLGRzbiA9ICJXb3JsZE1hcC5ncGtnIiwgYXBwZW5kID0gRikNCmBgYA0KDQo=