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)

## 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.8013 ymin: 37.72702 xmax: -119.3112 ymax: 38.09382
Geodetic CRS:  WGS 84
First 10 features:
                    datetime       common_name     scientific_name user_login                   geometry
1  2021-07-23 09:07:00 -0700        goldenrods            Solidago       leef POINT (-119.3467 38.06381)
2  2021-07-23 09:02:00 -0700 Norwegian Mugwort Artemisia norvegica       leef POINT (-119.3467 38.06381)
3  2021-07-23 08:58:00 -0700      Biscuitroots            Lomatium       leef POINT (-119.3467 38.06381)
4  2021-07-23 08:57:00 -0700                              Hackelia       leef POINT (-119.3467 38.06381)
5  2021-07-23 08:56:00 -0700       groundsmoke          Gayophytum       leef POINT (-119.3467 38.06381)
6  2021-07-23 08:48:00 -0700       mule's ears             Wyethia       leef POINT (-119.3467 38.06381)
7  2021-07-23 08:47:00 -0700                              Horkelia       leef POINT (-119.3467 38.06381)
8  2021-07-23 08:46:00 -0700                           Asteroideae       leef POINT (-119.3467 38.06381)
9  2021-07-23 08:45:00 -0700      Paintbrushes          Castilleja       leef POINT (-119.3467 38.06381)
10 2021-07-23 08:38:00 -0700       true lilies              Lilium       leef POINT (-119.3467 38.06381)

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 df 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>")
  )

Inspect 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

# Your answer here

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.

LS0tDQp0aXRsZTogIkltcG9ydCBhbmQgTWFwIGlOYXR1cmFsaXN0IE9ic2VydmF0aW9ucyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCkluIHRoaXMgTm90ZWJvb2ssIHdlJ2xsIGxlYXJuIGhvdyB0byBpbXBvcnQgb2JzZXJ2YXRpb25zIGZyb20gW2lOYXR1cmFsaXN0XShodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvKSB1c2luZyB0aGUgW3JpbmF0XShodHRwczovL2RvY3Mucm9wZW5zY2kub3JnL3JpbmF0LykgcGFja2FnZS4NCg0KIyMgTG9hZCBQYWNrYWdlcw0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byBsb2FkIHRoZSBwYWNrYWdlcyB3ZSdsbCBuZWVkLiBUaGVzZSBhcmUgYWxsIG9uIENSQU4gaWYgeW91IG5lZWQgdG8gaW5zdGFsbCBhbnkuDQoNCmBgYHtyIGNodW5rMDEsIG1lc3NhZ2UgPSBGQUxTRX0NCmxpYnJhcnkocmluYXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkobGVhZmxldCkNCg0KIyMgTG9hZCB0aGUgY29uZmxpY3RlZCBwYWNrYWdlIGFuZCBzZXQgcHJlZmVyZW5jZXMNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJhcnJhbmdlIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNCiMjIEltcG9ydCB0aGUgUGFyayBCb3VuZGFyeQ0KDQpOZXh0LCB3ZSBpbXBvcnQgdGhlIGJvdW5kYXJ5IG9mIFlvc2VtaXRlIGFuZCBncmFiIGl0cyBib3VuZGluZyBib3guIFdlJ2xsIHVzZSB0aGlzIGxhdGVyIHdoZW4gd2UgY2FsbCBpTmF0dXJhbGlzdCB0byBzcGVjaWZ5IHRoYXQgd2UncmUgd2UncmUgb25seSBpbnRlcmVzdGVkIGluIG9ic2VydmF0aW9ucyBpbiB0aGlzIGFyZWEuDQoNCmBgYHtyIGNodW5rMDJ9DQojIyBJbXBvcnQgdGhlIFlOUCBCb3VuZGFyeQ0KeW9zZV9ibmRfbGwgPC0gc2Y6OnN0X3JlYWQoZHNuPSIuL2RhdGEiLCBsYXllcj0ieW9zZV9ib3VuZGFyeSIpDQoNCiMjIEdldCB0aGUgYm91bmRpbmcgYm94DQp5b3NlX2JuZF9iYiA8LSBzdF9iYm94KHlvc2VfYm5kX2xsKQ0KYXMubnVtZXJpYyh5b3NlX2JuZF9iYikNCmBgYA0KDQojIyBSZXRyaWV2ZSBSZWNlbnQgaU5hdCBPYnNlcnZhdGlvbnMNCg0KTmV4dCwgd2UgY2FuIHJldHJpZXZlIG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgcGFyay4gQnkgZGVmYXVsdCwgdGhlIGBnZXRfaW5hdF9vYnMoKWAgd2lsbCByZXR1cm4gdGhlIG1vc3QgcmVjZW50IDEwMCBvYnNlcnZhdGlvbnM6DQoNCmBgYHtyIGNodW5rMDN9DQojIyBSZXRyaWV2ZSB0aGUgZmlyc3QgMTAwIGlOYXR1cmFsaXN0IG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveC4gDQojIyBOb3RlIHdlIGhhdmUgdG8gcmVhcnJhbmdlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgYm91bmRpbmcgYm94IGEgbGl0dGxlIGJpdCB0byANCiMjIGdpdmUgZ2V0X2luYXRfb2JzKCkgd2hhdCBpdCBleHBlY3RzDQoNCnlvc2VfaW5hdF9kZiA8LSBnZXRfaW5hdF9vYnMoYm91bmRzID0geW9zZV9ibmRfYmJbYygyLDEsNCwzKV0pDQpgYGANCg0KSW5zcGVjdCB0aGUgcmVzdWx0czoNCg0KYGBge3IgY2h1bmswNH0NCmRpbSh5b3NlX2luYXRfZGYpDQpoZWFkKHlvc2VfaW5hdF9kZikNCmBgYA0KDQpJbiBvcmRlciB0byBwbG90IHRoZXNlIG9ic2VydmF0aW9ucywgbGV0J3MgZmlyc3QgY29udmVydCB0aGUgZGF0YSBmcmFtZSBpbnRvIGEgc2Ygb2JqZWN0Og0KDQpgYGB7ciBjaHVuazA1fQ0KeW9zZV9pbmF0X3NmIDwtICB5b3NlX2luYXRfZGYgJT4lIA0KICBzZWxlY3QobG9uZ2l0dWRlLCBsYXRpdHVkZSwgZGF0ZXRpbWUsIGNvbW1vbl9uYW1lLCBzY2llbnRpZmljX25hbWUsIHVzZXJfbG9naW4pICU+JSANCiAgc3RfYXNfc2YoY29vcmRzPWMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCAgY3JzPTQzMjYpDQp5b3NlX2luYXRfc2YNCmBgYA0KDQojIyBQbG90IHdpdGggdG1hcA0KDQpgYGB7ciBjaHVuazA2fQ0KdG1hcF9tb2RlKCJ2aWV3IikNCg0KdG1fc2hhcGUoeW9zZV9ibmRfbGwpICsgDQogIHRtX2JvcmRlcnMoY29sID0gInJlZCIsIGx3ZCA9IDIpICsNCnRtX3NoYXBlKHlvc2VfaW5hdF9zZikgKyANCiAgdG1fc3ltYm9scygpDQpgYGANCg0KIyMgQWRkIGEgVGF4b24gdG8gdGhlIFF1ZXJ5IA0KDQpOZXh0IHdlJ2xsIGEgdGF4b24gdG8gb3VyIHF1ZXJ5LiBZb3NlbWl0ZSBoYXMgc29tZSBlbmRlbWljIHRvYWRzLiBXZSBjYW4gdGVsbCBpTmF0dXJhbGlzdCB3ZSBvbmx5IG9ubHkgd2FudCBjZXJ0YWluIHNwZWNpZXMgdXNpbmcgdGhlIG9wdGlvbmFsIHRheG9uX25hbWUgYXJndW1lbnQuIEhlcmUgd2UnbGwgc2V0IGl0IHRvICBbKkJ1Zm9uaWRhZSpdKGh0dHBzOi8vYW1waGliaWF3ZWIub3JnL2xpc3RzL0J1Zm9uaWRhZS5zaHRtbCkgKHRoZSB0b2FkIGZhbWlseSkuIA0KDQpgYGB7ciBjaHVuazA3fQ0KeW9zZV90b2Fkc19kZiA8LSBnZXRfaW5hdF9vYnMoYm91bmRzID0geW9zZV9ibmRfYmJbYygyLDEsNCwzKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXhvbl9uYW1lID0gIkJ1Zm9uaWRhZSIpDQpkaW0oeW9zZV90b2Fkc19kZikNCmdsaW1wc2UoeW9zZV90b2Fkc19kZiwgd2lkdGggPSAxMTApDQpgYGANCg0KIyMgQ29udmVydCB0aGUgdG9hZHMgZGYgdG8gc2YNCg0KQmVmb3JlIHdlIGNhbiBwbG90IGl0LCB3ZSdsbCBjb252ZXJ0IHRoZSB0b2FkcyBsYXllciB0byBhIHNmIG9iamVjdC4gQXQgdGhlIHNhbWUgdGltZSwgd2UnbGwgcmVkdWNlIHRoZSBudW1iZXIgb2YgY29sdW1ucyB0byBqdXN0IHRob3NlIHdlIHdhbnQgdG8gaW5jbHVkZSBvbiB0aGUgbWFwLg0KDQpgYGB7ciBjaHVuazA4fQ0KeW9zZV90b2Fkc19zZiA8LSAgeW9zZV90b2Fkc19kZiAlPiUgDQogIHNlbGVjdChsb25naXR1ZGUsIGxhdGl0dWRlLCBkYXRldGltZSwgY29tbW9uX25hbWUsIHNjaWVudGlmaWNfbmFtZSwgaW1hZ2VfdXJsLCB1c2VyX2xvZ2luKSAlPiUgDQogIHN0X2FzX3NmKGNvb3Jkcz1jKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgIGNycz00MzI2KQ0KDQp5b3NlX3RvYWRzX3NmDQpgYGANCg0KIyMgUGxvdCB0aGUgdG9hZHMgd2l0aCBsZWFmbGV0DQoNCldlJ2xsIG1ha2UgdGhlIGludGVyYWN0aXZlIG1hcCB3aXRoIGxlYWZsZXQsIGluc3RlYWQgb2YgdG1hcCwgYmVjYXVzZSBsZWFmbGV0IGdpdmVzIHVzIG1vcmUgY29udHJvbCBvdmVyIHRoZSBwb3B1cCB3aW5kb3dzLg0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byBhZGQgYSBjb2x1bW4gdG8gdGhlIHNmIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBIVE1MIGNvZGUgdGhhdCB3aWxsIGFwcGVhciBpbiB0aGUgcG9wdXAgd2luZG93cy4gV2UnbGwgYWRkIHRoaXMgY29sdW1uIHVzaW5nIG11dGF0ZSwgYW5kIHRoZSBzYXZlIHRoZSByZXN1bHQgYXMgYSBuZXcgc2Ygb2JqZWN0Og0KDQpgYGB7ciBjaHVuazA5fQ0KIyMgQWRkIGEgY29sdW1uIGNvbnRhaW5pbmcgSFRNTCB0aGF0IHdpbGwgYXBwZWFyIGluIHRoZSBwb3B1cCB3aW5kb3dzDQp5b3NlX3RvYWRzX3BvcHVwX3NmIDwtIHlvc2VfdG9hZHNfc2YgJT4lIA0KICBtdXRhdGUocG9wdXBfaHRtbCA9IHBhc3RlMCgiPHA+PGI+IiwgY29tbW9uX25hbWUsICI8L2I+PGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGk+Iiwgc2NpZW50aWZpY19uYW1lLCAiPC9pPjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+T2JzZXJ2ZWQ6ICIsIGRhdGV0aW1lLCAiPGJyLz4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVXNlcjogIiwgdXNlcl9sb2dpbiwgIjwvcD4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPHA+PGltZyBzcmM9JyIsIGltYWdlX3VybCwgIicgc3R5bGU9J3dpZHRoOjEwMCU7Jy8+PC9wPiIpDQogICkNCmBgYA0KDQojIyBJbnNwZWN0IHRoZSBQb3B1cCBIVE1MIA0KDQpUaGlzIGlzIHdoYXQgdGhlIEhUTUwgY29kZSBsb29rcyBsaWtlIGZvciB0aGUgZmlyc3QgZmVhdHVyZToNCg0KYGBge3IgY2h1bmsxMH0NCnlvc2VfdG9hZHNfcG9wdXBfc2YkcG9wdXBfaHRtbFsxXQ0KYGBgDQoNCk1ha2UgdGhlIG1hcA0KDQpgYGB7ciBjaHVuazExfQ0KbGVhZmxldCh5b3NlX3RvYWRzX3BvcHVwX3NmKSAlPiUgDQogIGFkZFRpbGVzKCkgJT4lIA0KICBhZGRDaXJjbGVNYXJrZXJzKHBvcHVwID0gfnBvcHVwX2h0bWwsIHJhZGl1cyA9IDUpDQpgYGANCg0KDQojIyBDSEFMTEVOR0U6IERvd25sb2FkIGFuZCBtYXAgaU5hdHVyYWxpc3QgT2JzZXJ2YXRpb25zIGZvciB0aGUgdGF4b24gb2YgeW91ciBjaG9pY2UNCg0KU2VhcmNoIGZvciBpTmF0dXJhbGlzdCBvYnNlcnZhdGlvbnMgZm9yIHlvdXIgZmF2b3JpdGUgWW9zZW1pdGUgYW5pbWFsIG9yIHBsYW50IGJ5IHBhc3NpbmcgYSBGYW1pbHksIEdlbnVzIG9yIFNwZWNpZXMgdG8gYGdldF9pbmF0X29icygpYC4gWW91IGNvdWxkIHNwZWNpZnkgZm9yIGV4YW1wbGUgKlVyc2lkYWUqIChiZWFycyksICpTZXF1b2lhZGVuZHJvbiogKFNlcXVvaWEpLCBvciAqTGlsaXVtKiAobGlsaWVzKS4gW0Fuc3dlcl0oaHR0cHM6Ly9iaXQubHkvM3JFVFhtWCkNCg0KYGBge3IgY2h1bmsxMn0NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgRW5kDQoNCkNvbmdyYXR1bGF0aW9ucywgeW91J3ZlIGNvbXBsZXRlZCB0aGUgTm90ZWJvb2shIA0KDQpUbyB2aWV3IHlvdXIgTm90ZWJvb2sgYXQgSFRNTCwgc2F2ZSBpdCAoYWdhaW4pLCB0aGVuIGNsaWNrIHRoZSAnUHJldmlldycgYnV0dG9uIGluIHRoZSBSU3R1ZGlvIHRvb2xiYXIuDQo=