In this Notebook, we’ll learn how to import observations from iNaturalist using the rinat package.

Load Packages

The first step is to load the packages we’ll need. These are all on CRAN if you need to install any.

library(rinat)
library(sf)
library(dplyr)
library(tmap)
library(leaflet)
library(tibble)

## Load the conflicted package and set preferences
library(conflicted)
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

Next, we import the boundary of Yosemite and grab its bounding box. We’ll use this later when we call iNaturalist to specify that we’re we’re only interested in observations in this area.

## Import the YNP Boundary
yose_bnd_ll <- sf::st_read(dsn="./data", layer="yose_boundary")
Reading layer `yose_boundary' from data source `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_data\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
## Get the bounding box
yose_bnd_bb <- st_bbox(yose_bnd_ll)
as.numeric(yose_bnd_bb)
[1] -119.88642   37.49470 -119.19640   38.18515

Retrieve Recent iNat observations

Next, we can retrieve observations within the bounding box of the park. By default, the get_inat_obs() will return the most recent 100 observations:

## Retrieve the first 100 iNaturalist observations within the bounding box. 
## Note we have to rearrange the coordinates of the bounding box a little bit to 
## give get_inat_obs() what it expects

yose_inat_df <- get_inat_obs(bounds = yose_bnd_bb[c(2,1,4,3)])

Inspect the results:

dim(yose_inat_df)
[1] 100  36
head(yose_inat_df)

In order to plot these observations, let’s first convert the data frame into a sf object:

yose_inat_sf <-  yose_inat_df %>% 
  select(longitude, latitude, datetime, common_name, scientific_name, user_login) %>% 
  st_as_sf(coords=c("longitude", "latitude"),  crs=4326)
yose_inat_sf
Simple feature collection with 100 features and 4 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -119.8788 ymin: 37.49961 xmax: -119.2064 ymax: 38.09382
Geodetic CRS:  WGS 84
First 10 features:
                    datetime                common_name          scientific_name   user_login
1  2021-07-22 11:21:22 -0700         bigflower agoseris     Agoseris grandiflora tereschubert
2  2021-07-22 11:20:02 -0700             Pacific Willow          Salix lasiandra tereschubert
3  2021-07-22 11:21:31 -0700             showy milkweed       Asclepias speciosa tereschubert
4  2021-07-22 10:44:36 -0700   California incense-cedar     Calocedrus decurrens tereschubert
5  2021-07-22 11:26:34 -0700                                                     tereschubert
6  2021-07-20 17:07:11 -0700 California Ground Squirrel Otospermophilus beecheyi tereschubert
7  2021-07-20 17:07:30 -0700                                                     tereschubert
8    2021-07-22 15:12:41 UTC              Steller's Jay      Cyanocitta stelleri tereschubert
9  2021-07-24 14:07:30 -0700                                                        travezoid
10 2021-07-15 10:25:42 -0700               Leaf Beetles            Chrysomelidae    oakwilson
                     geometry
1  POINT (-119.5924 37.74403)
2  POINT (-119.5923 37.74401)
3  POINT (-119.5924 37.74403)
4  POINT (-119.5842 37.74453)
5  POINT (-119.5935 37.74454)
6  POINT (-119.5711 37.73765)
7   POINT (-119.571 37.73769)
8  POINT (-119.5989 37.74382)
9  POINT (-119.6041 37.73558)
10   POINT (-119.57 37.74151)

Plot with tmap

tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(yose_bnd_ll) + 
  tm_borders(col = "red", lwd = 2) +
tm_shape(yose_inat_sf) + 
  tm_symbols()

Add a Taxon to the query

Next we’ll a taxon to our query. Yosemite has some endemic toads. We can tell iNaturalist we only only want certain species using the optional taxon_name argument. Here we’ll set it to Bufonidae (the toad family).

yose_toads_df <- get_inat_obs(bounds = yose_bnd_bb[c(2,1,4,3)],
                              taxon_name = "Bufonidae")
dim(yose_toads_df)
[1] 100  36
glimpse(yose_toads_df, width = 110)
Rows: 100
Columns: 36
$ scientific_name                  <chr> "Anaxyrus boreas", "Anaxyrus boreas", "Bufonidae", "Bufonidae", "Bu~
$ datetime                         <chr> "2021-07-04 01:47:00 -0700", "2021-06-29 04:19:00 -0700", "2021-07-~
$ description                      <chr> "M2E97A1L0-766.7319946-56.527000", "M2E96A1L0-761.0310058-57.527000~
$ place_guess                      <chr> "Mariposa County, US-CA, US", "Mariposa County, US-CA, US", "Tuolum~
$ latitude                         <dbl> 37.58961, 37.55444, 37.93532, 37.93802, 37.92954, 37.94146, 37.7414~
$ longitude                        <dbl> -119.8339, -119.8297, -119.3490, -119.3451, -119.3276, -119.2171, -~
$ tag_list                         <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",~
$ common_name                      <chr> "Western Toad", "Western Toad", "True Toads", "True Toads", "True T~
$ url                              <chr> "https://www.inaturalist.org/observations/86646941", "https://www.i~
$ image_url                        <chr> "https://inaturalist-open-data.s3.amazonaws.com/photos/142770127/me~
$ user_login                       <chr> "bethpratt1", "bethpratt1", "tothemax", "tothemax", "tothemax", "to~
$ id                               <int> 86646941, 86646911, 86222406, 86222404, 86177881, 86177876, 8583757~
$ species_guess                    <chr> "Western Toad", "Western Toad", "True Toads", "True Toads", "True T~
$ iconic_taxon_name                <chr> "Amphibia", "Amphibia", "Amphibia", "Amphibia", "Amphibia", "Amphib~
$ taxon_id                         <int> 64970, 64970, 21359, 21359, 21359, 64972, 64972, 64972, 64972, 6497~
$ num_identification_agreements    <int> 0, 1, 0, 0, 0, 2, 0, 1, 1, 1, 1, 1, 6, 1, 3, 1, 3, 0, 2, 2, 1, 1, 1~
$ num_identification_disagreements <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0~
$ observed_on_string               <chr> "2021/07/04 1:47 AM PDT", "2021/06/29 4:19 AM PDT", "2021/07/06 5:4~
$ observed_on                      <chr> "2021-07-04", "2021-06-29", "2021-07-06", "2021-07-06", "2021-07-06~
$ time_observed_at                 <chr> "2021-07-04 08:47:00 UTC", "2021-06-29 11:19:00 UTC", "2021-07-07 0~
$ time_zone                        <chr> "Pacific Time (US & Canada)", "Pacific Time (US & Canada)", "Pacifi~
$ positional_accuracy              <int> 154, 154, 4, 15, 9, 2, 500, 11, 4, 15, 9, 12, NA, 12, 6, 8, 30, 47,~
$ public_positional_accuracy       <int> 28388, 28388, 4, 15, 28329, 28329, 28359, 28329, 28329, 28329, 2832~
$ geoprivacy                       <chr> "obscured", "obscured", "", "", "obscured", "obscured", "", "obscur~
$ taxon_geoprivacy                 <chr> "open", "open", "", "", "", "obscured", "obscured", "obscured", "ob~
$ coordinates_obscured             <chr> "true", "true", "false", "false", "true", "true", "true", "true", "~
$ positioning_method               <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",~
$ positioning_device               <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",~
$ user_id                          <int> 8172, 8172, 583493, 583493, 583493, 583493, 839628, 583493, 583493,~
$ created_at                       <chr> "2021-07-11 23:40:45 UTC", "2021-07-11 23:40:40 UTC", "2021-07-08 2~
$ updated_at                       <chr> "2021-07-11 23:40:45 UTC", "2021-07-14 14:41:31 UTC", "2021-07-08 2~
$ quality_grade                    <chr> "needs_id", "research", "needs_id", "needs_id", "needs_id", "resear~
$ license                          <chr> "CC-BY", "CC-BY", "CC-BY-NC", "CC-BY-NC", "CC-BY-NC", "CC-BY-NC", "~
$ sound_url                        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
$ oauth_application_id             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,~
$ captive_cultivated               <chr> "false", "false", "false", "false", "false", "false", "false", "fal~

Convert the toads to sf

Before we can plot it, we’ll convert the toads layer to a sf object. At the same time, we’ll reduce the number of columns to just those we want to include on the map.

yose_toads_sf <-  yose_toads_df %>% 
  select(longitude, latitude, datetime, common_name, scientific_name, image_url, user_login) %>% 
  st_as_sf(coords=c("longitude", "latitude"),  crs=4326)

yose_toads_sf
Simple feature collection with 100 features and 5 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -119.8556 ymin: 37.50421 xmax: -119.2008 ymax: 38.17787
Geodetic CRS:  WGS 84
First 10 features:
                    datetime   common_name  scientific_name
1  2021-07-04 01:47:00 -0700  Western Toad  Anaxyrus boreas
2  2021-06-29 04:19:00 -0700  Western Toad  Anaxyrus boreas
3  2021-07-06 17:47:00 -0700    True Toads        Bufonidae
4  2021-07-06 17:20:00 -0700    True Toads        Bufonidae
5  2021-07-06 11:22:00 -0700    True Toads        Bufonidae
6  2021-07-06 11:08:00 -0700 Yosemite Toad Anaxyrus canorus
7  2021-07-03 20:37:00 -0700 Yosemite Toad Anaxyrus canorus
8  2021-07-02 15:40:00 -0700 Yosemite Toad Anaxyrus canorus
9  2021-07-02 14:10:00 -0700 Yosemite Toad Anaxyrus canorus
10 2021-07-02 16:02:00 -0700 Yosemite Toad Anaxyrus canorus
                                                                                image_url user_login
1  https://inaturalist-open-data.s3.amazonaws.com/photos/142770127/medium.jpeg?1626045288 bethpratt1
2  https://inaturalist-open-data.s3.amazonaws.com/photos/142769748/medium.jpeg?1626045201 bethpratt1
3  https://inaturalist-open-data.s3.amazonaws.com/photos/142003426/medium.jpeg?1625786527   tothemax
4  https://inaturalist-open-data.s3.amazonaws.com/photos/142003246/medium.jpeg?1625786468   tothemax
5  https://inaturalist-open-data.s3.amazonaws.com/photos/141922884/medium.jpeg?1625762651   tothemax
6  https://inaturalist-open-data.s3.amazonaws.com/photos/141922237/medium.jpeg?1625762496   tothemax
7                   https://static.inaturalist.org/photos/141296185/medium.jpg?1625524192 birdingman
8   https://inaturalist-open-data.s3.amazonaws.com/photos/140791976/medium.jpg?1625362516   tothemax
9   https://inaturalist-open-data.s3.amazonaws.com/photos/140791910/medium.jpg?1625362492   tothemax
10 https://inaturalist-open-data.s3.amazonaws.com/photos/140791403/medium.jpeg?1625362334   tothemax
                     geometry
1  POINT (-119.8339 37.58961)
2  POINT (-119.8297 37.55444)
3   POINT (-119.349 37.93532)
4  POINT (-119.3451 37.93802)
5  POINT (-119.3276 37.92954)
6  POINT (-119.2171 37.94146)
7  POINT (-119.7675 37.74145)
8  POINT (-119.2349 37.89038)
9  POINT (-119.2122 37.82828)
10 POINT (-119.2851 37.97464)

Plot the toads with leaflet

We’ll make the interactive map with leaflet, instead of tmap, because leaflet gives us more control over the popup windows.

The first step is to add a column to the sf object that contains the HTML code that will appear in the popup windows. We’ll add this column using mutate, and the save the result as a new sf object:

## Add a column containing HTML that will appear in the popup windows
yose_toads_popup_sf <- yose_toads_sf %>% 
  mutate(popup_html = paste0("<p><b>", common_name, "</b><br/>",
                             "<i>", scientific_name, "</i></p>",
                             "<p>Observed: ", datetime, "<br/>",
                             "User: ", user_login, "</p>",
                             "<p><img src='", image_url, "' style='width:100%;'/></p>")
  )

See an example of the popup HTML

This is what the HTML code looks like for the first feature:

yose_toads_popup_sf$popup_html[1]
[1] "<p><b>Western Toad</b><br/><i>Anaxyrus boreas</i></p><p>Observed: 2021-07-04 01:47:00 -0700<br/>User: bethpratt1</p><p><img src='https://inaturalist-open-data.s3.amazonaws.com/photos/142770127/medium.jpeg?1626045288' style='width:100%;'/></p>"

Make the map

leaflet(yose_toads_popup_sf) %>% 
  addTiles() %>% 
  addCircleMarkers(popup = ~popup_html, radius = 5)

CHALLENGE: Download and map iNaturalist Observations for the taxon of your choice

Search for iNaturalist observations for your favorite Yosemite animal or plant by passing a Family, Genus or Species to get_inat_obs(). You could specify for example Ursidae (bears), Sequoiadendron (Sequoia), or Lilium (lilies). Answer

taxon <- c("Ursidae", "Sequoiadendron", "Lilium")[1]
yose_taxon_sf <- get_inat_obs(bounds = yose_bnd_bb[c(2,1,4,3)],
                              taxon_name = taxon) %>% 
  select(longitude, latitude, datetime, common_name, scientific_name, 
         image_url, user_login) %>% 
  st_as_sf(coords=c("longitude", "latitude"),  crs=4326)

yose_taxon_sf <- yose_taxon_sf %>% 
  mutate(popup_html = paste0("<p><b>", common_name, "</b><br/>",
                             "<i>", scientific_name, "</i></p>",
                             "<p>Observed: ", datetime, "<br/>",
                             "User: ", user_login, "</p>",
                             "<p><img src='", image_url, "' style='width:100%;'/></p>"))

leaflet(yose_taxon_sf) %>% 
  addTiles() %>% 
  addCircleMarkers(popup = ~popup_html, radius = 5)

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.

LS0tDQp0aXRsZTogIkltcG9ydCBhbmQgTWFwIGlOYXR1cmFsaXN0IE9ic2VydmF0aW9ucyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCkluIHRoaXMgTm90ZWJvb2ssIHdlJ2xsIGxlYXJuIGhvdyB0byBpbXBvcnQgb2JzZXJ2YXRpb25zIGZyb20gW2lOYXR1cmFsaXN0XShodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvKSB1c2luZyB0aGUgW3JpbmF0XShodHRwczovL2RvY3Mucm9wZW5zY2kub3JnL3JpbmF0LykgcGFja2FnZS4NCg0KIyMgTG9hZCBQYWNrYWdlcw0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byBsb2FkIHRoZSBwYWNrYWdlcyB3ZSdsbCBuZWVkLiBUaGVzZSBhcmUgYWxsIG9uIENSQU4gaWYgeW91IG5lZWQgdG8gaW5zdGFsbCBhbnkuDQoNCmBgYHtyIGNodW5rMDEsIG1lc3NhZ2UgPSBGQUxTRX0NCmxpYnJhcnkocmluYXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkobGVhZmxldCkNCg0KIyMgTG9hZCB0aGUgY29uZmxpY3RlZCBwYWNrYWdlIGFuZCBzZXQgcHJlZmVyZW5jZXMNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJhcnJhbmdlIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNCiMjIEltcG9ydCB0aGUgUGFyayBCb3VuZGFyeQ0KDQpOZXh0LCB3ZSBpbXBvcnQgdGhlIGJvdW5kYXJ5IG9mIFlvc2VtaXRlIGFuZCBncmFiIGl0cyBib3VuZGluZyBib3guIFdlJ2xsIHVzZSB0aGlzIGxhdGVyIHdoZW4gd2UgY2FsbCBpTmF0dXJhbGlzdCB0byBzcGVjaWZ5IHRoYXQgd2UncmUgd2UncmUgb25seSBpbnRlcmVzdGVkIGluIG9ic2VydmF0aW9ucyBpbiB0aGlzIGFyZWEuDQoNCmBgYHtyIGNodW5rMDJ9DQojIyBJbXBvcnQgdGhlIFlOUCBCb3VuZGFyeQ0KeW9zZV9ibmRfbGwgPC0gc2Y6OnN0X3JlYWQoZHNuPSIuL2RhdGEiLCBsYXllcj0ieW9zZV9ib3VuZGFyeSIpDQoNCiMjIEdldCB0aGUgYm91bmRpbmcgYm94DQp5b3NlX2JuZF9iYiA8LSBzdF9iYm94KHlvc2VfYm5kX2xsKQ0KYXMubnVtZXJpYyh5b3NlX2JuZF9iYikNCmBgYA0KDQojIyBSZXRyaWV2ZSBSZWNlbnQgaU5hdCBvYnNlcnZhdGlvbnMNCg0KTmV4dCwgd2UgY2FuIHJldHJpZXZlIG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgcGFyay4gQnkgZGVmYXVsdCwgdGhlIGBnZXRfaW5hdF9vYnMoKWAgd2lsbCByZXR1cm4gdGhlIG1vc3QgcmVjZW50IDEwMCBvYnNlcnZhdGlvbnM6DQoNCmBgYHtyIGNodW5rMDN9DQojIyBSZXRyaWV2ZSB0aGUgZmlyc3QgMTAwIGlOYXR1cmFsaXN0IG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveC4gDQojIyBOb3RlIHdlIGhhdmUgdG8gcmVhcnJhbmdlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgYm91bmRpbmcgYm94IGEgbGl0dGxlIGJpdCB0byANCiMjIGdpdmUgZ2V0X2luYXRfb2JzKCkgd2hhdCBpdCBleHBlY3RzDQoNCnlvc2VfaW5hdF9kZiA8LSBnZXRfaW5hdF9vYnMoYm91bmRzID0geW9zZV9ibmRfYmJbYygyLDEsNCwzKV0pDQpgYGANCg0KSW5zcGVjdCB0aGUgcmVzdWx0czoNCg0KYGBge3IgY2h1bmswNH0NCmRpbSh5b3NlX2luYXRfZGYpDQpoZWFkKHlvc2VfaW5hdF9kZikNCmBgYA0KDQpJbiBvcmRlciB0byBwbG90IHRoZXNlIG9ic2VydmF0aW9ucywgbGV0J3MgZmlyc3QgY29udmVydCB0aGUgZGF0YSBmcmFtZSBpbnRvIGEgc2Ygb2JqZWN0Og0KDQpgYGB7ciBjaHVuazA1fQ0KeW9zZV9pbmF0X3NmIDwtICB5b3NlX2luYXRfZGYgJT4lIA0KICBzZWxlY3QobG9uZ2l0dWRlLCBsYXRpdHVkZSwgZGF0ZXRpbWUsIGNvbW1vbl9uYW1lLCBzY2llbnRpZmljX25hbWUsIHVzZXJfbG9naW4pICU+JSANCiAgc3RfYXNfc2YoY29vcmRzPWMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCAgY3JzPTQzMjYpDQp5b3NlX2luYXRfc2YNCmBgYA0KDQojIyBQbG90IHdpdGggdG1hcA0KDQpgYGB7ciBjaHVuazA2fQ0KdG1hcF9tb2RlKCJ2aWV3IikNCg0KdG1fc2hhcGUoeW9zZV9ibmRfbGwpICsgDQogIHRtX2JvcmRlcnMoY29sID0gInJlZCIsIGx3ZCA9IDIpICsNCnRtX3NoYXBlKHlvc2VfaW5hdF9zZikgKyANCiAgdG1fc3ltYm9scygpDQpgYGANCg0KIyMgQWRkIGEgVGF4b24gdG8gdGhlIHF1ZXJ5IA0KDQpOZXh0IHdlJ2xsIGEgdGF4b24gdG8gb3VyIHF1ZXJ5LiBZb3NlbWl0ZSBoYXMgc29tZSBlbmRlbWljIHRvYWRzLiBXZSBjYW4gdGVsbCBpTmF0dXJhbGlzdCB3ZSBvbmx5IG9ubHkgd2FudCBjZXJ0YWluIHNwZWNpZXMgdXNpbmcgdGhlIG9wdGlvbmFsIHRheG9uX25hbWUgYXJndW1lbnQuIEhlcmUgd2UnbGwgc2V0IGl0IHRvICBbKkJ1Zm9uaWRhZSpdKGh0dHBzOi8vYW1waGliaWF3ZWIub3JnL2xpc3RzL0J1Zm9uaWRhZS5zaHRtbCkgKHRoZSB0b2FkIGZhbWlseSkuIA0KDQpgYGB7ciBjaHVuazA3fQ0KeW9zZV90b2Fkc19kZiA8LSBnZXRfaW5hdF9vYnMoYm91bmRzID0geW9zZV9ibmRfYmJbYygyLDEsNCwzKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXhvbl9uYW1lID0gIkJ1Zm9uaWRhZSIpDQpkaW0oeW9zZV90b2Fkc19kZikNCmdsaW1wc2UoeW9zZV90b2Fkc19kZiwgd2lkdGggPSAxMTApDQpgYGANCg0KIyMgQ29udmVydCB0aGUgdG9hZHMgdG8gc2YNCg0KQmVmb3JlIHdlIGNhbiBwbG90IGl0LCB3ZSdsbCBjb252ZXJ0IHRoZSB0b2FkcyBsYXllciB0byBhIHNmIG9iamVjdC4gQXQgdGhlIHNhbWUgdGltZSwgd2UnbGwgcmVkdWNlIHRoZSBudW1iZXIgb2YgY29sdW1ucyB0byBqdXN0IHRob3NlIHdlIHdhbnQgdG8gaW5jbHVkZSBvbiB0aGUgbWFwLg0KDQpgYGB7ciBjaHVuazA4fQ0KeW9zZV90b2Fkc19zZiA8LSAgeW9zZV90b2Fkc19kZiAlPiUgDQogIHNlbGVjdChsb25naXR1ZGUsIGxhdGl0dWRlLCBkYXRldGltZSwgY29tbW9uX25hbWUsIHNjaWVudGlmaWNfbmFtZSwgaW1hZ2VfdXJsLCB1c2VyX2xvZ2luKSAlPiUgDQogIHN0X2FzX3NmKGNvb3Jkcz1jKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgIGNycz00MzI2KQ0KDQp5b3NlX3RvYWRzX3NmDQpgYGANCg0KIyMgUGxvdCB0aGUgdG9hZHMgd2l0aCBsZWFmbGV0DQoNCldlJ2xsIG1ha2UgdGhlIGludGVyYWN0aXZlIG1hcCB3aXRoIGxlYWZsZXQsIGluc3RlYWQgb2YgdG1hcCwgYmVjYXVzZSBsZWFmbGV0IGdpdmVzIHVzIG1vcmUgY29udHJvbCBvdmVyIHRoZSBwb3B1cCB3aW5kb3dzLg0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byBhZGQgYSBjb2x1bW4gdG8gdGhlIHNmIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBIVE1MIGNvZGUgdGhhdCB3aWxsIGFwcGVhciBpbiB0aGUgcG9wdXAgd2luZG93cy4gV2UnbGwgYWRkIHRoaXMgY29sdW1uIHVzaW5nIG11dGF0ZSwgYW5kIHRoZSBzYXZlIHRoZSByZXN1bHQgYXMgYSBuZXcgc2Ygb2JqZWN0Og0KDQpgYGB7ciBjaHVuazA5fQ0KIyMgQWRkIGEgY29sdW1uIGNvbnRhaW5pbmcgSFRNTCB0aGF0IHdpbGwgYXBwZWFyIGluIHRoZSBwb3B1cCB3aW5kb3dzDQp5b3NlX3RvYWRzX3BvcHVwX3NmIDwtIHlvc2VfdG9hZHNfc2YgJT4lIA0KICBtdXRhdGUocG9wdXBfaHRtbCA9IHBhc3RlMCgiPHA+PGI+IiwgY29tbW9uX25hbWUsICI8L2I+PGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGk+Iiwgc2NpZW50aWZpY19uYW1lLCAiPC9pPjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+T2JzZXJ2ZWQ6ICIsIGRhdGV0aW1lLCAiPGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVXNlcjogIiwgdXNlcl9sb2dpbiwgIjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+PGltZyBzcmM9JyIsIGltYWdlX3VybCwgIicgc3R5bGU9J3dpZHRoOjEwMCU7Jy8+PC9wPiIpDQogICkNCmBgYA0KDQojIyBTZWUgYW4gZXhhbXBsZSBvZiB0aGUgcG9wdXAgSFRNTCANCg0KVGhpcyBpcyB3aGF0IHRoZSBIVE1MIGNvZGUgbG9va3MgbGlrZSBmb3IgdGhlIGZpcnN0IGZlYXR1cmU6DQoNCmBgYHtyIGNodW5rMTB9DQp5b3NlX3RvYWRzX3BvcHVwX3NmJHBvcHVwX2h0bWxbMV0NCmBgYA0KDQpNYWtlIHRoZSBtYXANCg0KYGBge3IgY2h1bmsxMX0NCmxlYWZsZXQoeW9zZV90b2Fkc19wb3B1cF9zZikgJT4lIA0KICBhZGRUaWxlcygpICU+JSANCiAgYWRkQ2lyY2xlTWFya2Vycyhwb3B1cCA9IH5wb3B1cF9odG1sLCByYWRpdXMgPSA1KQ0KYGBgDQoNCg0KIyMgQ0hBTExFTkdFOiBEb3dubG9hZCBhbmQgbWFwIGlOYXR1cmFsaXN0IE9ic2VydmF0aW9ucyBmb3IgdGhlIHRheG9uIG9mIHlvdXIgY2hvaWNlDQoNClNlYXJjaCBmb3IgaU5hdHVyYWxpc3Qgb2JzZXJ2YXRpb25zIGZvciB5b3VyIGZhdm9yaXRlIFlvc2VtaXRlIGFuaW1hbCBvciBwbGFudCBieSBwYXNzaW5nIGEgRmFtaWx5LCBHZW51cyBvciBTcGVjaWVzIHRvIGBnZXRfaW5hdF9vYnMoKWAuIFlvdSBjb3VsZCBzcGVjaWZ5IGZvciBleGFtcGxlICpVcnNpZGFlKiAoYmVhcnMpLCAqU2VxdW9pYWRlbmRyb24qIChTZXF1b2lhKSwgb3IgKkxpbGl1bSogKGxpbGllcykuIFtBbnN3ZXJdKGh0dHBzOi8vYml0Lmx5LzNyRVRYbVgpDQoNCmBgYHtyIGNodW5rMTJ9DQp0YXhvbiA8LSBjKCJVcnNpZGFlIiwgIlNlcXVvaWFkZW5kcm9uIiwgIkxpbGl1bSIpWzFdDQp5b3NlX3RheG9uX3NmIDwtIGdldF9pbmF0X29icyhib3VuZHMgPSB5b3NlX2JuZF9iYltjKDIsMSw0LDMpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRheG9uX25hbWUgPSB0YXhvbikgJT4lIA0KICBzZWxlY3QobG9uZ2l0dWRlLCBsYXRpdHVkZSwgZGF0ZXRpbWUsIGNvbW1vbl9uYW1lLCBzY2llbnRpZmljX25hbWUsIA0KICAgICAgICAgaW1hZ2VfdXJsLCB1c2VyX2xvZ2luKSAlPiUgDQogIHN0X2FzX3NmKGNvb3Jkcz1jKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgIGNycz00MzI2KQ0KDQp5b3NlX3RheG9uX3NmIDwtIHlvc2VfdGF4b25fc2YgJT4lIA0KICBtdXRhdGUocG9wdXBfaHRtbCA9IHBhc3RlMCgiPHA+PGI+IiwgY29tbW9uX25hbWUsICI8L2I+PGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGk+Iiwgc2NpZW50aWZpY19uYW1lLCAiPC9pPjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+T2JzZXJ2ZWQ6ICIsIGRhdGV0aW1lLCAiPGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVXNlcjogIiwgdXNlcl9sb2dpbiwgIjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+PGltZyBzcmM9JyIsIGltYWdlX3VybCwgIicgc3R5bGU9J3dpZHRoOjEwMCU7Jy8+PC9wPiIpKQ0KDQpsZWFmbGV0KHlvc2VfdGF4b25fc2YpICU+JSANCiAgYWRkVGlsZXMoKSAlPiUgDQogIGFkZENpcmNsZU1hcmtlcnMocG9wdXAgPSB+cG9wdXBfaHRtbCwgcmFkaXVzID0gNSkNCmBgYA0KDQojIyBFbmQNCg0KQ29uZ3JhdHVsYXRpb25zLCB5b3UndmUgY29tcGxldGVkIHRoZSBOb3RlYm9vayEgDQoNClRvIHZpZXcgeW91ciBOb3RlYm9vayBhdCBIVE1MLCBzYXZlIGl0IChhZ2FpbiksIHRoZW4gY2xpY2sgdGhlICdQcmV2aWV3JyBidXR0b24gaW4gdGhlIFJTdHVkaW8gdG9vbGJhci4NCg==