diff --git a/config.yaml b/config.yaml
index 9d36f7f7..fa90f684 100644
--- a/config.yaml
+++ b/config.yaml
@@ -14,7 +14,7 @@ carpentry: 'incubator'
title: 'Geospatial Data Carpentry for Urbanism'
# Date the lesson was created (YYYY-MM-DD, this is empty by default)
-created:
+created:
# Comma-separated list of keywords for the lesson
keywords: 'software, data, lesson, The Carpentries, geospatial'
@@ -33,7 +33,7 @@ source: 'https://github.com/carpentries-incubator/r-geospatial-urban'
branch: 'main'
# Who to contact if there are any issues
-contact: 'c.forgaci@tudelft.nl'
+contact: 'rbanism@tudelft.nl'
# Navigation ------------------------------------------------
#
@@ -58,6 +58,7 @@ contact: 'c.forgaci@tudelft.nl'
# - another-learner.md
# Order of episodes in your lesson
+
episodes:
- 01-intro-to-r.Rmd
- 02-data-structures.Rmd
@@ -73,6 +74,8 @@ episodes:
- 15-reproject-raster-data.Rmd
- 16-raster-calculations.Rmd
- 17-work-with-multi-band-rasters.Rmd
+- 18-import-and-visualise-osm-data.Rmd
+- 19-basic-gis-with-r-sf.Rmd
# Information for Learners
learners:
diff --git a/episodes/.DS_Store b/episodes/.DS_Store
new file mode 100644
index 00000000..001027e6
Binary files /dev/null and b/episodes/.DS_Store differ
diff --git a/episodes/18-import-and-visualise-osm-data.Rmd b/episodes/18-import-and-visualise-osm-data.Rmd
new file mode 100644
index 00000000..4c4ebc11
--- /dev/null
+++ b/episodes/18-import-and-visualise-osm-data.Rmd
@@ -0,0 +1,241 @@
+---
+title: 'Import and Visualise OSM Data'
+teaching: 45
+exercises: 25
+---
+
+:::::::::::::::::::::::::::::::::::::: questions
+
+- How to import and work with vector data from OpenStreetMap?
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
+::::::::::::::::::::::::::::::::::::: objectives
+
+After completing this episode, participants should be able to…
+
+- Import OSM vector data from the API
+- Select and manipulate OSM vector data
+- Visualise and map OSM Vector data
+- Use Leaflet for interactive mapping
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
+```{r setup, include=FALSE}
+knitr::opts_chunk$set(warning = FALSE, message = FALSE)
+```
+
+## What is OpenStreetMap?
+
+OpenStreetMap (OSM) is a collaborative project which aims at mapping the world and sharing geospatial data in an open way. Anyone can contribute, by mapping geographical objects they encounter, by adding topical information on existing map objects (their name, function, capacity, etc.), or by mapping buildings and roads from satellite imagery (cf. [HOT: Humanitarian OpenStreetMap Team](https://www.hotosm.org/)).
+
+This information is then validated by other users and eventually added to the common "map" or information system. This ensures that the information is accessible, open, verified, accurate and up-to-date.
+
+The result looks like this:
+![](fig/OSM1.png)
+
+The geospatial data underlying this interface is made of geometrical objects (i.e. points, lines, polygons) and their associated tags (#building #height, #road #secondary #90kph, etc.).
+
+## How to extract geospatial data from OpenStreetMap?
+
+```{r message=FALSE}
+library(tidyverse)
+library(sf)
+assign("has_internet_via_proxy", TRUE, environment(curl::has_internet))
+
+```
+
+
+### Bounding box
+
+The first thing to do is to define the area within which you want to retrieve data, aka the *bounding box*. This can be defined easily using a place name and the package `nominatimlite` to access the free Nominatim API provided by OpenStreetMap.
+
+We are going to look at *Brielle* together, but you can also work with the small cities of *Naarden*, *Geertruidenberg*, *Gorinchem*, *Enkhuizen* or *Dokkum*.
+
+
+
+
+
+We first geocode our spatial text search and extract the corresponding polygon (`geo_lite_sf`) and then extract its bounding box (`st_bbox`).
+
+```{r}
+#install.packages("nominatimlite")
+library(nominatimlite)
+
+nominatim_polygon <- geo_lite_sf(address = "Brielle", points_only = FALSE)
+bb <- st_bbox(nominatim_polygon)
+bb
+```
+
+::::::::::::::::::::::::::::::::::::: callout
+
+If you encounter an error linked to your internet proxy ("Error: Overpass query unavailable without internet R"), run this line of code. It might not be needed, but ensures that your machine knows it has internet.
+
+```{r}
+assign("has_internet_via_proxy", TRUE, environment(curl::has_internet))
+```
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
+
+### A word of caution
+
+There might be multiple responses from the API query, corresponding to different objects at the same location, or different objects at different locations.
+For example: Brielle (Netherlands) and Brielle (New Jersey)
+
+![Brielle, Netherlands](fig/Brielle_NL.jpeg){width=40%}
+
+![Brielle, New Jersey](fig/Brielle_NJ.jpeg "Brielle, New Jersey"){width=40%}
+
+By default, `geo_lite_sf()` from the `nominatimlite` package returns the first item. This means that regardless of the number of returned locations with the given name, the function will return a bounding box and it might be that we are not looking for the first item. We should therefore try to be as unambiguous as possible by adding a country code or district name.
+
+```{r}
+nominatim_polygon <- geo_lite_sf(address = "Brielle, NL", points_only = FALSE)
+bb <- st_bbox(nominatim_polygon)
+bb
+```
+
+
+
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: instructor
+
+If this does not work for some reason, the `nominatim_polygon` can be found in the data folder: "episodes/data/boundingboxBrielle.shp".
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+
+
+## Extracting features
+
+A [feature](https://wiki.openstreetmap.org/wiki/Map_features) in the OSM language is a category or tag of a geospatial object. Features are described by general keys (e.g. "building", "boundary", "landuse", "highway"), themselves decomposed into sub-categories (values) such as "farm", "hotel" or "house" for `buildings`, "motorway", "secondary" and "residential" for `highway`. This determines how they are represented on the map.
+
+
+### Searching documentation
+
+Let's say we want to download data from OpenStreetMap and we know there is a package for it named `osmdata`, but we don't know which function to use and what arguments are needed. Where should we start?
+
+Let's check the documentation [online](https://docs.ropensci.org/osmdata/):
+
+![The OSMdata Documentation page](fig/osmdata.png){width=80%}
+
+It appears that there is a function to extract features, using the Overpass API. This function is `opq()` (for OverPassQuery) which, in combination with `add_osm_feature()`, seems to do the job. However, it might not be crystal clear how to apply it to our case. Let's click on the function name in the documentation to find out more.
+
+![The Overpass Query Documentation page](fig/opq.png){width=80%}
+
+
+
+On this page we can read about the arguments needed for each function: a bounding box for `opq()` and some `key` and `value` for `add_osm_feature()`. Thanks to the examples provided, we can assume that these keys and values correspond to different levels of tags from the OSM classification. In our case, we will keep it at the first level of classification, with "buildings" as `key`, and no value. We also see from the examples that another function is needed when working with the `sf` package: `osmdata_sf()`. This ensures that the type of object is suited for `sf`. With these tips and examples, we can write our feature extraction function as follows:
+
+
+```{r}
+#install.packages("osmdata")
+library(osmdata)
+
+
+x <- opq(bbox = bb) %>%
+ add_osm_feature(key = 'building') %>%
+ osmdata_sf()
+```
+
+
+
+### Structure of objects
+
+What is this `x` object made of? It is a data frame of all the buildings contained in the bounding box, which gives us their OSM id, their geometry and a range of attributes, such as their name, building material, building date, etc. The completion level of this data frame depends on user contributions and open resources. Here, for instance, the national [BAG](https://data.overheid.nl/dataset/10491-bag) dataset was used and it is quite complete, but that is different in other countries.
+
+
+
+```{r}
+str(x$osm_polygons)
+```
+
+
+
+## Mapping
+
+
+Let's map the building age of post-1900 Brielle buildings.
+
+
+### Projections
+
+First, we are going to select the polygons and reproject them with the Amersfoort/RD New projection, suited for maps centred on the Netherlands. This code for this projection is: 28992.
+
+```{r}
+buildings <- x$osm_polygons %>%
+ st_transform(.,crs=28992)
+```
+
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: instructor
+
+If this does not work for some reason, the `buildings` can be found in the data folder: "episodes/data/dataBrielle.shp".
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+
+
+### Visualisation
+
+Then we create a new variable using the threshold at 1900. Every date before 1900 will be recoded as `1900`, so that buildings older than 1900 will be represented with the same shade.
+
+Then we use the `ggplot()` function to visualise the buildings by age. The specific function to represent information as a map is `geom_sf()`. The rest works like other graphs and visualisation, with `aes()` for the aesthetics.
+
+```{r}
+start_date <- as.numeric(buildings$start_date)
+buildings$build_date <- if_else(start_date < 1900, 1900, start_date)
+
+ ggplot(data = buildings) +
+ geom_sf(aes(fill = build_date, colour=build_date)) +
+ scale_fill_viridis_c(option = "viridis")+
+ scale_colour_viridis_c(option = "viridis")
+```
+
+So this reveals the historical centre of Brielle (or the city you chose) and the various urban extensions through time.
+Anything odd? What? Around the centre? Why these limits / isolated points?
+
+
+
+::::::::::::::::::::::::::::::::::::: challenge
+
+## Challenge: import an interactive basemap layer under the buildings with `Leaflet` (20min)
+
+- Check out the [leaflet package documentation](https://rstudio.github.io/leaflet/)
+- Plot a basemap in Leaflet and try different tiles in the [basemap documentation](https://rstudio.github.io/leaflet/basemaps.html)
+- Transform the buildings into WGS84 projection and add them to the basemap layer with the `addPolygons()` function.
+- Have the `fillColor` of these polygons represent the `build_date` variable. See the [choropleth documentation](https://rstudio.github.io/leaflet/choropleths.html) for use of colors. Tip: use the examples given in the documentation and replace the variable names where needed.
+
+:::::::::::::::::::::::: solution
+
+## One solution
+
+```{r}
+#install.packages("leaflet")
+library(leaflet)
+
+
+buildings2 <- buildings %>%
+ st_transform(.,crs=4326)
+
+leaflet(buildings2) %>%
+# addTiles()
+ addProviderTiles(providers$CartoDB.Positron) %>%
+ addPolygons(color = "#444444", weight = 0.1, smoothFactor = 0.5,
+ opacity = 0.2, fillOpacity = 0.8,
+ fillColor = ~colorQuantile("YlGnBu", -build_date)(-build_date),
+ highlightOptions = highlightOptions(color = "white", weight = 2,
+ bringToFront = TRUE))
+```
+
+:::::::::::::::::::::::::::::::::
+
+:::::::::::::::::::::::::::::::::::::
+
+::::::::::::::::::::::::::::::::::::: keypoints
+
+- Use the `Nominatim` and `Overpass` APIs within R
+- Use the `osmdata` and `nominatimlite` packages to retrieve geospatial data
+- Select features and attributes among OSM tags
+- Use the `ggplot`, `sf` and `leaflet` packages to map data
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
diff --git a/episodes/19-basic-gis-with-r-sf.Rmd b/episodes/19-basic-gis-with-r-sf.Rmd
new file mode 100644
index 00000000..669b18c8
--- /dev/null
+++ b/episodes/19-basic-gis-with-r-sf.Rmd
@@ -0,0 +1,276 @@
+---
+title: 'Basic GIS operations with R and sf'
+teaching: 45
+exercises: 25
+---
+
+:::::::::::::::::::::::::::::::::::::: questions
+
+- How to perform basic GIS operations with the `sf` package?
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
+::::::::::::::::::::::::::::::::::::: objectives
+
+After completing this episode, participants should be able to…
+
+- Perform geoprocessing operations such as unions, joins and intersections with dedicated functions from the `sf` package
+- Compute the area of spatial polygons
+- Create buffers and centroids
+- Map the results
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+```{r setup, include=FALSE}
+knitr::opts_chunk$set(warning = FALSE, message = FALSE)
+```
+
+
+```{r message=FALSE}
+library(tidyverse)
+library(sf)
+library(osmdata)
+library(leaflet)
+library(nominatimlite)
+assign("has_internet_via_proxy", TRUE, environment(curl::has_internet))
+
+```
+
+
+## Why `sf` for GIS?
+
+As introduced in [an earlier lesson](episodes/09-open-and-plot-vector-layers.Rmd), `sf` is a package which supports simple features (sf), ["a standardized way to
+encode spatial vector data."](https://cran.r-project.org/web/packages/sf/sf.pdf).
+It contains a large set of functions to achieve all the operations on vector spatial data for which you might use traditional GIS software: change the coordinate system, join layers, intersect or unite polygons, create buffers and centroids, etc. cf. the `sf` [cheatsheet](https://raw.githubusercontent.com/rstudio/cheatsheets/main/sf.pdf).
+
+
+### Conservation in Brielle, NL
+
+Let's focus on old buildings and imagine we're in charge of their conservation. We want to know how much of the city would be affected by a non-construction zone of 100m around pre-1800 buildings.
+
+Let's select them and see where they are.
+
+```{r}
+nominatim_polygon <- nominatimlite::geo_lite_sf(address = "Brielle, NL", points_only = FALSE)
+bb <- sf::st_bbox(nominatim_polygon)
+x <- opq(bbox = bb) %>%
+ add_osm_feature(key = 'building') %>%
+ osmdata_sf()
+buildings <- x$osm_polygons %>%
+ st_transform(.,crs=28992)
+
+
+summary(buildings$start_date)
+
+
+old <- 1800 # year prior to which you consider a building old
+
+buildings$start_date <- as.numeric(buildings$start_date)
+
+old_buildings <- buildings %>%
+ filter(start_date <= old)
+
+ ggplot(data = old_buildings) + geom_sf(colour="red")
+
+```
+
+::::::::::::::::::::::::::::::::::::: callout
+
+If you encounter an error linked to your internet proxy ("Error: Overpass query unavailable without internet R"), run this line of code. It might not be needed, but ensures that your machine knows it has internet.
+
+```{r}
+assign("has_internet_via_proxy", TRUE, environment(curl::has_internet))
+```
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
+As conservationists, we want to create a zone around historical buildings where building regulation will have special restrictions to preserve historical buildings.
+
+## Buffers
+
+Let's say the conservation zone should be 100 meters. In GIS terms, we want to create a _buffer_ around polygons. The corresponding `sf` function is `st_buffer()`, with 2 arguments: the polygons around which to create buffers, and the radius of the buffer.
+
+```{r}
+distance <- 100 # in meters
+
+#First, we check that the "old_buildings" layer projection is measured in meters:
+st_crs(old_buildings)
+
+#then we use `st_buffer()`
+buffer_old_buildings <-
+ st_buffer(x = old_buildings, dist = distance)
+
+ggplot(data = buffer_old_buildings) + geom_sf()
+
+```
+
+## Union
+
+Now, we have a lot of overlapping buffers. We would rather create a unique conservation zone rather than overlapping ones in that case. So we have to fuse the overlapping buffers into one polygon. This operation is called _union_ and the corresponding function is `st_union()`.
+
+```{r}
+single_old_buffer <- st_union(buffer_old_buildings) %>%
+ st_cast(to = "POLYGON") %>%
+ st_as_sf()
+
+single_old_buffer<- single_old_buffer %>%
+ mutate("ID"=as.factor(1:nrow(single_old_buffer))) %>%
+ st_transform(.,crs=28992)
+
+
+```
+
+We also use `st_cast()` to explicit the type of the resulting object (*POLYGON* instead of the default *MULTIPOLYGON*) and `st_as_sf()` to transform the polygon into an `sf` object. With this function, we ensure that we end up with an `sf` object, which was not the case after we forced the union of old buildings into a *POLYGON* format.
+
+We create unique IDs to identify the new polygons.
+
+## Centroids
+For the sake of visualisation speed, we would like to represent buildings by a single point (for instance: their geometric centre) rather than their actual footprint. This operation means defining their _centroid_ and the corresponding function is `st_centroid()`.
+
+```{r}
+sf::sf_use_s2(FALSE) # s2 works with geographic projections, so to calculate centroids in projected CRS units (meters), we need to disable it.
+
+centroids_old <- st_centroid(old_buildings) %>%
+ st_transform(.,crs=28992)
+
+ggplot() +
+ geom_sf(data = single_old_buffer, aes(fill=ID)) +
+ geom_sf(data = centroids_old)
+```
+
+## Intersection
+Now, we would like to distinguish conservation areas based on the number of historic buildings they contain. In GIS terms, we would like to know how many centroids each fused buffer polygon contains. This operation means _intersecting_ the layer of polygons with the layer of points and the corresponding function is `st_intersection()`.
+
+
+```{r}
+ centroids_buffers <-
+ st_intersection(centroids_old,single_old_buffer) %>%
+ mutate(n = 1)
+
+ centroid_by_buffer <- centroids_buffers %>%
+ group_by(ID) %>%
+ summarise(n = sum(n))
+
+ single_buffer <- single_old_buffer %>%
+ mutate(n_buildings = centroid_by_buffer$n)
+
+
+ ggplot() +
+ geom_sf(data = single_buffer, aes(fill = n_buildings)) +
+ scale_fill_viridis_c(alpha = 0.8,
+ begin = 0.6,
+ end = 1,
+ direction = -1,
+ option = "B")
+```
+
+`st_intersection` here adds the attributes of the intersected polygon buffers to the data table of the centroids. This means we will now know about each centroid, the ID of its intersected polygon-buffer, and a variable called "n" which is population with 1 for everyone. This means that all centroids will have the same weight when aggregated.
+
+We aggregate them by ID number (`group_by(ID)`) and sum the variable `n` to know how many centroids are contained in each polygon-buffer.
+
+### Final output:
+
+Let's map this layer over the initial map of individual buildings.
+
+```{r}
+ggplot() +
+ geom_sf(data = buildings) +
+ geom_sf(data = single_buffer, aes(fill=n_buildings), colour = NA) +
+ scale_fill_viridis_c(alpha = 0.6,
+ begin = 0.6,
+ end = 1,
+ direction = -1,
+ option = "B")
+
+```
+
+::::::::::::::::::::::::::::::::::::: challenge
+
+## Challenge: Conservation rules have changed.
+
+The historical threshold now applies to all pre-war buildings, but the distance to these building is reduced to 10m. Can you map the number of all buildings per 10m fused buffer?
+
+
+:::::::::::::::::::::::: solution
+
+```{r}
+
+old <- 1939
+distance <- 10
+
+# select
+old_buildings <- buildings %>%
+ filter(start_date <= old)
+
+# buffer
+buffer_old_buildings <- st_buffer(old_buildings, dist = distance)
+
+# union
+single_old_buffer <- st_union(buffer_old_buildings) %>%
+ st_cast(to = "POLYGON") %>%
+ st_as_sf()
+
+single_old_buffer <- single_old_buffer %>%
+ mutate("ID"=1:nrow(single_old_buffer)) %>%
+ st_transform(single_old_buffer,crs=4326)
+
+# centroids
+centroids_old <- st_centroid(old_buildings) %>%
+ st_transform(.,crs=4326)
+
+# intersection
+centroids_buffers <- st_intersection(centroids_old,single_old_buffer) %>%
+ mutate(n=1)
+
+centroid_by_buffer <- centroids_buffers %>%
+ group_by(ID) %>%
+ summarise(n = sum(n))
+
+single_buffer <- single_old_buffer %>%
+ mutate(n_buildings = centroid_by_buffer$n)
+ ggplot() +
+ geom_sf(data = buildings) +
+ geom_sf(data = single_buffer, aes(fill = n_buildings), colour = NA) +
+ scale_fill_viridis_c(alpha = 0.6,
+ begin = 0.6,
+ end = 1,
+ direction = -1,
+ option = "B")
+```
+::::::::::::::::::::::::
+
+
+:::::::::::::::::::::::::::::::::::::
+
+
+*Problem: there are many pre-war buildings and the buffers are large so the number of old buildings is not very meaningful. Let's compute the density of old buildings per buffer zone.*
+
+## Area
+
+```{r}
+single_buffer$area <- sf::st_area(single_buffer) %>%
+ units::set_units(., km^2)
+
+single_buffer$old_buildings_per_km2 <- as.numeric(single_buffer$n_buildings / single_buffer$area)
+
+ ggplot() +
+ geom_sf(data = buildings) +
+ geom_sf(data = single_buffer, aes(fill=old_buildings_per_km2), colour = NA) +
+ scale_fill_viridis_c(alpha = 0.6,
+ begin = 0.6,
+ end = 1,
+ direction = -1,
+ option = "B")
+
+```
+
+
+
+
+::::::::::::::::::::::::::::::::::::: keypoints
+
+- Use the `st_*` functions from `sf` for basic GIS operations
+- Perform unions, joins and intersection operations
+- Compute the area of spatial polygons with `st_area()`
+
+::::::::::::::::::::::::::::::::::::::::::::::::
+
diff --git a/episodes/data/boundingboxBrielle.dbf b/episodes/data/boundingboxBrielle.dbf
new file mode 100644
index 00000000..db06d1fd
Binary files /dev/null and b/episodes/data/boundingboxBrielle.dbf differ
diff --git a/episodes/data/boundingboxBrielle.prj b/episodes/data/boundingboxBrielle.prj
new file mode 100644
index 00000000..f45cbadf
--- /dev/null
+++ b/episodes/data/boundingboxBrielle.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/episodes/data/boundingboxBrielle.shp b/episodes/data/boundingboxBrielle.shp
new file mode 100644
index 00000000..788a02af
Binary files /dev/null and b/episodes/data/boundingboxBrielle.shp differ
diff --git a/episodes/data/boundingboxBrielle.shx b/episodes/data/boundingboxBrielle.shx
new file mode 100644
index 00000000..1fb012ab
Binary files /dev/null and b/episodes/data/boundingboxBrielle.shx differ
diff --git a/episodes/data/dataBrielle.dbf b/episodes/data/dataBrielle.dbf
new file mode 100644
index 00000000..f76dd238
Binary files /dev/null and b/episodes/data/dataBrielle.dbf differ
diff --git a/episodes/data/dataBrielle.prj b/episodes/data/dataBrielle.prj
new file mode 100644
index 00000000..32540b1f
--- /dev/null
+++ b/episodes/data/dataBrielle.prj
@@ -0,0 +1 @@
+PROJCS["RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Double_Stereographic"],PARAMETER["False_Easting",155000.0],PARAMETER["False_Northing",463000.0],PARAMETER["Central_Meridian",5.38763888888889],PARAMETER["Scale_Factor",0.9999079],PARAMETER["Latitude_Of_Origin",52.1561605555556],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/episodes/data/dataBrielle.shp b/episodes/data/dataBrielle.shp
new file mode 100644
index 00000000..3d95e38a
Binary files /dev/null and b/episodes/data/dataBrielle.shp differ
diff --git a/episodes/data/dataBrielle.shx b/episodes/data/dataBrielle.shx
new file mode 100644
index 00000000..3d95e38a
Binary files /dev/null and b/episodes/data/dataBrielle.shx differ
diff --git a/episodes/fig/.DS_Store b/episodes/fig/.DS_Store
new file mode 100644
index 00000000..5008ddfc
Binary files /dev/null and b/episodes/fig/.DS_Store differ
diff --git a/episodes/fig/.gitkeep b/episodes/fig/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/episodes/fig/Brielle_NJ.jpeg b/episodes/fig/Brielle_NJ.jpeg
new file mode 100644
index 00000000..0f806bfd
Binary files /dev/null and b/episodes/fig/Brielle_NJ.jpeg differ
diff --git a/episodes/fig/Brielle_NL.jpeg b/episodes/fig/Brielle_NL.jpeg
new file mode 100644
index 00000000..b41776d7
Binary files /dev/null and b/episodes/fig/Brielle_NL.jpeg differ
diff --git a/episodes/fig/BufferCircular.png b/episodes/fig/BufferCircular.png
new file mode 100644
index 00000000..967efac8
Binary files /dev/null and b/episodes/fig/BufferCircular.png differ
diff --git a/episodes/fig/BufferSquare.png b/episodes/fig/BufferSquare.png
new file mode 100644
index 00000000..883c2626
Binary files /dev/null and b/episodes/fig/BufferSquare.png differ
diff --git a/episodes/fig/OSM1.png b/episodes/fig/OSM1.png
new file mode 100644
index 00000000..cb57dfbf
Binary files /dev/null and b/episodes/fig/OSM1.png differ
diff --git a/episodes/fig/OSM2.png b/episodes/fig/OSM2.png
new file mode 100644
index 00000000..621b4e6d
Binary files /dev/null and b/episodes/fig/OSM2.png differ
diff --git a/episodes/fig/opq.png b/episodes/fig/opq.png
new file mode 100644
index 00000000..5eed3708
Binary files /dev/null and b/episodes/fig/opq.png differ
diff --git a/episodes/fig/osmdata.png b/episodes/fig/osmdata.png
new file mode 100644
index 00000000..e86b9da7
Binary files /dev/null and b/episodes/fig/osmdata.png differ
diff --git a/episodes/fig/rmd-04a-map-age-1.png b/episodes/fig/rmd-04a-map-age-1.png
new file mode 100644
index 00000000..b179e8c3
Binary files /dev/null and b/episodes/fig/rmd-04a-map-age-1.png differ
diff --git a/episodes/fig/rmd-04area-1.pdf b/episodes/fig/rmd-04area-1.pdf
new file mode 100644
index 00000000..ff3f64ad
Binary files /dev/null and b/episodes/fig/rmd-04area-1.pdf differ
diff --git a/episodes/fig/rmd-04area-1.png b/episodes/fig/rmd-04area-1.png
new file mode 100644
index 00000000..0ff611cf
Binary files /dev/null and b/episodes/fig/rmd-04area-1.png differ
diff --git a/episodes/fig/rmd-04buffers-1.pdf b/episodes/fig/rmd-04buffers-1.pdf
new file mode 100644
index 00000000..5c77ca6a
Binary files /dev/null and b/episodes/fig/rmd-04buffers-1.pdf differ
diff --git a/episodes/fig/rmd-04buffers-1.png b/episodes/fig/rmd-04buffers-1.png
new file mode 100644
index 00000000..663a3e07
Binary files /dev/null and b/episodes/fig/rmd-04buffers-1.png differ
diff --git a/episodes/fig/rmd-04centroids-1.pdf b/episodes/fig/rmd-04centroids-1.pdf
new file mode 100644
index 00000000..8b65489b
Binary files /dev/null and b/episodes/fig/rmd-04centroids-1.pdf differ
diff --git a/episodes/fig/rmd-04centroids-1.png b/episodes/fig/rmd-04centroids-1.png
new file mode 100644
index 00000000..60e2aeb4
Binary files /dev/null and b/episodes/fig/rmd-04centroids-1.png differ
diff --git a/episodes/fig/rmd-04conservation-1.pdf b/episodes/fig/rmd-04conservation-1.pdf
new file mode 100644
index 00000000..6e199145
Binary files /dev/null and b/episodes/fig/rmd-04conservation-1.pdf differ
diff --git a/episodes/fig/rmd-04conservation-1.png b/episodes/fig/rmd-04conservation-1.png
new file mode 100644
index 00000000..77942d5b
Binary files /dev/null and b/episodes/fig/rmd-04conservation-1.png differ
diff --git a/episodes/fig/rmd-04intersection-1.pdf b/episodes/fig/rmd-04intersection-1.pdf
new file mode 100644
index 00000000..f6847696
Binary files /dev/null and b/episodes/fig/rmd-04intersection-1.pdf differ
diff --git a/episodes/fig/rmd-04intersection-1.png b/episodes/fig/rmd-04intersection-1.png
new file mode 100644
index 00000000..c6ac73cf
Binary files /dev/null and b/episodes/fig/rmd-04intersection-1.png differ
diff --git a/episodes/fig/rmd-04map-age-1.pdf b/episodes/fig/rmd-04map-age-1.pdf
new file mode 100644
index 00000000..fd282879
Binary files /dev/null and b/episodes/fig/rmd-04map-age-1.pdf differ
diff --git a/episodes/fig/rmd-04map-age-1.png b/episodes/fig/rmd-04map-age-1.png
new file mode 100644
index 00000000..979a50f9
Binary files /dev/null and b/episodes/fig/rmd-04map-age-1.png differ
diff --git a/episodes/fig/rmd-04prewar-replication-1.pdf b/episodes/fig/rmd-04prewar-replication-1.pdf
new file mode 100644
index 00000000..dc3875c0
Binary files /dev/null and b/episodes/fig/rmd-04prewar-replication-1.pdf differ
diff --git a/episodes/fig/rmd-04prewar-replication-1.png b/episodes/fig/rmd-04prewar-replication-1.png
new file mode 100644
index 00000000..7cb5261e
Binary files /dev/null and b/episodes/fig/rmd-04prewar-replication-1.png differ
diff --git a/episodes/fig/rmd-04visu-1800-1.pdf b/episodes/fig/rmd-04visu-1800-1.pdf
new file mode 100644
index 00000000..bdb7f1af
Binary files /dev/null and b/episodes/fig/rmd-04visu-1800-1.pdf differ
diff --git a/episodes/fig/rmd-04visu-1800-1.png b/episodes/fig/rmd-04visu-1800-1.png
new file mode 100644
index 00000000..7a05df74
Binary files /dev/null and b/episodes/fig/rmd-04visu-1800-1.png differ
diff --git a/episodes/fig/sf_cheatsheet.pdf b/episodes/fig/sf_cheatsheet.pdf
new file mode 100644
index 00000000..4bb87b6d
Binary files /dev/null and b/episodes/fig/sf_cheatsheet.pdf differ
diff --git a/episodes/fig/spatial_extent.png b/episodes/fig/spatial_extent.png
new file mode 100644
index 00000000..e8407385
Binary files /dev/null and b/episodes/fig/spatial_extent.png differ
diff --git a/episodes/fig/tudlib-green.png b/episodes/fig/tudlib-green.png
new file mode 100644
index 00000000..e76a6278
Binary files /dev/null and b/episodes/fig/tudlib-green.png differ
diff --git a/instructors/4-gis-slides.html b/instructors/4-gis-slides.html
new file mode 100644
index 00000000..cd154305
--- /dev/null
+++ b/instructors/4-gis-slides.html
@@ -0,0 +1,1633 @@
+
+
There might be multiple responses from the API query, corresponding to different objects at the same location, or different objects at different locations.
+
For example: or
+
+
+
Which Brielle?
+
By default, nominatimlite::geo_lite_sf() returns the first item.
+
Try to be as unambiguous as possible by adding a country code or district name.
A feature in the OSM language is a category or tag of a geospatial object.
+
Features are described by general keys (e.g. “building”, “boundary”, “landuse”, “highway”), themselves decomposed into sub-categories (values) such as “farm”, “hotel” or “house” for buildings, “motorway”, “secondary” and “residential” for highway. This determines how they are represented on the map.
+
+
+
Searching the documentation
+
+
+
Let’s say we want to download data from OpenStreetMap and we know there is a package for it named osmdata, but we don’t know which function to use and what arguments are needed. Where should we start?
It is a table of all the buildings contained in the bounding box, which gives us their OSM id, their geometry and a range of attributes, such as their name, building material, building date, etc.
+
The completion level of this table depends on user contributions and open resources (here for instance: BAG, different in other countries).
+
+
+
+
Mapping
+
Case Study: we are interested in mapping building age in Brielle. We need to look into specific attributes:
+
+
the age of buildings
+
with a subset of buildings from after 1900
+
within the Brielle bounding box
+
+
+
+
Projections
+
+
First, we are going to select the polygons and reproject them with the Amersfoort/RD New projection, suited for maps centred on the Netherlands.
Transform the buildings into WGS84 projection and add them to the basemap layer with the addPolygons function.
+
Have the fillColor of these polygons represent the build_date variable. See the choropleth documentation for use of colors. Tip: use the examples given in the documentation and replace the variable names where needed.
Let’s focus on old buildings and imagine we’re in charge of their conservation. We want to know how much of the city would be affected by a non-construction zone of 100m around pre-1800 buildings.
+
Let’s select them and see where they are.
+
+
+
Conservation in Brielle, NL 🏫
+
+
old <-1800# year prior to which you consider a building old
+
+
+nominatim_polygon <- nominatimlite::geo_lite_sf(
+address ="Brielle, NL", points_only =FALSE)
+bb <- sf::st_bbox(nominatim_polygon)
+x <-opq(bbox = bb) %>%
+add_osm_feature(key ='building') %>%
+osmdata_sf()
+buildings <- x$osm_polygons %>%
+st_transform(.,crs=28992)
+
+
+summary(buildings$start_date)
As conservationists, we want to create a zone around historical buildings where building regulation will have special restrictions to preserve historical buildings.
+
+
+
Buffer
+
Let’s say this zone should be 100 meters. In GIS terms, we want to create a buffer around polygons. The corresponding function sf is st_buffer, with 2 arguments:
+
+
“x”: the polygons around which to create buffers
+
“dist”: the radius of the buffer.
+
+
+
+
Buffer
+
+
distance <-100# in meters
+
+# First, we check that the "old_buildings"
+# layer projection is measured in meters:
+st_crs(old_buildings)
#then we use `st_buffer()`
+buffer_old_buildings <-
+st_buffer(x = old_buildings, dist = distance)
+
+ggplot(data = buffer_old_buildings) +geom_sf()
+
+
+
+
+
Union
+
Now, we have overlapping buffers.
+
To fuse (or dissolve) them into a unique conservation zone/polygon, we use sf::st_union with st_cast() to explicit the type of object (POLYGON instead of the default MULTIPOLYGON), and st_as_sf() to transform the polygon into an sf object.
We create unique IDs to identify the new polygons.
+
+
+
Centroids
+
For speed, we might want to represent buildings by a single point (for instance: their geometric centre) rather than footprint.
+
This operation means defining their centroid and the corresponding function is st_centroid.
+
+
+
Centroids
+
+
sf::sf_use_s2(FALSE) # s2 works with geographic projections, so to calculate centroids in projected CRS units (meters), we need to disable it.
+centroids_old <-st_centroid(old_buildings) %>%
+st_transform(.,crs=28992)
+
+ggplot() +
+geom_sf(data = single_old_buffer, aes(fill=ID)) +
+geom_sf(data = centroids_old)
+
+
+
+
+
Intersection
+
To distinguish conservation areas based on the number of historic buildings they contain, we need to know how many centroids each fused buffer polygon contains.
+
This means intersecting the layer of polygons with the layer of points. st_intersection adds the attributes of the intersected polygon buffers (e.g. ID) to the data table of the centroids.
+
+
+
Intersection
+
To distinguish conservation areas based on the number of historic buildings they contain, we need to know how many centroids each fused buffer polygon contains.
+
This means intersecting the layer of polygons with the layer of points. st_intersection adds the attributes of the intersected polygon buffers (e.g. ID) to the data table of the centroids.
+
We then aggregate them by ID number (group_by(ID)) and sum the variable n to know how many centroids are contained in each polygon-buffer.
Problem: there are many pre-war buildings and the buffers are large so the number of old buildings is not very meaningful. Let’s compute the density of old buildings per buffer zone.
+
+
+
+
+
+
+
+
+
⏰ Challenge: visualise the density of old buildings
+
+
+
+
+10:00
+
+
+
+
Let’s compute the density of old buildings per buffer zone (= number of buildings / area of buffer).
+
+
+
Solution
+
Let’s map this layer over the initial map of individual buildings.
We have taught you to think, type, try, test, read the documentation. This is not only the old fashion way, but the foundation.
+
When you encounter a bug, a challenge, a question that we have not covered, you could always make use of:
+
+
StackOverflow
+
chatGPT
+
< include name of future AI here >
+
+
+
+
🦜 A few words of caution
+
Be careful to keep it as a help, tool and support whose quality you can still assess.
+
They can provide fixes that you do not understand, answers that don’t make sense, and even wrong answers! So build yourself some foundations before you get into them.
+
+
+
🐰The end… wait!
+
+
Any questions?
+
+
+
Feedback
+
+
+
D: what we should drop because it’s not useful
+
A: what we should add because it’s missing
+
K: what we should keep because it’s well done
+
I: what we should improve because it’s not there yet
+
+
+
+
+
+
+
Thank you
+
+
the workshop team: Aleks, Ana, Claudiu, Clémentine, Daniele, Javi, Jérôme, Kyri, Manuel, Paula, Selin + TU Library & BK Faculty
+
you: the active, team-playing and motivated participants
+
+
+
+
Time for rewards
+
+
for PhD Candidates: some GS Credits!
+
for the competing teams…
+
+
+
+
Rbanism
+
+
+
A TUD community that promotes open-source software and open-science practices in the field of urbanism.
+
+
+
Join the community!
+
+
+
+
+
+
Call for helpers!
+
This workshop is offered on a yearly basis.
+
You are welcome to join the team for future events as helpers and/then as instructors.
+
This strengthens the community… and it can bring you GS Credits.
").addClass(errClass).css("position", "absolute")
+ .css("top", el.offsetTop)
+ .css("left", el.offsetLeft)
+ // setting width can push out the page size, forcing otherwise
+ // unnecessary scrollbars to appear and making it impossible for
+ // the element to shrink; so use max-width instead
+ .css("maxWidth", el.offsetWidth)
+ .css("height", el.offsetHeight);
+ errorDiv.text(err.message);
+ $el.after(errorDiv);
+
+ // Really dumb way to keep the size/position of the error in sync with
+ // the parent element as the window is resized or whatever.
+ var intId = setInterval(function() {
+ if (!errorDiv[0].parentElement) {
+ clearInterval(intId);
+ return;
+ }
+ errorDiv
+ .css("top", el.offsetTop)
+ .css("left", el.offsetLeft)
+ .css("maxWidth", el.offsetWidth)
+ .css("height", el.offsetHeight);
+ }, 500);
+ }
+ }
+ },
+ clearError: function(el) {
+ var $el = $(el);
+ var display = $el.data("restore-display-mode");
+ $el.data("restore-display-mode", null);
+
+ if (display === "inline" || display === "inline-block") {
+ if (display)
+ $el.css("display", display);
+ $(el.nextSibling).filter(".htmlwidgets-error").remove();
+ } else if (display === "block"){
+ $el.css("visibility", "inherit");
+ $(el.nextSibling).filter(".htmlwidgets-error").remove();
+ }
+ },
+ sizing: {}
+ };
+
+ // Called by widget bindings to register a new type of widget. The definition
+ // object can contain the following properties:
+ // - name (required) - A string indicating the binding name, which will be
+ // used by default as the CSS classname to look for.
+ // - initialize (optional) - A function(el) that will be called once per
+ // widget element; if a value is returned, it will be passed as the third
+ // value to renderValue.
+ // - renderValue (required) - A function(el, data, initValue) that will be
+ // called with data. Static contexts will cause this to be called once per
+ // element; Shiny apps will cause this to be called multiple times per
+ // element, as the data changes.
+ window.HTMLWidgets.widget = function(definition) {
+ if (!definition.name) {
+ throw new Error("Widget must have a name");
+ }
+ if (!definition.type) {
+ throw new Error("Widget must have a type");
+ }
+ // Currently we only support output widgets
+ if (definition.type !== "output") {
+ throw new Error("Unrecognized widget type '" + definition.type + "'");
+ }
+ // TODO: Verify that .name is a valid CSS classname
+
+ // Support new-style instance-bound definitions. Old-style class-bound
+ // definitions have one widget "object" per widget per type/class of
+ // widget; the renderValue and resize methods on such widget objects
+ // take el and instance arguments, because the widget object can't
+ // store them. New-style instance-bound definitions have one widget
+ // object per widget instance; the definition that's passed in doesn't
+ // provide renderValue or resize methods at all, just the single method
+ // factory(el, width, height)
+ // which returns an object that has renderValue(x) and resize(w, h).
+ // This enables a far more natural programming style for the widget
+ // author, who can store per-instance state using either OO-style
+ // instance fields or functional-style closure variables (I guess this
+ // is in contrast to what can only be called C-style pseudo-OO which is
+ // what we required before).
+ if (definition.factory) {
+ definition = createLegacyDefinitionAdapter(definition);
+ }
+
+ if (!definition.renderValue) {
+ throw new Error("Widget must have a renderValue function");
+ }
+
+ // For static rendering (non-Shiny), use a simple widget registration
+ // scheme. We also use this scheme for Shiny apps/documents that also
+ // contain static widgets.
+ window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || [];
+ // Merge defaults into the definition; don't mutate the original definition.
+ var staticBinding = extend({}, defaults, definition);
+ overrideMethod(staticBinding, "find", function(superfunc) {
+ return function(scope) {
+ var results = superfunc(scope);
+ // Filter out Shiny outputs, we only want the static kind
+ return filterByClass(results, "html-widget-output", false);
+ };
+ });
+ window.HTMLWidgets.widgets.push(staticBinding);
+
+ if (shinyMode) {
+ // Shiny is running. Register the definition with an output binding.
+ // The definition itself will not be the output binding, instead
+ // we will make an output binding object that delegates to the
+ // definition. This is because we foolishly used the same method
+ // name (renderValue) for htmlwidgets definition and Shiny bindings
+ // but they actually have quite different semantics (the Shiny
+ // bindings receive data that includes lots of metadata that it
+ // strips off before calling htmlwidgets renderValue). We can't
+ // just ignore the difference because in some widgets it's helpful
+ // to call this.renderValue() from inside of resize(), and if
+ // we're not delegating, then that call will go to the Shiny
+ // version instead of the htmlwidgets version.
+
+ // Merge defaults with definition, without mutating either.
+ var bindingDef = extend({}, defaults, definition);
+
+ // This object will be our actual Shiny binding.
+ var shinyBinding = new Shiny.OutputBinding();
+
+ // With a few exceptions, we'll want to simply use the bindingDef's
+ // version of methods if they are available, otherwise fall back to
+ // Shiny's defaults. NOTE: If Shiny's output bindings gain additional
+ // methods in the future, and we want them to be overrideable by
+ // HTMLWidget binding definitions, then we'll need to add them to this
+ // list.
+ delegateMethod(shinyBinding, bindingDef, "getId");
+ delegateMethod(shinyBinding, bindingDef, "onValueChange");
+ delegateMethod(shinyBinding, bindingDef, "onValueError");
+ delegateMethod(shinyBinding, bindingDef, "renderError");
+ delegateMethod(shinyBinding, bindingDef, "clearError");
+ delegateMethod(shinyBinding, bindingDef, "showProgress");
+
+ // The find, renderValue, and resize are handled differently, because we
+ // want to actually decorate the behavior of the bindingDef methods.
+
+ shinyBinding.find = function(scope) {
+ var results = bindingDef.find(scope);
+
+ // Only return elements that are Shiny outputs, not static ones
+ var dynamicResults = results.filter(".html-widget-output");
+
+ // It's possible that whatever caused Shiny to think there might be
+ // new dynamic outputs, also caused there to be new static outputs.
+ // Since there might be lots of different htmlwidgets bindings, we
+ // schedule execution for later--no need to staticRender multiple
+ // times.
+ if (results.length !== dynamicResults.length)
+ scheduleStaticRender();
+
+ return dynamicResults;
+ };
+
+ // Wrap renderValue to handle initialization, which unfortunately isn't
+ // supported natively by Shiny at the time of this writing.
+
+ shinyBinding.renderValue = function(el, data) {
+ Shiny.renderDependencies(data.deps);
+ // Resolve strings marked as javascript literals to objects
+ if (!(data.evals instanceof Array)) data.evals = [data.evals];
+ for (var i = 0; data.evals && i < data.evals.length; i++) {
+ window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]);
+ }
+ if (!bindingDef.renderOnNullValue) {
+ if (data.x === null) {
+ el.style.visibility = "hidden";
+ return;
+ } else {
+ el.style.visibility = "inherit";
+ }
+ }
+ if (!elementData(el, "initialized")) {
+ initSizing(el);
+
+ elementData(el, "initialized", true);
+ if (bindingDef.initialize) {
+ var rect = el.getBoundingClientRect();
+ var result = bindingDef.initialize(el, rect.width, rect.height);
+ elementData(el, "init_result", result);
+ }
+ }
+ bindingDef.renderValue(el, data.x, elementData(el, "init_result"));
+ evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]);
+ };
+
+ // Only override resize if bindingDef implements it
+ if (bindingDef.resize) {
+ shinyBinding.resize = function(el, width, height) {
+ // Shiny can call resize before initialize/renderValue have been
+ // called, which doesn't make sense for widgets.
+ if (elementData(el, "initialized")) {
+ bindingDef.resize(el, width, height, elementData(el, "init_result"));
+ }
+ };
+ }
+
+ Shiny.outputBindings.register(shinyBinding, bindingDef.name);
+ }
+ };
+
+ var scheduleStaticRenderTimerId = null;
+ function scheduleStaticRender() {
+ if (!scheduleStaticRenderTimerId) {
+ scheduleStaticRenderTimerId = setTimeout(function() {
+ scheduleStaticRenderTimerId = null;
+ window.HTMLWidgets.staticRender();
+ }, 1);
+ }
+ }
+
+ // Render static widgets after the document finishes loading
+ // Statically render all elements that are of this widget's class
+ window.HTMLWidgets.staticRender = function() {
+ var bindings = window.HTMLWidgets.widgets || [];
+ forEach(bindings, function(binding) {
+ var matches = binding.find(document.documentElement);
+ forEach(matches, function(el) {
+ var sizeObj = initSizing(el, binding);
+
+ var getSize = function(el) {
+ if (sizeObj) {
+ return {w: sizeObj.getWidth(), h: sizeObj.getHeight()}
+ } else {
+ var rect = el.getBoundingClientRect();
+ return {w: rect.width, h: rect.height}
+ }
+ };
+
+ if (hasClass(el, "html-widget-static-bound"))
+ return;
+ el.className = el.className + " html-widget-static-bound";
+
+ var initResult;
+ if (binding.initialize) {
+ var size = getSize(el);
+ initResult = binding.initialize(el, size.w, size.h);
+ elementData(el, "init_result", initResult);
+ }
+
+ if (binding.resize) {
+ var lastSize = getSize(el);
+ var resizeHandler = function(e) {
+ var size = getSize(el);
+ if (size.w === 0 && size.h === 0)
+ return;
+ if (size.w === lastSize.w && size.h === lastSize.h)
+ return;
+ lastSize = size;
+ binding.resize(el, size.w, size.h, initResult);
+ };
+
+ on(window, "resize", resizeHandler);
+
+ // This is needed for cases where we're running in a Shiny
+ // app, but the widget itself is not a Shiny output, but
+ // rather a simple static widget. One example of this is
+ // an rmarkdown document that has runtime:shiny and widget
+ // that isn't in a render function. Shiny only knows to
+ // call resize handlers for Shiny outputs, not for static
+ // widgets, so we do it ourselves.
+ if (window.jQuery) {
+ window.jQuery(document).on(
+ "shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets",
+ resizeHandler
+ );
+ window.jQuery(document).on(
+ "hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets",
+ resizeHandler
+ );
+ }
+
+ // This is needed for the specific case of ioslides, which
+ // flips slides between display:none and display:block.
+ // Ideally we would not have to have ioslide-specific code
+ // here, but rather have ioslides raise a generic event,
+ // but the rmarkdown package just went to CRAN so the
+ // window to getting that fixed may be long.
+ if (window.addEventListener) {
+ // It's OK to limit this to window.addEventListener
+ // browsers because ioslides itself only supports
+ // such browsers.
+ on(document, "slideenter", resizeHandler);
+ on(document, "slideleave", resizeHandler);
+ }
+ }
+
+ var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']");
+ if (scriptData) {
+ var data = JSON.parse(scriptData.textContent || scriptData.text);
+ // Resolve strings marked as javascript literals to objects
+ if (!(data.evals instanceof Array)) data.evals = [data.evals];
+ for (var k = 0; data.evals && k < data.evals.length; k++) {
+ window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]);
+ }
+ binding.renderValue(el, data.x, initResult);
+ evalAndRun(data.jsHooks.render, initResult, [el, data.x]);
+ }
+ });
+ });
+
+ invokePostRenderHandlers();
+ }
+
+
+ function has_jQuery3() {
+ if (!window.jQuery) {
+ return false;
+ }
+ var $version = window.jQuery.fn.jquery;
+ var $major_version = parseInt($version.split(".")[0]);
+ return $major_version >= 3;
+ }
+
+ /*
+ / Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
+ / on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
+ / really means $(setTimeout(fn)).
+ / https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
+ /
+ / Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
+ / one tick later than it did before, which means staticRender() is
+ / called renderValue() earlier than (advanced) widget authors might be expecting.
+ / https://github.com/rstudio/shiny/issues/2630
+ /
+ / For a concrete example, leaflet has some methods (e.g., updateBounds)
+ / which reference Shiny methods registered in initShiny (e.g., setInputValue).
+ / Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
+ / delay execution of those methods (until Shiny methods are ready)
+ / https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
+ /
+ / Ideally widget authors wouldn't need to use this setTimeout() hack that
+ / leaflet uses to call Shiny methods on a staticRender(). In the long run,
+ / the logic initShiny should be broken up so that method registration happens
+ / right away, but binding happens later.
+ */
+ function maybeStaticRenderLater() {
+ if (shinyMode && has_jQuery3()) {
+ window.jQuery(window.HTMLWidgets.staticRender);
+ } else {
+ window.HTMLWidgets.staticRender();
+ }
+ }
+
+ if (document.addEventListener) {
+ document.addEventListener("DOMContentLoaded", function() {
+ document.removeEventListener("DOMContentLoaded", arguments.callee, false);
+ maybeStaticRenderLater();
+ }, false);
+ } else if (document.attachEvent) {
+ document.attachEvent("onreadystatechange", function() {
+ if (document.readyState === "complete") {
+ document.detachEvent("onreadystatechange", arguments.callee);
+ maybeStaticRenderLater();
+ }
+ });
+ }
+
+
+ window.HTMLWidgets.getAttachmentUrl = function(depname, key) {
+ // If no key, default to the first item
+ if (typeof(key) === "undefined")
+ key = 1;
+
+ var link = document.getElementById(depname + "-" + key + "-attachment");
+ if (!link) {
+ throw new Error("Attachment " + depname + "/" + key + " not found in document");
+ }
+ return link.getAttribute("href");
+ };
+
+ window.HTMLWidgets.dataframeToD3 = function(df) {
+ var names = [];
+ var length;
+ for (var name in df) {
+ if (df.hasOwnProperty(name))
+ names.push(name);
+ if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") {
+ throw new Error("All fields must be arrays");
+ } else if (typeof(length) !== "undefined" && length !== df[name].length) {
+ throw new Error("All fields must be arrays of the same length");
+ }
+ length = df[name].length;
+ }
+ var results = [];
+ var item;
+ for (var row = 0; row < length; row++) {
+ item = {};
+ for (var col = 0; col < names.length; col++) {
+ item[names[col]] = df[names[col]][row];
+ }
+ results.push(item);
+ }
+ return results;
+ };
+
+ window.HTMLWidgets.transposeArray2D = function(array) {
+ if (array.length === 0) return array;
+ var newArray = array[0].map(function(col, i) {
+ return array.map(function(row) {
+ return row[i]
+ })
+ });
+ return newArray;
+ };
+ // Split value at splitChar, but allow splitChar to be escaped
+ // using escapeChar. Any other characters escaped by escapeChar
+ // will be included as usual (including escapeChar itself).
+ function splitWithEscape(value, splitChar, escapeChar) {
+ var results = [];
+ var escapeMode = false;
+ var currentResult = "";
+ for (var pos = 0; pos < value.length; pos++) {
+ if (!escapeMode) {
+ if (value[pos] === splitChar) {
+ results.push(currentResult);
+ currentResult = "";
+ } else if (value[pos] === escapeChar) {
+ escapeMode = true;
+ } else {
+ currentResult += value[pos];
+ }
+ } else {
+ currentResult += value[pos];
+ escapeMode = false;
+ }
+ }
+ if (currentResult !== "") {
+ results.push(currentResult);
+ }
+ return results;
+ }
+ // Function authored by Yihui/JJ Allaire
+ window.HTMLWidgets.evaluateStringMember = function(o, member) {
+ var parts = splitWithEscape(member, '.', '\\');
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var part = parts[i];
+ // part may be a character or 'numeric' member name
+ if (o !== null && typeof o === "object" && part in o) {
+ if (i == (l - 1)) { // if we are at the end of the line then evalulate
+ if (typeof o[part] === "string")
+ o[part] = tryEval(o[part]);
+ } else { // otherwise continue to next embedded object
+ o = o[part];
+ }
+ }
+ }
+ };
+
+ // Retrieve the HTMLWidget instance (i.e. the return value of an
+ // HTMLWidget binding's initialize() or factory() function)
+ // associated with an element, or null if none.
+ window.HTMLWidgets.getInstance = function(el) {
+ return elementData(el, "init_result");
+ };
+
+ // Finds the first element in the scope that matches the selector,
+ // and returns the HTMLWidget instance (i.e. the return value of
+ // an HTMLWidget binding's initialize() or factory() function)
+ // associated with that element, if any. If no element matches the
+ // selector, or the first matching element has no HTMLWidget
+ // instance associated with it, then null is returned.
+ //
+ // The scope argument is optional, and defaults to window.document.
+ window.HTMLWidgets.find = function(scope, selector) {
+ if (arguments.length == 1) {
+ selector = scope;
+ scope = document;
+ }
+
+ var el = scope.querySelector(selector);
+ if (el === null) {
+ return null;
+ } else {
+ return window.HTMLWidgets.getInstance(el);
+ }
+ };
+
+ // Finds all elements in the scope that match the selector, and
+ // returns the HTMLWidget instances (i.e. the return values of
+ // an HTMLWidget binding's initialize() or factory() function)
+ // associated with the elements, in an array. If elements that
+ // match the selector don't have an associated HTMLWidget
+ // instance, the returned array will contain nulls.
+ //
+ // The scope argument is optional, and defaults to window.document.
+ window.HTMLWidgets.findAll = function(scope, selector) {
+ if (arguments.length == 1) {
+ selector = scope;
+ scope = document;
+ }
+
+ var nodes = scope.querySelectorAll(selector);
+ var results = [];
+ for (var i = 0; i < nodes.length; i++) {
+ results.push(window.HTMLWidgets.getInstance(nodes[i]));
+ }
+ return results;
+ };
+
+ var postRenderHandlers = [];
+ function invokePostRenderHandlers() {
+ while (postRenderHandlers.length) {
+ var handler = postRenderHandlers.shift();
+ if (handler) {
+ handler();
+ }
+ }
+ }
+
+ // Register the given callback function to be invoked after the
+ // next time static widgets are rendered.
+ window.HTMLWidgets.addPostRenderHandler = function(callback) {
+ postRenderHandlers.push(callback);
+ };
+
+ // Takes a new-style instance-bound definition, and returns an
+ // old-style class-bound definition. This saves us from having
+ // to rewrite all the logic in this file to accomodate both
+ // types of definitions.
+ function createLegacyDefinitionAdapter(defn) {
+ var result = {
+ name: defn.name,
+ type: defn.type,
+ initialize: function(el, width, height) {
+ return defn.factory(el, width, height);
+ },
+ renderValue: function(el, x, instance) {
+ return instance.renderValue(x);
+ },
+ resize: function(el, width, height, instance) {
+ return instance.resize(width, height);
+ }
+ };
+
+ if (defn.find)
+ result.find = defn.find;
+ if (defn.renderError)
+ result.renderError = defn.renderError;
+ if (defn.clearError)
+ result.clearError = defn.clearError;
+
+ return result;
+ }
+})();
diff --git a/instructors/4-gis-slides_files/libs/jquery-1.12.4/jquery.min.js b/instructors/4-gis-slides_files/libs/jquery-1.12.4/jquery.min.js
new file mode 100644
index 00000000..e8364758
--- /dev/null
+++ b/instructors/4-gis-slides_files/libs/jquery-1.12.4/jquery.min.js
@@ -0,0 +1,5 @@
+/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0;
+}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="