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_scgis23\exercises\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)], year = 2022)

Inspect the results:

dim(yose_inat_df)
[1] 100  37
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.8834 ymin: 37.50313 xmax: -119.1968 ymax: 38.1744
Geodetic CRS:  WGS 84
First 10 features:
                    datetime                       common_name               scientific_name
1  2022-04-14 16:46:01 -0700                   Pacific Dogwood              Cornus nuttallii
2  2022-06-18 19:54:14 -0700                     Steller's Jay           Cyanocitta stelleri
3  2022-05-29 19:52:44 -0700                  flowering plants                  Angiospermae
4  2022-06-25 12:54:00 -0700 Northern Thick-billed Fox Sparrow Passerella iliaca megarhyncha
5  2022-06-25 22:01:00 -0700           Gray-crowned Rosy-Finch       Leucosticte tephrocotis
6  2022-07-23 08:27:08 -0700                  sulfur buckwheat          Eriogonum umbellatum
7  2022-06-25 20:06:00 -0700             Yellow-bellied Marmot          Marmota flaviventris
8  2022-08-28 11:29:00 -0700               Brewer's cinquefoil            Potentilla breweri
9  2022-08-28 11:34:00 -0700        Lemmon's Indian Paintbrush           Castilleja lemmonii
10 2022-08-28 10:58:00 -0700              Greater Fritillaries                      Argynnis
          user_login                   geometry
1         frank16962  POINT (-119.5572 37.7348)
2              hueyl POINT (-119.6005 37.74141)
3           mmnk1997  POINT (-119.5804 37.7018)
4    cameronjohnson2  POINT (-119.5383 37.8651)
5    cameronjohnson2 POINT (-119.6235 37.92146)
6        owen_hill10 POINT (-119.3716 37.87855)
7    cameronjohnson2 POINT (-119.4805 37.86814)
8  strixoccidentalis POINT (-119.2531 37.91316)
9  strixoccidentalis   POINT (-119.253 37.9132)
10 strixoccidentalis  POINT (-119.2525 37.9137)

Plot with tmap

tmap_mode("plot")
tmap mode set to plotting
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  37
glimpse(yose_toads_df, width = 110)
Rows: 100
Columns: 37
$ scientific_name                  <chr> "Anaxyrus", "Anaxyrus", "Anaxyrus canorus", "Bufonidae", "Anaxyrus"…
$ datetime                         <chr> "2023-08-08 11:31:25 -0700", "2023-08-08 11:29:18 -0700", "2023-07-…
$ description                      <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",…
$ place_guess                      <chr> "Yosemite National Park, Groveland, CA, US", "Yosemite National Par…
$ latitude                         <dbl> 37.85569, 37.85571, 37.81554, 37.96541, 37.74634, 37.87759, 37.9358…
$ longitude                        <dbl> -119.2157, -119.2160, -119.3609, -119.2672, -119.5735, -119.3770, -…
$ tag_list                         <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",…
$ common_name                      <chr> "North American Toads", "North American Toads", "Yosemite Toad", "T…
$ url                              <chr> "https://www.inaturalist.org/observations/178327407", "https://www.…
$ image_url                        <chr> "https://static.inaturalist.org/photos/310290293/medium.jpg", "http…
$ user_login                       <chr> "seancarson_", "seancarson_", "ljfilgas", "legolaws", "drjuliariley…
$ id                               <int> 178327407, 178326611, 178239691, 178191266, 178123790, 177561494, 1…
$ species_guess                    <chr> "", "", "", "True Toads", "", "True Toads", "Yosemite Toad", "Yosem…
$ iconic_taxon_name                <chr> "Amphibia", "Amphibia", "Amphibia", "Amphibia", "Amphibia", "Amphib…
$ taxon_id                         <int> 64747, 64747, 64972, 21359, 64747, 21359, 64972, 64972, 64972, 6497…
$ num_identification_agreements    <int> 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 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> "2023-08-08 11:31:25-07:00", "2023-08-08 11:29:18-07:00", "2023-07-…
$ observed_on                      <chr> "2023-08-08", "2023-08-08", "2023-07-31", "2023-08-12", "2023-08-12…
$ time_observed_at                 <chr> "2023-08-08 18:31:25 UTC", "2023-08-08 18:29:18 UTC", "2023-07-31 2…
$ time_zone                        <chr> "Pacific Time (US & Canada)", "Pacific Time (US & Canada)", "Pacifi…
$ positional_accuracy              <int> 9, 7, 7, 8, 24, 188, NA, 4, NA, NA, 9, 5262, NA, 23, 25028, 43, 4, …
$ public_positional_accuracy       <int> 9, 7, 28329, 8, 24, 188, 28329, 28329, 28329, 28329, 28329, 5262, N…
$ geoprivacy                       <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "obscured",…
$ taxon_geoprivacy                 <chr> "", "", "obscured", "", "", "", "obscured", "obscured", "obscured",…
$ coordinates_obscured             <chr> "false", "false", "true", "false", "false", "false", "true", "true"…
$ positioning_method               <chr> "", "", "", "", "", "gps", "", "", "", "", "", "", "", "", "", "", …
$ positioning_device               <chr> "", "", "", "", "", "gps", "", "", "", "", "", "", "", "", "", "", …
$ user_id                          <int> 923276, 923276, 2301182, 223936, 2048508, 2954954, 10326, 2383135, …
$ user_name                        <chr> "Sean Carson", "Sean Carson", "", "Eric Laws", "Dr. Julia Riley", "…
$ created_at                       <chr> "2023-08-14 06:55:28 UTC", "2023-08-14 06:42:45 UTC", "2023-08-13 1…
$ updated_at                       <chr> "2023-08-14 06:55:37 UTC", "2023-08-14 06:55:25 UTC", "2023-08-13 1…
$ quality_grade                    <chr> "needs_id", "needs_id", "needs_id", "needs_id", "casual", "needs_id…
$ license                          <chr> "", "", "CC-BY-NC", "CC-BY-NC", "", "", "CC-BY-NC-ND", "", "CC-BY-N…
$ sound_url                        <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",…
$ oauth_application_id             <int> 3, 3, 3, NA, 3, 2, NA, 3, NA, NA, 3, 3, 2, 3, NA, 3, 3, 3, 2, NA, 3…
$ 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.8798 ymin: 37.49542 xmax: -119.2021 ymax: 38.11162
Geodetic CRS:  WGS 84
First 10 features:
                    datetime          common_name  scientific_name
1  2023-08-08 11:31:25 -0700 North American Toads         Anaxyrus
2  2023-08-08 11:29:18 -0700 North American Toads         Anaxyrus
3  2023-07-31 16:20:32 -0700        Yosemite Toad Anaxyrus canorus
4  2023-08-12 15:39:00 -0700           True Toads        Bufonidae
5  2023-08-12 21:18:05 -0700 North American Toads         Anaxyrus
6  2023-08-04 13:55:47 -0700           True Toads        Bufonidae
7  2023-07-31 09:17:00 -0700        Yosemite Toad Anaxyrus canorus
8  2023-08-04 13:24:09 -0700        Yosemite Toad Anaxyrus canorus
9  2023-07-31 07:37:00 -0700        Yosemite Toad Anaxyrus canorus
10 2023-07-31 07:42:00 -0700        Yosemite Toad Anaxyrus canorus
                                                                     image_url       user_login
1                   https://static.inaturalist.org/photos/310290293/medium.jpg      seancarson_
2                   https://static.inaturalist.org/photos/310290266/medium.jpg      seancarson_
3   https://inaturalist-open-data.s3.amazonaws.com/photos/310116072/medium.jpg         ljfilgas
4  https://inaturalist-open-data.s3.amazonaws.com/photos/310014015/medium.jpeg         legolaws
5                                                                                  drjuliariley
6                  https://static.inaturalist.org/photos/308836043/medium.jpeg jessicafriedman1
7  https://inaturalist-open-data.s3.amazonaws.com/photos/307573855/medium.jpeg        natureali
8                   https://static.inaturalist.org/photos/307216487/medium.jpg            nyasa
9  https://inaturalist-open-data.s3.amazonaws.com/photos/306369732/medium.jpeg  twillrichardson
10 https://inaturalist-open-data.s3.amazonaws.com/photos/306369714/medium.jpeg  twillrichardson
                     geometry
1  POINT (-119.2157 37.85569)
2   POINT (-119.216 37.85571)
3  POINT (-119.3609 37.81554)
4  POINT (-119.2672 37.96541)
5  POINT (-119.5735 37.74634)
6   POINT (-119.377 37.87759)
7  POINT (-119.3918 37.93588)
8  POINT (-119.2021 37.88905)
9   POINT (-119.2878 37.9705)
10 POINT (-119.3019 37.96713)

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>North American Toads</b><br/><i>Anaxyrus</i></p><p>Observed: 2023-08-08 11:31:25 -0700<br/>User: seancarson_</p><p><img src='https://static.inaturalist.org/photos/310290293/medium.jpg' 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]
## 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.

LS0tDQp0aXRsZTogIkltcG9ydCBhbmQgTWFwIGlOYXR1cmFsaXN0IE9ic2VydmF0aW9ucyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCkluIHRoaXMgTm90ZWJvb2ssIHdlJ2xsIGxlYXJuIGhvdyB0byBpbXBvcnQgb2JzZXJ2YXRpb25zIGZyb20gW2lOYXR1cmFsaXN0XShodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvKSB1c2luZyB0aGUgW3JpbmF0XShodHRwczovL2RvY3Mucm9wZW5zY2kub3JnL3JpbmF0LykgcGFja2FnZS4NCg0KIyMgTG9hZCBQYWNrYWdlcw0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byBsb2FkIHRoZSBwYWNrYWdlcyB3ZSdsbCBuZWVkLiBUaGVzZSBhcmUgYWxsIG9uIENSQU4gaWYgeW91IG5lZWQgdG8gaW5zdGFsbCBhbnkuDQoNCmBgYHtyIGNodW5rMDEsIG1lc3NhZ2UgPSBGQUxTRX0NCmxpYnJhcnkocmluYXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkobGVhZmxldCkNCg0KIyMgTG9hZCB0aGUgY29uZmxpY3RlZCBwYWNrYWdlIGFuZCBzZXQgcHJlZmVyZW5jZXMNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmNvbmZsaWN0X3ByZWZlcigiZmlsdGVyIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJjb3VudCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KY29uZmxpY3RfcHJlZmVyKCJhcnJhbmdlIiwgImRwbHlyIiwgcXVpZXQgPSBUUlVFKQ0KYGBgDQoNCiMjIEltcG9ydCB0aGUgUGFyayBCb3VuZGFyeQ0KDQpOZXh0LCB3ZSBpbXBvcnQgdGhlIGJvdW5kYXJ5IG9mIFlvc2VtaXRlIGFuZCBncmFiIGl0cyBib3VuZGluZyBib3guIFdlJ2xsIHVzZSB0aGlzIGxhdGVyIHdoZW4gd2UgY2FsbCBpTmF0dXJhbGlzdCB0byBzcGVjaWZ5IHRoYXQgd2UncmUgd2UncmUgb25seSBpbnRlcmVzdGVkIGluIG9ic2VydmF0aW9ucyBpbiB0aGlzIGFyZWEuDQoNCmBgYHtyIGNodW5rMDJ9DQojIyBJbXBvcnQgdGhlIFlOUCBCb3VuZGFyeQ0KeW9zZV9ibmRfbGwgPC0gc2Y6OnN0X3JlYWQoZHNuPSIuL2RhdGEiLCBsYXllcj0ieW9zZV9ib3VuZGFyeSIpDQoNCiMjIEdldCB0aGUgYm91bmRpbmcgYm94DQp5b3NlX2JuZF9iYiA8LSBzdF9iYm94KHlvc2VfYm5kX2xsKQ0KYXMubnVtZXJpYyh5b3NlX2JuZF9iYikNCmBgYA0KDQojIyBSZXRyaWV2ZSBSZWNlbnQgaU5hdCBvYnNlcnZhdGlvbnMNCg0KTmV4dCwgd2UgY2FuIHJldHJpZXZlIG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveCBvZiB0aGUgcGFyay4gQnkgZGVmYXVsdCwgdGhlIGBnZXRfaW5hdF9vYnMoKWAgd2lsbCByZXR1cm4gdGhlIG1vc3QgcmVjZW50IDEwMCBvYnNlcnZhdGlvbnM6DQoNCmBgYHtyIGNodW5rMDN9DQojIyBSZXRyaWV2ZSB0aGUgZmlyc3QgMTAwIGlOYXR1cmFsaXN0IG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlIGJvdW5kaW5nIGJveC4gDQojIyBOb3RlIHdlIGhhdmUgdG8gcmVhcnJhbmdlIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgYm91bmRpbmcgYm94IGEgbGl0dGxlIGJpdCB0byANCiMjIGdpdmUgZ2V0X2luYXRfb2JzKCkgd2hhdCBpdCBleHBlY3RzDQoNCnlvc2VfaW5hdF9kZiA8LSBnZXRfaW5hdF9vYnMoYm91bmRzID0geW9zZV9ibmRfYmJbYygyLDEsNCwzKV0sIHllYXIgPSAyMDIyKQ0KYGBgDQoNCkluc3BlY3QgdGhlIHJlc3VsdHM6DQoNCmBgYHtyIGNodW5rMDR9DQpkaW0oeW9zZV9pbmF0X2RmKQ0KaGVhZCh5b3NlX2luYXRfZGYpDQpgYGANCg0KSW4gb3JkZXIgdG8gcGxvdCB0aGVzZSBvYnNlcnZhdGlvbnMsIGxldCdzIGZpcnN0IGNvbnZlcnQgdGhlIGRhdGEgZnJhbWUgaW50byBhIHNmIG9iamVjdDoNCg0KYGBge3IgY2h1bmswNX0NCnlvc2VfaW5hdF9zZiA8LSAgeW9zZV9pbmF0X2RmICU+JSANCiAgc2VsZWN0KGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGRhdGV0aW1lLCBjb21tb25fbmFtZSwgc2NpZW50aWZpY19uYW1lLCB1c2VyX2xvZ2luKSAlPiUgDQogIHN0X2FzX3NmKGNvb3Jkcz1jKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgIGNycz00MzI2KQ0KeW9zZV9pbmF0X3NmDQpgYGANCg0KIyMgUGxvdCB3aXRoIHRtYXANCg0KYGBge3IgY2h1bmswNn0NCnRtYXBfbW9kZSgicGxvdCIpDQoNCnRtX3NoYXBlKHlvc2VfYm5kX2xsKSArIA0KICB0bV9ib3JkZXJzKGNvbCA9ICJyZWQiLCBsd2QgPSAyKSArDQp0bV9zaGFwZSh5b3NlX2luYXRfc2YpICsgDQogIHRtX3N5bWJvbHMoKQ0KYGBgDQoNCiMjIEFkZCBhIFRheG9uIHRvIHRoZSBxdWVyeSANCg0KTmV4dCB3ZSdsbCBhIHRheG9uIHRvIG91ciBxdWVyeS4gWW9zZW1pdGUgaGFzIHNvbWUgZW5kZW1pYyB0b2Fkcy4gV2UgY2FuIHRlbGwgaU5hdHVyYWxpc3Qgd2Ugb25seSBvbmx5IHdhbnQgY2VydGFpbiBzcGVjaWVzIHVzaW5nIHRoZSBvcHRpb25hbCB0YXhvbl9uYW1lIGFyZ3VtZW50LiBIZXJlIHdlJ2xsIHNldCBpdCB0byAgWypCdWZvbmlkYWUqXShodHRwczovL2FtcGhpYmlhd2ViLm9yZy9saXN0cy9CdWZvbmlkYWUuc2h0bWwpICh0aGUgdG9hZCBmYW1pbHkpLiANCg0KYGBge3IgY2h1bmswN30NCnlvc2VfdG9hZHNfZGYgPC0gZ2V0X2luYXRfb2JzKGJvdW5kcyA9IHlvc2VfYm5kX2JiW2MoMiwxLDQsMyldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b25fbmFtZSA9ICJCdWZvbmlkYWUiKQ0KZGltKHlvc2VfdG9hZHNfZGYpDQpnbGltcHNlKHlvc2VfdG9hZHNfZGYsIHdpZHRoID0gMTEwKQ0KYGBgDQoNCiMjIENvbnZlcnQgdGhlIHRvYWRzIHRvIHNmDQoNCkJlZm9yZSB3ZSBjYW4gcGxvdCBpdCwgd2UnbGwgY29udmVydCB0aGUgdG9hZHMgbGF5ZXIgdG8gYSBzZiBvYmplY3QuIEF0IHRoZSBzYW1lIHRpbWUsIHdlJ2xsIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgdG8ganVzdCB0aG9zZSB3ZSB3YW50IHRvIGluY2x1ZGUgb24gdGhlIG1hcC4NCg0KYGBge3IgY2h1bmswOH0NCnlvc2VfdG9hZHNfc2YgPC0gIHlvc2VfdG9hZHNfZGYgJT4lIA0KICBzZWxlY3QobG9uZ2l0dWRlLCBsYXRpdHVkZSwgZGF0ZXRpbWUsIGNvbW1vbl9uYW1lLCBzY2llbnRpZmljX25hbWUsIGltYWdlX3VybCwgdXNlcl9sb2dpbikgJT4lIA0KICBzdF9hc19zZihjb29yZHM9YygibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksICBjcnM9NDMyNikNCg0KeW9zZV90b2Fkc19zZg0KYGBgDQoNCiMjIFBsb3QgdGhlIHRvYWRzIHdpdGggbGVhZmxldA0KDQpXZSdsbCBtYWtlIHRoZSBpbnRlcmFjdGl2ZSBtYXAgd2l0aCBsZWFmbGV0LCBpbnN0ZWFkIG9mIHRtYXAsIGJlY2F1c2UgbGVhZmxldCBnaXZlcyB1cyBtb3JlIGNvbnRyb2wgb3ZlciB0aGUgcG9wdXAgd2luZG93cy4NCg0KVGhlIGZpcnN0IHN0ZXAgaXMgdG8gYWRkIGEgY29sdW1uIHRvIHRoZSBzZiBvYmplY3QgdGhhdCBjb250YWlucyB0aGUgSFRNTCBjb2RlIHRoYXQgd2lsbCBhcHBlYXIgaW4gdGhlIHBvcHVwIHdpbmRvd3MuIFdlJ2xsIGFkZCB0aGlzIGNvbHVtbiB1c2luZyBtdXRhdGUsIGFuZCB0aGUgc2F2ZSB0aGUgcmVzdWx0IGFzIGEgbmV3IHNmIG9iamVjdDoNCg0KYGBge3IgY2h1bmswOX0NCiMjIEFkZCBhIGNvbHVtbiBjb250YWluaW5nIEhUTUwgdGhhdCB3aWxsIGFwcGVhciBpbiB0aGUgcG9wdXAgd2luZG93cw0KeW9zZV90b2Fkc19wb3B1cF9zZiA8LSB5b3NlX3RvYWRzX3NmICU+JSANCiAgbXV0YXRlKHBvcHVwX2h0bWwgPSBwYXN0ZTAoIjxwPjxiPiIsIGNvbW1vbl9uYW1lLCAiPC9iPjxici8+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxpPiIsIHNjaWVudGlmaWNfbmFtZSwgIjwvaT48L3A+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxwPk9ic2VydmVkOiAiLCBkYXRldGltZSwgIjxici8+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVzZXI6ICIsIHVzZXJfbG9naW4sICI8L3A+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxwPjxpbWcgc3JjPSciLCBpbWFnZV91cmwsICInIHN0eWxlPSd3aWR0aDoxMDAlOycvPjwvcD4iKQ0KICApDQpgYGANCg0KIyMgU2VlIGFuIGV4YW1wbGUgb2YgdGhlIHBvcHVwIEhUTUwgDQoNClRoaXMgaXMgd2hhdCB0aGUgSFRNTCBjb2RlIGxvb2tzIGxpa2UgZm9yIHRoZSBmaXJzdCBmZWF0dXJlOg0KDQpgYGB7ciBjaHVuazEwfQ0KeW9zZV90b2Fkc19wb3B1cF9zZiRwb3B1cF9odG1sWzFdDQpgYGANCg0KTWFrZSB0aGUgbWFwDQoNCmBgYHtyIGNodW5rMTF9DQpsZWFmbGV0KHlvc2VfdG9hZHNfcG9wdXBfc2YpICU+JSANCiAgYWRkVGlsZXMoKSAlPiUgDQogIGFkZENpcmNsZU1hcmtlcnMocG9wdXAgPSB+cG9wdXBfaHRtbCwgcmFkaXVzID0gNSkNCmBgYA0KDQoNCiMjIENIQUxMRU5HRTogRG93bmxvYWQgYW5kIG1hcCBpTmF0dXJhbGlzdCBPYnNlcnZhdGlvbnMgZm9yIHRoZSB0YXhvbiBvZiB5b3VyIGNob2ljZQ0KDQpTZWFyY2ggZm9yIGlOYXR1cmFsaXN0IG9ic2VydmF0aW9ucyBmb3IgeW91ciBmYXZvcml0ZSBZb3NlbWl0ZSBhbmltYWwgb3IgcGxhbnQgYnkgcGFzc2luZyBhIEZhbWlseSwgR2VudXMgb3IgU3BlY2llcyB0byBgZ2V0X2luYXRfb2JzKClgLiBZb3UgY291bGQgc3BlY2lmeSBmb3IgZXhhbXBsZSAqVXJzaWRhZSogKGJlYXJzKSwgKlNlcXVvaWFkZW5kcm9uKiAoU2VxdW9pYSksIG9yICpMaWxpdW0qIChsaWxpZXMpLiBbQW5zd2VyXShodHRwczovL2JpdC5seS8zckVUWG1YKQ0KDQpgYGB7ciBjaHVuazEyfQ0KdGF4b24gPC0gYygiVXJzaWRhZSIsICJTZXF1b2lhZGVuZHJvbiIsICJMaWxpdW0iKVsxXQ0KIyMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KIyMgRW5kDQoNCkNvbmdyYXR1bGF0aW9ucywgeW91J3ZlIGNvbXBsZXRlZCB0aGUgTm90ZWJvb2shIA0KDQpUbyB2aWV3IHlvdXIgTm90ZWJvb2sgYXQgSFRNTCwgc2F2ZSBpdCAoYWdhaW4pLCB0aGVuIGNsaWNrIHRoZSAnUHJldmlldycgYnV0dG9uIGluIHRoZSBSU3R1ZGlvIHRvb2xiYXIuDQo=